Skip to main content

rustc_lint/
levels.rs

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