@@ -15,7 +15,7 @@ use crate::{
15
15
} ;
16
16
use rustc_ast as ast;
17
17
use rustc_ast_pretty:: pprust;
18
- use rustc_data_structures:: fx:: FxIndexMap ;
18
+ use rustc_data_structures:: { fx:: FxIndexMap , sync :: Lrc } ;
19
19
use rustc_errors:: { Diag , DiagMessage , LintDiagnostic , MultiSpan } ;
20
20
use rustc_feature:: { Features , GateIssue } ;
21
21
use rustc_hir as hir;
@@ -153,30 +153,18 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp
153
153
154
154
/// Walk the whole crate collecting nodes where lint levels change
155
155
/// (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 ) )
180
168
}
181
169
182
170
#[ instrument( level = "trace" , skip( tcx) , ret) ]
@@ -478,6 +466,86 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'
478
466
}
479
467
}
480
468
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
+
481
549
pub struct LintLevelsBuilder < ' s , P > {
482
550
sess : & ' s Session ,
483
551
features : & ' s Features ,
@@ -1161,7 +1229,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
1161
1229
}
1162
1230
1163
1231
pub ( 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 } ;
1165
1234
}
1166
1235
1167
1236
pub fn parse_lint_and_tool_name ( lint_name : & str ) -> ( Option < Symbol > , & str ) {
0 commit comments