Skip to main content

rustc_middle/
lint.rs

1use std::cmp;
2
3use rustc_data_structures::fx::FxIndexMap;
4use rustc_data_structures::sorted_map::SortedMap;
5use rustc_errors::{Diag, Diagnostic, MultiSpan};
6use rustc_hir::{HirId, ItemLocalId};
7use rustc_lint_defs::EditionFcw;
8use rustc_macros::{Decodable, Encodable, HashStable};
9use rustc_session::Session;
10use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS};
11use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId};
12use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw};
13use tracing::instrument;
14
15use crate::ty::TyCtxt;
16
17/// How a lint level was set.
18#[derive(#[automatically_derived]
impl ::core::clone::Clone for LintLevelSource {
    #[inline]
    fn clone(&self) -> LintLevelSource {
        let _: ::core::clone::AssertParamIsClone<Symbol>;
        let _: ::core::clone::AssertParamIsClone<Span>;
        let _: ::core::clone::AssertParamIsClone<Option<Symbol>>;
        let _: ::core::clone::AssertParamIsClone<Level>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LintLevelSource { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for LintLevelSource {
    #[inline]
    fn eq(&self, other: &LintLevelSource) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (LintLevelSource::Node {
                    name: __self_0, span: __self_1, reason: __self_2 },
                    LintLevelSource::Node {
                    name: __arg1_0, span: __arg1_1, reason: __arg1_2 }) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1 &&
                        __self_2 == __arg1_2,
                (LintLevelSource::CommandLine(__self_0, __self_1),
                    LintLevelSource::CommandLine(__arg1_0, __arg1_1)) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LintLevelSource {
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Symbol>;
        let _: ::core::cmp::AssertParamIsEq<Span>;
        let _: ::core::cmp::AssertParamIsEq<Option<Symbol>>;
        let _: ::core::cmp::AssertParamIsEq<Level>;
    }
}Eq, const _: () =
    {
        impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
            for LintLevelSource {
            fn encode(&self, __encoder: &mut __E) {
                let disc =
                    match *self {
                        LintLevelSource::Default => { 0usize }
                        LintLevelSource::Node {
                            name: ref __binding_0,
                            span: ref __binding_1,
                            reason: ref __binding_2 } => {
                            1usize
                        }
                        LintLevelSource::CommandLine(ref __binding_0,
                            ref __binding_1) => {
                            2usize
                        }
                    };
                ::rustc_serialize::Encoder::emit_u8(__encoder, disc as u8);
                match *self {
                    LintLevelSource::Default => {}
                    LintLevelSource::Node {
                        name: ref __binding_0,
                        span: ref __binding_1,
                        reason: ref __binding_2 } => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_2,
                            __encoder);
                    }
                    LintLevelSource::CommandLine(ref __binding_0,
                        ref __binding_1) => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                    }
                }
            }
        }
    };Encodable, const _: () =
    {
        impl<__D: ::rustc_span::SpanDecoder> ::rustc_serialize::Decodable<__D>
            for LintLevelSource {
            fn decode(__decoder: &mut __D) -> Self {
                match ::rustc_serialize::Decoder::read_u8(__decoder) as usize
                    {
                    0usize => { LintLevelSource::Default }
                    1usize => {
                        LintLevelSource::Node {
                            name: ::rustc_serialize::Decodable::decode(__decoder),
                            span: ::rustc_serialize::Decodable::decode(__decoder),
                            reason: ::rustc_serialize::Decodable::decode(__decoder),
                        }
                    }
                    2usize => {
                        LintLevelSource::CommandLine(::rustc_serialize::Decodable::decode(__decoder),
                            ::rustc_serialize::Decodable::decode(__decoder))
                    }
                    n => {
                        ::core::panicking::panic_fmt(format_args!("invalid enum variant tag while decoding `LintLevelSource`, expected 0..3, actual {0}",
                                n));
                    }
                }
            }
        }
    };Decodable, const _: () =
    {
        impl<'__ctx>
            ::rustc_data_structures::stable_hasher::HashStable<::rustc_middle::ich::StableHashingContext<'__ctx>>
            for LintLevelSource {
            #[inline]
            fn hash_stable(&self,
                __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                ::std::mem::discriminant(self).hash_stable(__hcx, __hasher);
                match *self {
                    LintLevelSource::Default => {}
                    LintLevelSource::Node {
                        name: ref __binding_0,
                        span: ref __binding_1,
                        reason: ref __binding_2 } => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                        { __binding_2.hash_stable(__hcx, __hasher); }
                    }
                    LintLevelSource::CommandLine(ref __binding_0,
                        ref __binding_1) => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                    }
                }
            }
        }
    };HashStable, #[automatically_derived]
