11use rustc_ast_pretty:: pprust;
2- use rustc_data_structures:: fx:: { FxIndexMap , FxIndexSet } ;
2+ use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
3+ use rustc_data_structures:: unord:: UnordSet ;
34use rustc_errors:: { Diag , LintDiagnostic , MultiSpan } ;
45use rustc_feature:: { Features , GateIssue } ;
6+ use rustc_hir:: HirId ;
57use rustc_hir:: intravisit:: { self , Visitor } ;
6- use rustc_hir:: { CRATE_HIR_ID , HirId } ;
78use rustc_index:: IndexVec ;
89use rustc_middle:: bug;
910use rustc_middle:: hir:: nested_filter;
@@ -115,12 +116,10 @@ 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 ) ;
120121
121- let map = tcx. shallow_lint_levels_on ( rustc_hir:: CRATE_OWNER_ID ) ;
122-
123- let dont_need_to_run: FxIndexSet < LintId > = store
122+ let mut dont_need_to_run: FxHashSet < LintId > = store
124123 . get_lints ( )
125124 . into_iter ( )
126125 . filter ( |lint| {
@@ -129,24 +128,28 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
129128 lint. future_incompatible . is_some_and ( |fut| fut. reason . has_future_breakage ( ) ) ;
130129 !has_future_breakage && !lint. eval_always
131130 } )
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 :: Allow , ..) )
135- || ( matches ! ( lint_level, ( .., LintLevelSource :: Default ) ) )
136- && lint. default_level ( tcx. sess . edition ( ) ) == Level :: Allow
137- {
138- Some ( LintId :: of ( lint) )
139- } else {
140- None
141- }
142- } )
131+ . map ( |lint| LintId :: of ( * lint) )
143132 . collect ( ) ;
144133
145- let mut visitor = LintLevelMaximum { tcx, dont_need_to_run } ;
146- visitor. process_opts ( ) ;
147- tcx. hir ( ) . walk_attributes ( & mut visitor) ;
134+ let mut remove_non_allow_lints_from_owner = |owner| {
135+ let map = tcx. shallow_lint_levels_on ( owner) ;
148136
149- visitor. dont_need_to_run
137+ // All lints that appear with a non-allow level must be run.
138+ for ( _, specs) in map. specs . iter ( ) {
139+ for ( lint, ( level, _) ) in specs. iter ( ) {
140+ if !matches ! ( level, Level :: Allow ) {
141+ dont_need_to_run. remove ( lint) ;
142+ }
143+ }
144+ }
145+ } ;
146+
147+ remove_non_allow_lints_from_owner ( rustc_hir:: CRATE_OWNER_ID ) ;
148+ for owner in tcx. hir_crate_items ( ( ) ) . owners ( ) {
149+ remove_non_allow_lints_from_owner ( owner) ;
150+ }
151+
152+ dont_need_to_run. into ( )
150153}
151154
152155#[ instrument( level = "trace" , skip( tcx) , ret) ]
@@ -335,83 +338,6 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
335338 }
336339}
337340
338- /// Visitor with the only function of visiting every item-like in a crate and
339- /// computing the highest level that every lint gets put to.
340- ///
341- /// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
342- /// uses #[warn(lint)], this visitor will set that lint level as `Warn`
343- struct LintLevelMaximum < ' tcx > {
344- tcx : TyCtxt < ' tcx > ,
345- /// The actual list of detected lints.
346- dont_need_to_run : FxIndexSet < LintId > ,
347- }
348-
349- impl < ' tcx > LintLevelMaximum < ' tcx > {
350- fn process_opts ( & mut self ) {
351- let store = unerased_lint_store ( self . tcx . sess ) ;
352- for ( lint_group, level) in & self . tcx . sess . opts . lint_opts {
353- if * level != Level :: Allow {
354- let Ok ( lints) = store. find_lints ( lint_group) else {
355- return ;
356- } ;
357- for lint in lints {
358- self . dont_need_to_run . swap_remove ( & lint) ;
359- }
360- }
361- }
362- }
363- }
364-
365- impl < ' tcx > Visitor < ' tcx > for LintLevelMaximum < ' tcx > {
366- type NestedFilter = nested_filter:: All ;
367-
368- fn nested_visit_map ( & mut self ) -> Self :: Map {
369- self . tcx . hir ( )
370- }
371-
372- /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
373- /// but that is handled with more care
374- fn visit_attribute ( & mut self , attribute : & ' tcx ast:: Attribute ) {
375- if matches ! (
376- Level :: from_attr( attribute) ,
377- Some (
378- Level :: Warn
379- | Level :: Deny
380- | Level :: Forbid
381- | Level :: Expect ( ..)
382- | Level :: ForceWarn ( ..) ,
383- )
384- ) {
385- let store = unerased_lint_store ( self . tcx . sess ) ;
386- let Some ( meta) = attribute. meta ( ) else { return } ;
387- // Lint attributes are always a metalist inside a
388- // metalist (even with just one lint).
389- let Some ( meta_item_list) = meta. meta_item_list ( ) else { return } ;
390-
391- for meta_list in meta_item_list {
392- // Convert Path to String
393- let Some ( meta_item) = meta_list. meta_item ( ) else { return } ;
394- let ident: & str = & meta_item
395- . path
396- . segments
397- . iter ( )
398- . map ( |segment| segment. ident . as_str ( ) )
399- . collect :: < Vec < & str > > ( )
400- . join ( "::" ) ;
401- let Ok ( lints) = store. find_lints (
402- // Lint attributes can only have literals
403- ident,
404- ) else {
405- return ;
406- } ;
407- for lint in lints {
408- self . dont_need_to_run . swap_remove ( & lint) ;
409- }
410- }
411- }
412- }
413- }
414-
415341pub struct LintLevelsBuilder < ' s , P > {
416342 sess : & ' s Session ,
417343 features : & ' s Features ,
0 commit comments