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