]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/levels.rs
refactor: refactor to derive for some lints.
[rust.git] / compiler / rustc_lint / src / levels.rs
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 use crate::context::{CheckLintNameResult, LintStore};
4 use crate::late::unerased_lint_store;
5 use crate::lints::{
6     DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
7     RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
8 };
9 use rustc_ast as ast;
10 use rustc_ast_pretty::pprust;
11 use rustc_data_structures::fx::FxHashMap;
12 use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
13 use rustc_hir as hir;
14 use rustc_hir::intravisit::{self, Visitor};
15 use rustc_hir::HirId;
16 use rustc_index::vec::IndexVec;
17 use rustc_middle::hir::nested_filter;
18 use rustc_middle::lint::{
19     reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
20     ShallowLintLevelMap,
21 };
22 use rustc_middle::ty::query::Providers;
23 use rustc_middle::ty::{RegisteredTools, TyCtxt};
24 use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
25 use rustc_session::lint::{
26     builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
27     Level, Lint, LintExpectationId, LintId,
28 };
29 use rustc_session::parse::{add_feature_diagnostics, feature_err};
30 use rustc_session::Session;
31 use rustc_span::symbol::{sym, Symbol};
32 use rustc_span::{Span, DUMMY_SP};
33
34 use crate::errors::{
35     MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
36     UnknownToolInScopedLint,
37 };
38
39 /// Collection of lint levels for the whole crate.
40 /// This is used by AST-based lints, which do not
41 /// wait until we have built HIR to be emitted.
42 #[derive(Debug)]
43 struct LintLevelSets {
44     /// Linked list of specifications.
45     list: IndexVec<LintStackIndex, LintSet>,
46 }
47
48 rustc_index::newtype_index! {
49     #[custom_encodable] // we don't need encoding
50     struct LintStackIndex {
51         const COMMAND_LINE = 0;
52     }
53 }
54
55 /// Specifications found at this position in the stack.  This map only represents the lints
56 /// found for one set of attributes (like `shallow_lint_levels_on` does).
57 ///
58 /// We store the level specifications as a linked list.
59 /// Each `LintSet` represents a set of attributes on the same AST node.
60 /// The `parent` forms a linked list that matches the AST tree.
61 /// This way, walking the linked list is equivalent to walking the AST bottom-up
62 /// to find the specifications for a given lint.
63 #[derive(Debug)]
64 struct LintSet {
65     // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
66     // flag.
67     specs: FxHashMap<LintId, LevelAndSource>,
68     parent: LintStackIndex,
69 }
70
71 impl LintLevelSets {
72     fn new() -> Self {
73         LintLevelSets { list: IndexVec::new() }
74     }
75
76     fn get_lint_level(
77         &self,
78         lint: &'static Lint,
79         idx: LintStackIndex,
80         aux: Option<&FxHashMap<LintId, LevelAndSource>>,
81         sess: &Session,
82     ) -> LevelAndSource {
83         let lint = LintId::of(lint);
84         let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
85         let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
86             self.raw_lint_id_level(id, idx, aux)
87         });
88         (level, src)
89     }
90
91     fn raw_lint_id_level(
92         &self,
93         id: LintId,
94         mut idx: LintStackIndex,
95         aux: Option<&FxHashMap<LintId, LevelAndSource>>,
96     ) -> (Option<Level>, LintLevelSource) {
97         if let Some(specs) = aux {
98             if let Some(&(level, src)) = specs.get(&id) {
99                 return (Some(level), src);
100             }
101         }
102         loop {
103             let LintSet { ref specs, parent } = self.list[idx];
104             if let Some(&(level, src)) = specs.get(&id) {
105                 return (Some(level), src);
106             }
107             if idx == COMMAND_LINE {
108                 return (None, LintLevelSource::Default);
109             }
110             idx = parent;
111         }
112     }
113 }
114
115 fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
116     let store = unerased_lint_store(tcx);
117
118     let mut builder = LintLevelsBuilder {
119         sess: tcx.sess,
120         provider: QueryMapExpectationsWrapper {
121             tcx,
122             cur: hir::CRATE_HIR_ID,
123             specs: ShallowLintLevelMap::default(),
124             expectations: Vec::new(),
125             unstable_to_stable_ids: FxHashMap::default(),
126             empty: FxHashMap::default(),
127         },
128         warn_about_weird_lints: false,
129         store,
130         registered_tools: &tcx.resolutions(()).registered_tools,
131     };
132
133     builder.add_command_line();
134     builder.add_id(hir::CRATE_HIR_ID);
135     tcx.hir().walk_toplevel_module(&mut builder);
136
137     tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
138
139     builder.provider.expectations
140 }
141
142 #[instrument(level = "trace", skip(tcx), ret)]
143 fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
144     let store = unerased_lint_store(tcx);
145     let attrs = tcx.hir_attrs(owner);
146
147     let mut levels = LintLevelsBuilder {
148         sess: tcx.sess,
149         provider: LintLevelQueryMap {
150             tcx,
151             cur: owner.into(),
152             specs: ShallowLintLevelMap::default(),
153             empty: FxHashMap::default(),
154             attrs,
155         },
156         warn_about_weird_lints: false,
157         store,
158         registered_tools: &tcx.resolutions(()).registered_tools,
159     };
160
161     if owner == hir::CRATE_OWNER_ID {
162         levels.add_command_line();
163     }
164
165     match attrs.map.range(..) {
166         // There is only something to do if there are attributes at all.
167         [] => {}
168         // Most of the time, there is only one attribute.  Avoid fetching HIR in that case.
169         [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
170         // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
171         // a standard visit.
172         // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's.
173         _ => match tcx.hir().owner(owner) {
174             hir::OwnerNode::Item(item) => levels.visit_item(item),
175             hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
176             hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
177             hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
178             hir::OwnerNode::Crate(mod_) => {
179                 levels.add_id(hir::CRATE_HIR_ID);
180                 levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
181             }
182         },
183     }
184
185     let specs = levels.provider.specs;
186
187     #[cfg(debug_assertions)]
188     for (_, v) in specs.specs.iter() {
189         debug_assert!(!v.is_empty());
190     }
191
192     specs
193 }
194
195 pub struct TopDown {
196     sets: LintLevelSets,
197     cur: LintStackIndex,
198 }
199
200 pub trait LintLevelsProvider {
201     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>;
202     fn insert(&mut self, id: LintId, lvl: LevelAndSource);
203     fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
204     fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {}
205 }
206
207 impl LintLevelsProvider for TopDown {
208     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
209         &self.sets.list[self.cur].specs
210     }
211
212     fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
213         self.sets.list[self.cur].specs.insert(id, lvl);
214     }
215
216     fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
217         self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
218     }
219 }
220
221 struct LintLevelQueryMap<'tcx> {
222     tcx: TyCtxt<'tcx>,
223     cur: HirId,
224     specs: ShallowLintLevelMap,
225     /// Empty hash map to simplify code.
226     empty: FxHashMap<LintId, LevelAndSource>,
227     attrs: &'tcx hir::AttributeMap<'tcx>,
228 }
229
230 impl LintLevelsProvider for LintLevelQueryMap<'_> {
231     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
232         self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
233     }
234     fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
235         self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
236     }
237     fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
238         self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
239     }
240 }
241
242 struct QueryMapExpectationsWrapper<'tcx> {
243     tcx: TyCtxt<'tcx>,
244     cur: HirId,
245     specs: ShallowLintLevelMap,
246     expectations: Vec<(LintExpectationId, LintExpectation)>,
247     unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
248     /// Empty hash map to simplify code.
249     empty: FxHashMap<LintId, LevelAndSource>,
250 }
251
252 impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
253     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
254         self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
255     }
256     fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
257         let specs = self.specs.specs.get_mut_or_insert_default(self.cur.local_id);
258         specs.clear();
259         specs.insert(id, lvl);
260     }
261     fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
262         self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
263     }
264     fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
265         let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
266         let key = LintExpectationId::Unstable { attr_id, lint_index: None };
267
268         if !self.unstable_to_stable_ids.contains_key(&key) {
269             self.unstable_to_stable_ids.insert(
270                 key,
271                 LintExpectationId::Stable { hir_id, attr_index, lint_index: None, attr_id: None },
272             );
273         }
274
275         self.expectations.push((id.normalize(), expectation));
276     }
277 }
278
279 impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
280     fn add_id(&mut self, hir_id: HirId) {
281         self.provider.cur = hir_id;
282         self.add(
283             self.provider.attrs.get(hir_id.local_id),
284             hir_id == hir::CRATE_HIR_ID,
285             Some(hir_id),
286         );
287     }
288 }
289
290 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
291     type NestedFilter = nested_filter::OnlyBodies;
292
293     fn nested_visit_map(&mut self) -> Self::Map {
294         self.provider.tcx.hir()
295     }
296
297     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
298         self.add_id(param.hir_id);
299         intravisit::walk_param(self, param);
300     }
301
302     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
303         self.add_id(it.hir_id());
304         intravisit::walk_item(self, it);
305     }
306
307     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
308         self.add_id(it.hir_id());
309         intravisit::walk_foreign_item(self, it);
310     }
311
312     fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
313         // We will call `add_id` when we walk
314         // the `StmtKind`. The outer statement itself doesn't
315         // define the lint levels.
316         intravisit::walk_stmt(self, e);
317     }
318
319     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
320         self.add_id(e.hir_id);
321         intravisit::walk_expr(self, e);
322     }
323
324     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
325         self.add_id(s.hir_id);
326         intravisit::walk_field_def(self, s);
327     }
328
329     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
330         self.add_id(v.hir_id);
331         intravisit::walk_variant(self, v);
332     }
333
334     fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
335         self.add_id(l.hir_id);
336         intravisit::walk_local(self, l);
337     }
338
339     fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
340         self.add_id(a.hir_id);
341         intravisit::walk_arm(self, a);
342     }
343
344     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
345         self.add_id(trait_item.hir_id());
346         intravisit::walk_trait_item(self, trait_item);
347     }
348
349     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
350         self.add_id(impl_item.hir_id());
351         intravisit::walk_impl_item(self, impl_item);
352     }
353 }
354
355 impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
356     fn add_id(&mut self, hir_id: HirId) {
357         self.provider.cur = hir_id;
358         self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
359     }
360 }
361
362 impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
363     type NestedFilter = nested_filter::All;
364
365     fn nested_visit_map(&mut self) -> Self::Map {
366         self.provider.tcx.hir()
367     }
368
369     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
370         self.add_id(param.hir_id);
371         intravisit::walk_param(self, param);
372     }
373
374     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
375         self.add_id(it.hir_id());
376         intravisit::walk_item(self, it);
377     }
378
379     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
380         self.add_id(it.hir_id());
381         intravisit::walk_foreign_item(self, it);
382     }
383
384     fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
385         // We will call `add_id` when we walk
386         // the `StmtKind`. The outer statement itself doesn't
387         // define the lint levels.
388         intravisit::walk_stmt(self, e);
389     }
390
391     fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
392         self.add_id(e.hir_id);
393         intravisit::walk_expr(self, e);
394     }
395
396     fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
397         self.add_id(s.hir_id);
398         intravisit::walk_field_def(self, s);
399     }
400
401     fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
402         self.add_id(v.hir_id);
403         intravisit::walk_variant(self, v);
404     }
405
406     fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
407         self.add_id(l.hir_id);
408         intravisit::walk_local(self, l);
409     }
410
411     fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
412         self.add_id(a.hir_id);
413         intravisit::walk_arm(self, a);
414     }
415
416     fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
417         self.add_id(trait_item.hir_id());
418         intravisit::walk_trait_item(self, trait_item);
419     }
420
421     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
422         self.add_id(impl_item.hir_id());
423         intravisit::walk_impl_item(self, impl_item);
424     }
425 }
426
427 pub struct LintLevelsBuilder<'s, P> {
428     sess: &'s Session,
429     provider: P,
430     warn_about_weird_lints: bool,
431     store: &'s LintStore,
432     registered_tools: &'s RegisteredTools,
433 }
434
435 pub(crate) struct BuilderPush {
436     prev: LintStackIndex,
437 }
438
439 impl<'s> LintLevelsBuilder<'s, TopDown> {
440     pub(crate) fn new(
441         sess: &'s Session,
442         warn_about_weird_lints: bool,
443         store: &'s LintStore,
444         registered_tools: &'s RegisteredTools,
445     ) -> Self {
446         let mut builder = LintLevelsBuilder {
447             sess,
448             provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
449             warn_about_weird_lints,
450             store,
451             registered_tools,
452         };
453         builder.process_command_line();
454         assert_eq!(builder.provider.sets.list.len(), 1);
455         builder
456     }
457
458     fn process_command_line(&mut self) {
459         self.provider.cur = self
460             .provider
461             .sets
462             .list
463             .push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
464         self.add_command_line();
465     }
466
467     /// Pushes a list of AST lint attributes onto this context.
468     ///
469     /// This function will return a `BuilderPush` object which should be passed
470     /// to `pop` when this scope for the attributes provided is exited.
471     ///
472     /// This function will perform a number of tasks:
473     ///
474     /// * It'll validate all lint-related attributes in `attrs`
475     /// * It'll mark all lint-related attributes as used
476     /// * Lint levels will be updated based on the attributes provided
477     /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
478     ///   `#[allow]`
479     ///
480     /// Don't forget to call `pop`!
481     pub(crate) fn push(
482         &mut self,
483         attrs: &[ast::Attribute],
484         is_crate_node: bool,
485         source_hir_id: Option<HirId>,
486     ) -> BuilderPush {
487         let prev = self.provider.cur;
488         self.provider.cur =
489             self.provider.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
490
491         self.add(attrs, is_crate_node, source_hir_id);
492
493         if self.provider.current_specs().is_empty() {
494             self.provider.sets.list.pop();
495             self.provider.cur = prev;
496         }
497
498         BuilderPush { prev }
499     }
500
501     /// Called after `push` when the scope of a set of attributes are exited.
502     pub(crate) fn pop(&mut self, push: BuilderPush) {
503         self.provider.cur = push.prev;
504         std::mem::forget(push);
505     }
506 }
507
508 #[cfg(debug_assertions)]
509 impl Drop for BuilderPush {
510     fn drop(&mut self) {
511         panic!("Found a `push` without a `pop`.");
512     }
513 }
514
515 impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
516     pub(crate) fn sess(&self) -> &Session {
517         self.sess
518     }
519
520     pub(crate) fn lint_store(&self) -> &LintStore {
521         self.store
522     }
523
524     fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
525         self.provider.current_specs()
526     }
527
528     fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
529         self.provider.insert(id, lvl)
530     }
531
532     fn add_command_line(&mut self) {
533         for &(ref lint_name, level) in &self.sess.opts.lint_opts {
534             self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
535             let orig_level = level;
536             let lint_flag_val = Symbol::intern(lint_name);
537
538             let Ok(ids) = self.store.find_lints(&lint_name) else {
539                 // errors handled in check_lint_name_cmdline above
540                 continue
541             };
542             for id in ids {
543                 // ForceWarn and Forbid cannot be overridden
544                 if let Some((Level::ForceWarn(_) | Level::Forbid, _)) =
545                     self.current_specs().get(&id)
546                 {
547                     continue;
548                 }
549
550                 if self.check_gated_lint(id, DUMMY_SP) {
551                     let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
552                     self.insert(id, (level, src));
553                 }
554             }
555         }
556     }
557
558     /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
559     /// (e.g. if a forbid was already inserted on the same scope), then emits a
560     /// diagnostic with no change to `specs`.
561     fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
562         let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess);
563         if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id {
564             *id = id.normalize();
565         }
566         // Setting to a non-forbid level is an error if the lint previously had
567         // a forbid level. Note that this is not necessarily true even with a
568         // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
569         //
570         // This means that this only errors if we're truly lowering the lint
571         // level from forbid.
572         if level != Level::Forbid {
573             if let Level::Forbid = old_level {
574                 // Backwards compatibility check:
575                 //
576                 // We used to not consider `forbid(lint_group)`
577                 // as preventing `allow(lint)` for some lint `lint` in
578                 // `lint_group`. For now, issue a future-compatibility
579                 // warning for this case.
580                 let id_name = id.lint.name_lower();
581                 let fcw_warning = match old_src {
582                     LintLevelSource::Default => false,
583                     LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
584                     LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
585                 };
586                 debug!(
587                     "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
588                     fcw_warning,
589                     self.current_specs(),
590                     old_src,
591                     id_name
592                 );
593                 let sub = match old_src {
594                     LintLevelSource::Default => {
595                         OverruledAttributeSub::DefaultSource { id: id.to_string() }
596                     }
597                     LintLevelSource::Node { span, reason, .. } => {
598                         OverruledAttributeSub::NodeSource { span, reason }
599                     }
600                     LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
601                 };
602                 if !fcw_warning {
603                     self.sess.emit_err(OverruledAttribute {
604                         span: src.span(),
605                         overruled: src.span(),
606                         lint_level: level.as_str(),
607                         lint_source: src.name(),
608                         sub,
609                     });
610                 } else {
611                     self.emit_spanned_lint(
612                         FORBIDDEN_LINT_GROUPS,
613                         src.span().into(),
614                         OverruledAtributeLint {
615                             overruled: src.span(),
616                             lint_level: level.as_str(),
617                             lint_source: src.name(),
618                             sub,
619                         },
620                     );
621                 }
622
623                 // Retain the forbid lint level, unless we are
624                 // issuing a FCW. In the FCW case, we want to
625                 // respect the new setting.
626                 if !fcw_warning {
627                     return;
628                 }
629             }
630         }
631
632         // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
633         // Handling expectations of this lint would add additional complexity with little to no
634         // benefit. The expect level for this lint will therefore be ignored.
635         if let Level::Expect(_) = level && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) {
636             return;
637         }
638
639         match (old_level, level) {
640             // If the new level is an expectation store it in `ForceWarn`
641             (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
642                 self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
643             }
644             // Keep `ForceWarn` level but drop the expectation
645             (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
646             // Set the lint level as normal
647             _ => self.insert(id, (level, src)),
648         };
649     }
650
651     fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
652         let sess = self.sess;
653         for (attr_index, attr) in attrs.iter().enumerate() {
654             if attr.has_name(sym::automatically_derived) {
655                 self.insert(
656                     LintId::of(SINGLE_USE_LIFETIMES),
657                     (Level::Allow, LintLevelSource::Default),
658                 );
659                 continue;
660             }
661
662             let level = match Level::from_attr(attr) {
663                 None => continue,
664                 // This is the only lint level with a `LintExpectationId` that can be created from an attribute
665                 Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
666                     let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id
667                         else { bug!("stable id Level::from_attr") };
668
669                     let stable_id = LintExpectationId::Stable {
670                         hir_id,
671                         attr_index: attr_index.try_into().unwrap(),
672                         lint_index,
673                         // we pass the previous unstable attr_id such that we can trace the ast id when building a map
674                         // to go from unstable to stable id.
675                         attr_id: Some(attr_id),
676                     };
677
678                     Level::Expect(stable_id)
679                 }
680                 Some(lvl) => lvl,
681             };
682
683             let Some(mut metas) = attr.meta_item_list() else {
684                 continue
685             };
686
687             if metas.is_empty() {
688                 // This emits the unused_attributes lint for `#[level()]`
689                 continue;
690             }
691
692             // Before processing the lint names, look for a reason (RFC 2383)
693             // at the end.
694             let mut reason = None;
695             let tail_li = &metas[metas.len() - 1];
696             if let Some(item) = tail_li.meta_item() {
697                 match item.kind {
698                     ast::MetaItemKind::Word => {} // actual lint names handled later
699                     ast::MetaItemKind::NameValue(ref name_value) => {
700                         if item.path == sym::reason {
701                             if let ast::LitKind::Str(rationale, _) = name_value.kind {
702                                 if !self.sess.features_untracked().lint_reasons {
703                                     feature_err(
704                                         &self.sess.parse_sess,
705                                         sym::lint_reasons,
706                                         item.span,
707                                         "lint reasons are experimental",
708                                     )
709                                     .emit();
710                                 }
711                                 reason = Some(rationale);
712                             } else {
713                                 sess.emit_err(MalformedAttribute {
714                                     span: name_value.span,
715                                     sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
716                                         name_value.span,
717                                     ),
718                                 });
719                             }
720                             // found reason, reslice meta list to exclude it
721                             metas.pop().unwrap();
722                         } else {
723                             sess.emit_err(MalformedAttribute {
724                                 span: item.span,
725                                 sub: MalformedAttributeSub::BadAttributeArgument(item.span),
726                             });
727                         }
728                     }
729                     ast::MetaItemKind::List(_) => {
730                         sess.emit_err(MalformedAttribute {
731                             span: item.span,
732                             sub: MalformedAttributeSub::BadAttributeArgument(item.span),
733                         });
734                     }
735                 }
736             }
737
738             for (lint_index, li) in metas.iter_mut().enumerate() {
739                 let level = match level {
740                     Level::Expect(mut id) => {
741                         id.set_lint_index(Some(lint_index as u16));
742                         Level::Expect(id)
743                     }
744                     level => level,
745                 };
746
747                 let sp = li.span();
748                 let meta_item = match li {
749                     ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
750                     _ => {
751                         if let Some(item) = li.meta_item() {
752                             if let ast::MetaItemKind::NameValue(_) = item.kind {
753                                 if item.path == sym::reason {
754                                     sess.emit_err(MalformedAttribute {
755                                         span: sp,
756                                         sub: MalformedAttributeSub::ReasonMustComeLast(sp),
757                                     });
758                                     continue;
759                                 }
760                             }
761                         }
762                         sess.emit_err(MalformedAttribute {
763                             span: sp,
764                             sub: MalformedAttributeSub::BadAttributeArgument(sp),
765                         });
766                         continue;
767                     }
768                 };
769                 let tool_ident = if meta_item.path.segments.len() > 1 {
770                     Some(meta_item.path.segments.remove(0).ident)
771                 } else {
772                     None
773                 };
774                 let tool_name = tool_ident.map(|ident| ident.name);
775                 let name = pprust::path_to_string(&meta_item.path);
776                 let lint_result =
777                     self.store.check_lint_name(&name, tool_name, self.registered_tools);
778                 match &lint_result {
779                     CheckLintNameResult::Ok(ids) => {
780                         // This checks for instances where the user writes `#[expect(unfulfilled_lint_expectations)]`
781                         // in that case we want to avoid overriding the lint level but instead add an expectation that
782                         // can't be fulfilled. The lint message will include an explanation, that the
783                         // `unfulfilled_lint_expectations` lint can't be expected.
784                         if let Level::Expect(expect_id) = level {
785                             // The `unfulfilled_lint_expectations` lint is not part of any lint groups. Therefore. we
786                             // only need to check the slice if it contains a single lint.
787                             let is_unfulfilled_lint_expectations = match ids {
788                                 [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
789                                 _ => false,
790                             };
791                             self.provider.push_expectation(
792                                 expect_id,
793                                 LintExpectation::new(
794                                     reason,
795                                     sp,
796                                     is_unfulfilled_lint_expectations,
797                                     tool_name,
798                                 ),
799                             );
800                         }
801                         let src = LintLevelSource::Node {
802                             name: meta_item
803                                 .path
804                                 .segments
805                                 .last()
806                                 .expect("empty lint name")
807                                 .ident
808                                 .name,
809                             span: sp,
810                             reason,
811                         };
812                         for &id in *ids {
813                             if self.check_gated_lint(id, attr.span) {
814                                 self.insert_spec(id, (level, src));
815                             }
816                         }
817                     }
818
819                     CheckLintNameResult::Tool(result) => {
820                         match *result {
821                             Ok(ids) => {
822                                 let complete_name =
823                                     &format!("{}::{}", tool_ident.unwrap().name, name);
824                                 let src = LintLevelSource::Node {
825                                     name: Symbol::intern(complete_name),
826                                     span: sp,
827                                     reason,
828                                 };
829                                 for &id in ids {
830                                     if self.check_gated_lint(id, attr.span) {
831                                         self.insert_spec(id, (level, src));
832                                     }
833                                 }
834                                 if let Level::Expect(expect_id) = level {
835                                     self.provider.push_expectation(
836                                         expect_id,
837                                         LintExpectation::new(reason, sp, false, tool_name),
838                                     );
839                                 }
840                             }
841                             Err((Some(ids), ref new_lint_name)) => {
842                                 let lint = builtin::RENAMED_AND_REMOVED_LINTS;
843                                 self.emit_spanned_lint(
844                                     lint,
845                                     sp.into(),
846                                     DeprecatedLintName {
847                                         name,
848                                         suggestion: sp,
849                                         replace: &new_lint_name,
850                                     },
851                                 );
852
853                                 let src = LintLevelSource::Node {
854                                     name: Symbol::intern(&new_lint_name),
855                                     span: sp,
856                                     reason,
857                                 };
858                                 for id in ids {
859                                     self.insert_spec(*id, (level, src));
860                                 }
861                                 if let Level::Expect(expect_id) = level {
862                                     self.provider.push_expectation(
863                                         expect_id,
864                                         LintExpectation::new(reason, sp, false, tool_name),
865                                     );
866                                 }
867                             }
868                             Err((None, _)) => {
869                                 // If Tool(Err(None, _)) is returned, then either the lint does not
870                                 // exist in the tool or the code was not compiled with the tool and
871                                 // therefore the lint was never added to the `LintStore`. To detect
872                                 // this is the responsibility of the lint tool.
873                             }
874                         }
875                     }
876
877                     &CheckLintNameResult::NoTool => {
878                         sess.emit_err(UnknownToolInScopedLint {
879                             span: tool_ident.map(|ident| ident.span),
880                             tool_name: tool_name.unwrap(),
881                             lint_name: pprust::path_to_string(&meta_item.path),
882                             is_nightly_build: sess.is_nightly_build().then_some(()),
883                         });
884                         continue;
885                     }
886
887                     _ if !self.warn_about_weird_lints => {}
888
889                     CheckLintNameResult::Warning(msg, renamed) => {
890                         let suggestion =
891                             renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
892                                 suggestion: sp,
893                                 replace: replace.as_str(),
894                             });
895                         self.emit_spanned_lint(
896                             RENAMED_AND_REMOVED_LINTS,
897                             sp.into(),
898                             RenamedOrRemovedLint { msg, suggestion },
899                         );
900                     }
901                     CheckLintNameResult::NoLint(suggestion) => {
902                         let name = if let Some(tool_ident) = tool_ident {
903                             format!("{}::{}", tool_ident.name, name)
904                         } else {
905                             name.to_string()
906                         };
907                         let suggestion = suggestion
908                             .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
909                         self.emit_spanned_lint(
910                             UNKNOWN_LINTS,
911                             sp.into(),
912                             UnknownLint { name, suggestion },
913                         );
914                     }
915                 }
916                 // If this lint was renamed, apply the new lint instead of ignoring the attribute.
917                 // This happens outside of the match because the new lint should be applied even if
918                 // we don't warn about the name change.
919                 if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
920                     // Ignore any errors or warnings that happen because the new name is inaccurate
921                     // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
922                     if let CheckLintNameResult::Ok(ids) =
923                         self.store.check_lint_name(&new_name, None, self.registered_tools)
924                     {
925                         let src = LintLevelSource::Node {
926                             name: Symbol::intern(&new_name),
927                             span: sp,
928                             reason,
929                         };
930                         for &id in ids {
931                             if self.check_gated_lint(id, attr.span) {
932                                 self.insert_spec(id, (level, src));
933                             }
934                         }
935                         if let Level::Expect(expect_id) = level {
936                             self.provider.push_expectation(
937                                 expect_id,
938                                 LintExpectation::new(reason, sp, false, tool_name),
939                             );
940                         }
941                     } else {
942                         panic!("renamed lint does not exist: {}", new_name);
943                     }
944                 }
945             }
946         }
947
948         if !is_crate_node {
949             for (id, &(level, ref src)) in self.current_specs().iter() {
950                 if !id.lint.crate_level_only {
951                     continue;
952                 }
953
954                 let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src else {
955                     continue
956                 };
957
958                 self.emit_spanned_lint(
959                     UNUSED_ATTRIBUTES,
960                     lint_attr_span.into(),
961                     IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
962                 );
963                 // don't set a separate error for every lint in the group
964                 break;
965             }
966         }
967     }
968
969     /// Checks if the lint is gated on a feature that is not enabled.
970     ///
971     /// Returns `true` if the lint's feature is enabled.
972     // FIXME only emit this once for each attribute, instead of repeating it 4 times for
973     // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
974     fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
975         if let Some(feature) = lint_id.lint.feature_gate {
976             if !self.sess.features_untracked().enabled(feature) {
977                 let lint = builtin::UNKNOWN_LINTS;
978                 let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
979                 struct_lint_level(
980                     self.sess,
981                     lint,
982                     level,
983                     src,
984                     Some(span.into()),
985                     fluent::lint_unknown_gated_lint,
986                     |lint| {
987                         lint.set_arg("name", lint_id.lint.name_lower());
988                         lint.note(fluent::note);
989                         add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
990                         lint
991                     },
992                 );
993                 return false;
994             }
995         }
996         true
997     }
998
999     /// Find the lint level for a lint.
1000     pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
1001         self.provider.get_lint_level(lint, self.sess)
1002     }
1003
1004     /// Used to emit a lint-related diagnostic based on the current state of
1005     /// this lint context.
1006     ///
1007     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
1008     ///
1009     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
1010     #[rustc_lint_diagnostics]
1011     pub(crate) fn struct_lint(
1012         &self,
1013         lint: &'static Lint,
1014         span: Option<MultiSpan>,
1015         msg: impl Into<DiagnosticMessage>,
1016         decorate: impl for<'a, 'b> FnOnce(
1017             &'b mut DiagnosticBuilder<'a, ()>,
1018         ) -> &'b mut DiagnosticBuilder<'a, ()>,
1019     ) {
1020         let (level, src) = self.lint_level(lint);
1021         struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
1022     }
1023
1024     pub fn emit_spanned_lint(
1025         &self,
1026         lint: &'static Lint,
1027         span: MultiSpan,
1028         decorate: impl for<'a> DecorateLint<'a, ()>,
1029     ) {
1030         let (level, src) = self.lint_level(lint);
1031         struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
1032             decorate.decorate_lint(lint)
1033         });
1034     }
1035
1036     pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
1037         let (level, src) = self.lint_level(lint);
1038         struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
1039             decorate.decorate_lint(lint)
1040         });
1041     }
1042 }
1043
1044 pub(crate) fn provide(providers: &mut Providers) {
1045     *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
1046 }