1
1
use std:: cmp;
2
2
3
3
use rustc_data_structures:: fx:: FxHashMap ;
4
+ use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
4
5
use rustc_errors:: { Diagnostic , DiagnosticId , LintDiagnosticBuilder , MultiSpan } ;
5
6
use rustc_hir:: HirId ;
7
+ use rustc_index:: vec:: IndexVec ;
8
+ use rustc_query_system:: ich:: StableHashingContext ;
6
9
use rustc_session:: lint:: {
7
10
builtin:: { self , FORBIDDEN_LINT_GROUPS } ,
8
- FutureIncompatibilityReason , Level , Lint , LintId ,
11
+ FutureIncompatibilityReason , Level , Lint , LintExpectationId , LintId ,
9
12
} ;
10
13
use rustc_session:: Session ;
11
14
use rustc_span:: hygiene:: MacroKind ;
12
15
use rustc_span:: source_map:: { DesugaringKind , ExpnKind } ;
13
16
use rustc_span:: { symbol, Span , Symbol , DUMMY_SP } ;
14
17
15
- use crate :: ty:: TyCtxt ;
16
-
17
18
/// How a lint level was set.
18
19
#[ derive( Clone , Copy , PartialEq , Eq , HashStable , Debug ) ]
19
20
pub enum LintLevelSource {
@@ -22,12 +23,7 @@ pub enum LintLevelSource {
22
23
Default ,
23
24
24
25
/// Lint level was set by an attribute.
25
- Node {
26
- name : Symbol ,
27
- span : Span ,
28
- /// RFC 2383 reason
29
- reason : Option < Symbol > ,
30
- } ,
26
+ Node ( Symbol , Span , Option < Symbol > /* RFC 2383 reason */ ) ,
31
27
32
28
/// Lint level was set by a command-line flag.
33
29
/// The provided `Level` is the level specified on the command line.
@@ -39,15 +35,15 @@ impl LintLevelSource {
39
35
pub fn name ( & self ) -> Symbol {
40
36
match * self {
41
37
LintLevelSource :: Default => symbol:: kw:: Default ,
42
- LintLevelSource :: Node { name, .. } => name,
38
+ LintLevelSource :: Node ( name, _ , _ ) => name,
43
39
LintLevelSource :: CommandLine ( name, _) => name,
44
40
}
45
41
}
46
42
47
43
pub fn span ( & self ) -> Span {
48
44
match * self {
49
45
LintLevelSource :: Default => DUMMY_SP ,
50
- LintLevelSource :: Node { span, .. } => span,
46
+ LintLevelSource :: Node ( _ , span, _ ) => span,
51
47
LintLevelSource :: CommandLine ( _, _) => DUMMY_SP ,
52
48
}
53
49
}
@@ -56,115 +52,145 @@ impl LintLevelSource {
56
52
/// A tuple of a lint level and its source.
57
53
pub type LevelAndSource = ( Level , LintLevelSource ) ;
58
54
59
- /// Return type for the `shallow_lint_levels_on` query.
60
- ///
61
- /// This map represents the set of allowed lints and allowance levels given
62
- /// by the attributes for *a single HirId*.
63
- #[ derive( Default , Debug , HashStable ) ]
64
- pub struct ShallowLintLevelMap {
55
+ #[ derive( Debug , HashStable ) ]
56
+ pub struct LintLevelSets {
57
+ pub list : IndexVec < LintStackIndex , LintSet > ,
58
+ pub lint_cap : Level ,
59
+ }
60
+
61
+ rustc_index:: newtype_index! {
62
+ #[ derive( HashStable ) ]
63
+ pub struct LintStackIndex {
64
+ const COMMAND_LINE = 0 ,
65
+ }
66
+ }
67
+
68
+ #[ derive( Debug , HashStable ) ]
69
+ pub struct LintSet {
70
+ // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
71
+ // flag.
65
72
pub specs : FxHashMap < LintId , LevelAndSource > ,
73
+
74
+ pub parent : LintStackIndex ,
66
75
}
67
76
68
- /// From an initial level and source, verify the effect of special annotations:
69
- /// `warnings` lint level and lint caps.
70
- ///
71
- /// The return of this function is suitable for diagnostics.
72
- pub fn reveal_actual_level (
73
- level : Option < Level > ,
74
- src : & mut LintLevelSource ,
75
- sess : & Session ,
76
- lint : LintId ,
77
- probe_for_lint_level : impl FnOnce ( LintId ) -> ( Option < Level > , LintLevelSource ) ,
78
- ) -> Level {
79
- // If `level` is none then we actually assume the default level for this lint.
80
- let mut level = level. unwrap_or_else ( || lint. lint . default_level ( sess. edition ( ) ) ) ;
81
-
82
- // If we're about to issue a warning, check at the last minute for any
83
- // directives against the warnings "lint". If, for example, there's an
84
- // `allow(warnings)` in scope then we want to respect that instead.
85
- //
86
- // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
87
- // triggers in cases (like #80988) where you have `forbid(warnings)`,
88
- // and so if we turned that into an error, it'd defeat the purpose of the
89
- // future compatibility warning.
90
- if level == Level :: Warn && lint != LintId :: of ( FORBIDDEN_LINT_GROUPS ) {
91
- let ( warnings_level, warnings_src) = probe_for_lint_level ( LintId :: of ( builtin:: WARNINGS ) ) ;
92
- if let Some ( configured_warning_level) = warnings_level {
93
- if configured_warning_level != Level :: Warn {
94
- level = configured_warning_level;
95
- * src = warnings_src;
77
+ impl LintLevelSets {
78
+ pub fn new ( ) -> Self {
79
+ LintLevelSets { list : IndexVec :: new ( ) , lint_cap : Level :: Forbid }
80
+ }
81
+
82
+ pub fn get_lint_level (
83
+ & self ,
84
+ lint : & ' static Lint ,
85
+ idx : LintStackIndex ,
86
+ aux : Option < & FxHashMap < LintId , LevelAndSource > > ,
87
+ sess : & Session ,
88
+ ) -> LevelAndSource {
89
+ let ( level, mut src) = self . get_lint_id_level ( LintId :: of ( lint) , idx, aux) ;
90
+
91
+ // If `level` is none then we actually assume the default level for this
92
+ // lint.
93
+ let mut level = level. unwrap_or_else ( || lint. default_level ( sess. edition ( ) ) ) ;
94
+
95
+ // If we're about to issue a warning, check at the last minute for any
96
+ // directives against the warnings "lint". If, for example, there's an
97
+ // `allow(warnings)` in scope then we want to respect that instead.
98
+ //
99
+ // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
100
+ // triggers in cases (like #80988) where you have `forbid(warnings)`,
101
+ // and so if we turned that into an error, it'd defeat the purpose of the
102
+ // future compatibility warning.
103
+ if level == Level :: Warn && LintId :: of ( lint) != LintId :: of ( FORBIDDEN_LINT_GROUPS ) {
104
+ let ( warnings_level, warnings_src) =
105
+ self . get_lint_id_level ( LintId :: of ( builtin:: WARNINGS ) , idx, aux) ;
106
+ if let Some ( configured_warning_level) = warnings_level {
107
+ if configured_warning_level != Level :: Warn {
108
+ level = configured_warning_level;
109
+ src = warnings_src;
110
+ }
96
111
}
97
112
}
98
- }
99
113
100
- // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
101
- level = if let LintLevelSource :: CommandLine ( _, Level :: ForceWarn ( _) ) = src {
102
- level
103
- } else {
104
- cmp:: min ( level, sess. opts . lint_cap . unwrap_or ( Level :: Forbid ) )
105
- } ;
114
+ // Ensure that we never exceed the `--cap-lints` argument
115
+ // unless the source is a --force-warn
116
+ level = if let LintLevelSource :: CommandLine ( _, Level :: ForceWarn ( _) ) = src {
117
+ level
118
+ } else {
119
+ cmp:: min ( level, self . lint_cap )
120
+ } ;
121
+
122
+ if let Some ( driver_level) = sess. driver_lint_caps . get ( & LintId :: of ( lint) ) {
123
+ // Ensure that we never exceed driver level.
124
+ level = cmp:: min ( * driver_level, level) ;
125
+ }
106
126
107
- if let Some ( driver_level) = sess. driver_lint_caps . get ( & lint) {
108
- // Ensure that we never exceed driver level.
109
- level = cmp:: min ( * driver_level, level) ;
127
+ ( level, src)
110
128
}
111
129
112
- level
113
- }
114
-
115
- impl ShallowLintLevelMap {
116
- /// Perform a deep probe in the HIR tree looking for the actual level for the lint.
117
- /// This lint level is not usable for diagnostics, it needs to be corrected by
118
- /// `reveal_actual_level` beforehand.
119
- fn probe_for_lint_level (
130
+ pub fn get_lint_id_level (
120
131
& self ,
121
- tcx : TyCtxt < ' _ > ,
122
132
id : LintId ,
123
- start : HirId ,
133
+ mut idx : LintStackIndex ,
134
+ aux : Option < & FxHashMap < LintId , LevelAndSource > > ,
124
135
) -> ( Option < Level > , LintLevelSource ) {
125
- if let Some ( & ( level, src) ) = self . specs . get ( & id) {
126
- return ( Some ( level) , src) ;
136
+ if let Some ( specs) = aux {
137
+ if let Some ( & ( level, src) ) = specs. get ( & id) {
138
+ return ( Some ( level) , src) ;
139
+ }
127
140
}
128
-
129
- for parent in tcx. hir ( ) . parent_id_iter ( start) {
130
- let specs = tcx. shallow_lint_levels_on ( parent) ;
131
- if let Some ( & ( level, src) ) = specs. specs . get ( & id) {
141
+ loop {
142
+ let LintSet { ref specs, parent } = self . list [ idx] ;
143
+ if let Some ( & ( level, src) ) = specs. get ( & id) {
132
144
return ( Some ( level) , src) ;
133
145
}
146
+ if idx == COMMAND_LINE {
147
+ return ( None , LintLevelSource :: Default ) ;
148
+ }
149
+ idx = parent;
134
150
}
135
- ( None , LintLevelSource :: Default )
136
151
}
152
+ }
153
+
154
+ #[ derive( Debug ) ]
155
+ pub struct LintLevelMap {
156
+ /// This is a collection of lint expectations as described in RFC 2383, that
157
+ /// can be fulfilled during this compilation session. This means that at least
158
+ /// one expected lint is currently registered in the lint store.
159
+ ///
160
+ /// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect)
161
+ /// lint level.
162
+ pub lint_expectations : Vec < ( LintExpectationId , LintExpectation ) > ,
163
+ pub sets : LintLevelSets ,
164
+ pub id_to_set : FxHashMap < HirId , LintStackIndex > ,
165
+ }
137
166
138
- /// Fetch and return the user-visible lint level for the given lint at the given HirId.
139
- pub fn lint_level_id_at_node (
167
+ impl LintLevelMap {
168
+ /// If the `id` was previously registered with `register_id` when building
169
+ /// this `LintLevelMap` this returns the corresponding lint level and source
170
+ /// of the lint level for the lint provided.
171
+ ///
172
+ /// If the `id` was not previously registered, returns `None`. If `None` is
173
+ /// returned then the parent of `id` should be acquired and this function
174
+ /// should be called again.
175
+ pub fn level_and_source (
140
176
& self ,
141
- tcx : TyCtxt < ' _ > ,
142
- lint : LintId ,
177
+ lint : & ' static Lint ,
143
178
id : HirId ,
144
- ) -> ( Level , LintLevelSource ) {
145
- let ( level, mut src) = self . probe_for_lint_level ( tcx, lint, id) ;
146
- let level = reveal_actual_level ( level, & mut src, tcx. sess , lint, |lint| {
147
- self . probe_for_lint_level ( tcx, lint, id)
148
- } ) ;
149
- debug ! ( ?id, ?level, ?src) ;
150
- ( level, src)
179
+ session : & Session ,
180
+ ) -> Option < LevelAndSource > {
181
+ self . id_to_set . get ( & id) . map ( |idx| self . sets . get_lint_level ( lint, * idx, None , session) )
151
182
}
152
183
}
153
184
154
- impl TyCtxt < ' _ > {
155
- /// Fetch and return the user-visible lint level for the given lint at the given HirId.
156
- pub fn lint_level_at_node ( self , lint : & ' static Lint , id : HirId ) -> ( Level , LintLevelSource ) {
157
- self . shallow_lint_levels_on ( id) . lint_level_id_at_node ( self , LintId :: of ( lint) , id)
158
- }
185
+ impl < ' a > HashStable < StableHashingContext < ' a > > for LintLevelMap {
186
+ #[ inline]
187
+ fn hash_stable ( & self , hcx : & mut StableHashingContext < ' a > , hasher : & mut StableHasher ) {
188
+ let LintLevelMap { ref sets, ref id_to_set, ref lint_expectations } = * self ;
159
189
160
- /// Walks upwards from `id` to find a node which might change lint levels with attributes.
161
- /// It stops at `bound` and just returns it if reached.
162
- pub fn maybe_lint_level_root_bounded ( self , mut id : HirId , bound : HirId ) -> HirId {
163
- let hir = self . hir ( ) ;
164
- while id != bound && self . shallow_lint_levels_on ( id) . specs . is_empty ( ) {
165
- id = hir. get_parent_node ( id)
166
- }
167
- id
190
+ id_to_set. hash_stable ( hcx, hasher) ;
191
+ lint_expectations. hash_stable ( hcx, hasher) ;
192
+
193
+ hcx. while_hashing_spans ( true , |hcx| sets. hash_stable ( hcx, hasher) )
168
194
}
169
195
}
170
196
@@ -235,11 +261,11 @@ pub fn explain_lint_level_source(
235
261
) ) ;
236
262
}
237
263
}
238
- LintLevelSource :: Node { name : lint_attr_name, span , reason, .. } => {
264
+ LintLevelSource :: Node ( lint_attr_name, src , reason) => {
239
265
if let Some ( rationale) = reason {
240
266
err. note ( rationale. as_str ( ) ) ;
241
267
}
242
- err. span_note_once ( span , "the lint level is defined here" ) ;
268
+ err. span_note_once ( src , "the lint level is defined here" ) ;
243
269
if lint_attr_name. as_str ( ) != name {
244
270
let level_str = level. as_str ( ) ;
245
271
err. note_once ( & format ! (
0 commit comments