]>
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; |
f2b60f7d | 6 | use rustc_errors::{Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan}; |
dfeec247 | 7 | use rustc_hir as hir; |
5099ac24 FG |
8 | use rustc_hir::{intravisit, HirId}; |
9 | use rustc_middle::hir::nested_filter; | |
fc512014 | 10 | use rustc_middle::lint::{ |
064997fb FG |
11 | struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets, |
12 | LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE, | |
fc512014 | 13 | }; |
ba9703b0 | 14 | use rustc_middle::ty::query::Providers; |
5099ac24 | 15 | use rustc_middle::ty::{RegisteredTools, TyCtxt}; |
fc512014 | 16 | use rustc_session::lint::{ |
923072b8 | 17 | builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, |
5e7ed085 | 18 | Level, Lint, LintExpectationId, LintId, |
fc512014 | 19 | }; |
5e7ed085 | 20 | use rustc_session::parse::{add_feature_diagnostics, feature_err}; |
dfeec247 | 21 | use rustc_session::Session; |
74b04a01 | 22 | use rustc_span::symbol::{sym, Symbol}; |
04454e1e | 23 | use rustc_span::{Span, DUMMY_SP}; |
f2b60f7d FG |
24 | |
25 | use crate::errors::{ | |
26 | MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub, | |
27 | UnknownToolInScopedLint, | |
28 | }; | |
dfeec247 | 29 | |
17df50a5 | 30 | fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { |
dfeec247 | 31 | let store = unerased_lint_store(tcx); |
5099ac24 FG |
32 | let levels = |
33 | LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools); | |
34 | let mut builder = LintLevelMapBuilder { levels, tcx }; | |
dfeec247 XL |
35 | let krate = tcx.hir().krate(); |
36 | ||
94222f64 | 37 | builder.levels.id_to_set.reserve(krate.owners.len() + 1); |
fc512014 | 38 | |
5e7ed085 FG |
39 | let push = |
40 | builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true, Some(hir::CRATE_HIR_ID)); | |
41 | ||
dfeec247 | 42 | builder.levels.register_id(hir::CRATE_HIR_ID); |
c295e0f8 | 43 | tcx.hir().walk_toplevel_module(&mut builder); |
dfeec247 | 44 | builder.levels.pop(push); |
60c5eb7d | 45 | |
5e7ed085 | 46 | builder.levels.update_unstable_expectation_ids(); |
f9f354fc | 47 | builder.levels.build_map() |
3b2f2976 XL |
48 | } |
49 | ||
74b04a01 XL |
50 | pub struct LintLevelsBuilder<'s> { |
51 | sess: &'s Session, | |
5e7ed085 FG |
52 | lint_expectations: Vec<(LintExpectationId, LintExpectation)>, |
53 | /// Each expectation has a stable and an unstable identifier. This map | |
54 | /// is used to map from unstable to stable [`LintExpectationId`]s. | |
55 | expectation_id_map: FxHashMap<LintExpectationId, LintExpectationId>, | |
dfeec247 | 56 | sets: LintLevelSets, |
136023e0 XL |
57 | id_to_set: FxHashMap<HirId, LintStackIndex>, |
58 | cur: LintStackIndex, | |
dfeec247 | 59 | warn_about_weird_lints: bool, |
fc512014 | 60 | store: &'s LintStore, |
5099ac24 | 61 | registered_tools: &'s RegisteredTools, |
3b2f2976 XL |
62 | } |
63 | ||
dfeec247 | 64 | pub struct BuilderPush { |
136023e0 | 65 | prev: LintStackIndex, |
dfeec247 XL |
66 | pub changed: bool, |
67 | } | |
3b2f2976 | 68 | |
74b04a01 | 69 | impl<'s> LintLevelsBuilder<'s> { |
6a06907d XL |
70 | pub fn new( |
71 | sess: &'s Session, | |
72 | warn_about_weird_lints: bool, | |
73 | store: &'s LintStore, | |
5099ac24 | 74 | registered_tools: &'s RegisteredTools, |
6a06907d | 75 | ) -> Self { |
dfeec247 XL |
76 | let mut builder = LintLevelsBuilder { |
77 | sess, | |
5e7ed085 FG |
78 | lint_expectations: Default::default(), |
79 | expectation_id_map: Default::default(), | |
dfeec247 | 80 | sets: LintLevelSets::new(), |
136023e0 | 81 | cur: COMMAND_LINE, |
dfeec247 XL |
82 | id_to_set: Default::default(), |
83 | warn_about_weird_lints, | |
fc512014 | 84 | store, |
5099ac24 | 85 | registered_tools, |
dfeec247 XL |
86 | }; |
87 | builder.process_command_line(sess, store); | |
88 | assert_eq!(builder.sets.list.len(), 1); | |
89 | builder | |
3b2f2976 XL |
90 | } |
91 | ||
5099ac24 FG |
92 | pub(crate) fn sess(&self) -> &Session { |
93 | self.sess | |
94 | } | |
95 | ||
96 | pub(crate) fn lint_store(&self) -> &LintStore { | |
97 | self.store | |
98 | } | |
99 | ||
5e7ed085 FG |
100 | fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> { |
101 | &self.sets.list[self.cur].specs | |
102 | } | |
103 | ||
104 | fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> { | |
105 | &mut self.sets.list[self.cur].specs | |
106 | } | |
107 | ||
e74abb32 | 108 | fn process_command_line(&mut self, sess: &Session, store: &LintStore) { |
dfeec247 | 109 | self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); |
3b2f2976 | 110 | |
5e7ed085 FG |
111 | self.cur = |
112 | self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE }); | |
3b2f2976 | 113 | for &(ref lint_name, level) in &sess.opts.lint_opts { |
5099ac24 | 114 | store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools); |
29967ef6 | 115 | let orig_level = level; |
3b2f2976 | 116 | let lint_flag_val = Symbol::intern(lint_name); |
f9f354fc | 117 | |
5e7ed085 FG |
118 | let Ok(ids) = store.find_lints(&lint_name) else { |
119 | // errors handled in check_lint_name_cmdline above | |
120 | continue | |
3b2f2976 XL |
121 | }; |
122 | for id in ids { | |
5e7ed085 | 123 | // ForceWarn and Forbid cannot be overridden |
923072b8 FG |
124 | if let Some((Level::ForceWarn(_) | Level::Forbid, _)) = |
125 | self.current_specs().get(&id) | |
126 | { | |
136023e0 XL |
127 | continue; |
128 | } | |
129 | ||
5e7ed085 FG |
130 | if self.check_gated_lint(id, DUMMY_SP) { |
131 | let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); | |
132 | self.current_specs_mut().insert(id, (level, src)); | |
133 | } | |
3b2f2976 XL |
134 | } |
135 | } | |
3b2f2976 XL |
136 | } |
137 | ||
fc512014 XL |
138 | /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful |
139 | /// (e.g. if a forbid was already inserted on the same scope), then emits a | |
140 | /// diagnostic with no change to `specs`. | |
5e7ed085 | 141 | fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) { |
136023e0 | 142 | let (old_level, old_src) = |
5e7ed085 | 143 | self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess); |
fc512014 XL |
144 | // Setting to a non-forbid level is an error if the lint previously had |
145 | // a forbid level. Note that this is not necessarily true even with a | |
5e7ed085 | 146 | // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`. |
fc512014 XL |
147 | // |
148 | // This means that this only errors if we're truly lowering the lint | |
149 | // level from forbid. | |
150 | if level != Level::Forbid { | |
136023e0 | 151 | if let Level::Forbid = old_level { |
fc512014 XL |
152 | // Backwards compatibility check: |
153 | // | |
154 | // We used to not consider `forbid(lint_group)` | |
155 | // as preventing `allow(lint)` for some lint `lint` in | |
156 | // `lint_group`. For now, issue a future-compatibility | |
157 | // warning for this case. | |
158 | let id_name = id.lint.name_lower(); | |
159 | let fcw_warning = match old_src { | |
160 | LintLevelSource::Default => false, | |
161 | LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol), | |
162 | LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), | |
163 | }; | |
164 | debug!( | |
165 | "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", | |
5e7ed085 FG |
166 | fcw_warning, |
167 | self.current_specs(), | |
168 | old_src, | |
169 | id_name | |
fc512014 XL |
170 | ); |
171 | ||
5e7ed085 FG |
172 | let decorate_diag = |diag: &mut Diagnostic| { |
173 | diag.span_label(src.span(), "overruled by previous forbid"); | |
fc512014 XL |
174 | match old_src { |
175 | LintLevelSource::Default => { | |
5e7ed085 | 176 | diag.note(&format!( |
fc512014 XL |
177 | "`forbid` lint level is the default for {}", |
178 | id.to_string() | |
179 | )); | |
180 | } | |
181 | LintLevelSource::Node(_, forbid_source_span, reason) => { | |
5e7ed085 | 182 | diag.span_label(forbid_source_span, "`forbid` level set here"); |
fc512014 | 183 | if let Some(rationale) = reason { |
5e7ed085 | 184 | diag.note(rationale.as_str()); |
fc512014 XL |
185 | } |
186 | } | |
187 | LintLevelSource::CommandLine(_, _) => { | |
5e7ed085 | 188 | diag.note("`forbid` lint level was set on command line"); |
fc512014 XL |
189 | } |
190 | } | |
fc512014 XL |
191 | }; |
192 | if !fcw_warning { | |
f2b60f7d FG |
193 | self.sess.emit_err(OverruledAttribute { |
194 | span: src.span(), | |
195 | overruled: src.span(), | |
196 | lint_level: level.as_str().to_string(), | |
197 | lint_source: src.name(), | |
198 | sub: match old_src { | |
199 | LintLevelSource::Default => { | |
200 | OverruledAttributeSub::DefaultSource { id: id.to_string() } | |
201 | } | |
202 | LintLevelSource::Node(_, forbid_source_span, reason) => { | |
203 | OverruledAttributeSub::NodeSource { | |
204 | span: forbid_source_span, | |
205 | reason, | |
206 | } | |
207 | } | |
208 | LintLevelSource::CommandLine(_, _) => { | |
209 | OverruledAttributeSub::CommandLineSource | |
210 | } | |
211 | }, | |
212 | }); | |
fc512014 XL |
213 | } else { |
214 | self.struct_lint( | |
215 | FORBIDDEN_LINT_GROUPS, | |
216 | Some(src.span().into()), | |
217 | |diag_builder| { | |
5e7ed085 | 218 | let mut diag_builder = diag_builder.build(&format!( |
fc512014 XL |
219 | "{}({}) incompatible with previous forbid", |
220 | level.as_str(), | |
221 | src.name(), | |
222 | )); | |
5e7ed085 FG |
223 | decorate_diag(&mut diag_builder); |
224 | diag_builder.emit(); | |
fc512014 XL |
225 | }, |
226 | ); | |
227 | } | |
228 | ||
229 | // Retain the forbid lint level, unless we are | |
230 | // issuing a FCW. In the FCW case, we want to | |
231 | // respect the new setting. | |
232 | if !fcw_warning { | |
233 | return; | |
234 | } | |
235 | } | |
236 | } | |
5e7ed085 FG |
237 | |
238 | // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself. | |
239 | // Handling expectations of this lint would add additional complexity with little to no | |
240 | // benefit. The expect level for this lint will therefore be ignored. | |
241 | if let Level::Expect(_) = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) { | |
242 | return; | |
243 | } | |
244 | ||
923072b8 FG |
245 | match (old_level, level) { |
246 | // If the new level is an expectation store it in `ForceWarn` | |
247 | (Level::ForceWarn(_), Level::Expect(expectation_id)) => self | |
248 | .current_specs_mut() | |
249 | .insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)), | |
250 | // Keep `ForceWarn` level but drop the expectation | |
251 | (Level::ForceWarn(_), _) => { | |
252 | self.current_specs_mut().insert(id, (Level::ForceWarn(None), old_src)) | |
253 | } | |
254 | // Set the lint level as normal | |
255 | _ => self.current_specs_mut().insert(id, (level, src)), | |
256 | }; | |
fc512014 XL |
257 | } |
258 | ||
3b2f2976 XL |
259 | /// Pushes a list of AST lint attributes onto this context. |
260 | /// | |
0731742a XL |
261 | /// This function will return a `BuilderPush` object which should be passed |
262 | /// to `pop` when this scope for the attributes provided is exited. | |
3b2f2976 XL |
263 | /// |
264 | /// This function will perform a number of tasks: | |
265 | /// | |
266 | /// * It'll validate all lint-related attributes in `attrs` | |
a1dfa0c6 | 267 | /// * It'll mark all lint-related attributes as used |
3b2f2976 | 268 | /// * Lint levels will be updated based on the attributes provided |
f9f354fc XL |
269 | /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to |
270 | /// `#[allow]` | |
3b2f2976 XL |
271 | /// |
272 | /// Don't forget to call `pop`! | |
5e7ed085 FG |
273 | pub(crate) fn push( |
274 | &mut self, | |
275 | attrs: &[ast::Attribute], | |
276 | is_crate_node: bool, | |
277 | source_hir_id: Option<HirId>, | |
278 | ) -> BuilderPush { | |
279 | let prev = self.cur; | |
280 | self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev }); | |
281 | ||
3b2f2976 | 282 | let sess = self.sess; |
5e7ed085 | 283 | for (attr_index, attr) in attrs.iter().enumerate() { |
923072b8 FG |
284 | if attr.has_name(sym::automatically_derived) { |
285 | self.current_specs_mut().insert( | |
286 | LintId::of(SINGLE_USE_LIFETIMES), | |
287 | (Level::Allow, LintLevelSource::Default), | |
288 | ); | |
289 | continue; | |
290 | } | |
291 | ||
5e7ed085 FG |
292 | let level = match Level::from_attr(attr) { |
293 | None => continue, | |
923072b8 | 294 | // This is the only lint level with a `LintExpectationId` that can be created from an attribute |
5e7ed085 FG |
295 | Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { |
296 | let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index); | |
297 | ||
298 | Level::Expect(stable_id) | |
299 | } | |
300 | Some(lvl) => lvl, | |
3b2f2976 XL |
301 | }; |
302 | ||
a2a8927a XL |
303 | let Some(mut metas) = attr.meta_item_list() else { |
304 | continue | |
c295e0f8 | 305 | }; |
3b2f2976 | 306 | |
a1dfa0c6 | 307 | if metas.is_empty() { |
5e7ed085 | 308 | // This emits the unused_attributes lint for `#[level()]` |
a1dfa0c6 XL |
309 | continue; |
310 | } | |
311 | ||
312 | // Before processing the lint names, look for a reason (RFC 2383) | |
313 | // at the end. | |
314 | let mut reason = None; | |
dfeec247 | 315 | let tail_li = &metas[metas.len() - 1]; |
a1dfa0c6 | 316 | if let Some(item) = tail_li.meta_item() { |
e74abb32 | 317 | match item.kind { |
dfeec247 | 318 | ast::MetaItemKind::Word => {} // actual lint names handled later |
a1dfa0c6 | 319 | ast::MetaItemKind::NameValue(ref name_value) => { |
48663c56 | 320 | if item.path == sym::reason { |
e74abb32 | 321 | if let ast::LitKind::Str(rationale, _) = name_value.kind { |
0731742a | 322 | if !self.sess.features_untracked().lint_reasons { |
dfeec247 | 323 | feature_err( |
a1dfa0c6 | 324 | &self.sess.parse_sess, |
48663c56 | 325 | sym::lint_reasons, |
a1dfa0c6 | 326 | item.span, |
dfeec247 | 327 | "lint reasons are experimental", |
60c5eb7d XL |
328 | ) |
329 | .emit(); | |
a1dfa0c6 | 330 | } |
0731742a | 331 | reason = Some(rationale); |
a1dfa0c6 | 332 | } else { |
f2b60f7d FG |
333 | sess.emit_err(MalformedAttribute { |
334 | span: name_value.span, | |
335 | sub: MalformedAttributeSub::ReasonMustBeStringLiteral( | |
336 | name_value.span, | |
337 | ), | |
338 | }); | |
a1dfa0c6 | 339 | } |
cdc7bbd5 XL |
340 | // found reason, reslice meta list to exclude it |
341 | metas.pop().unwrap(); | |
a1dfa0c6 | 342 | } else { |
f2b60f7d FG |
343 | sess.emit_err(MalformedAttribute { |
344 | span: item.span, | |
345 | sub: MalformedAttributeSub::BadAttributeArgument(item.span), | |
346 | }); | |
a1dfa0c6 | 347 | } |
dfeec247 | 348 | } |
a1dfa0c6 | 349 | ast::MetaItemKind::List(_) => { |
f2b60f7d FG |
350 | sess.emit_err(MalformedAttribute { |
351 | span: item.span, | |
352 | sub: MalformedAttributeSub::BadAttributeArgument(item.span), | |
353 | }); | |
a1dfa0c6 XL |
354 | } |
355 | } | |
356 | } | |
357 | ||
5e7ed085 FG |
358 | for (lint_index, li) in metas.iter_mut().enumerate() { |
359 | let level = match level { | |
360 | Level::Expect(mut id) => { | |
361 | id.set_lint_index(Some(lint_index as u16)); | |
362 | Level::Expect(id) | |
363 | } | |
364 | level => level, | |
365 | }; | |
366 | ||
cdc7bbd5 | 367 | let sp = li.span(); |
5e7ed085 | 368 | let meta_item = match li { |
cdc7bbd5 | 369 | ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, |
9fa01778 | 370 | _ => { |
a1dfa0c6 | 371 | if let Some(item) = li.meta_item() { |
e74abb32 | 372 | if let ast::MetaItemKind::NameValue(_) = item.kind { |
48663c56 | 373 | if item.path == sym::reason { |
f2b60f7d FG |
374 | sess.emit_err(MalformedAttribute { |
375 | span: sp, | |
376 | sub: MalformedAttributeSub::ReasonMustComeLast(sp), | |
377 | }); | |
378 | continue; | |
a1dfa0c6 XL |
379 | } |
380 | } | |
381 | } | |
f2b60f7d FG |
382 | sess.emit_err(MalformedAttribute { |
383 | span: sp, | |
384 | sub: MalformedAttributeSub::BadAttributeArgument(sp), | |
385 | }); | |
a1dfa0c6 | 386 | continue; |
3b2f2976 XL |
387 | } |
388 | }; | |
136023e0 XL |
389 | let tool_ident = if meta_item.path.segments.len() > 1 { |
390 | Some(meta_item.path.segments.remove(0).ident) | |
b7449926 XL |
391 | } else { |
392 | None | |
393 | }; | |
136023e0 | 394 | let tool_name = tool_ident.map(|ident| ident.name); |
cdc7bbd5 | 395 | let name = pprust::path_to_string(&meta_item.path); |
5099ac24 FG |
396 | let lint_result = |
397 | self.store.check_lint_name(&name, tool_name, self.registered_tools); | |
6a06907d | 398 | match &lint_result { |
3b2f2976 | 399 | CheckLintNameResult::Ok(ids) => { |
5e7ed085 FG |
400 | // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]` |
401 | // in that case we want to avoid overriding the lint level but instead add an expectation that | |
402 | // can't be fulfilled. The lint message will include an explanation, that the | |
403 | // `unfulfilled_lint_expectations` lint can't be expected. | |
404 | if let Level::Expect(expect_id) = level { | |
405 | // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we | |
406 | // only need to check the slice if it contains a single lint. | |
407 | let is_unfulfilled_lint_expectations = match ids { | |
408 | [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), | |
409 | _ => false, | |
410 | }; | |
411 | self.lint_expectations.push(( | |
412 | expect_id, | |
04454e1e FG |
413 | LintExpectation::new( |
414 | reason, | |
415 | sp, | |
416 | is_unfulfilled_lint_expectations, | |
417 | tool_name, | |
418 | ), | |
5e7ed085 FG |
419 | )); |
420 | } | |
cdc7bbd5 XL |
421 | let src = LintLevelSource::Node( |
422 | meta_item.path.segments.last().expect("empty lint name").ident.name, | |
423 | sp, | |
424 | reason, | |
425 | ); | |
6a06907d | 426 | for &id in *ids { |
5e7ed085 FG |
427 | if self.check_gated_lint(id, attr.span) { |
428 | self.insert_spec(id, (level, src)); | |
429 | } | |
3b2f2976 XL |
430 | } |
431 | } | |
432 | ||
b7449926 | 433 | CheckLintNameResult::Tool(result) => { |
6a06907d | 434 | match *result { |
b7449926 | 435 | Ok(ids) => { |
136023e0 XL |
436 | let complete_name = |
437 | &format!("{}::{}", tool_ident.unwrap().name, name); | |
fc512014 | 438 | let src = LintLevelSource::Node( |
dfeec247 | 439 | Symbol::intern(complete_name), |
cdc7bbd5 | 440 | sp, |
dfeec247 | 441 | reason, |
a1dfa0c6 | 442 | ); |
f2b60f7d FG |
443 | for &id in ids { |
444 | if self.check_gated_lint(id, attr.span) { | |
445 | self.insert_spec(id, (level, src)); | |
446 | } | |
5e7ed085 FG |
447 | } |
448 | if let Level::Expect(expect_id) = level { | |
04454e1e FG |
449 | self.lint_expectations.push(( |
450 | expect_id, | |
451 | LintExpectation::new(reason, sp, false, tool_name), | |
452 | )); | |
b7449926 XL |
453 | } |
454 | } | |
6a06907d | 455 | Err((Some(ids), ref new_lint_name)) => { |
b7449926 | 456 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
5e7ed085 FG |
457 | let (lvl, src) = self.sets.get_lint_level( |
458 | lint, | |
459 | self.cur, | |
460 | Some(self.current_specs()), | |
461 | &sess, | |
462 | ); | |
dfeec247 | 463 | struct_lint_level( |
b7449926 XL |
464 | self.sess, |
465 | lint, | |
466 | lvl, | |
467 | src, | |
cdc7bbd5 | 468 | Some(sp.into()), |
74b04a01 XL |
469 | |lint| { |
470 | let msg = format!( | |
471 | "lint name `{}` is deprecated \ | |
5869c6ff | 472 | and may not have an effect in the future.", |
74b04a01 XL |
473 | name |
474 | ); | |
475 | lint.build(&msg) | |
476 | .span_suggestion( | |
cdc7bbd5 | 477 | sp, |
74b04a01 | 478 | "change it to", |
923072b8 | 479 | new_lint_name, |
74b04a01 XL |
480 | Applicability::MachineApplicable, |
481 | ) | |
482 | .emit(); | |
483 | }, | |
484 | ); | |
b7449926 | 485 | |
fc512014 | 486 | let src = LintLevelSource::Node( |
dfeec247 | 487 | Symbol::intern(&new_lint_name), |
cdc7bbd5 | 488 | sp, |
dfeec247 | 489 | reason, |
a1dfa0c6 | 490 | ); |
b7449926 | 491 | for id in ids { |
5e7ed085 FG |
492 | self.insert_spec(*id, (level, src)); |
493 | } | |
494 | if let Level::Expect(expect_id) = level { | |
04454e1e FG |
495 | self.lint_expectations.push(( |
496 | expect_id, | |
497 | LintExpectation::new(reason, sp, false, tool_name), | |
498 | )); | |
b7449926 XL |
499 | } |
500 | } | |
501 | Err((None, _)) => { | |
502 | // If Tool(Err(None, _)) is returned, then either the lint does not | |
503 | // exist in the tool or the code was not compiled with the tool and | |
504 | // therefore the lint was never added to the `LintStore`. To detect | |
505 | // this is the responsibility of the lint tool. | |
506 | } | |
507 | } | |
508 | } | |
509 | ||
136023e0 | 510 | &CheckLintNameResult::NoTool => { |
f2b60f7d FG |
511 | sess.emit_err(UnknownToolInScopedLint { |
512 | span: tool_ident.map(|ident| ident.span), | |
513 | tool_name: tool_name.unwrap(), | |
514 | lint_name: pprust::path_to_string(&meta_item.path), | |
515 | is_nightly_build: sess.is_nightly_build().then_some(()), | |
516 | }); | |
136023e0 XL |
517 | continue; |
518 | } | |
519 | ||
3b2f2976 XL |
520 | _ if !self.warn_about_weird_lints => {} |
521 | ||
8faf50e0 | 522 | CheckLintNameResult::Warning(msg, renamed) => { |
3b2f2976 | 523 | let lint = builtin::RENAMED_AND_REMOVED_LINTS; |
5e7ed085 FG |
524 | let (renamed_lint_level, src) = self.sets.get_lint_level( |
525 | lint, | |
526 | self.cur, | |
527 | Some(self.current_specs()), | |
528 | &sess, | |
529 | ); | |
74b04a01 | 530 | struct_lint_level( |
dfeec247 XL |
531 | self.sess, |
532 | lint, | |
6a06907d | 533 | renamed_lint_level, |
dfeec247 | 534 | src, |
cdc7bbd5 | 535 | Some(sp.into()), |
74b04a01 | 536 | |lint| { |
064997fb | 537 | let mut err = lint.build(msg); |
6a06907d | 538 | if let Some(new_name) = &renamed { |
74b04a01 | 539 | err.span_suggestion( |
cdc7bbd5 | 540 | sp, |
74b04a01 | 541 | "use the new name", |
923072b8 | 542 | new_name, |
74b04a01 XL |
543 | Applicability::MachineApplicable, |
544 | ); | |
545 | } | |
546 | err.emit(); | |
547 | }, | |
dfeec247 | 548 | ); |
3b2f2976 | 549 | } |
0731742a | 550 | CheckLintNameResult::NoLint(suggestion) => { |
3b2f2976 | 551 | let lint = builtin::UNKNOWN_LINTS; |
5e7ed085 FG |
552 | let (level, src) = self.sets.get_lint_level( |
553 | lint, | |
554 | self.cur, | |
555 | Some(self.current_specs()), | |
556 | self.sess, | |
557 | ); | |
cdc7bbd5 | 558 | struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| { |
136023e0 XL |
559 | let name = if let Some(tool_ident) = tool_ident { |
560 | format!("{}::{}", tool_ident.name, name) | |
cdc7bbd5 XL |
561 | } else { |
562 | name.to_string() | |
563 | }; | |
064997fb | 564 | let mut db = lint.build(format!("unknown lint: `{}`", name)); |
cdc7bbd5 XL |
565 | if let Some(suggestion) = suggestion { |
566 | db.span_suggestion( | |
567 | sp, | |
568 | "did you mean", | |
923072b8 | 569 | suggestion, |
cdc7bbd5 XL |
570 | Applicability::MachineApplicable, |
571 | ); | |
572 | } | |
573 | db.emit(); | |
574 | }); | |
3b2f2976 XL |
575 | } |
576 | } | |
6a06907d XL |
577 | // If this lint was renamed, apply the new lint instead of ignoring the attribute. |
578 | // This happens outside of the match because the new lint should be applied even if | |
579 | // we don't warn about the name change. | |
580 | if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result { | |
581 | // Ignore any errors or warnings that happen because the new name is inaccurate | |
cdc7bbd5 | 582 | // NOTE: `new_name` already includes the tool name, so we don't have to add it again. |
136023e0 | 583 | if let CheckLintNameResult::Ok(ids) = |
5099ac24 | 584 | self.store.check_lint_name(&new_name, None, self.registered_tools) |
136023e0 | 585 | { |
cdc7bbd5 | 586 | let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); |
6a06907d | 587 | for &id in ids { |
5e7ed085 FG |
588 | if self.check_gated_lint(id, attr.span) { |
589 | self.insert_spec(id, (level, src)); | |
590 | } | |
591 | } | |
592 | if let Level::Expect(expect_id) = level { | |
04454e1e FG |
593 | self.lint_expectations.push(( |
594 | expect_id, | |
595 | LintExpectation::new(reason, sp, false, tool_name), | |
596 | )); | |
6a06907d | 597 | } |
cdc7bbd5 XL |
598 | } else { |
599 | panic!("renamed lint does not exist: {}", new_name); | |
6a06907d XL |
600 | } |
601 | } | |
3b2f2976 XL |
602 | } |
603 | } | |
604 | ||
f035d41b | 605 | if !is_crate_node { |
5e7ed085 | 606 | for (id, &(level, ref src)) in self.current_specs().iter() { |
f035d41b XL |
607 | if !id.lint.crate_level_only { |
608 | continue; | |
609 | } | |
610 | ||
a2a8927a XL |
611 | let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else { |
612 | continue | |
f035d41b XL |
613 | }; |
614 | ||
615 | let lint = builtin::UNUSED_ATTRIBUTES; | |
616 | let (lint_level, lint_src) = | |
5e7ed085 | 617 | self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess); |
f035d41b XL |
618 | struct_lint_level( |
619 | self.sess, | |
620 | lint, | |
621 | lint_level, | |
622 | lint_src, | |
623 | Some(lint_attr_span.into()), | |
624 | |lint| { | |
625 | let mut db = lint.build(&format!( | |
626 | "{}({}) is ignored unless specified at crate level", | |
627 | level.as_str(), | |
628 | lint_attr_name | |
629 | )); | |
630 | db.emit(); | |
631 | }, | |
632 | ); | |
633 | // don't set a separate error for every lint in the group | |
634 | break; | |
635 | } | |
636 | } | |
637 | ||
5e7ed085 FG |
638 | if self.current_specs().is_empty() { |
639 | self.sets.list.pop(); | |
640 | self.cur = prev; | |
3b2f2976 XL |
641 | } |
642 | ||
74b04a01 | 643 | BuilderPush { prev, changed: prev != self.cur } |
3b2f2976 XL |
644 | } |
645 | ||
5e7ed085 FG |
646 | fn create_stable_id( |
647 | &mut self, | |
648 | unstable_id: LintExpectationId, | |
649 | hir_id: HirId, | |
650 | attr_index: usize, | |
651 | ) -> LintExpectationId { | |
652 | let stable_id = | |
653 | LintExpectationId::Stable { hir_id, attr_index: attr_index as u16, lint_index: None }; | |
654 | ||
655 | self.expectation_id_map.insert(unstable_id, stable_id); | |
656 | ||
657 | stable_id | |
658 | } | |
659 | ||
f035d41b | 660 | /// Checks if the lint is gated on a feature that is not enabled. |
5e7ed085 FG |
661 | /// |
662 | /// Returns `true` if the lint's feature is enabled. | |
663 | fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool { | |
f035d41b XL |
664 | if let Some(feature) = lint_id.lint.feature_gate { |
665 | if !self.sess.features_untracked().enabled(feature) { | |
5e7ed085 FG |
666 | let lint = builtin::UNKNOWN_LINTS; |
667 | let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); | |
668 | struct_lint_level(self.sess, lint, level, src, Some(span.into()), |lint_db| { | |
669 | let mut db = | |
670 | lint_db.build(&format!("unknown lint: `{}`", lint_id.lint.name_lower())); | |
671 | db.note(&format!("the `{}` lint is unstable", lint_id.lint.name_lower(),)); | |
672 | add_feature_diagnostics(&mut db, &self.sess.parse_sess, feature); | |
673 | db.emit(); | |
674 | }); | |
675 | return false; | |
f035d41b | 676 | } |
f9f354fc | 677 | } |
5e7ed085 | 678 | true |
f9f354fc XL |
679 | } |
680 | ||
3b2f2976 XL |
681 | /// Called after `push` when the scope of a set of attributes are exited. |
682 | pub fn pop(&mut self, push: BuilderPush) { | |
683 | self.cur = push.prev; | |
684 | } | |
685 | ||
f9f354fc | 686 | /// Find the lint level for a lint. |
fc512014 | 687 | pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) { |
f9f354fc XL |
688 | self.sets.get_lint_level(lint, self.cur, None, self.sess) |
689 | } | |
690 | ||
3b2f2976 XL |
691 | /// Used to emit a lint-related diagnostic based on the current state of |
692 | /// this lint context. | |
dfeec247 XL |
693 | pub fn struct_lint( |
694 | &self, | |
695 | lint: &'static Lint, | |
696 | span: Option<MultiSpan>, | |
5e7ed085 | 697 | decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>), |
74b04a01 | 698 | ) { |
f9f354fc | 699 | let (level, src) = self.lint_level(lint); |
74b04a01 | 700 | struct_lint_level(self.sess, lint, level, src, span, decorate) |
3b2f2976 XL |
701 | } |
702 | ||
703 | /// Registers the ID provided with the current set of lints stored in | |
704 | /// this context. | |
705 | pub fn register_id(&mut self, id: HirId) { | |
706 | self.id_to_set.insert(id, self.cur); | |
707 | } | |
708 | ||
5e7ed085 FG |
709 | fn update_unstable_expectation_ids(&self) { |
710 | self.sess.diagnostic().update_unstable_expectation_id(&self.expectation_id_map); | |
711 | } | |
712 | ||
3b2f2976 | 713 | pub fn build_map(self) -> LintLevelMap { |
5e7ed085 FG |
714 | LintLevelMap { |
715 | sets: self.sets, | |
716 | id_to_set: self.id_to_set, | |
717 | lint_expectations: self.lint_expectations, | |
718 | } | |
3b2f2976 XL |
719 | } |
720 | } | |
721 | ||
5099ac24 | 722 | struct LintLevelMapBuilder<'tcx> { |
dfeec247 XL |
723 | levels: LintLevelsBuilder<'tcx>, |
724 | tcx: TyCtxt<'tcx>, | |
3b2f2976 XL |
725 | } |
726 | ||
5099ac24 | 727 | impl LintLevelMapBuilder<'_> { |
6a06907d | 728 | fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F) |
dfeec247 XL |
729 | where |
730 | F: FnOnce(&mut Self), | |
3b2f2976 | 731 | { |
f035d41b | 732 | let is_crate_hir = id == hir::CRATE_HIR_ID; |
6a06907d | 733 | let attrs = self.tcx.hir().attrs(id); |
5e7ed085 FG |
734 | let push = self.levels.push(attrs, is_crate_hir, Some(id)); |
735 | ||
dfeec247 XL |
736 | if push.changed { |
737 | self.levels.register_id(id); | |
738 | } | |
739 | f(self); | |
740 | self.levels.pop(push); | |
3b2f2976 | 741 | } |
ea8adc8c XL |
742 | } |
743 | ||
5099ac24 FG |
744 | impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> { |
745 | type NestedFilter = nested_filter::All; | |
ea8adc8c | 746 | |
5099ac24 FG |
747 | fn nested_visit_map(&mut self) -> Self::Map { |
748 | self.tcx.hir() | |
dfeec247 | 749 | } |
ea8adc8c | 750 | |
dfeec247 | 751 | fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { |
6a06907d | 752 | self.with_lint_attrs(param.hir_id, |builder| { |
dfeec247 XL |
753 | intravisit::walk_param(builder, param); |
754 | }); | |
755 | } | |
ea8adc8c | 756 | |
dfeec247 | 757 | fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) { |
6a06907d | 758 | self.with_lint_attrs(it.hir_id(), |builder| { |
dfeec247 XL |
759 | intravisit::walk_item(builder, it); |
760 | }); | |
761 | } | |
ea8adc8c | 762 | |
dfeec247 | 763 | fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) { |
6a06907d | 764 | self.with_lint_attrs(it.hir_id(), |builder| { |
dfeec247 XL |
765 | intravisit::walk_foreign_item(builder, it); |
766 | }) | |
767 | } | |
768 | ||
29967ef6 XL |
769 | fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) { |
770 | // We will call `with_lint_attrs` when we walk | |
771 | // the `StmtKind`. The outer statement itself doesn't | |
772 | // define the lint levels. | |
773 | intravisit::walk_stmt(self, e); | |
774 | } | |
775 | ||
dfeec247 | 776 | fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { |
6a06907d | 777 | self.with_lint_attrs(e.hir_id, |builder| { |
dfeec247 XL |
778 | intravisit::walk_expr(builder, e); |
779 | }) | |
780 | } | |
ea8adc8c | 781 | |
f2b60f7d FG |
782 | fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { |
783 | self.with_lint_attrs(field.hir_id, |builder| { | |
784 | intravisit::walk_expr_field(builder, field); | |
785 | }) | |
786 | } | |
787 | ||
6a06907d XL |
788 | fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) { |
789 | self.with_lint_attrs(s.hir_id, |builder| { | |
790 | intravisit::walk_field_def(builder, s); | |
dfeec247 XL |
791 | }) |
792 | } | |
ea8adc8c | 793 | |
f2b60f7d | 794 | fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) { |
6a06907d | 795 | self.with_lint_attrs(v.id, |builder| { |
f2b60f7d | 796 | intravisit::walk_variant(builder, v); |
ea8adc8c XL |
797 | }) |
798 | } | |
dfeec247 XL |
799 | |
800 | fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { | |
6a06907d | 801 | self.with_lint_attrs(l.hir_id, |builder| { |
dfeec247 XL |
802 | intravisit::walk_local(builder, l); |
803 | }) | |
804 | } | |
805 | ||
806 | fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) { | |
6a06907d | 807 | self.with_lint_attrs(a.hir_id, |builder| { |
dfeec247 XL |
808 | intravisit::walk_arm(builder, a); |
809 | }) | |
810 | } | |
811 | ||
812 | fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { | |
6a06907d | 813 | self.with_lint_attrs(trait_item.hir_id(), |builder| { |
dfeec247 XL |
814 | intravisit::walk_trait_item(builder, trait_item); |
815 | }); | |
816 | } | |
817 | ||
818 | fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { | |
6a06907d | 819 | self.with_lint_attrs(impl_item.hir_id(), |builder| { |
dfeec247 XL |
820 | intravisit::walk_impl_item(builder, impl_item); |
821 | }); | |
822 | } | |
f2b60f7d FG |
823 | |
824 | fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) { | |
825 | self.with_lint_attrs(field.hir_id, |builder| { | |
826 | intravisit::walk_pat_field(builder, field); | |
827 | }) | |
828 | } | |
829 | ||
830 | fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { | |
831 | self.with_lint_attrs(p.hir_id, |builder| { | |
832 | intravisit::walk_generic_param(builder, p); | |
833 | }); | |
834 | } | |
dfeec247 XL |
835 | } |
836 | ||
f035d41b | 837 | pub fn provide(providers: &mut Providers) { |
dfeec247 | 838 | providers.lint_levels = lint_levels; |
ea8adc8c | 839 | } |