impl ::core::fmt::Debug for LintLevelSource {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            LintLevelSource::Default =>
                ::core::fmt::Formatter::write_str(f, "Default"),
            LintLevelSource::Node {
                name: __self_0, span: __self_1, reason: __self_2 } =>
                ::core::fmt::Formatter::debug_struct_field3_finish(f, "Node",
                    "name", __self_0, "span", __self_1, "reason", &__self_2),
            LintLevelSource::CommandLine(__self_0, __self_1) =>
                ::core::fmt::Formatter::debug_tuple_field2_finish(f,
                    "CommandLine", __self_0, &__self_1),
        }
    }
}Debug)]
19pub enum LintLevelSource {
20    /// Lint is at the default level as declared in rustc.
21    Default,
22
23    /// Lint level was set by an attribute.
24    Node {
25        name: Symbol,
26        span: Span,
27        /// RFC 2383 reason
28        reason: Option<Symbol>,
29    },
30
31    /// Lint level was set by a command-line flag.
32    /// The provided `Level` is the level specified on the command line.
33    /// (The actual level may be lower due to `--cap-lints`.)
34    CommandLine(Symbol, Level),
35}
36
37impl LintLevelSource {
38    pub fn name(&self) -> Symbol {
39        match *self {
40            LintLevelSource::Default => kw::Default,
41            LintLevelSource::Node { name, .. } => name,
42            LintLevelSource::CommandLine(name, _) => name,
43        }
44    }
45
46    pub fn span(&self) -> Span {
47        match *self {
48            LintLevelSource::Default => DUMMY_SP,
49            LintLevelSource::Node { span, .. } => span,
50            LintLevelSource::CommandLine(_, _) => DUMMY_SP,
51        }
52    }
53}
54
55/// Convenience helper for moving things around together that frequently are paired
56#[derive(#[automatically_derived]
impl ::core::marker::Copy for LevelAndSource { }Copy, #[automatically_derived]
impl ::core::clone::Clone for LevelAndSource {
    #[inline]
    fn clone(&self) -> LevelAndSource {
        let _: ::core::clone::AssertParamIsClone<Level>;
        let _: ::core::clone::AssertParamIsClone<Option<LintExpectationId>>;
        let _: ::core::clone::AssertParamIsClone<LintLevelSource>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for LevelAndSource {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "LevelAndSource", "level", &self.level, "lint_id", &self.lint_id,
            "src", &&self.src)
    }
}Debug, const _: () =
    {
        impl<'__ctx>
            ::rustc_data_structures::stable_hasher::HashStable<::rustc_middle::ich::StableHashingContext<'__ctx>>
            for LevelAndSource {
            #[inline]
            fn hash_stable(&self,
                __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                match *self {
                    LevelAndSource {
                        level: ref __binding_0,
                        lint_id: ref __binding_1,
                        src: ref __binding_2 } => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                        { __binding_2.hash_stable(__hcx, __hasher); }
                    }
                }
            }
        }
    };HashStable, const _: () =
    {
        impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
            for LevelAndSource {
            fn encode(&self, __encoder: &mut __E) {
                match *self {
                    LevelAndSource {
                        level: ref __binding_0,
                        lint_id: ref __binding_1,
                        src: ref __binding_2 } => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_2,
                            __encoder);
                    }
                }
            }
        }
    };Encodable, const _: () =
    {
        impl<__D: ::rustc_span::SpanDecoder> ::rustc_serialize::Decodable<__D>
            for LevelAndSource {
            fn decode(__decoder: &mut __D) -> Self {
                LevelAndSource {
                    level: ::rustc_serialize::Decodable::decode(__decoder),
                    lint_id: ::rustc_serialize::Decodable::decode(__decoder),
                    src: ::rustc_serialize::Decodable::decode(__decoder),
                }
            }
        }
    };Decodable)]
