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