Skip to main content

rustc_lint/
levels.rs

1use std::fmt::Debug;
2
3use rustc_ast as ast;
4use rustc_ast::CRATE_NODE_ID;
5use rustc_attr_ir::lint::{LintCheck, LintCheckKind};
6use rustc_attr_ir::target::{AstTarget, Target};
7use rustc_attr_ir::{Attribute, DocAttribute, find_attr};
8use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit};
9use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
10use rustc_data_structures::unord::UnordSet;
11use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg};
12use rustc_feature::{Features, GateIssue};
13use rustc_hir as hir;
14use rustc_hir::HirId;
15use rustc_hir::intravisit::{self, Visitor};
16use rustc_index::IndexVec;
17use rustc_lint_defs::builtin::{
18    self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
19    UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
20};
21use rustc_lint_defs::{
22    Level, Lint, LintExpectationId, LintId, StableLintExpectationId, UnstableLintExpectationId,
23};
24use rustc_middle::hir::nested_filter;
25use rustc_middle::lint::{
26    LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, StableLevelSpec,
27    UnstableLevelSpec, emit_lint_base, reveal_actual_level_spec,
28};
29use rustc_middle::query::Providers;
30use rustc_middle::ty::{RegisteredTools, TyCtxt};
31use rustc_session::Session;
32use rustc_span::def_id::CRATE_MOD_ID;
33use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym};
34use rustc_structures::LintName;
35use tracing::{debug, instrument};
36
37use crate::builtin::MISSING_DOCS;
38use crate::context::{CheckLintNameResult, LintStore};
39use crate::diagnostics::{
40    CheckNameUnknownTool, DeprecatedLintName, DeprecatedLintNameFromCommandLine,
41    IgnoredUnlessCrateSpecified, OverruledAttribute, OverruledAttributeLint, OverruledAttributeSub,
42    RemovedLint, RemovedLintFromCommandLine, RenamedLint, RenamedLintFromCommandLine,
43    RenamedLintSuggestion, RequestedLevel, UnknownLint, UnknownLintFromCommandLine,
44    UnknownLintSuggestion, UnknownToolInScopedLint, UnsupportedGroup,
45};
46use crate::late::unerased_lint_store;
47
48/// Collection of lint levels for the whole crate.
49/// This is used by AST-based lints, which do not
50/// wait until we have built HIR to be emitted.
51#[derive(#[automatically_derived]impl ::core::fmt::Debug for LintLevelSets {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "LintLevelSets list", &&self.list)
    }
}Debug)]
52struct LintLevelSets {
53    /// Linked list of specifications.
54    list: IndexVec<LintStackIndex, LintSet>,
55}
56
57#[automatically_derived]impl ::core::marker::Copy for LintStackIndex { }
const COMMAND_LINE: LintStackIndex = LintStackIndex::from_u32(0);
impl LintStackIndex {
    #[doc = r" Maximum value the index can take, as a `u32`."]
    const MAX_AS_U32: u32 = 0xFFFF_FF00;
    #[doc = r" Maximum value the index can take."]
    const MAX: Self = Self::from_u32(0xFFFF_FF00);
    #[doc = r" Zero value of the index."]
    const ZERO: Self = Self::from_u32(0);
    #[doc = r" Creates a new index from a given `usize`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    const fn from_usize(value: usize) -> Self {
        if !(value <= (0xFFFF_FF00 as usize)) {
            ::core::panicking::panic("assertion failed: value <= (0xFFFF_FF00 as usize)")
        };
        unsafe { Self::from_u32_unchecked(value as u32) }
    }
    #[doc = r" Creates a new index from a given `u32`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    const fn from_u32(value: u32) -> Self {
        if !(value <= 0xFFFF_FF00) {
            ::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
        };
        unsafe { Self::from_u32_unchecked(value) }
    }
    #[doc = r" Creates a new index from a given `u16`."]
    #[doc = r""]
    #[doc = r" # Panics"]
    #[doc = r""]
    #[doc = r" Will panic if `value` exceeds `MAX`."]
    #[inline]
    const fn from_u16(value: u16) -> Self {
        let value = value as u32;
        if !(value <= 0xFFFF_FF00) {
            ::core::panicking::panic("assertion failed: value <= 0xFFFF_FF00")
        };
        unsafe { Self::from_u32_unchecked(value) }
    }
    #[doc = r" Creates a new index from a given `u32`."]
    #[doc = r""]
    #[doc = r" # Safety"]
    #[doc = r""]
    #[doc =
    r" The provided value must be less than or equal to the maximum value for the newtype."]
    #[doc =
    r" Providing a value outside this range is undefined due to layout restrictions."]
    #[doc = r""]
    #[doc = r" Prefer using `from_u32`."]
    #[inline]
    const unsafe fn from_u32_unchecked(value: u32) -> Self {
        Self {
            private_use_as_methods_instead: unsafe {
                std::mem::transmute(value)
            },
        }
    }
    #[doc = r" Extracts the value of this index as a `usize`."]
    #[inline]
    const fn index(self) -> usize { self.as_usize() }
    #[doc = r" Extracts the value of this index as a `u32`."]
    #[inline]
    const fn as_u32(self) -> u32 {
        unsafe { std::mem::transmute(self.private_use_as_methods_instead) }
    }
    #[doc = r" Extracts the value of this index as a `usize`."]
    #[inline]
    const fn as_usize(self) -> usize { self.as_u32() as usize }
}
impl std::ops::Add<usize> for LintStackIndex {
    type Output = Self;
    #[inline]
    fn add(self, other: usize) -> Self {
        Self::from_usize(self.index() + other)
    }
}
impl std::ops::AddAssign<usize> for LintStackIndex {
    #[inline]
    fn add_assign(&mut self, other: usize) { *self = *self + other; }
}
impl rustc_index::Idx for LintStackIndex {
    #[inline]
    fn new(value: usize) -> Self { Self::from_usize(value) }
    #[inline]
    fn index(self) -> usize { self.as_usize() }
}
impl From<LintStackIndex> for u32 {
    #[inline]
    fn from(v: LintStackIndex) -> u32 { v.as_u32() }
}
impl From<LintStackIndex> for usize {
    #[inline]
    fn from(v: LintStackIndex) -> usize { v.as_usize() }
}
impl From<usize> for LintStackIndex {
    #[inline]
    fn from(value: usize) -> Self { Self::from_usize(value) }
}
impl From<u32> for LintStackIndex {
    #[inline]
    fn from(value: u32) -> Self { Self::from_u32(value) }
}
impl ::std::cmp::Eq for LintStackIndex {}
impl ::std::cmp::PartialEq for LintStackIndex {
    fn eq(&self, other: &Self) -> bool { self.as_u32().eq(&other.as_u32()) }
}
impl ::std::marker::StructuralPartialEq for LintStackIndex {}
impl ::std::hash::Hash for LintStackIndex {
    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
        self.as_u32().hash(state)
    }
}
impl ::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! {
58    struct LintStackIndex {
59        const COMMAND_LINE = 0;
60    }
61}
62
63/// Specifications found at this position in the stack. This map only represents the lints
64/// found for one set of attributes (like `shallow_lint_levels_on` does).
65///
66/// We store the level specifications as a linked list.
67/// Each `LintSet` represents a set of attributes on the same AST node.
68/// The `parent` forms a linked list that matches the AST tree.
69/// This way, walking the linked list is equivalent to walking the AST bottom-up
70/// to find the specifications for a given lint.
71#[derive(#[automatically_derived]impl ::core::fmt::Debug for LintSet {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "LintSet specs parent", &self.specs, &&self.parent)
    }
}Debug)]
72struct LintSet {
73    specs: FxIndexMap<LintId, UnstableLevelSpec>,
74    parent: LintStackIndex,
75}
76
77impl LintLevelSets {
78    fn new() -> Self {
79        LintLevelSets { list: IndexVec::new() }
80    }
81
82    fn get_lint_level_spec(
83        &self,
84        lint: &'static Lint,
85        idx: LintStackIndex,
86        aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
87        sess: &Session,
88    ) -> UnstableLevelSpec {
89        reveal_actual_level_spec(sess, LintId::of(lint), |id| {
90            self.raw_lint_level_spec(id, idx, aux)
91        })
92    }
93
94    fn raw_lint_level_spec(
95        &self,
96        id: LintId,
97        mut idx: LintStackIndex,
98        aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
99    ) -> Option<UnstableLevelSpec> {
100        if let Some(specs) = aux
101            && let Some(level_spec) = specs.get(&id)
102        {
103            return Some(*level_spec);
104        }
105
106        loop {
107            let LintSet { ref specs, parent } = self.list[idx];
108            if let Some(level_spec) = specs.get(&id) {
109                return Some(*level_spec);
110            }
111            if idx == COMMAND_LINE {
112                return None;
113            }
114            idx = parent;
115        }
116    }
117}
118
119fn skippable_lints(tcx: TyCtxt<'_>, (): ()) -> UnordSet<LintId> {
120    let store = unerased_lint_store(&tcx.sess);
121    let root_map = tcx.shallow_lint_levels_on(hir::CRATE_OWNER_ID);
122
123    let mut skippable: FxHashSet<LintId> = store
124        .get_lints()
125        .into_iter()
126        .filter(|lint| {
127            // Lints that show up in future-compat reports must always be run.
128            let has_future_breakage =
129                lint.future_incompatible.is_some_and(|fut| fut.report_in_deps);
130            !has_future_breakage && !lint.eval_always
131        })
132        .filter(|lint| {
133            let level_spec =
134                root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID);
135            // Only include lints that are allowed at crate root or by default.
136            level_spec.is_allow()
137                || (#[allow(non_exhaustive_omitted_patterns)] match level_spec.src {
    LintLevelSource::Default => true,
    _ => false,
}matches!(level_spec.src, LintLevelSource::Default)
138                    && lint.default_level(tcx.sess.edition()) == Level::Allow)
139        })
140        .map(|lint| LintId::of(*lint))
141        .collect();
142
143    for owner in tcx.hir_crate_items(()).owners() {
144        let map = tcx.shallow_lint_levels_on(owner);
145
146        // All lints that appear with a non-allow level must be run.
147        for (_, specs) in map.specs.iter() {
148            for (lint, level_spec) in specs.iter() {
149                if !level_spec.is_allow() {
150                    skippable.remove(lint);
151                }
152            }
153        }
154    }
155
156    skippable.into()
157}
158
159{}
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
            ::tracing::Level::TRACE <=
                ::tracing::level_filters::LevelFilter::current() || { false }
    {
    __tracing_attr_span =
        {
            use ::tracing::__macro_support::Callsite as _;
            static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                {
                    static META: ::tracing::Metadata<'static> =
                        {
                            ::tracing_core::metadata::Metadata::new("shallow_lint_levels_on",
                                "rustc_lint::levels", ::tracing::Level::TRACE,
                                ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/levels.rs"),
                                ::tracing_core::__macro_support::Option::Some(159u32),
                                ::tracing_core::__macro_support::Option::Some("rustc_lint::levels"),
                                ::tracing_core::field::FieldSet::new(&[{
                                                    const NAME:
                                                        ::tracing::__macro_support::FieldName<{
                                                            ::tracing::__macro_support::FieldName::len("owner")
                                                        }> =
                                                        ::tracing::__macro_support::FieldName::new("owner");
                                                    NAME.as_str()
                                                }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                ::tracing::metadata::Kind::SPAN)
                        };
                    ::tracing::callsite::DefaultCallsite::new(&META)
                };
            let mut interest = ::tracing::subscriber::Interest::never();
            if ::tracing::Level::TRACE <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::TRACE <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        { interest = __CALLSITE.interest(); !interest.is_never() }
                    &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest) {
                let meta = __CALLSITE.metadata();
                ::tracing::Span::new(meta,
                    &{
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&owner)
                                                        as &dyn ::tracing::field::Value))])
                        })
            } else {
                let span =
                    ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                {};
                span
            }
        };
    __tracing_attr_guard = __tracing_attr_span.enter();
}
#[allow(clippy :: redundant_closure_call)]
let x =
    (move ||
                {

                    #[allow(unknown_lints, unreachable_code, clippy ::
                    diverging_sub_expression, clippy :: empty_loop, clippy ::
                    let_unit_value, clippy :: let_with_type_underscore, clippy
                    :: needless_return, clippy :: unreachable)]
                    if false {
                        let __tracing_attr_fake_return: ShallowLintLevelMap =
                            loop {};
                        return __tracing_attr_fake_return;
                    }
                    {
                        let store = unerased_lint_store(tcx.sess);
                        let attrs = tcx.hir_attr_map(owner);
                        let mut levels =
                            LintLevelsBuilder {
                                sess: tcx.sess,
                                features: tcx.features(),
                                provider: LintLevelQueryMap {
                                    tcx,
                                    cur: owner.into(),
                                    specs: ShallowLintLevelMap::default(),
                                    empty: FxIndexMap::default(),
                                    attrs,
                                },
                                lint_added_lints: false,
                                store,
                                registered_lint_tools: tcx.registered_lint_tools(()),
                            };
                        if owner == hir::CRATE_OWNER_ID {
                            levels.add_command_line();
                        }
                        match attrs.map.range(..) {
                            [] => {}
                            &[(local_id, _)] =>
                                levels.add_id(HirId { owner, local_id }),
                            _ =>
                                match tcx.hir_owner_node(owner) {
                                    hir::OwnerNode::Item(item) => levels.visit_item(item),
                                    hir::OwnerNode::ForeignItem(item) =>
                                        levels.visit_foreign_item(item),
                                    hir::OwnerNode::TraitItem(item) =>
                                        levels.visit_trait_item(item),
                                    hir::OwnerNode::ImplItem(item) =>
                                        levels.visit_impl_item(item),
                                    hir::OwnerNode::Crate(mod_) => {
                                        levels.add_id(hir::CRATE_HIR_ID);
                                        levels.visit_mod(mod_, mod_.spans.inner_span, CRATE_MOD_ID)
                                    }
                                    hir::OwnerNode::Synthetic =>
                                        ::core::panicking::unreachable_default_msg(),
                                },
                        }
                        let specs = levels.provider.specs;
                        for (_, v) in specs.specs.iter() {
                            if true {
                                if !!v.is_empty() {
                                    ::core::panicking::panic("assertion failed: !v.is_empty()")
                                };
                            };
                        }
                        specs
                    }
                })();
{
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/levels.rs:159",
                        "rustc_lint::levels", ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/levels.rs"),
                        ::tracing_core::__macro_support::Option::Some(159u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::levels"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("return")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("return");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&x)
                                            as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};
x;#[instrument(level = "trace", skip(tcx), ret)]
160fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
161    let store = unerased_lint_store(tcx.sess);
162    let attrs = tcx.hir_attr_map(owner);
163
164    let mut levels = LintLevelsBuilder {
165        sess: tcx.sess,
166        features: tcx.features(),
167        provider: LintLevelQueryMap {
168            tcx,
169            cur: owner.into(),
170            specs: ShallowLintLevelMap::default(),
171            empty: FxIndexMap::default(),
172            attrs,
173        },
174        lint_added_lints: false,
175        store,
176        registered_lint_tools: tcx.registered_lint_tools(()),
177    };
178
179    if owner == hir::CRATE_OWNER_ID {
180        levels.add_command_line();
181    }
182
183    match attrs.map.range(..) {
184        // There is only something to do if there are attributes at all.
185        [] => {}
186        // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
187        &[(local_id, _)] => levels.add_id(HirId { owner, local_id }),
188        // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
189        // a standard visit.
190        // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's.
191        _ => match tcx.hir_owner_node(owner) {
192            hir::OwnerNode::Item(item) => levels.visit_item(item),
193            hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item),
194            hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item),
195            hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
196            hir::OwnerNode::Crate(mod_) => {
197                levels.add_id(hir::CRATE_HIR_ID);
198                levels.visit_mod(mod_, mod_.spans.inner_span, CRATE_MOD_ID)
199            }
200            hir::OwnerNode::Synthetic => unreachable!(),
201        },
202    }
203
204    let specs = levels.provider.specs;
205
206    #[cfg(debug_assertions)]
207    for (_, v) in specs.specs.iter() {
208        debug_assert!(!v.is_empty());
209    }
210
211    specs
212}
213
214pub struct TopDown {
215    sets: LintLevelSets,
216    cur: LintStackIndex,
217}
218
219pub trait LintLevelsProvider {
220    type LintExpectationId: Copy + Debug + Into<LintExpectationId>;
221
222    fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec<Self::LintExpectationId>>;
223
224    fn insert(&mut self, id: LintId, level_spec: LevelSpec<Self::LintExpectationId>);
225
226    fn get_lint_level_spec(
227        &self,
228        lint: &'static Lint,
229        sess: &Session,
230    ) -> LevelSpec<Self::LintExpectationId>;
231
232    fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation);
233
234    fn mk_lint_expectation_id(&self, attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId;
235}
236
237impl LintLevelsProvider for TopDown {
238    type LintExpectationId = UnstableLintExpectationId;
239
240    fn current_specs(&self) -> &FxIndexMap<LintId, UnstableLevelSpec> {
241        &self.sets.list[self.cur].specs
242    }
243
244    fn insert(&mut self, id: LintId, level_spec: UnstableLevelSpec) {
245        self.sets.list[self.cur].specs.insert(id, level_spec);
246    }
247
248    fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> UnstableLevelSpec {
249        self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess)
250    }
251
252    fn push_expectation(&mut self, _: Self::LintExpectationId, _: LintExpectation) {}
253
254    fn mk_lint_expectation_id(&self, attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId {
255        UnstableLintExpectationId { attr_id, lint_index }
256    }
257}
258
259struct LintLevelQueryMap<'tcx> {
260    tcx: TyCtxt<'tcx>,
261    cur: HirId,
262    specs: ShallowLintLevelMap,
263    /// Empty hash map to simplify code.
264    empty: FxIndexMap<LintId, StableLevelSpec>,
265    attrs: &'tcx hir::AttributeMap<'tcx>,
266}
267
268impl LintLevelsProvider for LintLevelQueryMap<'_> {
269    type LintExpectationId = StableLintExpectationId;
270
271    fn current_specs(&self) -> &FxIndexMap<LintId, StableLevelSpec> {
272        self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
273    }
274
275    fn insert(&mut self, id: LintId, level_spec: StableLevelSpec) {
276        self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec);
277    }
278
279    fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> StableLevelSpec {
280        self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur)
281    }
282
283    fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation) {
284        self.specs.expectations.push((id, expectation))
285    }
286
287    fn mk_lint_expectation_id(&self, _attr_id: AttrId, lint_index: u16) -> Self::LintExpectationId {
288        StableLintExpectationId { hir_id: self.cur, lint_index }
289    }
290}
291
292impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
293    fn add_id(&mut self, hir_id: HirId) {
294        self.provider.cur = hir_id;
295        self.add_hir(self.provider.attrs.get(hir_id.local_id), hir_id == hir::CRATE_HIR_ID);
296    }
297}
298
299impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
300    type NestedFilter = nested_filter::OnlyBodies;
301
302    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
303        self.provider.tcx
304    }
305
306    fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
307        self.add_id(param.hir_id);
308        intravisit::walk_param(self, param);
309    }
310
311    fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
312        self.add_id(it.hir_id());
313        intravisit::walk_item(self, it);
314    }
315
316    fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
317        self.add_id(it.hir_id());
318        intravisit::walk_foreign_item(self, it);
319    }
320
321    fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
322        self.add_id(s.hir_id);
323        intravisit::walk_stmt(self, s);
324    }
325
326    fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
327        self.add_id(e.hir_id);
328        intravisit::walk_expr(self, e);
329    }
330
331    fn visit_pat_field(&mut self, f: &'tcx hir::PatField<'tcx>) -> Self::Result {
332        self.add_id(f.hir_id);
333        intravisit::walk_pat_field(self, f);
334    }
335
336    fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
337        self.add_id(f.hir_id);
338        intravisit::walk_expr_field(self, f);
339    }
340
341    fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
342        self.add_id(s.hir_id);
343        intravisit::walk_field_def(self, s);
344    }
345
346    fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
347        self.add_id(v.hir_id);
348        intravisit::walk_variant(self, v);
349    }
350
351    fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) {
352        self.add_id(l.hir_id);
353        intravisit::walk_local(self, l);
354    }
355
356    fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
357        self.add_id(a.hir_id);
358        intravisit::walk_arm(self, a);
359    }
360
361    fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
362        self.add_id(trait_item.hir_id());
363        intravisit::walk_trait_item(self, trait_item);
364    }
365
366    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
367        self.add_id(impl_item.hir_id());
368        intravisit::walk_impl_item(self, impl_item);
369    }
370}
371
372pub struct LintLevelsBuilder<'s, P> {
373    sess: &'s Session,
374    features: &'s Features,
375    provider: P,
376    lint_added_lints: bool,
377    store: &'s LintStore,
378    registered_lint_tools: &'s RegisteredTools,
379}
380
381pub(crate) struct BuilderPush {
382    prev: LintStackIndex,
383}
384
385impl<'s> LintLevelsBuilder<'s, TopDown> {
386    pub(crate) fn new(
387        sess: &'s Session,
388        features: &'s Features,
389        lint_added_lints: bool,
390        store: &'s LintStore,
391        registered_lint_tools: &'s RegisteredTools,
392    ) -> Self {
393        let mut builder = LintLevelsBuilder {
394            sess,
395            features,
396            provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
397            lint_added_lints,
398            store,
399            registered_lint_tools,
400        };
401        builder.process_command_line();
402        {
    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);
403        builder
404    }
405
406    pub fn crate_root(
407        sess: &'s Session,
408        features: &'s Features,
409        lint_added_lints: bool,
410        store: &'s LintStore,
411        registered_lint_tools: &'s RegisteredTools,
412        crate_attrs: &[ast::Attribute],
413    ) -> Self {
414        let mut builder = Self::new(sess, features, lint_added_lints, store, registered_lint_tools);
415        builder.add_ast(crate_attrs, true);
416        builder
417    }
418
419    fn process_command_line(&mut self) {
420        self.provider.cur = self
421            .provider
422            .sets
423            .list
424            .push(LintSet { specs: FxIndexMap::default(), parent: COMMAND_LINE });
425        self.add_command_line();
426    }
427
428    /// Pushes a list of AST lint attributes onto this context.
429    ///
430    /// This function will return a `BuilderPush` object which should be passed
431    /// to `pop` when this scope for the attributes provided is exited.
432    ///
433    /// This function will perform a number of tasks:
434    ///
435    /// * It'll validate all lint-related attributes in `attrs`
436    /// * It'll mark all lint-related attributes as used
437    /// * Lint levels will be updated based on the attributes provided
438    /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
439    ///   `#[allow]`
440    ///
441    /// Don't forget to call `pop`!
442    pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush {
443        let prev = self.provider.cur;
444        self.provider.cur =
445            self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
446
447        self.add_ast(attrs, is_crate_node);
448
449        if self.provider.current_specs().is_empty() {
450            self.provider.sets.list.pop();
451            self.provider.cur = prev;
452        }
453
454        BuilderPush { prev }
455    }
456
457    /// Called after `push` when the scope of a set of attributes are exited.
458    pub(crate) fn pop(&mut self, push: BuilderPush) {
459        self.provider.cur = push.prev;
460        std::mem::forget(push);
461    }
462}
463
464#[cfg(debug_assertions)]
465impl Drop for BuilderPush {
466    fn drop(&mut self) {
467        {
    ::core::panicking::panic_fmt(format_args!("Found a `push` without a `pop`."));
};panic!("Found a `push` without a `pop`.");
468    }
469}
470
471impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P>
472where
473    LevelSpec<P::LintExpectationId>: Into<LevelSpec>,
474{
475    pub(crate) fn sess(&self) -> &Session {
476        self.sess
477    }
478
479    pub(crate) fn features(&self) -> &Features {
480        self.features
481    }
482
483    fn add_command_line(&mut self) {
484        for &(ref lint_name, level) in &self.sess.opts.lint_opts {
485            let lint_name = LintName::from_str(lint_name);
486            // Checks the validity of lint names derived from the command line.
487            if lint_name.is(sym::warnings) && #[allow(non_exhaustive_omitted_patterns)] match level {
    Level::ForceWarn => true,
    _ => false,
}matches!(level, Level::ForceWarn) {
488                self.sess
489                    .dcx()
490                    .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
491            }
492
493            match self.store.check_lint_name(lint_name, self.registered_lint_tools) {
494                CheckLintNameResult::Renamed(replacement) => {
495                    let suggestion = RenamedLintSuggestion::WithoutSpan { replacement };
496                    let requested_level = RequestedLevel { level, lint_name };
497                    let lint = RenamedLintFromCommandLine {
498                        lint_name,
499                        replacement,
500                        suggestion,
501                        requested_level,
502                    };
503                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
504                }
505                CheckLintNameResult::Removed(ref reason) => {
506                    let requested_level = RequestedLevel { level, lint_name };
507                    let lint = RemovedLintFromCommandLine { lint_name, reason, requested_level };
508                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
509                }
510                CheckLintNameResult::NoLint(suggestion) => {
511                    let suggestion = suggestion.map(|(replacement, from_rustc)| {
512                        UnknownLintSuggestion::WithoutSpan { replacement, from_rustc }
513                    });
514                    let requested_level = RequestedLevel { level, lint_name };
515                    let lint =
516                        UnknownLintFromCommandLine { lint_name, suggestion, requested_level };
517                    self.emit_lint(UNKNOWN_LINTS, lint);
518                }
519                CheckLintNameResult::Tool(_, Some(replacement)) => {
520                    let requested_level = RequestedLevel { level, lint_name };
521                    let lint = DeprecatedLintNameFromCommandLine {
522                        lint_name,
523                        replacement,
524                        requested_level,
525                    };
526                    self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
527                }
528                CheckLintNameResult::NoTool(tool_name) => {
529                    self.sess.dcx().emit_err(CheckNameUnknownTool {
530                        tool_name,
531                        sub: RequestedLevel { level, lint_name },
532                    });
533                }
534                _ => {}
535            };
536
537            let Some(ids) = self.store.find_lints(lint_name) else {
538                // errors already handled above
539                continue;
540            };
541            for &id in ids {
542                // ForceWarn and Forbid cannot be overridden
543                if let Some(level_spec) = self.provider.current_specs().get(&id)
544                    && #[allow(non_exhaustive_omitted_patterns)] match level_spec.level() {
    Level::ForceWarn | Level::Forbid => true,
    _ => false,
}matches!(level_spec.level(), Level::ForceWarn | Level::Forbid)
545                {
546                    continue;
547                }
548
549                if self.check_gated_lint(id, DUMMY_SP, true) {
550                    let src = LintLevelSource::CommandLine(lint_name, level);
551                    self.provider.insert(id, LevelSpec::new(level, None, src));
552                }
553            }
554        }
555    }
556
557    /// Attempts to insert the `id` to `LevelSpec` map entry. If unsuccessful
558    /// (e.g. if a forbid was already inserted on the same scope), then emits a
559    /// diagnostic with no change to `specs`.
560    fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec<P::LintExpectationId>) {
561        let level = level_spec.level();
562        let lint_id = level_spec.lint_id();
563        let src = level_spec.src;
564
565        let old_level_spec = self.provider.get_lint_level_spec(id.lint, self.sess);
566        let old_level = old_level_spec.level();
567        let old_src = old_level_spec.src;
568
569        // Setting to a non-forbid level is an error if the lint previously had
570        // a forbid level. Note that this is not necessarily true even with a
571        // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
572        //
573        // This means that this only errors if we're truly lowering the lint
574        // level from forbid.
575        if self.lint_added_lints && level == Level::Deny && old_level == Level::Forbid {
576            // Having a deny inside a forbid is fine and is ignored, so we skip this check.
577            return;
578        } else if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid {
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 fcw_warning = match old_src {
586                LintLevelSource::Default => false,
587                LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
588                LintLevelSource::CommandLine(name, _) => self.store.is_lint_group(name),
589            };
590            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/levels.rs:590",
                        "rustc_lint::levels", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/levels.rs"),
                        ::tracing_core::__macro_support::Option::Some(590u32),
                        ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::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 ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
591                "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
592                fcw_warning,
593                self.provider.current_specs(),
594                old_src,
595                id.lint.name_lower(),
596            );
597            let sub = match old_src {
598                LintLevelSource::Default => {
599                    OverruledAttributeSub::DefaultSource { id: id.to_string() }
600                }
601                LintLevelSource::Node { span, reason, .. } => {
602                    OverruledAttributeSub::NodeSource { span, reason }
603                }
604                LintLevelSource::CommandLine(lint_name, _) => {
605                    OverruledAttributeSub::CommandLineSource { forbidden_lint_name: lint_name }
606                }
607            };
608            if !fcw_warning {
609                self.sess.dcx().emit_err(OverruledAttribute {
610                    span: src.span(),
611                    overruled: src.span(),
612                    lint_level: level.as_str(),
613                    lint_name: src.name(),
614                    sub,
615                });
616            } else {
617                self.emit_span_lint(
618                    FORBIDDEN_LINT_GROUPS,
619                    src.span().into(),
620                    OverruledAttributeLint {
621                        overruled: src.span(),
622                        lint_level: level.as_str(),
623                        lint_name: src.name(),
624                        sub,
625                    },
626                );
627            }
628
629            // Retain the forbid lint level, unless we are
630            // issuing a FCW. In the FCW case, we want to
631            // respect the new setting.
632            if !fcw_warning {
633                return;
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
641            && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
642        {
643            return;
644        }
645
646        match (old_level, level) {
647            // If the new level is an expectation store it in `ForceWarn`
648            (Level::ForceWarn, Level::Expect) => {
649                self.provider.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src))
650            }
651            // Keep `ForceWarn` level but drop the expectation
652            (Level::ForceWarn, _) => {
653                self.provider.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src))
654            }
655            // Set the lint level as normal
656            _ => self.provider.insert(id, LevelSpec::new(level, lint_id, src)),
657        };
658    }
659
660    fn add_ast(&mut self, attrs: &[rustc_ast::Attribute], is_crate_node: bool) {
661        if attrs.is_empty() {
662            return;
663        }
664        for attr in attrs {
665            // `SINGLE_USE_LIFETIMES` is an AST lint so we only insert this here
666            if attr.has_name(sym::automatically_derived) {
667                self.provider.insert(
668                    LintId::of(SINGLE_USE_LIFETIMES),
669                    LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
670                );
671            }
672        }
673        let parsed = AttributeParser::parse_limited_all(
674            self.sess,
675            attrs,
676            Some(&|attr| {
677                // This is ...complicated. Sometimes we need to parse lint check
678                // attributes pre-expansion, but of course things are allowed to
679                // be invalid if they're removed by a macro/cfg attribute before
680                // we do attribute parsing, which happens (mostly) post-expansion.
681                //
682                // Unfortunately we're somewhat inconsistent - sometimes we just
683                // give up and sometimes we error. Take for example:
684                //
685                // #[expect] // OK
686                // #[expect[wut]] // OK
687                // #[expect(expect)] // OK
688                // #[expect(expect(expect))] //~ ERROR malformed lint attribute input
689                // #[deny(({!}))] // OK
690                // #[expect[helix::<ub>]] // OK
691                // #[cfg(false)]
692                // const _: () = ();
693                //
694                // FIXME: just allow everything?
695                (attr.path_matches(&[sym::allow])
696                    || attr.path_matches(&[sym::warn])
697                    || attr.path_matches(&[sym::deny])
698                    || attr.path_matches(&[sym::forbid])
699                    || attr.path_matches(&[sym::expect]))
700                    && attr.meta_item_list().is_some()
701            }),
702            AstTarget::None(Target::Crate),
703            DUMMY_SP,
704            CRATE_NODE_ID,
705            Some(self.features),
706            ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
707            Some(self.registered_lint_tools),
708        );
709        self.add_hir_inner(&parsed, is_crate_node)
710    }
711
712    fn add_hir(&mut self, attrs: &[Attribute], is_crate_node: bool) {
713        if attrs.is_empty() {
714            return;
715        }
716        // `MISSING_DOCS` is a HIR lint so we only insert this here
717        if {
    {
            'done:
                {
                for i in attrs {
                    #[allow(unused_imports)]
                    use ::rustc_attr_ir::AttributeKind::*;
                    let i: &::rustc_attr_ir::Attribute = i;
                    match i {
                        ::rustc_attr_ir::Attribute::Parsed(Doc(DocAttribute {
                            hidden: Some(_), .. })) => {
                            break 'done Some(());
                        }
                        ::rustc_attr_ir::Attribute::Tool(..) |
                            ::rustc_attr_ir::Attribute::DeriveHelper(..) =>
                            {}
                            #[deny(unreachable_patterns)]
                            _ => {}
                    }
                }
                None
            }
        }.is_some()
}find_attr!(attrs, Doc(DocAttribute { hidden: Some(_), .. })) {
718            self.provider.insert(
719                LintId::of(MISSING_DOCS),
720                LevelSpec::new(Level::Allow, None, LintLevelSource::Default),
721            );
722        }
723        self.add_hir_inner(attrs, is_crate_node)
724    }
725
726    fn add_hir_inner(&mut self, attrs: &[Attribute], is_crate_node: bool) {
727        let Some(lint_checks) = {
    'done:
        {
        for i in attrs {
            #[allow(unused_imports)]
            use ::rustc_attr_ir::AttributeKind::*;
            let i: &::rustc_attr_ir::Attribute = i;
            match i {
                ::rustc_attr_ir::Attribute::Parsed(LintCheck(lints)) => {
                    break 'done Some(lints.clone());
                }
                ::rustc_attr_ir::Attribute::Tool(..) |
                    ::rustc_attr_ir::Attribute::DeriveHelper(..) =>
                    {}
                    #[deny(unreachable_patterns)]
                    _ => {}
            }
        }
        None
    }
}find_attr!(attrs, LintCheck(lints) => lints.clone()) else {
728            return;
729        };
730
731        let sess = self.sess;
732
733        // FIXME: consider performing these checks in attribute parsing instead
734        for (lint_index, lint_check) in lint_checks.into_iter().enumerate() {
735            let LintCheck { lint_name, kind, reason, attr_id, attr_span: _, lint_span, tool_span } =
736                lint_check;
737            let LintName { tool, name: _ } = lint_name;
738
739            let level = match kind {
740                LintCheckKind::Allow => Level::Allow,
741                LintCheckKind::Warn => Level::Warn,
742                LintCheckKind::Deny => Level::Deny,
743                LintCheckKind::Forbid => Level::Forbid,
744                LintCheckKind::Expect => Level::Expect,
745            };
746
747            // `Expect` is the only lint level with a `LintExpectationId` that can be created
748            // from an attribute.
749            let lint_id = (level == Level::Expect)
750                .then(|| self.provider.mk_lint_expectation_id(attr_id.attr_id, lint_index as u16));
751
752            let lint_result = self.store.check_lint_name(lint_name, self.registered_lint_tools);
753
754            let (ids, name): (_, LintName) = match lint_result {
755                CheckLintNameResult::Ok(ids) => (ids, lint_name),
756
757                CheckLintNameResult::Tool(ids, new_lint_name) => {
758                    let name = match new_lint_name {
759                        None => lint_name,
760                        Some(replacement) => {
761                            self.emit_span_lint(
762                                builtin::RENAMED_AND_REMOVED_LINTS,
763                                lint_span.into(),
764                                DeprecatedLintName {
765                                    lint_name,
766                                    suggestion: lint_span,
767                                    replacement,
768                                },
769                            );
770                            replacement
771                        }
772                    };
773                    (ids, name)
774                }
775
776                CheckLintNameResult::MissingTool => {
777                    // If `MissingTool` is returned, then either the lint does not
778                    // exist in the tool or the code was not compiled with the tool and
779                    // therefore the lint was never added to the `LintStore`. To detect
780                    // this is the responsibility of the lint tool.
781                    continue;
782                }
783
784                CheckLintNameResult::NoTool(tool_name) => {
785                    sess.dcx().emit_err(UnknownToolInScopedLint {
786                        span: Some(tool_span.unwrap()),
787                        tool_name,
788                        lint_name,
789                        is_nightly_build: sess.is_nightly_build(),
790                    });
791                    continue;
792                }
793
794                CheckLintNameResult::Renamed(replacement) => {
795                    if self.lint_added_lints {
796                        let suggestion =
797                            RenamedLintSuggestion::WithSpan { suggestion: lint_span, replacement };
798
799                        self.emit_span_lint(
800                            RENAMED_AND_REMOVED_LINTS,
801                            lint_span.into(),
802                            RenamedLint { name: lint_name, replacement, suggestion },
803                        );
804                    }
805
806                    // If this lint was renamed, apply the new lint instead of ignoring the
807                    // attribute. Ignore any errors or warnings that happen because the new
808                    // name is inaccurate.
809                    let replace_check =
810                        self.store.check_lint_name(replacement, self.registered_lint_tools);
811                    let (CheckLintNameResult::Ok(ids) | CheckLintNameResult::Tool(ids, _)) =
812                        replace_check
813                    else {
814                        {
    ::core::panicking::panic_fmt(format_args!("renamed lint does not exist: {0} :{1:?}",
            replacement, replace_check));
};panic!("renamed lint does not exist: {replacement} :{replace_check:?}");
815                    };
816
817                    (ids, replacement)
818                }
819
820                CheckLintNameResult::Removed(ref reason) => {
821                    if self.lint_added_lints {
822                        self.emit_span_lint(
823                            RENAMED_AND_REMOVED_LINTS,
824                            lint_span.into(),
825                            RemovedLint { name: lint_name, reason },
826                        );
827                    }
828                    continue;
829                }
830
831                CheckLintNameResult::NoLint(suggestion) => {
832                    if self.lint_added_lints {
833                        let suggestion = suggestion.map(|(replacement, from_rustc)| {
834                            UnknownLintSuggestion::WithSpan {
835                                suggestion: lint_span,
836                                replacement,
837                                from_rustc,
838                            }
839                        });
840                        self.emit_span_lint(
841                            UNKNOWN_LINTS,
842                            lint_span.into(),
843                            UnknownLint { name: lint_name, suggestion },
844                        );
845                    }
846                    continue;
847                }
848            };
849
850            let src = LintLevelSource::Node { name, span: lint_span, reason };
851            for &id in ids {
852                if self.check_gated_lint(id, lint_span, false) {
853                    self.insert_spec(id, LevelSpec::new(level, lint_id, src));
854                }
855            }
856
857            // This checks for instances where the user writes
858            // `#[expect(unfulfilled_lint_expectations)]` in that case we want to avoid
859            // overriding the lint level but instead add an expectation that can't be
860            // fulfilled. The lint message will include an explanation, that the
861            // `unfulfilled_lint_expectations` lint can't be expected.
862            if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
863                // The `unfulfilled_lint_expectations` lint is not part of any lint
864                // groups. Therefore. we only need to check the slice if it contains a
865                // single lint.
866                let is_unfulfilled_lint_expectations = match ids {
867                    [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
868                    _ => false,
869                };
870                self.provider.push_expectation(
871                    expect_id,
872                    LintExpectation::new(reason, lint_span, is_unfulfilled_lint_expectations, tool),
873                );
874            }
875        }
876
877        if self.lint_added_lints && !is_crate_node {
878            for (id, level_spec) in self.provider.current_specs().iter() {
879                if !id.lint.crate_level_only {
880                    continue;
881                }
882
883                let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } =
884                    level_spec.src
885                else {
886                    continue;
887                };
888
889                self.emit_span_lint(
890                    UNUSED_ATTRIBUTES,
891                    lint_attr_span.into(),
892                    IgnoredUnlessCrateSpecified {
893                        level: level_spec.level().as_str(),
894                        lint_name: lint_attr_name,
895                    },
896                );
897                // don't set a separate error for every lint in the group
898                break;
899            }
900        }
901    }
902
903    /// Checks if the lint is gated on a feature that is not enabled.
904    ///
905    /// Returns `true` if the lint's feature is enabled.
906    #[track_caller]
907    fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
908        let feature = if let Some(feature) = lint_id.lint.feature_gate
909            && !self.features.enabled(feature)
910            && !span.allows_unstable(feature)
911        {
912            // Lint is behind a feature that is not enabled; eventually return false.
913            feature
914        } else {
915            // Lint is ungated or its feature is enabled; exit early.
916            return true;
917        };
918
919        struct UnknownLint<'a> {
920            sess: &'a Session,
921            lint_id: LintId,
922            feature: Symbol,
923            lint_from_cli: bool,
924        }
925
926        impl<'a, 'b> Diagnostic<'a> for UnknownLint<'b> {
927            fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: rustc_errors::Level) -> Diag<'a> {
928                let Self { sess, lint_id, feature, lint_from_cli } = self;
929                let mut lint = Diag::new(dcx, level, rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unknown lint: `{$name}`"))msg!("unknown lint: `{$name}`"))
930                    .with_arg("name", lint_id.lint.name_lower())
931                    .with_note(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("the `{$name}` lint is unstable"))msg!("the `{$name}` lint is unstable"));
932                // `staged_api` is only intended for the standard library, so don't
933                // suggest enabling it just to use this lint.
934                if feature != sym::staged_api {
935                    rustc_session::diagnostics::add_feature_diagnostics_for_issue(
936                        &mut lint,
937                        sess,
938                        feature,
939                        GateIssue::Language,
940                        lint_from_cli,
941                        None,
942                    );
943                }
944                lint
945            }
946        }
947
948        if self.lint_added_lints {
949            let lint = builtin::UNKNOWN_LINTS;
950            let level_spec = self.lint_level_spec(builtin::UNKNOWN_LINTS);
951            emit_lint_base(
952                self.sess,
953                lint,
954                level_spec,
955                Some(span.into()),
956                UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli },
957            );
958        }
959
960        false
961    }
962
963    /// Find the lint level for a lint.
964    pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<P::LintExpectationId> {
965        self.provider.get_lint_level_spec(lint, self.sess)
966    }
967
968    /// Used to emit a lint-related diagnostic based on the current state of
969    /// this lint context.
970    #[track_caller]
971    pub(crate) fn opt_span_lint(
972        &self,
973        lint: &'static Lint,
974        span: Option<MultiSpan>,
975        decorator: impl for<'a> Diagnostic<'a>,
976    ) {
977        let level_spec = self.lint_level_spec(lint);
978        emit_lint_base(self.sess, lint, level_spec, span, decorator)
979    }
980
981    #[track_caller]
982    pub fn emit_span_lint(
983        &self,
984        lint: &'static Lint,
985        span: MultiSpan,
986        decorator: impl for<'a> Diagnostic<'a>,
987    ) {
988        let level_spec = self.lint_level_spec(lint);
989        emit_lint_base(self.sess, lint, level_spec, Some(span), decorator);
990    }
991
992    #[track_caller]
993    pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a>) {
994        let level_spec = self.lint_level_spec(lint);
995        emit_lint_base(self.sess, lint, level_spec, None, decorator);
996    }
997}
998
999pub(crate) fn provide(providers: &mut Providers) {
1000    *providers = Providers { shallow_lint_levels_on, skippable_lints, ..*providers };
1001}