Skip to main content

rustc_lint/
levels.rs

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