@@ -20,23 +20,23 @@ use rustc_ast::ptr::P;
20
20
use rustc_ast:: visit:: { self as ast_visit, Visitor } ;
21
21
use rustc_ast:: { self as ast, walk_list, HasAttrs } ;
22
22
use rustc_middle:: ty:: RegisteredTools ;
23
- use rustc_session:: lint:: { BufferedEarlyLint , LintBuffer } ;
23
+ use rustc_session:: lint:: { BufferedEarlyLint , LintBuffer , LintPass } ;
24
24
use rustc_session:: Session ;
25
25
use rustc_span:: symbol:: Ident ;
26
26
use rustc_span:: Span ;
27
27
28
- macro_rules! run_early_passes { ( $cx: expr, $f: ident, $( $args: expr) ,* ) => ( {
29
- for pass in $cx. passes. iter_mut( ) {
30
- pass. $f( & $cx. context, $( $args) ,* ) ;
31
- }
28
+ macro_rules! lint_callback { ( $cx: expr, $f: ident, $( $args: expr) ,* ) => ( {
29
+ $cx. pass. $f( & $cx. context, $( $args) ,* ) ;
32
30
} ) }
33
31
34
- pub struct EarlyContextAndPasses < ' a > {
32
+ /// Implements the AST traversal for early lint passes. `T` provides the the
33
+ /// `check_*` methods.
34
+ pub struct EarlyContextAndPass < ' a , T : EarlyLintPass > {
35
35
context : EarlyContext < ' a > ,
36
- passes : Vec < EarlyLintPassObject > ,
36
+ pass : T ,
37
37
}
38
38
39
- impl < ' a > EarlyContextAndPasses < ' a > {
39
+ impl < ' a , T : EarlyLintPass > EarlyContextAndPass < ' a , T > {
40
40
// This always-inlined function is for the hot call site.
41
41
#[ inline( always) ]
42
42
fn inlined_check_id ( & mut self , id : ast:: NodeId ) {
@@ -70,27 +70,27 @@ impl<'a> EarlyContextAndPasses<'a> {
70
70
71
71
self . inlined_check_id ( id) ;
72
72
debug ! ( "early context: enter_attrs({:?})" , attrs) ;
73
- run_early_passes ! ( self , enter_lint_attrs, attrs) ;
73
+ lint_callback ! ( self , enter_lint_attrs, attrs) ;
74
74
f ( self ) ;
75
75
debug ! ( "early context: exit_attrs({:?})" , attrs) ;
76
- run_early_passes ! ( self , exit_lint_attrs, attrs) ;
76
+ lint_callback ! ( self , exit_lint_attrs, attrs) ;
77
77
self . context . builder . pop ( push) ;
78
78
}
79
79
}
80
80
81
- impl < ' a > ast_visit:: Visitor < ' a > for EarlyContextAndPasses < ' a > {
81
+ impl < ' a , T : EarlyLintPass > ast_visit:: Visitor < ' a > for EarlyContextAndPass < ' a , T > {
82
82
fn visit_param ( & mut self , param : & ' a ast:: Param ) {
83
83
self . with_lint_attrs ( param. id , & param. attrs , |cx| {
84
- run_early_passes ! ( cx, check_param, param) ;
84
+ lint_callback ! ( cx, check_param, param) ;
85
85
ast_visit:: walk_param ( cx, param) ;
86
86
} ) ;
87
87
}
88
88
89
89
fn visit_item ( & mut self , it : & ' a ast:: Item ) {
90
90
self . with_lint_attrs ( it. id , & it. attrs , |cx| {
91
- run_early_passes ! ( cx, check_item, it) ;
91
+ lint_callback ! ( cx, check_item, it) ;
92
92
ast_visit:: walk_item ( cx, it) ;
93
- run_early_passes ! ( cx, check_item_post, it) ;
93
+ lint_callback ! ( cx, check_item_post, it) ;
94
94
} )
95
95
}
96
96
@@ -101,10 +101,10 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
101
101
}
102
102
103
103
fn visit_pat ( & mut self , p : & ' a ast:: Pat ) {
104
- run_early_passes ! ( self , check_pat, p) ;
104
+ lint_callback ! ( self , check_pat, p) ;
105
105
self . check_id ( p. id ) ;
106
106
ast_visit:: walk_pat ( self , p) ;
107
- run_early_passes ! ( self , check_pat_post, p) ;
107
+ lint_callback ! ( self , check_pat_post, p) ;
108
108
}
109
109
110
110
fn visit_pat_field ( & mut self , field : & ' a ast:: PatField ) {
@@ -120,7 +120,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
120
120
121
121
fn visit_expr ( & mut self , e : & ' a ast:: Expr ) {
122
122
self . with_lint_attrs ( e. id , & e. attrs , |cx| {
123
- run_early_passes ! ( cx, check_expr, e) ;
123
+ lint_callback ! ( cx, check_expr, e) ;
124
124
ast_visit:: walk_expr ( cx, e) ;
125
125
} )
126
126
}
@@ -141,7 +141,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
141
141
// Note that statements get their attributes from
142
142
// the AST struct that they wrap (e.g. an item)
143
143
self . with_lint_attrs ( s. id , s. attrs ( ) , |cx| {
144
- run_early_passes ! ( cx, check_stmt, s) ;
144
+ lint_callback ! ( cx, check_stmt, s) ;
145
145
cx. check_id ( s. id ) ;
146
146
} ) ;
147
147
// The visitor for the AST struct wrapped
@@ -152,7 +152,7 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
152
152
}
153
153
154
154
fn visit_fn ( & mut self , fk : ast_visit:: FnKind < ' a > , span : Span , id : ast:: NodeId ) {
155
- run_early_passes ! ( self , check_fn, fk, span, id) ;
155
+ lint_callback ! ( self , check_fn, fk, span, id) ;
156
156
self . check_id ( id) ;
157
157
ast_visit:: walk_fn ( self , fk) ;
158
158
@@ -180,37 +180,37 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
180
180
181
181
fn visit_variant ( & mut self , v : & ' a ast:: Variant ) {
182
182
self . with_lint_attrs ( v. id , & v. attrs , |cx| {
183
- run_early_passes ! ( cx, check_variant, v) ;
183
+ lint_callback ! ( cx, check_variant, v) ;
184
184
ast_visit:: walk_variant ( cx, v) ;
185
185
} )
186
186
}
187
187
188
188
fn visit_ty ( & mut self , t : & ' a ast:: Ty ) {
189
- run_early_passes ! ( self , check_ty, t) ;
189
+ lint_callback ! ( self , check_ty, t) ;
190
190
self . check_id ( t. id ) ;
191
191
ast_visit:: walk_ty ( self , t) ;
192
192
}
193
193
194
194
fn visit_ident ( & mut self , ident : Ident ) {
195
- run_early_passes ! ( self , check_ident, ident) ;
195
+ lint_callback ! ( self , check_ident, ident) ;
196
196
}
197
197
198
198
fn visit_local ( & mut self , l : & ' a ast:: Local ) {
199
199
self . with_lint_attrs ( l. id , & l. attrs , |cx| {
200
- run_early_passes ! ( cx, check_local, l) ;
200
+ lint_callback ! ( cx, check_local, l) ;
201
201
ast_visit:: walk_local ( cx, l) ;
202
202
} )
203
203
}
204
204
205
205
fn visit_block ( & mut self , b : & ' a ast:: Block ) {
206
- run_early_passes ! ( self , check_block, b) ;
206
+ lint_callback ! ( self , check_block, b) ;
207
207
self . check_id ( b. id ) ;
208
208
ast_visit:: walk_block ( self , b) ;
209
209
}
210
210
211
211
fn visit_arm ( & mut self , a : & ' a ast:: Arm ) {
212
212
self . with_lint_attrs ( a. id , & a. attrs , |cx| {
213
- run_early_passes ! ( cx, check_arm, a) ;
213
+ lint_callback ! ( cx, check_arm, a) ;
214
214
ast_visit:: walk_arm ( cx, a) ;
215
215
} )
216
216
}
@@ -229,19 +229,19 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
229
229
}
230
230
231
231
fn visit_generic_arg ( & mut self , arg : & ' a ast:: GenericArg ) {
232
- run_early_passes ! ( self , check_generic_arg, arg) ;
232
+ lint_callback ! ( self , check_generic_arg, arg) ;
233
233
ast_visit:: walk_generic_arg ( self , arg) ;
234
234
}
235
235
236
236
fn visit_generic_param ( & mut self , param : & ' a ast:: GenericParam ) {
237
237
self . with_lint_attrs ( param. id , & param. attrs , |cx| {
238
- run_early_passes ! ( cx, check_generic_param, param) ;
238
+ lint_callback ! ( cx, check_generic_param, param) ;
239
239
ast_visit:: walk_generic_param ( cx, param) ;
240
240
} ) ;
241
241
}
242
242
243
243
fn visit_generics ( & mut self , g : & ' a ast:: Generics ) {
244
- run_early_passes ! ( self , check_generics, g) ;
244
+ lint_callback ! ( self , check_generics, g) ;
245
245
ast_visit:: walk_generics ( self , g) ;
246
246
}
247
247
@@ -250,18 +250,18 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
250
250
}
251
251
252
252
fn visit_poly_trait_ref ( & mut self , t : & ' a ast:: PolyTraitRef ) {
253
- run_early_passes ! ( self , check_poly_trait_ref, t) ;
253
+ lint_callback ! ( self , check_poly_trait_ref, t) ;
254
254
ast_visit:: walk_poly_trait_ref ( self , t) ;
255
255
}
256
256
257
257
fn visit_assoc_item ( & mut self , item : & ' a ast:: AssocItem , ctxt : ast_visit:: AssocCtxt ) {
258
258
self . with_lint_attrs ( item. id , & item. attrs , |cx| match ctxt {
259
259
ast_visit:: AssocCtxt :: Trait => {
260
- run_early_passes ! ( cx, check_trait_item, item) ;
260
+ lint_callback ! ( cx, check_trait_item, item) ;
261
261
ast_visit:: walk_assoc_item ( cx, item, ctxt) ;
262
262
}
263
263
ast_visit:: AssocCtxt :: Impl => {
264
- run_early_passes ! ( cx, check_impl_item, item) ;
264
+ lint_callback ! ( cx, check_impl_item, item) ;
265
265
ast_visit:: walk_assoc_item ( cx, item, ctxt) ;
266
266
}
267
267
} ) ;
@@ -282,28 +282,57 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
282
282
}
283
283
284
284
fn visit_attribute ( & mut self , attr : & ' a ast:: Attribute ) {
285
- run_early_passes ! ( self , check_attribute, attr) ;
285
+ lint_callback ! ( self , check_attribute, attr) ;
286
286
}
287
287
288
288
fn visit_mac_def ( & mut self , mac : & ' a ast:: MacroDef , id : ast:: NodeId ) {
289
- run_early_passes ! ( self , check_mac_def, mac) ;
289
+ lint_callback ! ( self , check_mac_def, mac) ;
290
290
self . check_id ( id) ;
291
291
}
292
292
293
293
fn visit_mac_call ( & mut self , mac : & ' a ast:: MacCall ) {
294
- run_early_passes ! ( self , check_mac, mac) ;
294
+ lint_callback ! ( self , check_mac, mac) ;
295
295
ast_visit:: walk_mac ( self , mac) ;
296
296
}
297
297
}
298
298
299
+ // Combines multiple lint passes into a single pass, at runtime. Each
300
+ // `check_foo` method in `$methods` within this pass simply calls `check_foo`
301
+ // once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is
302
+ // similar, but combines lint passes at compile time.
303
+ struct RuntimeCombinedEarlyLintPass < ' a > {
304
+ passes : & ' a mut [ EarlyLintPassObject ] ,
305
+ }
306
+
307
+ #[ allow( rustc:: lint_pass_impl_without_macro) ]
308
+ impl LintPass for RuntimeCombinedEarlyLintPass < ' _ > {
309
+ fn name ( & self ) -> & ' static str {
310
+ panic ! ( )
311
+ }
312
+ }
313
+
314
+ macro_rules! impl_early_lint_pass {
315
+ ( [ ] , [ $( $( #[ $attr: meta] ) * fn $f: ident( $( $param: ident: $arg: ty) ,* ) ; ) * ] ) => (
316
+ impl EarlyLintPass for RuntimeCombinedEarlyLintPass <' _> {
317
+ $( fn $f( & mut self , context: & EarlyContext <' _>, $( $param: $arg) ,* ) {
318
+ for pass in self . passes. iter_mut( ) {
319
+ pass. $f( context, $( $param) ,* ) ;
320
+ }
321
+ } ) *
322
+ }
323
+ )
324
+ }
325
+
326
+ crate :: early_lint_methods!( impl_early_lint_pass, [ ] ) ;
327
+
299
328
/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
300
329
/// This trait generalizes over those nodes.
301
330
pub trait EarlyCheckNode < ' a > : Copy {
302
331
fn id ( self ) -> ast:: NodeId ;
303
332
fn attrs < ' b > ( self ) -> & ' b [ ast:: Attribute ]
304
333
where
305
334
' a : ' b ;
306
- fn check < ' b > ( self , cx : & mut EarlyContextAndPasses < ' b > )
335
+ fn check < ' b , T : EarlyLintPass > ( self , cx : & mut EarlyContextAndPass < ' b , T > )
307
336
where
308
337
' a : ' b ;
309
338
}
@@ -318,13 +347,13 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
318
347
{
319
348
& self . attrs
320
349
}
321
- fn check < ' b > ( self , cx : & mut EarlyContextAndPasses < ' b > )
350
+ fn check < ' b , T : EarlyLintPass > ( self , cx : & mut EarlyContextAndPass < ' b , T > )
322
351
where
323
352
' a : ' b ,
324
353
{
325
- run_early_passes ! ( cx, check_crate, self ) ;
354
+ lint_callback ! ( cx, check_crate, self ) ;
326
355
ast_visit:: walk_crate ( cx, self ) ;
327
- run_early_passes ! ( cx, check_crate_post, self ) ;
356
+ lint_callback ! ( cx, check_crate_post, self ) ;
328
357
}
329
358
}
330
359
@@ -338,7 +367,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::
338
367
{
339
368
self . 1
340
369
}
341
- fn check < ' b > ( self , cx : & mut EarlyContextAndPasses < ' b > )
370
+ fn check < ' b , T : EarlyLintPass > ( self , cx : & mut EarlyContextAndPass < ' b , T > )
342
371
where
343
372
' a : ' b ,
344
373
{
@@ -356,21 +385,37 @@ pub fn check_ast_node<'a>(
356
385
builtin_lints : impl EarlyLintPass + ' static ,
357
386
check_node : impl EarlyCheckNode < ' a > ,
358
387
) {
388
+ let context = EarlyContext :: new (
389
+ sess,
390
+ !pre_expansion,
391
+ lint_store,
392
+ registered_tools,
393
+ lint_buffer. unwrap_or_default ( ) ,
394
+ ) ;
395
+
396
+ // Note: `passes` is often empty. In that case, it's faster to run
397
+ // `builtin_lints` directly rather than bundling it up into the
398
+ // `RuntimeCombinedEarlyLintPass`.
359
399
let passes =
360
400
if pre_expansion { & lint_store. pre_expansion_passes } else { & lint_store. early_passes } ;
361
- let mut passes: Vec < EarlyLintPassObject > = passes. iter ( ) . map ( |p| ( p) ( ) ) . collect ( ) ;
362
- passes. push ( Box :: new ( builtin_lints) ) ;
363
-
364
- let mut cx = EarlyContextAndPasses {
365
- context : EarlyContext :: new (
366
- sess,
367
- !pre_expansion,
368
- lint_store,
369
- registered_tools,
370
- lint_buffer. unwrap_or_default ( ) ,
371
- ) ,
372
- passes,
373
- } ;
401
+ if passes. is_empty ( ) {
402
+ check_ast_node_inner ( sess, check_node, context, builtin_lints) ;
403
+ } else {
404
+ let mut passes: Vec < _ > = passes. iter ( ) . map ( |mk_pass| ( mk_pass) ( ) ) . collect ( ) ;
405
+ passes. push ( Box :: new ( builtin_lints) ) ;
406
+ let pass = RuntimeCombinedEarlyLintPass { passes : & mut passes[ ..] } ;
407
+ check_ast_node_inner ( sess, check_node, context, pass) ;
408
+ }
409
+ }
410
+
411
+ pub fn check_ast_node_inner < ' a , T : EarlyLintPass > (
412
+ sess : & Session ,
413
+ check_node : impl EarlyCheckNode < ' a > ,
414
+ context : EarlyContext < ' _ > ,
415
+ pass : T ,
416
+ ) {
417
+ let mut cx = EarlyContextAndPass { context, pass } ;
418
+
374
419
cx. with_lint_attrs ( check_node. id ( ) , check_node. attrs ( ) , |cx| check_node. check ( cx) ) ;
375
420
376
421
// All of the buffered lints should have been emitted at this point.
0 commit comments