1
1
use rustc_ast:: attr:: AttributeExt ;
2
2
use rustc_ast_pretty:: pprust;
3
- use rustc_data_structures:: fx:: { FxIndexMap , FxIndexSet } ;
3
+ use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
4
+ use rustc_data_structures:: unord:: UnordSet ;
4
5
use rustc_errors:: { Diag , LintDiagnostic , MultiSpan } ;
5
6
use rustc_feature:: { Features , GateIssue } ;
7
+ use rustc_hir:: HirId ;
6
8
use rustc_hir:: intravisit:: { self , Visitor } ;
7
- use rustc_hir:: { CRATE_HIR_ID , HirId } ;
8
9
use rustc_index:: IndexVec ;
9
10
use rustc_middle:: bug;
10
11
use rustc_middle:: hir:: nested_filter;
@@ -115,12 +116,11 @@ impl LintLevelSets {
115
116
}
116
117
}
117
118
118
- fn lints_that_dont_need_to_run ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> FxIndexSet < LintId > {
119
+ fn lints_that_dont_need_to_run ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> UnordSet < LintId > {
119
120
let store = unerased_lint_store ( & tcx. sess ) ;
121
+ let root_map = tcx. shallow_lint_levels_on ( hir:: CRATE_OWNER_ID ) ;
120
122
121
- let map = tcx. shallow_lint_levels_on ( rustc_hir:: CRATE_OWNER_ID ) ;
122
-
123
- let dont_need_to_run: FxIndexSet < LintId > = store
123
+ let mut dont_need_to_run: FxHashSet < LintId > = store
124
124
. get_lints ( )
125
125
. into_iter ( )
126
126
. filter ( |lint| {
@@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
129
129
lint. future_incompatible . is_some_and ( |fut| fut. reason . has_future_breakage ( ) ) ;
130
130
!has_future_breakage && !lint. eval_always
131
131
} )
132
- . filter_map ( |lint| {
133
- let lint_level = map. lint_level_id_at_node ( tcx, LintId :: of ( lint) , CRATE_HIR_ID ) ;
134
- if matches ! ( lint_level. level, Level :: Allow )
135
- || ( matches ! ( lint_level. src, LintLevelSource :: Default ) )
136
- && lint. default_level ( tcx. sess . edition ( ) ) == Level :: Allow
137
- {
138
- Some ( LintId :: of ( lint) )
139
- } else {
140
- None
141
- }
132
+ . filter ( |lint| {
133
+ let lint_level =
134
+ root_map. lint_level_id_at_node ( tcx, LintId :: of ( lint) , hir:: CRATE_HIR_ID ) ;
135
+ // Only include lints that are allowed at crate root or by default.
136
+ matches ! ( lint_level. level, Level :: Allow )
137
+ || ( matches ! ( lint_level. src, LintLevelSource :: Default )
138
+ && lint. default_level ( tcx. sess . edition ( ) ) == Level :: Allow )
142
139
} )
140
+ . map ( |lint| LintId :: of ( * lint) )
143
141
. collect ( ) ;
144
142
145
- let mut visitor = LintLevelMaximum { tcx, dont_need_to_run } ;
146
- visitor. process_opts ( ) ;
147
- tcx. hir_walk_attributes ( & mut visitor) ;
143
+ for owner in tcx. hir_crate_items ( ( ) ) . owners ( ) {
144
+ let map = tcx. shallow_lint_levels_on ( owner) ;
145
+
146
+ // All lints that appear with a non-allow level must be run.
147
+ for ( _, specs) in map. specs . iter ( ) {
148
+ for ( lint, level_and_source) in specs. iter ( ) {
149
+ if !matches ! ( level_and_source. level, Level :: Allow ) {
150
+ dont_need_to_run. remove ( lint) ;
151
+ }
152
+ }
153
+ }
154
+ }
148
155
149
- visitor . dont_need_to_run
156
+ dont_need_to_run. into ( )
150
157
}
151
158
152
159
#[ instrument( level = "trace" , skip( tcx) , ret) ]
@@ -340,76 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
340
347
}
341
348
}
342
349
343
- /// Visitor with the only function of visiting every item-like in a crate and
344
- /// computing the highest level that every lint gets put to.
345
- ///
346
- /// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
347
- /// uses #[warn(lint)], this visitor will set that lint level as `Warn`
348
- struct LintLevelMaximum < ' tcx > {
349
- tcx : TyCtxt < ' tcx > ,
350
- /// The actual list of detected lints.
351
- dont_need_to_run : FxIndexSet < LintId > ,
352
- }
353
-
354
- impl < ' tcx > LintLevelMaximum < ' tcx > {
355
- fn process_opts ( & mut self ) {
356
- let store = unerased_lint_store ( self . tcx . sess ) ;
357
- for ( lint_group, level) in & self . tcx . sess . opts . lint_opts {
358
- if * level != Level :: Allow {
359
- let Ok ( lints) = store. find_lints ( lint_group) else {
360
- return ;
361
- } ;
362
- for lint in lints {
363
- self . dont_need_to_run . swap_remove ( & lint) ;
364
- }
365
- }
366
- }
367
- }
368
- }
369
-
370
- impl < ' tcx > Visitor < ' tcx > for LintLevelMaximum < ' tcx > {
371
- type NestedFilter = nested_filter:: All ;
372
-
373
- fn maybe_tcx ( & mut self ) -> Self :: MaybeTyCtxt {
374
- self . tcx
375
- }
376
-
377
- /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
378
- /// but that is handled with more care
379
- fn visit_attribute ( & mut self , attribute : & ' tcx hir:: Attribute ) {
380
- if matches ! (
381
- Level :: from_attr( attribute) ,
382
- Some ( ( Level :: Warn | Level :: Deny | Level :: Forbid | Level :: Expect | Level :: ForceWarn , _) )
383
- ) {
384
- let store = unerased_lint_store ( self . tcx . sess ) ;
385
- // Lint attributes are always a metalist inside a
386
- // metalist (even with just one lint).
387
- let Some ( meta_item_list) = attribute. meta_item_list ( ) else { return } ;
388
-
389
- for meta_list in meta_item_list {
390
- // Convert Path to String
391
- let Some ( meta_item) = meta_list. meta_item ( ) else { return } ;
392
- let ident: & str = & meta_item
393
- . path
394
- . segments
395
- . iter ( )
396
- . map ( |segment| segment. ident . as_str ( ) )
397
- . collect :: < Vec < & str > > ( )
398
- . join ( "::" ) ;
399
- let Ok ( lints) = store. find_lints (
400
- // Lint attributes can only have literals
401
- ident,
402
- ) else {
403
- return ;
404
- } ;
405
- for lint in lints {
406
- self . dont_need_to_run . swap_remove ( & lint) ;
407
- }
408
- }
409
- }
410
- }
411
- }
412
-
413
350
pub struct LintLevelsBuilder < ' s , P > {
414
351
sess : & ' s Session ,
415
352
features : & ' s Features ,
0 commit comments