57pub struct LevelAndSource {
58    pub level: Level,
59    pub lint_id: Option<LintExpectationId>,
60    pub src: LintLevelSource,
61}
62
63/// Return type for the `shallow_lint_levels_on` query.
64///
65/// This map represents the set of allowed lints and allowance levels given
66/// by the attributes for *a single HirId*.
67#[derive(#[automatically_derived]
impl ::core::default::Default for ShallowLintLevelMap {
    #[inline]
    fn default() -> ShallowLintLevelMap {
        ShallowLintLevelMap {
            expectations: ::core::default::Default::default(),
            specs: ::core::default::Default::default(),
        }
    }
}Default, #[automatically_derived]
impl ::core::fmt::Debug for ShallowLintLevelMap {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "ShallowLintLevelMap", "expectations", &self.expectations,
            "specs", &&self.specs)
    }
}Debug, const _: () =
    {
        impl<'__ctx>
            ::rustc_data_structures::stable_hasher::HashStable<::rustc_middle::ich::StableHashingContext<'__ctx>>
            for ShallowLintLevelMap {
            #[inline]
            fn hash_stable(&self,
                __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                match *self {
                    ShallowLintLevelMap {
                        expectations: ref __binding_0, specs: ref __binding_1 } => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                    }
                }
            }
        }
    };HashStable)]
68pub struct ShallowLintLevelMap {
69    pub expectations: Vec<(LintExpectationId, LintExpectation)>,
70    pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelAndSource>>,
71}
72
73/// From an initial level and source, verify the effect of special annotations:
74/// `warnings` lint level and lint caps.
75///
76/// The return of this function is suitable for diagnostics.
77pub fn reveal_actual_level(
78    level: Option<(Level, Option<LintExpectationId>)>,
79    src: &mut LintLevelSource,
80    sess: &Session,
81    lint: LintId,
82    probe_for_lint_level: impl FnOnce(
83        LintId,
84    )
85        -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
86) -> (Level, Option<LintExpectationId>) {
87    // If `level` is none then we actually assume the default level for this lint.
88    let (mut level, mut lint_id) =
89        level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
90
91    // If we're about to issue a warning, check at the last minute for any
92    // directives against the warnings "lint". If, for example, there's an
93    // `allow(warnings)` in scope then we want to respect that instead.
94    //
95    // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
96    // triggers in cases (like #80988) where you have `forbid(warnings)`,
97    // and so if we turned that into an error, it'd defeat the purpose of the
98    // future compatibility warning.
99    if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
100        let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
101        if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
102            if configured_warning_level != Level::Warn {
103                level = configured_warning_level;
104                lint_id = configured_lint_id;
105                *src = warnings_src;
106            }
107        }
108    }
109
110    // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
111    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
112        level
113    } else {
114        cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
115    };
116
117    if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
118        // Ensure that we never exceed driver level.
119        level = cmp::min(*driver_level, level);
120    }
121
122    (level, lint_id)
123}
124
125impl ShallowLintLevelMap {
126    /// Perform a deep probe in the HIR tree looking for the actual level for the lint.
127    /// This lint level is not usable for diagnostics, it needs to be corrected by
128    /// `reveal_actual_level` beforehand.
129    x;#[instrument(level = "trace", skip(self, tcx), ret)]
130    fn probe_for_lint_level(
131        &self,
132        tcx: TyCtxt<'_>,
133        id: LintId,
134        start: HirId,
135    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
136        if let Some(map) = self.specs.get(&start.local_id)
137            && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
138        {
139            return (Some((level, lint_id)), src);
140        }
141
142        let mut owner = start.owner;
143        let mut specs = &self.specs;
144
145        for parent in tcx.hir_parent_id_iter(start) {
146            if parent.owner != owner {
147                owner = parent.owner;
148                specs = &tcx.shallow_lint_levels_on(owner).specs;
149            }
150            if let Some(map) = specs.get(&parent.local_id)
151                && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
152            {
153                return (Some((level, lint_id)), src);
154            }
155        }
156
157        (None, LintLevelSource::Default)
158    }
159
160    /// Fetch and return the user-visible lint level for the given lint at the given HirId.
161    x;#[instrument(level = "trace", skip(self, tcx), ret)]
162    pub fn lint_level_id_at_node(
163        &self,
164        tcx: TyCtxt<'_>,
165        lint: LintId,
166        cur: HirId,
167    ) -> LevelAndSource {
168        let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
169        let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
170            self.probe_for_lint_level(tcx, lint, cur)
171        });
172        LevelAndSource { level, lint_id, src }
173    }
174}
175
176impl TyCtxt<'_> {
177    /// Fetch and return the user-visible lint level for the given lint at the given HirId.
178    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
179        self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
180    }
181}
182
183/// This struct represents a lint expectation and holds all required information
184/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after
185/// the `LateLintPass` has completed.
186#[derive(#[automatically_derived]
impl ::core::clone::Clone for LintExpectation {
    #[inline]
    fn clone(&self) -> LintExpectation {
        LintExpectation {
            reason: ::core::clone::Clone::clone(&self.reason),
            emission_span: ::core::clone::Clone::clone(&self.emission_span),
            is_unfulfilled_lint_expectations: ::core::clone::Clone::clone(&self.is_unfulfilled_lint_expectations),
            lint_tool: ::core::clone::Clone::clone(&self.lint_tool),
        }
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for LintExpectation {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "LintExpectation", "reason", &self.reason, "emission_span",
            &self.emission_span, "is_unfulfilled_lint_expectations",
            &self.is_unfulfilled_lint_expectations, "lint_tool",
            &&self.lint_tool)
    }
}Debug, const _: () =
    {
        impl<__E: ::rustc_span::SpanEncoder> ::rustc_serialize::Encodable<__E>
            for LintExpectation {
            fn encode(&self, __encoder: &mut __E) {
                match *self {
                    LintExpectation {
                        reason: ref __binding_0,
                        emission_span: ref __binding_1,
                        is_unfulfilled_lint_expectations: ref __binding_2,
                        lint_tool: ref __binding_3 } => {
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_0,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_1,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_2,
                            __encoder);
                        ::rustc_serialize::Encodable::<__E>::encode(__binding_3,
                            __encoder);
                    }
                }
            }
        }
    };Encodable, const _: () =
    {
        impl<__D: ::rustc_span::SpanDecoder> ::rustc_serialize::Decodable<__D>
            for LintExpectation {
            fn decode(__decoder: &mut __D) -> Self {
                LintExpectation {
                    reason: ::rustc_serialize::Decodable::decode(__decoder),
                    emission_span: ::rustc_serialize::Decodable::decode(__decoder),
                    is_unfulfilled_lint_expectations: ::rustc_serialize::Decodable::decode(__decoder),
                    lint_tool: ::rustc_serialize::Decodable::decode(__decoder),
                }
            }
        }
    };Decodable, const _: () =
    {
        impl<'__ctx>
            ::rustc_data_structures::stable_hasher::HashStable<::rustc_middle::ich::StableHashingContext<'__ctx>>
            for LintExpectation {
            #[inline]
            fn hash_stable(&self,
                __hcx: &mut ::rustc_middle::ich::StableHashingContext<'__ctx>,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                match *self {
                    LintExpectation {
                        reason: ref __binding_0,
                        emission_span: ref __binding_1,
                        is_unfulfilled_lint_expectations: ref __binding_2,
                        lint_tool: ref __binding_3 } => {
                        { __binding_0.hash_stable(__hcx, __hasher); }
                        { __binding_1.hash_stable(__hcx, __hasher); }
                        { __binding_2.hash_stable(__hcx, __hasher); }
                        { __binding_3.hash_stable(__hcx, __hasher); }
                    }
                }
            }
        }
    };HashStable)]
