]>
Commit | Line | Data |
---|---|---|
dfeec247 XL |
1 | use crate::context::{CheckLintNameResult, LintStore}; |
2 | use crate::late::unerased_lint_store; | |
3dfed10e | 3 | use rustc_ast as ast; |
74b04a01 | 4 | use rustc_ast_pretty::pprust; |
dfeec247 | 5 | use rustc_data_structures::fx::FxHashMap; |
fc512014 | 6 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; |
dfeec247 | 7 | use rustc_hir as hir; |
5099ac24 FG |
8 | use rustc_hir::{intravisit, HirId}; |
9 | use rustc_middle::hir::nested_filter; | |
5869c6ff | 10 | use rustc_middle::lint::LevelAndSource; |
ba9703b0 | 11 | use rustc_middle::lint::LintDiagnosticBuilder; |
fc512014 | 12 | use rustc_middle::lint::{ |
136023e0 XL |
13 | struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, LintStackIndex, |
14 | COMMAND_LINE, | |
fc512014 | 15 | }; |
ba9703b0 | 16 | use rustc_middle::ty::query::Providers; |
5099ac24 | 17 | use rustc_middle::ty::{RegisteredTools, TyCtxt}; |
fc512014 XL |
18 | use rustc_session::lint::{ |
19 | builtin::{self, FORBIDDEN_LINT_GROUPS}, | |
20 | Level, Lint, LintId, | |
21 | }; | |
74b04a01 | 22 | use rustc_session::parse::feature_err; |
dfeec247 | 23 | use rustc_session::Session; |
74b04a01 | 24 | use rustc_span::symbol::{sym, Symbol}; |
f9f354fc | 25 | use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP}; |
fc512014 | 26 | use tracing::debug; |
dfeec247 | 27 | |
17df50a5 | 28 | fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { |
dfeec247 | 29 | let store = unerased_lint_store(tcx); |
5099ac24 FG |
30 | let levels = |
31 | LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools); | |
32 | let mut builder = LintLevelMapBuilder { levels, tcx }; | |
dfeec247 XL |
33 | let krate = tcx.hir().krate(); |
34 | ||
94222f64 | 35 | builder.levels.id_to_set.reserve(krate.owners.len() + 1); |
fc512014 | 36 | |
5099ac24 | 37 | let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true); |
dfeec247 | 38 | builder.levels.register_id(hir::CRATE_HIR_ID); |
c295e0f8 | 39 | tcx.hir().walk_toplevel_module(&mut builder); |
dfeec247 | 40 | builder.levels.pop(push); |
60c5eb7d | 41 | |
f9f354fc | 42 | builder.levels.build_map() |
3b2f2976 XL |
43 | } |
44 | ||
74b04a01 XL |
45 | pub struct LintLevelsBuilder<'s> { |
46 | sess: &'s Session, | |
dfeec247 | 47 | sets: LintLevelSets, |
136023e0 XL |
48 | id_to_set: FxHashMap<HirId, LintStackIndex>, |
49 | cur: LintStackIndex, | |
dfeec247 | 50 | warn_about_weird_lints: bool, |
fc512014 | 51 | store: &'s LintStore, |
5099ac24 | 52 | registered_tools: &'s RegisteredTools, |
3b2f2976 XL |
53 | } |
54 | ||
dfeec247 | 55 | pub struct BuilderPush { |
136023e0 | 56 | prev: LintStackIndex, |
dfeec247 XL |
57 | pub changed: bool, |
58 | } | |
3b2f2976 | 59 | |
74b04a01 | 60 | impl<'s> LintLevelsBuilder<'s> { |
6a06907d XL |
61 | pub fn new( |
62 | sess: &'s Session, | |
63 | warn_about_weird_lints: bool, | |
64 | store: &'s LintStore, | |
5099ac24 | 65 | registered_tools: &'s RegisteredTools, |
6a06907d | 66 | ) -> Self { |
dfeec247 XL |
67 | let mut builder = LintLevelsBuilder { |
68 | sess, | |
69 | sets: LintLevelSets::new(), | |
136023e0 | 70 | cur: COMMAND_LINE, |
dfeec247 XL |
71 | id_to_set: Default::default(), |
72 | warn_about_weird_lints, | |
fc512014 | 73 | store, |
5099ac24 | 74 | registered_tools, |
dfeec247 XL |
75 | }; |
76 | builder.process_command_line(sess, store); | |
77 | assert_eq!(builder.sets.list.len(), 1); | |
78 | builder | |
3b2f2976 XL |
79 | } |
80 | ||
5099ac24 FG |
81 | pub(crate) fn sess(&self) -> &Session { |
82 | self.sess | |
83 | } | |
84 | ||
85 | pub(crate) fn lint_store(&self) -> &LintStore { | |
86 | self.store | |
87 | } | |
88 | ||
e74abb32 | 89 | fn process_command_line(&mut self, sess: &Session, store: &LintStore) { |
0bf4aa26 | 90 | let mut specs = FxHashMap::default(); |
dfeec247 | 91 | self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); |
3b2f2976 XL |
92 | |
93 | for &(ref lint_name, level) in &sess.opts.lint_opts { | |
5099ac24 | 94 | store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools); |
29967ef6 | 95 | let orig_level = level; |
3b2f2976 | 96 | let lint_flag_val = Symbol::intern(lint_name); |
f9f354fc | 97 | |
3b2f2976 XL |
98 | let ids = match store.find_lints(&lint_name) { |
99 | Ok(ids) => ids, | |
100 | Err(_) => continue, // errors handled in check_lint_name_cmdline above | |
101 | }; | |
102 | for id in ids { | |
136023e0 XL |
103 | // ForceWarn and Forbid cannot be overriden |
104 | if let Some((Level::ForceWarn | Level::Forbid, _)) = specs.get(&id) { | |
105 | continue; | |
106 | } | |
107 | ||
f9f354fc | 108 | self.check_gated_lint(id, DUMMY_SP); |
fc512014 | 109 | let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); |
3b2f2976 XL |
110 | specs.insert(id, (level, src)); |
111 | } | |
112 | } | |
113 | ||
136023e0 | 114 | self.cur = self.sets.list.push(LintSet { specs, parent: COMMAND_LINE }); |
3b2f2976 XL |
115 | } |
116 | ||
fc512014 XL |
117 | /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful |
118 | /// (e.g. if a forbid was already inserted on the same scope), then emits a | |
119 | /// diagnostic with no change to `specs`. | |
120 | fn insert_spec( | |
121 | &mut self, | |
5869c6ff | 122 | specs: &mut FxHashMap<LintId, LevelAndSource>, |
fc512014 | 123 | id: LintId, |
5869c6ff | 124 | (level, src): LevelAndSource, |
fc512014 | 125 | ) { |
136023e0 XL |
126 | let (old_level, old_src) = |
127 | self.sets.get_lint_level(id.lint, self.cur, Some(&specs), &self.sess); | |
fc512014 XL |
128 | // Setting to a non-forbid level is an error if the lint previously had |
129 | // a forbid level. Note that this is not necessarily true even with a | |
130 | // `#[forbid(..)]` attribute present, as that is overriden by `--cap-lints`. | |
131 | // | |
132 | // This means that this only errors if we're truly lowering the lint | |
133 | // level from forbid. | |
134 | if level != Level::Forbid { | |
136023e0 | 135 | if let Level::Forbid = old_level { |
fc512014 XL |
136 | // Backwards compatibility check: |
137 | // | |
138 | // We used to not consider `forbid(lint_group)` | |
139 | // as preventing `allow(lint)` for some lint `lint` in | |
140 | // `lint_group`. For now, issue a future-compatibility | |
141 | // warning for this case. | |
142 | let id_name = id.lint.name_lower(); | |
143 | let fcw_warning = match old_src { | |
144 | LintLevelSource::Default => false, | |
145 | LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol), | |
146 | LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), | |
147 | }; | |
148 | debug!( | |
149 | "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", | |
150 | fcw_warning, specs, old_src, id_name | |
151 | ); | |
152 | ||
153 | let decorate_diag_builder = |mut diag_builder: DiagnosticBuilder<'_>| { | |
154 | diag_builder.span_label(src.span(), "overruled by previous forbid"); | |
155 | match old_src { | |
156 | LintLevelSource::Default => { | |
157 | diag_builder.note(&format!( | |
158 | "`forbid` lint level is the default for {}", | |
159 | id.to_string() | |
160 | )); | |
161 | } | |
162 | LintLevelSource::Node(_, forbid_source_span, reason) => { | |
163 | diag_builder.span_label(forbid_source_span, "`forbid` level set here"); | |
164 | if let Some(rationale) = reason { | |
a2a8927a | 165 | diag_builder.note(rationale.as_str()); |
fc512014 XL |
166 | } |
167 | } | |
168 | LintLevelSource::CommandLine(_, _) => { | |
169 | diag_builder.note("`forbid` lint level was set on command line"); | |
170 | } | |
171 | } | |
172 | diag_builder.emit(); | |
173 | }; | |
174 | if !fcw_warning { | |
175 | let diag_builder = struct_span_err!( | |
176 | self.sess, | |
177 | src.span(), | |
178 | E0453, | |
179 | "{}({}) incompatible with previous forbid", | |
180 | level.as_str(), | |
181 | src.name(), | |
182 | ); | |
183 | decorate_diag_builder(diag_builder); | |
184 | } else { | |
185 | self.struct_lint( | |
186 | FORBIDDEN_LINT_GROUPS, | |
187 | Some(src.span().into()), | |
188 | |diag_builder| { | |
189 | let diag_builder = diag_builder.build(&format!( | |
190 | "{}({}) incompatible with previous forbid", | |
191 | level.as_str(), | |
192 | src.name(), | |
193 | )); | |
194 | decorate_diag_builder(diag_builder); | |
195 | }, | |
196 | ); | |
197 | } | |
198 | ||
199 | // Retain the forbid lint level, unless we are | |
200 | // issuing a FCW. In the FCW case, we want to | |
201 | // respect the new setting. | |
202 | if !fcw_warning { | |
203 | return; | |
204 | } | |
205 | } | |
206 | } | |
136023e0 XL |
207 | if let Level::ForceWarn = old_level { |
208 | specs.insert(id, (old_level, old_src)); | |
209 | } else { | |
210 | specs.insert(id, (level, src)); | |
211 | } | |
fc512014 XL |
212 | } |
213 | ||
3b2f2976 XL |
214 | /// Pushes a list of AST lint attributes onto this context. |
215 | /// | |
0731742a XL |
216 | /// This function will return a `BuilderPush` object which should be passed |
217 | /// to `pop` when this scope for the attributes provided is exited. | |
3b2f2976 XL |
218 | /// |
219 | /// This function will perform a number of tasks: | |
220 | /// | |
221 | /// * It'll validate all lint-related attributes in `attrs` | |
a1dfa0c6 | 222 | /// * It'll mark all lint-related attributes as used |
3b2f2976 | 223 | /// * Lint levels will be updated based on the attributes provided |
f9f354fc XL |
224 | /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to |
225 | /// `#[allow]` | |
3b2f2976 XL |
226 | /// |
227 | /// Don't forget to call `pop`! | |
5099ac24 | 228 | pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush { |
0bf4aa26 | 229 | let mut specs = FxHashMap::default(); |
3b2f2976 | 230 | let sess = self.sess; |
dfeec247 | 231 | let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); |
3b2f2976 | 232 | for attr in attrs { |
a2a8927a XL |
233 | let Some(level) = Level::from_symbol(attr.name_or_empty()) else { |
234 | continue | |
3b2f2976 XL |
235 | }; |
236 | ||
a2a8927a XL |
237 | let Some(mut metas) = attr.meta_item_list() else { |
238 | continue | |
c295e0f8 | 239 | }; |
3b2f2976 | 240 | |
a1dfa0c6 XL |
241 | if metas.is_empty() { |
242 | // FIXME (#55112): issue unused-attributes lint for `#[level()]` | |
243 | continue; | |
244 | } | |
245 | ||
246 | // Before processing the lint names, look for a reason (RFC 2383) | |
247 | // at the end. | |
248 | let mut reason = None; | |
dfeec247 | 249 | let tail_li = &metas[metas.len() - 1]; |
a1dfa0c6 | 250 | if let Some(item) = tail_li.meta_item() { |
e74abb32 | 251 | match item.kind { |
dfeec247 | 252 | ast::MetaItemKind::Word => {} // actual lint names handled later |
a1dfa0c6 | 253 | ast::MetaItemKind::NameValue(ref name_value) => { |
48663c56 | 254 | if item.path == sym::reason { |
a1dfa0c6 XL |
255 | // FIXME (#55112): issue unused-attributes lint if we thereby |
256 | // don't have any lint names (`#[level(reason = "foo")]`) | |
e74abb32 | 257 | if let ast::LitKind::Str(rationale, _) = name_value.kind { |
0731742a | 258 | if !self.sess.features_untracked().lint_reasons { |
dfeec247 | 259 | feature_err( |
a1dfa0c6 | 260 | &self.sess.parse_sess, |
48663c56 | 261 | sym::lint_reasons, |
a1dfa0c6 | 262 | item.span, |
dfeec247 | 263 | "lint reasons are experimental", |
60c5eb7d XL |
264 | ) |
265 | .emit(); | |
a1dfa0c6 | 266 | } |
0731742a | 267 | reason = Some(rationale); |
a1dfa0c6 | 268 | } else { |
dc9dc135 XL |
269 | bad_attr(name_value.span) |
270 | .span_label(name_value.span, "reason must be a string literal") | |
271 | .emit(); | |
a1dfa0c6 | 272 | } |
cdc7bbd5 XL |
273 | // found reason, reslice meta list to exclude it |
274 | metas.pop().unwrap(); | |
a1dfa0c6 | 275 | } else { |
dc9dc135 XL |
276 | bad_attr(item.span) |
277 | .span_label(item.span, "bad attribute argument") | |
278 | .emit(); | |
a1dfa0c6 | 279 | } |
dfeec247 | 280 | } |
a1dfa0c6 | 281 | ast::MetaItemKind::List(_) => { |
dfeec247 | 282 | bad_attr(item.span).span_label(item.span, "bad attribute argument").emit(); |
a1dfa0c6 XL |
283 | } |
284 | } | |
285 | } | |
286 | ||
3b2f2976 | 287 | for li in metas { |
cdc7bbd5 XL |
288 | let sp = li.span(); |
289 | let mut meta_item = match li { | |
290 | ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, | |
9fa01778 | 291 | _ => { |
dc9dc135 XL |
292 | let mut err = bad_attr(sp); |
293 | let mut add_label = true; | |
a1dfa0c6 | 294 | if let Some(item) = li.meta_item() { |
e74abb32 | 295 | if let ast::MetaItemKind::NameValue(_) = item.kind { |
48663c56 | 296 | if item.path == sym::reason { |
dc9dc135 XL |
297 | err.span_label(sp, "reason in lint attribute must come last"); |
298 | add_label = false; | |
a1dfa0c6 XL |
299 | } |
300 | } | |
301 | } | |
dc9dc135 XL |
302 | if add_label { |
303 | err.span_label(sp, "bad attribute argument"); | |
304 | } | |
a1dfa0c6 XL |
305 | err.emit(); |
306 | continue; | |
3b2f2976 XL |
307 | } |
308 | }; | |
136023e0 XL |
309 | let tool_ident = if meta_item.path.segments.len() > 1 { |
310 | Some(meta_item.path.segments.remove(0).ident) | |
b7449926 XL |
311 | } else { |
312 | None | |
313 | }; | |
136023e0 | 314 | let tool_name = tool_ident.map(|ident| ident.name); |
cdc7bbd5 | 315 | let name = pprust::path_to_string(&meta_item.path); |
5099ac24 FG |
316 | let lint_result = |
317 | self.store.check_lint_name(&name, tool_name, self.registered_tools); | |
6a06907d | 318 | match &lint_result { |
3b2f2976 | 319 | CheckLintNameResult::Ok(ids) => { |
cdc7bbd5 XL |
320 | let src = LintLevelSource::Node( |
321 | meta_item.path.segments.last().expect("empty lint name").ident.name, | |
322 | sp, | |
323 | reason, | |
324 | ); | |
6a06907d | 325 | for &id in *ids { |
f035d41b | 326 | self.check_gated_lint(id, attr.span); |
fc512014 | 327 | self.insert_spec(&mut specs, id, (level, src)); |
3b2f2976 XL |
328 | } |
329 | } | |
330 | ||
b7449926 | 331 | CheckLintNameResult::Tool(result) => { |
6a06907d | 332 | match *result { |
b7449926 | 333 | Ok(ids) => { |
136023e0 XL |
334 | let complete_name = |
335 | &format!("{}::{}", tool_ident.unwrap().name, name); | |
fc512014 | 336 | let src = LintLevelSource::Node( |
dfeec247 | 337 | Symbol::intern(complete_name), |
cdc7bbd5 | 338 | sp, |
dfeec247 | 339 | reason, |
a1dfa0c6 | 340 | ); |
b7449926 | 341 | for id in ids { |
fc512014 | 342 | self.insert_spec(&mut specs, *id, (level, src)); |
b7449926 XL |
343 | } |
344 | } | |
6a06907d | 345 | Err((Some(ids), ref new_lint_name)) => { |
b7449926 XL |
346 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
347 | let (lvl, src) = | |
dfeec247 | 348 | self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); |
dfeec247 | 349 | struct_lint_level( |
b7449926 XL |
350 | self.sess, |
351 | lint, | |
352 | lvl, | |
353 | src, | |
cdc7bbd5 | 354 | Some(sp.into()), |
74b04a01 XL |
355 | |lint| { |
356 | let msg = format!( | |
357 | "lint name `{}` is deprecated \ | |
5869c6ff | 358 | and may not have an effect in the future.", |
74b04a01 XL |
359 | name |
360 | ); | |
361 | lint.build(&msg) | |
362 | .span_suggestion( | |
cdc7bbd5 | 363 | sp, |
74b04a01 XL |
364 | "change it to", |
365 | new_lint_name.to_string(), | |
366 | Applicability::MachineApplicable, | |
367 | ) | |
368 | .emit(); | |
369 | }, | |
370 | ); | |
b7449926 | 371 | |
fc512014 | 372 | let src = LintLevelSource::Node( |
dfeec247 | 373 | Symbol::intern(&new_lint_name), |
cdc7bbd5 | 374 | sp, |
dfeec247 | 375 | reason, |
a1dfa0c6 | 376 | ); |
b7449926 | 377 | for id in ids { |
fc512014 | 378 | self.insert_spec(&mut specs, *id, (level, src)); |
b7449926 XL |
379 | } |
380 | } | |
381 | Err((None, _)) => { | |
382 | // If Tool(Err(None, _)) is returned, then either the lint does not | |
383 | // exist in the tool or the code was not compiled with the tool and | |
384 | // therefore the lint was never added to the `LintStore`. To detect | |
385 | // this is the responsibility of the lint tool. | |
386 | } | |
387 | } | |
388 | } | |
389 | ||
136023e0 XL |
390 | &CheckLintNameResult::NoTool => { |
391 | let mut err = struct_span_err!( | |
392 | sess, | |
393 | tool_ident.map_or(DUMMY_SP, |ident| ident.span), | |
394 | E0710, | |
395 | "unknown tool name `{}` found in scoped lint: `{}::{}`", | |
396 | tool_name.unwrap(), | |
397 | tool_name.unwrap(), | |
398 | pprust::path_to_string(&meta_item.path), | |
399 | ); | |
400 | if sess.is_nightly_build() { | |
401 | err.help(&format!( | |
402 | "add `#![register_tool({})]` to the crate root", | |
403 | tool_name.unwrap() | |
404 | )); | |
405 | } | |
406 | err.emit(); | |
407 | continue; | |
408 | } | |
409 | ||
3b2f2976 XL |
410 | _ if !self.warn_about_weird_lints => {} |
411 | ||
8faf50e0 | 412 | CheckLintNameResult::Warning(msg, renamed) => { |
3b2f2976 | 413 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
6a06907d | 414 | let (renamed_lint_level, src) = |
dfeec247 | 415 | self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); |
74b04a01 | 416 | struct_lint_level( |
dfeec247 XL |
417 | self.sess, |
418 | lint, | |
6a06907d | 419 | renamed_lint_level, |
dfeec247 | 420 | src, |
cdc7bbd5 | 421 | Some(sp.into()), |
74b04a01 XL |
422 | |lint| { |
423 | let mut err = lint.build(&msg); | |
6a06907d | 424 | if let Some(new_name) = &renamed { |
74b04a01 | 425 | err.span_suggestion( |
cdc7bbd5 | 426 | sp, |
74b04a01 | 427 | "use the new name", |
6a06907d | 428 | new_name.to_string(), |
74b04a01 XL |
429 | Applicability::MachineApplicable, |
430 | ); | |
431 | } | |
432 | err.emit(); | |
433 | }, | |
dfeec247 | 434 | ); |
3b2f2976 | 435 | } |
0731742a | 436 | CheckLintNameResult::NoLint(suggestion) => { |
3b2f2976 | 437 | let lint = builtin::UNKNOWN_LINTS; |
dfeec247 XL |
438 | let (level, src) = |
439 | self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); | |
cdc7bbd5 | 440 | struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| { |
136023e0 XL |
441 | let name = if let Some(tool_ident) = tool_ident { |
442 | format!("{}::{}", tool_ident.name, name) | |
cdc7bbd5 XL |
443 | } else { |
444 | name.to_string() | |
445 | }; | |
446 | let mut db = lint.build(&format!("unknown lint: `{}`", name)); | |
447 | if let Some(suggestion) = suggestion { | |
448 | db.span_suggestion( | |
449 | sp, | |
450 | "did you mean", | |
451 | suggestion.to_string(), | |
452 | Applicability::MachineApplicable, | |
453 | ); | |
454 | } | |
455 | db.emit(); | |
456 | }); | |
3b2f2976 XL |
457 | } |
458 | } | |
6a06907d XL |
459 | // If this lint was renamed, apply the new lint instead of ignoring the attribute. |
460 | // This happens outside of the match because the new lint should be applied even if | |
461 | // we don't warn about the name change. | |
462 | if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result { | |
463 | // Ignore any errors or warnings that happen because the new name is inaccurate | |
cdc7bbd5 | 464 | // NOTE: `new_name` already includes the tool name, so we don't have to add it again. |
136023e0 | 465 | if let CheckLintNameResult::Ok(ids) = |
5099ac24 | 466 | self.store.check_lint_name(&new_name, None, self.registered_tools) |
136023e0 | 467 | { |
cdc7bbd5 | 468 | let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); |
6a06907d XL |
469 | for &id in ids { |
470 | self.check_gated_lint(id, attr.span); | |
471 | self.insert_spec(&mut specs, id, (level, src)); | |
472 | } | |
cdc7bbd5 XL |
473 | } else { |
474 | panic!("renamed lint does not exist: {}", new_name); | |
6a06907d XL |
475 | } |
476 | } | |
3b2f2976 XL |
477 | } |
478 | } | |
479 | ||
f035d41b XL |
480 | if !is_crate_node { |
481 | for (id, &(level, ref src)) in specs.iter() { | |
482 | if !id.lint.crate_level_only { | |
483 | continue; | |
484 | } | |
485 | ||
a2a8927a XL |
486 | let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else { |
487 | continue | |
f035d41b XL |
488 | }; |
489 | ||
490 | let lint = builtin::UNUSED_ATTRIBUTES; | |
491 | let (lint_level, lint_src) = | |
492 | self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); | |
493 | struct_lint_level( | |
494 | self.sess, | |
495 | lint, | |
496 | lint_level, | |
497 | lint_src, | |
498 | Some(lint_attr_span.into()), | |
499 | |lint| { | |
500 | let mut db = lint.build(&format!( | |
501 | "{}({}) is ignored unless specified at crate level", | |
502 | level.as_str(), | |
503 | lint_attr_name | |
504 | )); | |
505 | db.emit(); | |
506 | }, | |
507 | ); | |
508 | // don't set a separate error for every lint in the group | |
509 | break; | |
510 | } | |
511 | } | |
512 | ||
3b2f2976 | 513 | let prev = self.cur; |
74b04a01 | 514 | if !specs.is_empty() { |
136023e0 | 515 | self.cur = self.sets.list.push(LintSet { specs, parent: prev }); |
3b2f2976 XL |
516 | } |
517 | ||
74b04a01 | 518 | BuilderPush { prev, changed: prev != self.cur } |
3b2f2976 XL |
519 | } |
520 | ||
f035d41b XL |
521 | /// Checks if the lint is gated on a feature that is not enabled. |
522 | fn check_gated_lint(&self, lint_id: LintId, span: Span) { | |
523 | if let Some(feature) = lint_id.lint.feature_gate { | |
524 | if !self.sess.features_untracked().enabled(feature) { | |
525 | feature_err( | |
526 | &self.sess.parse_sess, | |
527 | feature, | |
528 | span, | |
529 | &format!("the `{}` lint is unstable", lint_id.lint.name_lower()), | |
530 | ) | |
531 | .emit(); | |
532 | } | |
f9f354fc XL |
533 | } |
534 | } | |
535 | ||
3b2f2976 XL |
536 | /// Called after `push` when the scope of a set of attributes are exited. |
537 | pub fn pop(&mut self, push: BuilderPush) { | |
538 | self.cur = push.prev; | |
539 | } | |
540 | ||
f9f354fc | 541 | /// Find the lint level for a lint. |
fc512014 | 542 | pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) { |
f9f354fc XL |
543 | self.sets.get_lint_level(lint, self.cur, None, self.sess) |
544 | } | |
545 | ||
3b2f2976 XL |
546 | /// Used to emit a lint-related diagnostic based on the current state of |
547 | /// this lint context. | |
dfeec247 XL |
548 | pub fn struct_lint( |
549 | &self, | |
550 | lint: &'static Lint, | |
551 | span: Option<MultiSpan>, | |
74b04a01 XL |
552 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>), |
553 | ) { | |
f9f354fc | 554 | let (level, src) = self.lint_level(lint); |
74b04a01 | 555 | struct_lint_level(self.sess, lint, level, src, span, decorate) |
3b2f2976 XL |
556 | } |
557 | ||
558 | /// Registers the ID provided with the current set of lints stored in | |
559 | /// this context. | |
560 | pub fn register_id(&mut self, id: HirId) { | |
561 | self.id_to_set.insert(id, self.cur); | |
562 | } | |
563 | ||
3b2f2976 | 564 | pub fn build_map(self) -> LintLevelMap { |
dfeec247 | 565 | LintLevelMap { sets: self.sets, id_to_set: self.id_to_set } |
3b2f2976 XL |
566 | } |
567 | } | |
568 | ||
5099ac24 | 569 | struct LintLevelMapBuilder<'tcx> { |
dfeec247 XL |
570 | levels: LintLevelsBuilder<'tcx>, |
571 | tcx: TyCtxt<'tcx>, | |
3b2f2976 XL |
572 | } |
573 | ||
5099ac24 | 574 | impl LintLevelMapBuilder<'_> { |
6a06907d | 575 | fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F) |
dfeec247 XL |
576 | where |
577 | F: FnOnce(&mut Self), | |
3b2f2976 | 578 | { |
f035d41b | 579 | let is_crate_hir = id == hir::CRATE_HIR_ID; |
6a06907d | 580 | let attrs = self.tcx.hir().attrs(id); |
5099ac24 | 581 | let push = self.levels.push(attrs, is_crate_hir); |
dfeec247 XL |
582 | if push.changed { |
583 | self.levels.register_id(id); | |
584 | } | |
585 | f(self); | |
586 | self.levels.pop(push); | |
3b2f2976 | 587 | } |
ea8adc8c XL |
588 | } |
589 | ||
5099ac24 FG |
590 | impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { |
591 | type NestedFilter = nested_filter::All; | |
ea8adc8c | 592 | |
5099ac24 FG |
593 | fn nested_visit_map(&mut self) -> Self::Map { |
594 | self.tcx.hir() | |
dfeec247 | 595 | } |
ea8adc8c | 596 | |
dfeec247 | 597 | fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { |
6a06907d | 598 | self.with_lint_attrs(param.hir_id, |builder| { |
dfeec247 XL |
599 | intravisit::walk_param(builder, param); |
600 | }); | |
601 | } | |
ea8adc8c | 602 | |
dfeec247 | 603 | fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { |
6a06907d | 604 | self.with_lint_attrs(it.hir_id(), |builder| { |
dfeec247 XL |
605 | intravisit::walk_item(builder, it); |
606 | }); | |
607 | } | |
ea8adc8c | 608 | |
dfeec247 | 609 | fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { |
6a06907d | 610 | self.with_lint_attrs(it.hir_id(), |builder| { |
dfeec247 XL |
611 | intravisit::walk_foreign_item(builder, it); |
612 | }) | |
613 | } | |
614 | ||
29967ef6 XL |
615 | fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) { |
616 | // We will call `with_lint_attrs` when we walk | |
617 | // the `StmtKind`. The outer statement itself doesn't | |
618 | // define the lint levels. | |
619 | intravisit::walk_stmt(self, e); | |
620 | } | |
621 | ||
dfeec247 | 622 | fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { |
6a06907d | 623 | self.with_lint_attrs(e.hir_id, |builder| { |
dfeec247 XL |
624 | intravisit::walk_expr(builder, e); |
625 | }) | |
626 | } | |
ea8adc8c | 627 | |
6a06907d XL |
628 | fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { |
629 | self.with_lint_attrs(s.hir_id, |builder| { | |
630 | intravisit::walk_field_def(builder, s); | |
dfeec247 XL |
631 | }) |
632 | } | |
ea8adc8c | 633 | |
dfeec247 XL |
634 | fn visit_variant( |
635 | &mut self, | |
636 | v: &'tcx hir::Variant<'tcx>, | |
637 | g: &'tcx hir::Generics<'tcx>, | |
638 | item_id: hir::HirId, | |
639 | ) { | |
6a06907d | 640 | self.with_lint_attrs(v.id, |builder| { |
dfeec247 | 641 | intravisit::walk_variant(builder, v, g, item_id); |
ea8adc8c XL |
642 | }) |
643 | } | |
dfeec247 XL |
644 | |
645 | fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { | |
6a06907d | 646 | self.with_lint_attrs(l.hir_id, |builder| { |
dfeec247 XL |
647 | intravisit::walk_local(builder, l); |
648 | }) | |
649 | } | |
650 | ||
651 | fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { | |
6a06907d | 652 | self.with_lint_attrs(a.hir_id, |builder| { |
dfeec247 XL |
653 | intravisit::walk_arm(builder, a); |
654 | }) | |
655 | } | |
656 | ||
657 | fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { | |
6a06907d | 658 | self.with_lint_attrs(trait_item.hir_id(), |builder| { |
dfeec247 XL |
659 | intravisit::walk_trait_item(builder, trait_item); |
660 | }); | |
661 | } | |
662 | ||
663 | fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { | |
6a06907d | 664 | self.with_lint_attrs(impl_item.hir_id(), |builder| { |
dfeec247 XL |
665 | intravisit::walk_impl_item(builder, impl_item); |
666 | }); | |
667 | } | |
668 | } | |
669 | ||
f035d41b | 670 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 671 | providers.lint_levels = lint_levels; |
ea8adc8c | 672 | } |