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