Skip to main content

rustc_lint/
levels.rs

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