@@ -15,7 +15,7 @@ use crate::{
1515} ;
1616use rustc_ast as ast;
1717use rustc_ast_pretty:: pprust;
18- use rustc_data_structures:: fx:: FxIndexMap ;
18+ use rustc_data_structures:: { fx:: FxIndexMap , sync :: Lrc } ;
1919use rustc_errors:: { Diag , DiagMessage , LintDiagnostic , MultiSpan } ;
2020use rustc_feature:: { Features , GateIssue } ;
2121use rustc_hir as hir;
@@ -153,30 +153,18 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
153153
154154/// Walk the whole crate collecting nodes where lint levels change
155155/// (e.g. `#[allow]` attributes), and joins that list with the warn-by-default
156- /// (and not allowed in the crate) and CLI lints. The final result is a builder
157- /// that has information about just lints that can be emitted (leaving out
158- /// globally-allowed lints)
159- pub fn lints_that_can_emit (
160- tcx : TyCtxt < ' _ > ,
161- ( ) : ( )
162- ) -> Vec < LintId > {
163- let store = unerased_lint_store ( & tcx. sess ) ;
164-
165- let specs = tcx. shallow_lint_levels_on ( hir:: CRATE_HIR_ID . owner ) ;
166- let lints = store. get_lints ( ) ;
167-
168- let mut hashmap: Vec < LintId > = Vec :: new ( ) ;
169- hashmap. reserve ( ( lints. len ( ) >> 1 ) * usize:: from ( tcx. sess . opts . lint_cap . is_some ( ) ) ) ; // Avoid allocations
170-
171- for & lint in lints {
172- let lint_id = LintId :: of ( lint) ;
173- let actual_level = specs. probe_for_lint_level ( tcx, lint_id, hir:: CRATE_HIR_ID ) . 0 . unwrap_or ( lint. default_level ) ;
174- if actual_level > Level :: Allow {
175- hashmap. push ( lint_id) ;
176- }
177- }
178-
179- hashmap
156+ /// (and not allowed in the crate) and CLI lints. The returned value is a tuple
157+ /// of 1. The lints that will emit (or at least, should run), and 2.
158+ /// The lints that are allowed at the crate level and will not emit.
159+ pub fn lints_that_can_emit ( tcx : TyCtxt < ' _ > , ( ) : ( ) ) -> Lrc < ( Vec < Symbol > , Vec < Symbol > ) > {
160+ // builder.add_command_line();
161+ // builder.add_id(hir::CRATE_HIR_ID);
162+
163+ let mut visitor = LintLevelMinimumVisitor :: new ( tcx) ;
164+ visitor. process_opts ( ) ;
165+ tcx. hir ( ) . walk_attributes ( & mut visitor) ;
166+
167+ Lrc :: new ( ( visitor. lints_to_emit , visitor. lints_allowed ) )
180168}
181169
182170#[ instrument( level = "trace" , skip( tcx) , ret) ]
@@ -478,6 +466,86 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'
478466 }
479467}
480468
469+ /// Visitor with the only function of visiting every item-like in a crate and
470+ /// computing the highest level that every lint gets put to.
471+ ///
472+ /// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
473+ /// uses #[warn(lint)], this visitor will set that lint level as `Warn`
474+ struct LintLevelMinimumVisitor < ' tcx > {
475+ tcx : TyCtxt < ' tcx > ,
476+ /// The actual list of detected lints.
477+ lints_to_emit : Vec < Symbol > ,
478+ lints_allowed : Vec < Symbol > ,
479+ }
480+
481+ impl < ' tcx > LintLevelMinimumVisitor < ' tcx > {
482+ pub fn new ( tcx : TyCtxt < ' tcx > ) -> Self {
483+ Self {
484+ tcx,
485+ // That magic number is the current number of lints + some more for possible future lints
486+ lints_to_emit : Vec :: with_capacity ( 230 ) ,
487+ lints_allowed : Vec :: with_capacity ( 100 ) ,
488+ }
489+ }
490+
491+ fn process_opts ( & mut self ) {
492+ for ( lint, level) in & self . tcx . sess . opts . lint_opts {
493+ if * level == Level :: Allow {
494+ self . lints_allowed . push ( Symbol :: intern ( & lint) ) ;
495+ } else {
496+ self . lints_to_emit . push ( Symbol :: intern ( & lint) ) ;
497+ }
498+ }
499+ }
500+ }
501+
502+ impl < ' tcx > Visitor < ' tcx > for LintLevelMinimumVisitor < ' tcx > {
503+ type NestedFilter = nested_filter:: All ;
504+
505+ fn nested_visit_map ( & mut self ) -> Self :: Map {
506+ self . tcx . hir ( )
507+ }
508+
509+ fn visit_attribute ( & mut self , attribute : & ' tcx ast:: Attribute ) {
510+ if let Some ( meta) = attribute. meta ( ) {
511+ if [ sym:: warn, sym:: deny, sym:: forbid, sym:: expect]
512+ . iter ( )
513+ . any ( |kind| meta. has_name ( * kind) )
514+ {
515+ // SAFETY: Lint attributes are always a metalist inside a
516+ // metalist (even with just one lint).
517+ for meta_list in meta. meta_item_list ( ) . unwrap ( ) {
518+ // If it's a tool lint (e.g. clippy::my_clippy_lint)
519+ if let ast:: NestedMetaItem :: MetaItem ( meta_item) = meta_list {
520+ if meta_item. path . segments . len ( ) == 1 {
521+ self . lints_to_emit . push (
522+ // SAFETY: Lint attributes can only have literals
523+ meta_list. ident ( ) . unwrap ( ) . name ,
524+ ) ;
525+ } else {
526+ self . lints_to_emit . push ( meta_item. path . segments [ 1 ] . ident . name ) ;
527+ }
528+ }
529+ }
530+ // We handle #![allow]s differently, as these remove checking rather than adding.
531+ } else if meta. has_name ( sym:: allow)
532+ && let ast:: AttrStyle :: Inner = attribute. style
533+ {
534+ for meta_list in meta. meta_item_list ( ) . unwrap ( ) {
535+ // If it's a tool lint (e.g. clippy::my_clippy_lint)
536+ if let ast:: NestedMetaItem :: MetaItem ( meta_item) = meta_list {
537+ if meta_item. path . segments . len ( ) == 1 {
538+ self . lints_allowed . push ( meta_list. name_or_empty ( ) )
539+ } else {
540+ self . lints_allowed . push ( meta_item. path . segments [ 1 ] . ident . name ) ;
541+ }
542+ }
543+ }
544+ }
545+ }
546+ }
547+ }
548+
481549pub struct LintLevelsBuilder < ' s , P > {
482550 sess : & ' s Session ,
483551 features : & ' s Features ,
@@ -1161,7 +1229,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
11611229}
11621230
11631231pub ( crate ) fn provide ( providers : & mut Providers ) {
1164- * providers = Providers { shallow_lint_levels_on, lint_expectations, lints_that_can_emit, ..* providers } ;
1232+ * providers =
1233+ Providers { shallow_lint_levels_on, lint_expectations, lints_that_can_emit, ..* providers } ;
11651234}
11661235
11671236pub fn parse_lint_and_tool_name ( lint_name : & str ) -> ( Option < Symbol > , & str ) {
0 commit comments