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