Skip to main content

rustc_lint/
levels.rs

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