@@ -151,18 +151,24 @@ fn extract_metavariable<'a>(
151
151
}
152
152
}
153
153
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 ) ]
154
165
enum MetavarState {
155
- /// The metavariable was referenced inside of an unsafe block
166
+ /// The metavariable was referenced inside of an unsafe block.
156
167
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 ...) => {} }
161
170
/// ^^^^^^^
162
171
decl_span : Span ,
163
- /// unsafe { $v }
164
- /// ^^^^^^^^
165
- unsafe_bl_span : Span ,
166
172
} ,
167
173
168
174
/// The metavariable was not found yet, but we're still looking for it
@@ -209,6 +215,29 @@ impl UnsafeMetavariableFinder {
209
215
self . enclosing . clear ( ) ;
210
216
}
211
217
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
+
212
241
/// Given the following delimited token stream:
213
242
/// ```ignore
214
243
/// macro_rules! x { (... $var:expr ...) => { ... } }
@@ -258,41 +287,47 @@ impl UnsafeMetavariableFinder {
258
287
/// }
259
288
/// ```
260
289
/// (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 > {
262
304
let mut angle_bracket_depth = 0u32 ;
263
305
306
+ let mut last_token = None ;
264
307
self . enclosing . push ( EnclosingNode :: Body ) ;
265
308
for tt in tts {
266
309
match tt {
267
310
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
+ _ => { } ,
278
325
}
279
326
} ,
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 ( ..) => { } ,
293
327
}
294
328
}
295
329
self . enclosing . pop ( ) ;
330
+ last_token
296
331
}
297
332
298
333
/// ```ignore
@@ -312,11 +347,11 @@ impl UnsafeMetavariableFinder {
312
347
let mut angle_bracket_depth = 0u32 ;
313
348
314
349
self . enclosing . push ( EnclosingNode :: Body ) ;
315
- for tt in tts {
350
+ while let Some ( tt ) = tts. next ( ) {
316
351
match tt {
317
352
TokenTree :: Delimited ( .., Delimiter :: Brace , body) => {
318
353
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:
320
355
// fn() -> Ty<{ ... }> {}
321
356
// ^^^^^^^
322
357
// which we know if angle_bracket_depth > 0,
@@ -326,19 +361,21 @@ impl UnsafeMetavariableFinder {
326
361
break ;
327
362
}
328
363
} ,
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 ( ..) => { } ,
342
379
}
343
380
}
344
381
self . enclosing . pop ( ) ;
@@ -377,18 +414,25 @@ impl UnsafeMetavariableFinder {
377
414
EnclosingNode :: UnsafeBlock ( span) => Some ( * span) ,
378
415
} ) ;
379
416
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
+ } ,
382
424
( _, None ) => {
383
425
// Metavar was referenced outside of an unsafe block. Set to
384
426
// suppressed because we don't want to lint.
385
427
* state = MetavarState :: Suppressed ;
386
428
} ,
387
- ( MetavarState :: Unreferenced { decl_span } , Some ( unsafe_bl_span ) ) => {
429
+ ( MetavarState :: Unreferenced { decl_span } , Some ( unsafe_block_span ) ) => {
388
430
* state = MetavarState :: Referenced {
389
- reference_span : span,
431
+ occurences : vec ! [ ReferencedMetavar {
432
+ span,
433
+ unsafe_block_span,
434
+ } ] ,
390
435
decl_span : * decl_span,
391
- unsafe_bl_span,
392
436
} ;
393
437
} ,
394
438
( MetavarState :: Suppressed , _) => { } ,
@@ -433,6 +477,8 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
433
477
// ^^^^^^^ look in here for references to one of the collected metavariables
434
478
with_token_stream ( tts. next ( ) , |body| finder. find_metavariables_in_body ( body) ) ;
435
479
480
+ finder. report_warnings ( cx) ;
481
+
436
482
// skip any trailing semicolons
437
483
// (...) => {};;;
438
484
// ^^^
@@ -452,30 +498,6 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
452
498
. is_some ( )
453
499
{ }
454
500
}
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
- }
479
501
}
480
502
}
481
503
}
0 commit comments