11use rustc_ast:: attr:: AttributeExt ;
22use 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 ;
45use rustc_errors:: { Diag , LintDiagnostic , MultiSpan } ;
56use rustc_feature:: { Features , GateIssue } ;
7+ use rustc_hir:: HirId ;
68use rustc_hir:: intravisit:: { self , Visitor } ;
7- use rustc_hir:: { CRATE_HIR_ID , HirId } ;
89use rustc_index:: IndexVec ;
910use rustc_middle:: bug;
1011use rustc_middle:: hir:: nested_filter;
@@ -115,12 +116,11 @@ impl LintLevelSets {
115116 }
116117}
117118
118- fn lints_that_dont_need_to_run ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> FxIndexSet < LintId > {
119+ fn lints_that_dont_need_to_run ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> UnordSet < LintId > {
119120 let store = unerased_lint_store ( & tcx. sess ) ;
121+ let root_map = tcx. shallow_lint_levels_on ( hir:: CRATE_OWNER_ID ) ;
120122
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
124124 . get_lints ( )
125125 . into_iter ( )
126126 . filter ( |lint| {
@@ -129,24 +129,31 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
129129 lint. future_incompatible . is_some_and ( |fut| fut. reason . has_future_breakage ( ) ) ;
130130 !has_future_breakage && !lint. eval_always
131131 } )
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 )
142139 } )
140+ . map ( |lint| LintId :: of ( * lint) )
143141 . collect ( ) ;
144142
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+ }
148155
149- visitor . dont_need_to_run
156+ dont_need_to_run. into ( )
150157}
151158
152159#[ instrument( level = "trace" , skip( tcx) , ret) ]
@@ -340,76 +347,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
340347 }
341348}
342349
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-
413350pub struct LintLevelsBuilder < ' s , P > {
414351 sess : & ' s Session ,
415352 features : & ' s Features ,
0 commit comments