187pub struct LintExpectation {
188    /// The reason for this expectation that can optionally be added as part of
189    /// the attribute. It will be displayed as part of the lint message.
190    pub reason: Option<Symbol>,
191    /// The [`Span`] of the attribute that this expectation originated from.
192    pub emission_span: Span,
193    /// Lint messages for the `unfulfilled_lint_expectations` lint will be
194    /// adjusted to include an additional note. Therefore, we have to track if
195    /// the expectation is for the lint.
196    pub is_unfulfilled_lint_expectations: bool,
197    /// This will hold the name of the tool that this lint belongs to. For
198    /// the lint `clippy::some_lint` the tool would be `clippy`, the same
199    /// goes for `rustdoc`. This will be `None` for rustc lints
200    pub lint_tool: Option<Symbol>,
201}
202
203impl LintExpectation {
204    pub fn new(
205        reason: Option<Symbol>,
206        emission_span: Span,
207        is_unfulfilled_lint_expectations: bool,
208        lint_tool: Option<Symbol>,
209    ) -> Self {
210        Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
211    }
212}
213
214fn explain_lint_level_source(
215    sess: &Session,
216    lint: &'static Lint,
217    level: Level,
218    src: LintLevelSource,
219    err: &mut Diag<'_, ()>,
220) {
221    // Find the name of the lint group that contains the given lint.
222    // Assumes the lint only belongs to one group.
223    let lint_group_name = |lint| {
224        let lint_groups_iter = sess.lint_groups_iter();
225        let lint_id = LintId::of(lint);
226        lint_groups_iter
227            .filter(|lint_group| !lint_group.is_externally_loaded)
228            .find(|lint_group| {
229                lint_group
230                    .lints
231                    .iter()
232                    .find(|lint_group_lint| **lint_group_lint == lint_id)
233                    .is_some()
234            })
235            .map(|lint_group| lint_group.name)
236    };
237    let name = lint.name_lower();
238    if let Level::Allow = level {
239        // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
240        // triggering. (#121009)
241        return;
242    }
243    match src {
244        LintLevelSource::Default => {
245            let level_str = level.as_str();
246            match lint_group_name(lint) {
247                Some(group_name) => {
248                    err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`#[{0}({1})]` (part of `#[{0}({2})]`) on by default",
                level_str, name, group_name))
    })format!("`#[{level_str}({name})]` (part of `#[{level_str}({group_name})]`) on by default"));
