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