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