249                }
250                None => {
251                    err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`#[{0}({1})]` on by default",
                level_str, name))
    })format!("`#[{level_str}({name})]` on by default"));
252                }
253            }
254        }
255        LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
256            let flag = orig_level.to_cmd_flag();
257            let hyphen_case_lint_name = name.replace('_', "-");
258            if lint_flag_val.as_str() == name {
259                err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("requested on the command line with `{0} {1}`",
                flag, hyphen_case_lint_name))
    })format!(
260                    "requested on the command line with `{flag} {hyphen_case_lint_name}`"
261                ));
262            } else {
263                let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
264                err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0} {1}` implied by `{0} {2}`",
                flag, hyphen_case_lint_name, hyphen_case_flag_val))
    })format!(
265                    "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`"
266                ));
267                if #[allow(non_exhaustive_omitted_patterns)] match orig_level {
    Level::Warn | Level::Deny => true,
    _ => false,
}matches!(orig_level, Level::Warn | Level::Deny) {
268                    let help = if name == "dead_code" {
269                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("to override `{0} {1}` add `#[expect({2})]` or `#[allow({2})]`",
                flag, hyphen_case_flag_val, name))
    })format!(
270                            "to override `{flag} {hyphen_case_flag_val}` add `#[expect({name})]` or `#[allow({name})]`"
271                        )
272                    } else {
273                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("to override `{0} {1}` add `#[allow({2})]`",
                flag, hyphen_case_flag_val, name))
    })format!(
274                            "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`"
275                        )
276                    };
277                    err.help_once(help);
278                }
279            }
280        }
281        LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
282            if let Some(rationale) = reason {
283                err.note(rationale.to_string());
284            }
285            err.span_note_once(span, "the lint level is defined here");
286            if lint_attr_name.as_str() != name {
287                let level_str = level.as_str();
288                err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`#[{0}({1})]` implied by `#[{0}({2})]`",
                level_str, name, lint_attr_name))
    })format!(
289                    "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`"
290                ));
291            }
292        }
293    }
294}
295
296/// The innermost function for emitting lints.
297///
298/// If you are looking to implement a lint, look for higher level functions,
299/// for example:
300/// - [`TyCtxt::emit_node_span_lint`]
301/// - [`TyCtxt::node_span_lint`]
302/// - [`TyCtxt::node_lint`]
303/// - `LintContext::opt_span_lint`
304///
305/// ## `decorate`
306///
307/// It is not intended to call `emit`/`cancel` on the `Diag` passed in the `decorate` callback.
308#[track_caller]
309pub fn lint_level(
310    sess: &Session,
311    lint: &'static Lint,
312    level: LevelAndSource,
313    span: Option<MultiSpan>,
314    decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
315) {
316    // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
317    // the "real" work.
318    #[track_caller]
319    fn lint_level_impl(
320        sess: &Session,
321        lint: &'static Lint,
322        level: LevelAndSource,
323        span: Option<MultiSpan>,
324        decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
325    ) {
326        let LevelAndSource { level, lint_id, src } = level;
327
328        // Check for future incompatibility lints and issue a stronger warning.
329        let future_incompatible = lint.future_incompatible;
330
331        let has_future_breakage = future_incompatible.map_or(
332            // Default allow lints trigger too often for testing.
333            sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
334            |incompat| incompat.report_in_deps,
335        );
336
337        // Convert lint level to error level.
338        let err_level = match level {
339            Level::Allow => {
340                if has_future_breakage {
341                    rustc_errors::Level::Allow
342                } else {
343                    return;
344                }
345            }
346            Level::Expect => {
347                // This case is special as we actually allow the lint itself in this context, but
348                // we can't return early like in the case for `Level::Allow` because we still
349                // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
350                //
351                // We can also not mark the lint expectation as fulfilled here right away, as it
352                // can still be cancelled in the decorate function. All of this means that we simply
353                // create a `Diag` and continue as we would for warnings.
354                rustc_errors::Level::Expect
355            }
356            Level::ForceWarn => rustc_errors::Level::ForceWarning,
357            Level::Warn => rustc_errors::Level::Warning,
358            Level::Deny | Level::Forbid => rustc_errors::Level::Error,
359        };
360        let mut err = Diag::new(sess.dcx(), err_level, "");
361        if let Some(span) = span {
362            err.span(span);
363        }
364        if let Some(lint_id) = lint_id {
365            err.lint_id(lint_id);
366        }
367
368        // If this code originates in a foreign macro, aka something that this crate
369        // did not itself author, then it's likely that there's nothing this crate
370        // can do about it. We probably want to skip the lint entirely.
371        if err.span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) {
372            // Any suggestions made here are likely to be incorrect, so anything we
373            // emit shouldn't be automatically fixed by rustfix.
374            err.disable_suggestions();
375
376            // If this is a future incompatible that is not an edition fixing lint
377            // it'll become a hard error, so we have to emit *something*. Also,
378            // if this lint occurs in the expansion of a macro from an external crate,
379            // allow individual lints to opt-out from being reported.
380            let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
381
382            // In rustc, for the find_attr macro, we want to always emit this.
383            // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates.
384            // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little.
385            let is_in_find_attr = sess.enable_internal_lints()
386                && err.span.primary_spans().iter().any(|s| {
387                    s.source_callee().is_some_and(
388                    |i| #[allow(non_exhaustive_omitted_patterns)] match i.kind {
    ExpnKind::Macro(_, name) if name.as_str() == "find_attr" => true,
    _ => false,
}matches!(i.kind, ExpnKind::Macro(_, name) if name.as_str() == "find_attr")
389                    )
390                });
391
392            if !incompatible && !lint.report_in_external_macro && !is_in_find_attr {
393                err.cancel();
394
395                // Don't continue further, since we don't want to have
396                // `diag_span_note_once` called for a diagnostic that isn't emitted.
397                return;
398            }
399        }
400
401        err.is_lint(lint.name_lower(), has_future_breakage);
402
403        // Lint diagnostics that are covered by the expect level will not be emitted outside
404        // the compiler. It is therefore not necessary to add any information for the user.
405        // This will therefore directly call the decorate function which will in turn emit
406        // the diagnostic.
407        if let Level::Expect = level {
408            decorate(&mut err);
409            err.emit();
410            return;
411        }
412
413        if let Some(future_incompatible) = future_incompatible {
414            let explanation = match future_incompatible.reason {
415                FutureIncompatibilityReason::FutureReleaseError(_) => {
416                    "this was previously accepted by the compiler but is being phased out; \
417                         it will become a hard error in a future release!"
418                        .to_owned()
419                }
420                FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => {
421                    "this will change its meaning in a future release!".to_owned()
422                }
423                FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => {
424                    let current_edition = sess.edition();
425                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this is accepted in the current edition (Rust {0}) but is a hard error in Rust {1}!",
                current_edition, edition))
    })format!(
426                        "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!"
427                    )
428                }
429                FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw {
430                    edition, ..
431                }) => {
432                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this changes meaning in Rust {0}",
                edition))
    })format!("this changes meaning in Rust {edition}")
