]>
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; |
17df50a5 | 8 | use rustc_hir::{intravisit, HirId, CRATE_HIR_ID}; |
ba9703b0 | 9 | use rustc_middle::hir::map::Map; |
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 XL |
16 | use rustc_middle::ty::query::Providers; |
17 | use rustc_middle::ty::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); |
17df50a5 | 30 | let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID); |
6a06907d | 31 | let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs); |
dfeec247 XL |
32 | let mut builder = LintLevelMapBuilder { levels, tcx, store }; |
33 | let krate = tcx.hir().krate(); | |
34 | ||
94222f64 | 35 | builder.levels.id_to_set.reserve(krate.owners.len() + 1); |
fc512014 | 36 | |
6a06907d | 37 | let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, 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, |
6a06907d | 52 | crate_attrs: &'s [ast::Attribute], |
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, | |
65 | crate_attrs: &'s [ast::Attribute], | |
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, |
6a06907d | 74 | crate_attrs, |
dfeec247 XL |
75 | }; |
76 | builder.process_command_line(sess, store); | |
77 | assert_eq!(builder.sets.list.len(), 1); | |
78 | builder | |
3b2f2976 XL |
79 | } |
80 | ||
e74abb32 | 81 | fn process_command_line(&mut self, sess: &Session, store: &LintStore) { |
0bf4aa26 | 82 | let mut specs = FxHashMap::default(); |
dfeec247 | 83 | self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); |
3b2f2976 XL |
84 | |
85 | for &(ref lint_name, level) in &sess.opts.lint_opts { | |
136023e0 | 86 | store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs); |
29967ef6 | 87 | let orig_level = level; |
3b2f2976 | 88 | let lint_flag_val = Symbol::intern(lint_name); |
f9f354fc | 89 | |
3b2f2976 XL |
90 | let ids = match store.find_lints(&lint_name) { |
91 | Ok(ids) => ids, | |
92 | Err(_) => continue, // errors handled in check_lint_name_cmdline above | |
93 | }; | |
94 | for id in ids { | |
136023e0 XL |
95 | // ForceWarn and Forbid cannot be overriden |
96 | if let Some((Level::ForceWarn | Level::Forbid, _)) = specs.get(&id) { | |
97 | continue; | |
98 | } | |
99 | ||
f9f354fc | 100 | self.check_gated_lint(id, DUMMY_SP); |
fc512014 | 101 | let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); |
3b2f2976 XL |
102 | specs.insert(id, (level, src)); |
103 | } | |
104 | } | |
105 | ||
136023e0 | 106 | self.cur = self.sets.list.push(LintSet { specs, parent: COMMAND_LINE }); |
3b2f2976 XL |
107 | } |
108 | ||
fc512014 XL |
109 | /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful |
110 | /// (e.g. if a forbid was already inserted on the same scope), then emits a | |
111 | /// diagnostic with no change to `specs`. | |
112 | fn insert_spec( | |
113 | &mut self, | |
5869c6ff | 114 | specs: &mut FxHashMap<LintId, LevelAndSource>, |
fc512014 | 115 | id: LintId, |
5869c6ff | 116 | (level, src): LevelAndSource, |
fc512014 | 117 | ) { |
136023e0 XL |
118 | let (old_level, old_src) = |
119 | self.sets.get_lint_level(id.lint, self.cur, Some(&specs), &self.sess); | |
fc512014 XL |
120 | // Setting to a non-forbid level is an error if the lint previously had |
121 | // a forbid level. Note that this is not necessarily true even with a | |
122 | // `#[forbid(..)]` attribute present, as that is overriden by `--cap-lints`. | |
123 | // | |
124 | // This means that this only errors if we're truly lowering the lint | |
125 | // level from forbid. | |
126 | if level != Level::Forbid { | |
136023e0 | 127 | if let Level::Forbid = old_level { |
fc512014 XL |
128 | // Backwards compatibility check: |
129 | // | |
130 | // We used to not consider `forbid(lint_group)` | |
131 | // as preventing `allow(lint)` for some lint `lint` in | |
132 | // `lint_group`. For now, issue a future-compatibility | |
133 | // warning for this case. | |
134 | let id_name = id.lint.name_lower(); | |
135 | let fcw_warning = match old_src { | |
136 | LintLevelSource::Default => false, | |
137 | LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol), | |
138 | LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), | |
139 | }; | |
140 | debug!( | |
141 | "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", | |
142 | fcw_warning, specs, old_src, id_name | |
143 | ); | |
144 | ||
145 | let decorate_diag_builder = |mut diag_builder: DiagnosticBuilder<'_>| { | |
146 | diag_builder.span_label(src.span(), "overruled by previous forbid"); | |
147 | match old_src { | |
148 | LintLevelSource::Default => { | |
149 | diag_builder.note(&format!( | |
150 | "`forbid` lint level is the default for {}", | |
151 | id.to_string() | |
152 | )); | |
153 | } | |
154 | LintLevelSource::Node(_, forbid_source_span, reason) => { | |
155 | diag_builder.span_label(forbid_source_span, "`forbid` level set here"); | |
156 | if let Some(rationale) = reason { | |
157 | diag_builder.note(&rationale.as_str()); | |
158 | } | |
159 | } | |
160 | LintLevelSource::CommandLine(_, _) => { | |
161 | diag_builder.note("`forbid` lint level was set on command line"); | |
162 | } | |
163 | } | |
164 | diag_builder.emit(); | |
165 | }; | |
166 | if !fcw_warning { | |
167 | let diag_builder = struct_span_err!( | |
168 | self.sess, | |
169 | src.span(), | |
170 | E0453, | |
171 | "{}({}) incompatible with previous forbid", | |
172 | level.as_str(), | |
173 | src.name(), | |
174 | ); | |
175 | decorate_diag_builder(diag_builder); | |
176 | } else { | |
177 | self.struct_lint( | |
178 | FORBIDDEN_LINT_GROUPS, | |
179 | Some(src.span().into()), | |
180 | |diag_builder| { | |
181 | let diag_builder = diag_builder.build(&format!( | |
182 | "{}({}) incompatible with previous forbid", | |
183 | level.as_str(), | |
184 | src.name(), | |
185 | )); | |
186 | decorate_diag_builder(diag_builder); | |
187 | }, | |
188 | ); | |
189 | } | |
190 | ||
191 | // Retain the forbid lint level, unless we are | |
192 | // issuing a FCW. In the FCW case, we want to | |
193 | // respect the new setting. | |
194 | if !fcw_warning { | |
195 | return; | |
196 | } | |
197 | } | |
198 | } | |
136023e0 XL |
199 | if let Level::ForceWarn = old_level { |
200 | specs.insert(id, (old_level, old_src)); | |
201 | } else { | |
202 | specs.insert(id, (level, src)); | |
203 | } | |
fc512014 XL |
204 | } |
205 | ||
3b2f2976 XL |
206 | /// Pushes a list of AST lint attributes onto this context. |
207 | /// | |
0731742a XL |
208 | /// This function will return a `BuilderPush` object which should be passed |
209 | /// to `pop` when this scope for the attributes provided is exited. | |
3b2f2976 XL |
210 | /// |
211 | /// This function will perform a number of tasks: | |
212 | /// | |
213 | /// * It'll validate all lint-related attributes in `attrs` | |
a1dfa0c6 | 214 | /// * It'll mark all lint-related attributes as used |
3b2f2976 | 215 | /// * Lint levels will be updated based on the attributes provided |
f9f354fc XL |
216 | /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to |
217 | /// `#[allow]` | |
3b2f2976 XL |
218 | /// |
219 | /// Don't forget to call `pop`! | |
fc512014 | 220 | pub(crate) fn push( |
f035d41b XL |
221 | &mut self, |
222 | attrs: &[ast::Attribute], | |
223 | store: &LintStore, | |
224 | is_crate_node: bool, | |
225 | ) -> BuilderPush { | |
0bf4aa26 | 226 | let mut specs = FxHashMap::default(); |
3b2f2976 | 227 | let sess = self.sess; |
dfeec247 | 228 | let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); |
3b2f2976 | 229 | for attr in attrs { |
48663c56 | 230 | let level = match Level::from_symbol(attr.name_or_empty()) { |
3b2f2976 XL |
231 | None => continue, |
232 | Some(lvl) => lvl, | |
233 | }; | |
234 | ||
c295e0f8 XL |
235 | let mut metas = match attr.meta_item_list() { |
236 | Some(x) => x, | |
237 | None => continue, | |
238 | }; | |
3b2f2976 | 239 | |
a1dfa0c6 XL |
240 | if metas.is_empty() { |
241 | // FIXME (#55112): issue unused-attributes lint for `#[level()]` | |
242 | continue; | |
243 | } | |
244 | ||
245 | // Before processing the lint names, look for a reason (RFC 2383) | |
246 | // at the end. | |
247 | let mut reason = None; | |
dfeec247 | 248 | let tail_li = &metas[metas.len() - 1]; |
a1dfa0c6 | 249 | if let Some(item) = tail_li.meta_item() { |
e74abb32 | 250 | match item.kind { |
dfeec247 | 251 | ast::MetaItemKind::Word => {} // actual lint names handled later |
a1dfa0c6 | 252 | ast::MetaItemKind::NameValue(ref name_value) => { |
48663c56 | 253 | if item.path == sym::reason { |
a1dfa0c6 XL |
254 | // FIXME (#55112): issue unused-attributes lint if we thereby |
255 | // don't have any lint names (`#[level(reason = "foo")]`) | |
e74abb32 | 256 | if let ast::LitKind::Str(rationale, _) = name_value.kind { |
0731742a | 257 | if !self.sess.features_untracked().lint_reasons { |
dfeec247 | 258 | feature_err( |
a1dfa0c6 | 259 | &self.sess.parse_sess, |
48663c56 | 260 | sym::lint_reasons, |
a1dfa0c6 | 261 | item.span, |
dfeec247 | 262 | "lint reasons are experimental", |
60c5eb7d XL |
263 | ) |
264 | .emit(); | |
a1dfa0c6 | 265 | } |
0731742a | 266 | reason = Some(rationale); |
a1dfa0c6 | 267 | } else { |
dc9dc135 XL |
268 | bad_attr(name_value.span) |
269 | .span_label(name_value.span, "reason must be a string literal") | |
270 | .emit(); | |
a1dfa0c6 | 271 | } |
cdc7bbd5 XL |
272 | // found reason, reslice meta list to exclude it |
273 | metas.pop().unwrap(); | |
a1dfa0c6 | 274 | } else { |
dc9dc135 XL |
275 | bad_attr(item.span) |
276 | .span_label(item.span, "bad attribute argument") | |
277 | .emit(); | |
a1dfa0c6 | 278 | } |
dfeec247 | 279 | } |
a1dfa0c6 | 280 | ast::MetaItemKind::List(_) => { |
dfeec247 | 281 | bad_attr(item.span).span_label(item.span, "bad attribute argument").emit(); |
a1dfa0c6 XL |
282 | } |
283 | } | |
284 | } | |
285 | ||
3b2f2976 | 286 | for li in metas { |
cdc7bbd5 XL |
287 | let sp = li.span(); |
288 | let mut meta_item = match li { | |
289 | ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, | |
9fa01778 | 290 | _ => { |
dc9dc135 XL |
291 | let mut err = bad_attr(sp); |
292 | let mut add_label = true; | |
a1dfa0c6 | 293 | if let Some(item) = li.meta_item() { |
e74abb32 | 294 | if let ast::MetaItemKind::NameValue(_) = item.kind { |
48663c56 | 295 | if item.path == sym::reason { |
dc9dc135 XL |
296 | err.span_label(sp, "reason in lint attribute must come last"); |
297 | add_label = false; | |
a1dfa0c6 XL |
298 | } |
299 | } | |
300 | } | |
dc9dc135 XL |
301 | if add_label { |
302 | err.span_label(sp, "bad attribute argument"); | |
303 | } | |
a1dfa0c6 XL |
304 | err.emit(); |
305 | continue; | |
3b2f2976 XL |
306 | } |
307 | }; | |
136023e0 XL |
308 | let tool_ident = if meta_item.path.segments.len() > 1 { |
309 | Some(meta_item.path.segments.remove(0).ident) | |
b7449926 XL |
310 | } else { |
311 | None | |
312 | }; | |
136023e0 | 313 | let tool_name = tool_ident.map(|ident| ident.name); |
cdc7bbd5 | 314 | let name = pprust::path_to_string(&meta_item.path); |
136023e0 | 315 | let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs); |
6a06907d | 316 | match &lint_result { |
3b2f2976 | 317 | CheckLintNameResult::Ok(ids) => { |
cdc7bbd5 XL |
318 | let src = LintLevelSource::Node( |
319 | meta_item.path.segments.last().expect("empty lint name").ident.name, | |
320 | sp, | |
321 | reason, | |
322 | ); | |
6a06907d | 323 | for &id in *ids { |
f035d41b | 324 | self.check_gated_lint(id, attr.span); |
fc512014 | 325 | self.insert_spec(&mut specs, id, (level, src)); |
3b2f2976 XL |
326 | } |
327 | } | |
328 | ||
b7449926 | 329 | CheckLintNameResult::Tool(result) => { |
6a06907d | 330 | match *result { |
b7449926 | 331 | Ok(ids) => { |
136023e0 XL |
332 | let complete_name = |
333 | &format!("{}::{}", tool_ident.unwrap().name, name); | |
fc512014 | 334 | let src = LintLevelSource::Node( |
dfeec247 | 335 | Symbol::intern(complete_name), |
cdc7bbd5 | 336 | sp, |
dfeec247 | 337 | reason, |
a1dfa0c6 | 338 | ); |
b7449926 | 339 | for id in ids { |
fc512014 | 340 | self.insert_spec(&mut specs, *id, (level, src)); |
b7449926 XL |
341 | } |
342 | } | |
6a06907d | 343 | Err((Some(ids), ref new_lint_name)) => { |
b7449926 XL |
344 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
345 | let (lvl, src) = | |
dfeec247 | 346 | self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); |
dfeec247 | 347 | struct_lint_level( |
b7449926 XL |
348 | self.sess, |
349 | lint, | |
350 | lvl, | |
351 | src, | |
cdc7bbd5 | 352 | Some(sp.into()), |
74b04a01 XL |
353 | |lint| { |
354 | let msg = format!( | |
355 | "lint name `{}` is deprecated \ | |
5869c6ff | 356 | and may not have an effect in the future.", |
74b04a01 XL |
357 | name |
358 | ); | |
359 | lint.build(&msg) | |
360 | .span_suggestion( | |
cdc7bbd5 | 361 | sp, |
74b04a01 XL |
362 | "change it to", |
363 | new_lint_name.to_string(), | |
364 | Applicability::MachineApplicable, | |
365 | ) | |
366 | .emit(); | |
367 | }, | |
368 | ); | |
b7449926 | 369 | |
fc512014 | 370 | let src = LintLevelSource::Node( |
dfeec247 | 371 | Symbol::intern(&new_lint_name), |
cdc7bbd5 | 372 | sp, |
dfeec247 | 373 | reason, |
a1dfa0c6 | 374 | ); |
b7449926 | 375 | for id in ids { |
fc512014 | 376 | self.insert_spec(&mut specs, *id, (level, src)); |
b7449926 XL |
377 | } |
378 | } | |
379 | Err((None, _)) => { | |
380 | // If Tool(Err(None, _)) is returned, then either the lint does not | |
381 | // exist in the tool or the code was not compiled with the tool and | |
382 | // therefore the lint was never added to the `LintStore`. To detect | |
383 | // this is the responsibility of the lint tool. | |
384 | } | |
385 | } | |
386 | } | |
387 | ||
136023e0 XL |
388 | &CheckLintNameResult::NoTool => { |
389 | let mut err = struct_span_err!( | |
390 | sess, | |
391 | tool_ident.map_or(DUMMY_SP, |ident| ident.span), | |
392 | E0710, | |
393 | "unknown tool name `{}` found in scoped lint: `{}::{}`", | |
394 | tool_name.unwrap(), | |
395 | tool_name.unwrap(), | |
396 | pprust::path_to_string(&meta_item.path), | |
397 | ); | |
398 | if sess.is_nightly_build() { | |
399 | err.help(&format!( | |
400 | "add `#![register_tool({})]` to the crate root", | |
401 | tool_name.unwrap() | |
402 | )); | |
403 | } | |
404 | err.emit(); | |
405 | continue; | |
406 | } | |
407 | ||
3b2f2976 XL |
408 | _ if !self.warn_about_weird_lints => {} |
409 | ||
8faf50e0 | 410 | CheckLintNameResult::Warning(msg, renamed) => { |
3b2f2976 | 411 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
6a06907d | 412 | let (renamed_lint_level, src) = |
dfeec247 | 413 | self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); |
74b04a01 | 414 | struct_lint_level( |
dfeec247 XL |
415 | self.sess, |
416 | lint, | |
6a06907d | 417 | renamed_lint_level, |
dfeec247 | 418 | src, |
cdc7bbd5 | 419 | Some(sp.into()), |
74b04a01 XL |
420 | |lint| { |
421 | let mut err = lint.build(&msg); | |
6a06907d | 422 | if let Some(new_name) = &renamed { |
74b04a01 | 423 | err.span_suggestion( |
cdc7bbd5 | 424 | sp, |
74b04a01 | 425 | "use the new name", |
6a06907d | 426 | new_name.to_string(), |
74b04a01 XL |
427 | Applicability::MachineApplicable, |
428 | ); | |
429 | } | |
430 | err.emit(); | |
431 | }, | |
dfeec247 | 432 | ); |
3b2f2976 | 433 | } |
0731742a | 434 | CheckLintNameResult::NoLint(suggestion) => { |
3b2f2976 | 435 | let lint = builtin::UNKNOWN_LINTS; |
dfeec247 XL |
436 | let (level, src) = |
437 | self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); | |
cdc7bbd5 | 438 | struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| { |
136023e0 XL |
439 | let name = if let Some(tool_ident) = tool_ident { |
440 | format!("{}::{}", tool_ident.name, name) | |
cdc7bbd5 XL |
441 | } else { |
442 | name.to_string() | |
443 | }; | |
444 | let mut db = lint.build(&format!("unknown lint: `{}`", name)); | |
445 | if let Some(suggestion) = suggestion { | |
446 | db.span_suggestion( | |
447 | sp, | |
448 | "did you mean", | |
449 | suggestion.to_string(), | |
450 | Applicability::MachineApplicable, | |
451 | ); | |
452 | } | |
453 | db.emit(); | |
454 | }); | |
3b2f2976 XL |
455 | } |
456 | } | |
6a06907d XL |
457 | // If this lint was renamed, apply the new lint instead of ignoring the attribute. |
458 | // This happens outside of the match because the new lint should be applied even if | |
459 | // we don't warn about the name change. | |
460 | if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result { | |
461 | // Ignore any errors or warnings that happen because the new name is inaccurate | |
cdc7bbd5 | 462 | // NOTE: `new_name` already includes the tool name, so we don't have to add it again. |
136023e0 XL |
463 | if let CheckLintNameResult::Ok(ids) = |
464 | store.check_lint_name(sess, &new_name, None, self.crate_attrs) | |
465 | { | |
cdc7bbd5 | 466 | let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); |
6a06907d XL |
467 | for &id in ids { |
468 | self.check_gated_lint(id, attr.span); | |
469 | self.insert_spec(&mut specs, id, (level, src)); | |
470 | } | |
cdc7bbd5 XL |
471 | } else { |
472 | panic!("renamed lint does not exist: {}", new_name); | |
6a06907d XL |
473 | } |
474 | } | |
3b2f2976 XL |
475 | } |
476 | } | |
477 | ||
f035d41b XL |
478 | if !is_crate_node { |
479 | for (id, &(level, ref src)) in specs.iter() { | |
480 | if !id.lint.crate_level_only { | |
481 | continue; | |
482 | } | |
483 | ||
484 | let (lint_attr_name, lint_attr_span) = match *src { | |
fc512014 | 485 | LintLevelSource::Node(name, span, _) => (name, span), |
f035d41b XL |
486 | _ => continue, |
487 | }; | |
488 | ||
489 | let lint = builtin::UNUSED_ATTRIBUTES; | |
490 | let (lint_level, lint_src) = | |
491 | self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess); | |
492 | struct_lint_level( | |
493 | self.sess, | |
494 | lint, | |
495 | lint_level, | |
496 | lint_src, | |
497 | Some(lint_attr_span.into()), | |
498 | |lint| { | |
499 | let mut db = lint.build(&format!( | |
500 | "{}({}) is ignored unless specified at crate level", | |
501 | level.as_str(), | |
502 | lint_attr_name | |
503 | )); | |
504 | db.emit(); | |
505 | }, | |
506 | ); | |
507 | // don't set a separate error for every lint in the group | |
508 | break; | |
509 | } | |
510 | } | |
511 | ||
3b2f2976 | 512 | let prev = self.cur; |
74b04a01 | 513 | if !specs.is_empty() { |
136023e0 | 514 | self.cur = self.sets.list.push(LintSet { 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 | ||
136023e0 | 568 | pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool { |
6a06907d XL |
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()) | |
94222f64 | 576 | .flatten() |
6a06907d XL |
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 | } |