Skip to main content

rustc_lint/
levels.rs

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