433                }
434                FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw {
435                    edition,
436                    ..
437                }) => {
438                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust {0} and in a future release in all editions!",
                edition))
    })format!(
439                        "this was previously accepted by the compiler but is being phased out; \
440                         it will become a hard error in Rust {edition} and in a future release in all editions!"
441                    )
442                }
443                FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(
444                    EditionFcw { edition, .. },
445                ) => {
446                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this changes meaning in Rust {0} and in a future release in all editions!",
                edition))
    })format!(
447                        "this changes meaning in Rust {edition} and in a future release in all editions!"
448                    )
449                }
450                FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(),
451                FutureIncompatibilityReason::Unreachable => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
452            };
453
454            if future_incompatible.explain_reason {
455                err.warn(explanation);
456            }
457
458            let citation =
459                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("for more information, see {0}",
                future_incompatible.reason.reference()))
    })format!("for more information, see {}", future_incompatible.reason.reference());
460            err.note(citation);
461        }
462
463        // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly),
464        // so we need to make sure when we do call `decorate` that the diagnostic is eventually
465        // emitted or we'll get a `must_produce_diag` ICE.
466        //
467        // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors:
468        // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)`
469        //    or `Error`, then the diagnostic will be emitted regardless of CLI options.
470        // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by
471        //    `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic
472        //    will be emitted if `can_emit_warnings` is true.
473        let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings();
474
475        if !skip {
476            decorate(&mut err);
477        }
478
479        explain_lint_level_source(sess, lint, level, src, &mut err);
480        err.emit()
481    }
482    lint_level_impl(sess, lint, level, span, Box::new(decorate))
483}
484
485/// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait.
486///
487/// If you are looking to implement a lint, look for higher level functions,
488/// for example:
489///
490/// - [`TyCtxt::emit_node_span_lint`]
491/// - [`TyCtxt::node_span_lint`]
492/// - [`TyCtxt::node_lint`]
493/// - `LintContext::opt_span_lint`
494///
495/// This function will replace `lint_level` once all `LintDiagnostic` items have been migrated to
496/// `Diagnostic`.
497#[track_caller]
498pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>(
499    sess: &'a Session,
500    lint: &'static Lint,
501    level: LevelAndSource,
502    span: Option<MultiSpan>,
503    decorate: D,
504) {
505    // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
506    // the "real" work.
507    #[track_caller]
508    fn diag_lint_level_impl<'a>(
509        sess: &'a Session,
510        lint: &'static Lint,
511        level: LevelAndSource,
512        span: Option<MultiSpan>,
513        decorate: Box<
514            dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a,
515        >,
516    ) {
517        let LevelAndSource { level, lint_id, src } = level;
518
519        // Check for future incompatibility lints and issue a stronger warning.
520        let future_incompatible = lint.future_incompatible;
521
522        let has_future_breakage = future_incompatible.map_or(
523            // Default allow lints trigger too often for testing.
524            sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
525            |incompat| incompat.report_in_deps,
526        );
527
528        // Convert lint level to error level.
529        let err_level = match level {
530            Level::Allow => {
531                if has_future_breakage {
532                    rustc_errors::Level::Allow
533                } else {
534                    return;
535                }
536            }
537            Level::Expect => {
538                // This case is special as we actually allow the lint itself in this context, but
539                // we can't return early like in the case for `Level::Allow` because we still
540                // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
541                //
542                // We can also not mark the lint expectation as fulfilled here right away, as it
543                // can still be cancelled in the decorate function. All of this means that we simply
544                // create a `Diag` and continue as we would for warnings.
545                rustc_errors::Level::Expect
546            }
547            Level::ForceWarn => rustc_errors::Level::ForceWarning,
548            Level::Warn => rustc_errors::Level::Warning,
549            Level::Deny | Level::Forbid => rustc_errors::Level::Error,
550        };
551
552        let disable_suggestions = if let Some(ref span) = span
553            // If this code originates in a foreign macro, aka something that this crate
554            // did not itself author, then it's likely that there's nothing this crate
555            // can do about it. We probably want to skip the lint entirely.
556            && span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map()))
557        {
558            true
559        } else {
560            false
561        };
562
563        if disable_suggestions {
564            // If this is a future incompatible that is not an edition fixing lint
565            // it'll become a hard error, so we have to emit *something*. Also,
566            // if this lint occurs in the expansion of a macro from an external crate,
567            // allow individual lints to opt-out from being reported.
568            let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
569
570            // In rustc, for the find_attr macro, we want to always emit this.
571            // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates.
572            // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little.
573            let is_in_find_attr = sess.enable_internal_lints()
574                && span.as_ref().is_some_and(|span| {
575                    span.primary_spans().iter().any(|s| {
576                        s.source_callee().is_some_and(|i| {
577                            #[allow(non_exhaustive_omitted_patterns)] match i.kind {
    ExpnKind::Macro(_, name) if name.as_str() == "find_attr" => true,
    _ => false,
}matches!(i.kind, ExpnKind::Macro(_, name) if name.as_str() == "find_attr")
578                        })
579                    })
580                });
581
582            if !incompatible && !lint.report_in_external_macro && !is_in_find_attr {
583                // Don't continue further, since we don't want to have
584                // `diag_span_note_once` called for a diagnostic that isn't emitted.
585                return;
586            }
587        }
588        // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly),
589        // so we need to make sure when we do call `decorate` that the diagnostic is eventually
590        // emitted or we'll get a `must_produce_diag` ICE.
591        //
592        // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors:
593        // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)`
594        //    or `Error`, then the diagnostic will be emitted regardless of CLI options.
595        // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by
596        //    `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic
597        //    will be emitted if `can_emit_warnings` is true.
598        let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings();
599
600        let mut err: Diag<'_, ()> = if !skip {
601            decorate(sess.dcx(), err_level)
602        } else {
603            Diag::new(sess.dcx(), err_level, "")
604        };
605        if let Some(span) = span
606            && err.span.primary_span().is_none()
607        {
608            // We can't use `err.span()` because it overwrites the labels, so we need to do it manually.
609            for primary in span.primary_spans() {
610                err.span.push_primary_span(*primary);
611            }
612            for (label_span, label) in span.span_labels_raw() {
613                err.span.push_span_diag(*label_span, label.clone());
614            }
615        }
616        if let Some(lint_id) = lint_id {
617            err.lint_id(lint_id);
618        }
619
620        if disable_suggestions {
621            // Any suggestions made here are likely to be incorrect, so anything we
622            // emit shouldn't be automatically fixed by rustfix.
623            err.disable_suggestions();
624        }
625
626        err.is_lint(lint.name_lower(), has_future_breakage);
627        // Lint diagnostics that are covered by the expect level will not be emitted outside
628        // the compiler. It is therefore not necessary to add any information for the user.
629        // This will therefore directly call the decorate function which will in turn emit
630        // the diagnostic.
631        if let Level::Expect = level {
632            err.emit();
633            return;
634        }
635
636        if let Some(future_incompatible) = future_incompatible {
637            let explanation = match future_incompatible.reason {
638                FutureIncompatibilityReason::FutureReleaseError(_) => {
639                    "this was previously accepted by the compiler but is being phased out; \
640                         it will become a hard error in a future release!"
641                        .to_owned()
642                }
643                FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => {
644                    "this will change its meaning in a future release!".to_owned()
645                }
646                FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => {
647                    let current_edition = sess.edition();
648                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this is accepted in the current edition (Rust {0}) but is a hard error in Rust {1}!",
                current_edition, edition))
    })format!(
649                        "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!"
650                    )
651                }
652                FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw {
653                    edition, ..
654                }) => {
655                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this changes meaning in Rust {0}",
                edition))
    })format!("this changes meaning in Rust {edition}")
