@@ -151,18 +151,24 @@ fn extract_metavariable<'a>(
151151 }
152152}
153153
154+ #[ derive( Debug ) ]
155+ struct ReferencedMetavar {
156+ /// unsafe { $v; };
157+ /// ^^
158+ span : Span ,
159+ /// unsafe { $v; };
160+ /// ^^^^^^^^
161+ unsafe_block_span : Span ,
162+ }
163+
164+ #[ derive( Debug ) ]
154165enum MetavarState {
155- /// The metavariable was referenced inside of an unsafe block
166+ /// The metavariable was referenced inside of an unsafe block.
156167 Referenced {
157- /// unsafe { $v }
158- /// ^^
159- reference_span : Span ,
160- /// macro_rules! x { (... $v:expr ...) => {} }
168+ occurences : Vec < ReferencedMetavar > ,
169+ /// macro_rules! x { (... $x:expr ...) => {} }
161170 /// ^^^^^^^
162171 decl_span : Span ,
163- /// unsafe { $v }
164- /// ^^^^^^^^
165- unsafe_bl_span : Span ,
166172 } ,
167173
168174 /// The metavariable was not found yet, but we're still looking for it
@@ -209,6 +215,29 @@ impl UnsafeMetavariableFinder {
209215 self . enclosing . clear ( ) ;
210216 }
211217
218+ fn report_warnings ( & self , cx : & LateContext < ' _ > ) {
219+ for metavar in self . expr_variables . values ( ) {
220+ if let MetavarState :: Referenced { decl_span, occurences } = metavar {
221+ for occurence in occurences {
222+ span_lint_and_then (
223+ cx,
224+ EXPR_METAVARS_IN_UNSAFE ,
225+ occurence. span ,
226+ "expanding an expression metavariable in an unsafe block" ,
227+ |diag| {
228+ diag. span_note ( * decl_span, "metavariable declared here" ) ;
229+ diag. span_note ( occurence. unsafe_block_span , "unsafe block entered here" ) ;
230+ diag. help ( "consider expanding it outside of this block, e.g. by storing it in a variable" ) ;
231+ diag. note (
232+ "this allows the user of the macro to write unsafe code without an unsafe block (at the callsite)" ,
233+ ) ;
234+ } ,
235+ ) ;
236+ }
237+ }
238+ }
239+ }
240+
212241 /// Given the following delimited token stream:
213242 /// ```ignore
214243 /// macro_rules! x { (... $var:expr ...) => { ... } }
@@ -258,41 +287,47 @@ impl UnsafeMetavariableFinder {
258287 /// }
259288 /// ```
260289 /// (statics don't have any extra rules so they are also handled here)
261- fn find_metavariables_in_const_static < ' b > ( & mut self , tts : & mut Peekable < impl Iterator < Item = & ' b TokenTree > > ) {
290+ ///
291+ /// Also note that this might be a const generic parameter and not a const item, which only
292+ /// spans until the next comma, or right angle bracket:
293+ /// ```ignore
294+ /// struct T<const N: usize = { $v }, const O: usize = 1>;
295+ /// ^^^^^^^^^^^^^^^^^ `,` ends the const generic
296+ /// struct T<const N: usize = { $v }>;
297+ /// ^^^^^^^^^^^^^^^^^ `>` ends the const generic
298+ /// ```
299+ /// Returns the terminating token. This is either `,` `;` or `>` as explained above
300+ fn find_metavariables_in_const_static < ' b > (
301+ & mut self ,
302+ tts : & mut Peekable < impl Iterator < Item = & ' b TokenTree > > ,
303+ ) -> Option < TokenKind > {
262304 let mut angle_bracket_depth = 0u32 ;
263305
306+ let mut last_token = None ;
264307 self . enclosing . push ( EnclosingNode :: Body ) ;
265308 for tt in tts {
266309 match tt {
267310 TokenTree :: Delimited ( .., body) => self . find_metavariables_in_body ( body) ,
268- TokenTree :: Token (
269- Token {
270- kind : TokenKind :: Semi , ..
271- } ,
272- _,
273- ) => {
274- // We might be in a const generic position: don't break early for the first semicolon in
275- // `const _: Ty<{ 2; 1 }> = ...;`
276- if angle_bracket_depth == 0 {
277- break ;
311+ TokenTree :: Token ( token, _) => {
312+ last_token = Some ( token. kind . clone ( ) ) ;
313+ match token. kind {
314+ // We might be in a const generic position: don't break early for the first comma in
315+ // `const _: Ty<1, 2> = ...,`
316+ TokenKind :: Semi | TokenKind :: Comma if angle_bracket_depth == 0 => {
317+ break ;
318+ } ,
319+ TokenKind :: Lt => angle_bracket_depth += 1 ,
320+ // If we see a `>` without having seen any `<`, it must mean that we are in a const
321+ // generic position and this ends it.
322+ TokenKind :: Gt if angle_bracket_depth == 0 => break ,
323+ TokenKind :: Gt => angle_bracket_depth -= 1 ,
324+ _ => { } ,
278325 }
279326 } ,
280- TokenTree :: Token (
281- Token {
282- kind : TokenKind :: Lt , ..
283- } ,
284- _,
285- ) => angle_bracket_depth += 1 ,
286- TokenTree :: Token (
287- Token {
288- kind : TokenKind :: Gt , ..
289- } ,
290- _,
291- ) => angle_bracket_depth = angle_bracket_depth. saturating_sub ( 1 ) ,
292- TokenTree :: Token ( ..) => { } ,
293327 }
294328 }
295329 self . enclosing . pop ( ) ;
330+ last_token
296331 }
297332
298333 /// ```ignore
@@ -312,11 +347,11 @@ impl UnsafeMetavariableFinder {
312347 let mut angle_bracket_depth = 0u32 ;
313348
314349 self . enclosing . push ( EnclosingNode :: Body ) ;
315- for tt in tts {
350+ while let Some ( tt ) = tts. next ( ) {
316351 match tt {
317352 TokenTree :: Delimited ( .., Delimiter :: Brace , body) => {
318353 self . find_metavariables_in_body ( body) ;
319- // We might be in a const generic position:
354+ // We might be in a const generic argument position:
320355 // fn() -> Ty<{ ... }> {}
321356 // ^^^^^^^
322357 // which we know if angle_bracket_depth > 0,
@@ -326,19 +361,21 @@ impl UnsafeMetavariableFinder {
326361 break ;
327362 }
328363 } ,
329- TokenTree :: Token (
330- Token {
331- kind : TokenKind :: Lt , ..
332- } ,
333- _,
334- ) => angle_bracket_depth += 1 ,
335- TokenTree :: Token (
336- Token {
337- kind : TokenKind :: Gt , ..
338- } ,
339- _,
340- ) => angle_bracket_depth = angle_bracket_depth. saturating_sub ( 1 ) ,
341- _ => { } ,
364+ TokenTree :: Token ( token, _) => {
365+ match token. kind {
366+ TokenKind :: Ident ( kw:: Const , false ) => {
367+ // fn ...<const
368+ if let Some ( TokenKind :: Gt ) = self . find_metavariables_in_const_static ( tts) {
369+ // should not underflow, but macros don't have to be valid syntax if unexpanded
370+ angle_bracket_depth = angle_bracket_depth. saturating_sub ( 1 ) ;
371+ }
372+ } ,
373+ TokenKind :: Lt => angle_bracket_depth += 1 ,
374+ TokenKind :: Gt => angle_bracket_depth = angle_bracket_depth. saturating_sub ( 1 ) ,
375+ _ => { } ,
376+ }
377+ } ,
378+ TokenTree :: Delimited ( ..) => { } ,
342379 }
343380 }
344381 self . enclosing . pop ( ) ;
@@ -377,18 +414,25 @@ impl UnsafeMetavariableFinder {
377414 EnclosingNode :: UnsafeBlock ( span) => Some ( * span) ,
378415 } ) ;
379416
380- match ( & * state, unsafe_block_span) {
381- ( MetavarState :: Referenced { .. } , Some ( _) ) => { } ,
417+ match ( & mut * state, unsafe_block_span) {
418+ ( MetavarState :: Referenced { occurences, .. } , Some ( unsafe_block_span) ) => {
419+ occurences. push ( ReferencedMetavar {
420+ span,
421+ unsafe_block_span,
422+ } ) ;
423+ } ,
382424 ( _, None ) => {
383425 // Metavar was referenced outside of an unsafe block. Set to
384426 // suppressed because we don't want to lint.
385427 * state = MetavarState :: Suppressed ;
386428 } ,
387- ( MetavarState :: Unreferenced { decl_span } , Some ( unsafe_bl_span ) ) => {
429+ ( MetavarState :: Unreferenced { decl_span } , Some ( unsafe_block_span ) ) => {
388430 * state = MetavarState :: Referenced {
389- reference_span : span,
431+ occurences : vec ! [ ReferencedMetavar {
432+ span,
433+ unsafe_block_span,
434+ } ] ,
390435 decl_span : * decl_span,
391- unsafe_bl_span,
392436 } ;
393437 } ,
394438 ( MetavarState :: Suppressed , _) => { } ,
@@ -433,6 +477,8 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
433477 // ^^^^^^^ look in here for references to one of the collected metavariables
434478 with_token_stream ( tts. next ( ) , |body| finder. find_metavariables_in_body ( body) ) ;
435479
480+ finder. report_warnings ( cx) ;
481+
436482 // skip any trailing semicolons
437483 // (...) => {};;;
438484 // ^^^
@@ -452,30 +498,6 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
452498 . is_some ( )
453499 { }
454500 }
455-
456- for metavar in finder. expr_variables . values ( ) {
457- if let & MetavarState :: Referenced {
458- decl_span,
459- reference_span,
460- unsafe_bl_span,
461- } = metavar
462- {
463- span_lint_and_then (
464- cx,
465- EXPR_METAVARS_IN_UNSAFE ,
466- reference_span,
467- "expanding an expression metavariable in an unsafe block" ,
468- |diag| {
469- diag. span_note ( decl_span, "metavariable declared here" ) ;
470- diag. span_note ( unsafe_bl_span, "unsafe block entered here" ) ;
471- diag. help ( "consider expanding it outside of this block, e.g. by storing it in a variable" ) ;
472- diag. note (
473- "this allows the user of the macro to write unsafe code without an unsafe block (at the callsite)" ,
474- ) ;
475- } ,
476- ) ;
477- }
478- }
479501 }
480502 }
481503}
0 commit comments