656                }
657                FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw {
658                    edition,
659                    ..
660                }) => {
661                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this was previously accepted by the compiler but is being phased out; it will become a hard error in Rust {0} and in a future release in all editions!",
                edition))
    })format!(
662                        "this was previously accepted by the compiler but is being phased out; \
663                         it will become a hard error in Rust {edition} and in a future release in all editions!"
664                    )
665                }
666                FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(
667                    EditionFcw { edition, .. },
668                ) => {
669                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this changes meaning in Rust {0} and in a future release in all editions!",
                edition))
    })format!(
670                        "this changes meaning in Rust {edition} and in a future release in all editions!"
671                    )
672                }
673                FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(),
674                FutureIncompatibilityReason::Unreachable => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
675            };
676
677            if future_incompatible.explain_reason {
678                err.warn(explanation);
679            }
680
681            let citation =
682                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("for more information, see {0}",
                future_incompatible.reason.reference()))
    })format!("for more information, see {}", future_incompatible.reason.reference());
683            err.note(citation);
684        }
685
686        explain_lint_level_source(sess, lint, level, src, &mut err);
687        err.emit();
688    }
689    diag_lint_level_impl(
690        sess,
691        lint,
692        level,
693        span,
694        Box::new(move |dcx, level| decorate.into_diag(dcx, level)),
695    );
696}