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, DiagLocation, Diagnostic, MultiSpan};
6use rustc_hir::{HirId, ItemLocalId};
7use rustc_lint_defs::EditionFcw;
8use rustc_macros::{Decodable, Encodable, StableHash};
9use rustc_session::Session;
10use rustc_session::lint::{
11    FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, builtin,
12};
13use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw};
14use tracing::instrument;
15
16use crate::ty::TyCtxt;
17
18/// How a lint level was set.
19#[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 {
    #[inline]
    #[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 ::rustc_data_structures::stable_hasher::StableHash for
            LintLevelSource {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hasher::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                ::std::mem::discriminant(self).stable_hash(__hcx, __hasher);
                match *self {
                    LintLevelSource::Default => {}
                    LintLevelSource::Node {
                        name: ref __binding_0,
                        span: ref __binding_1,
                        reason: ref __binding_2 } => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                        { __binding_2.stable_hash(__hcx, __hasher); }
                    }
                    LintLevelSource::CommandLine(ref __binding_0,
                        ref __binding_1) => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash, #[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)]
20pub enum LintLevelSource {
21    /// Lint is at the default level as declared in rustc.
22    Default,
23
24    /// Lint level was set by an attribute.
25    Node {
26        name: Symbol,
27        span: Span,
28        /// RFC 2383 reason
29        reason: Option<Symbol>,
30    },
31
32    /// Lint level was set by a command-line flag.
33    /// The provided `Level` is the level specified on the command line.
34    /// (The actual level may be lower due to `--cap-lints`.)
35    CommandLine(Symbol, Level),
36}
37
38impl LintLevelSource {
39    pub fn name(&self) -> Symbol {
40        match *self {
41            LintLevelSource::Default => kw::Default,
42            LintLevelSource::Node { name, .. } => name,
43            LintLevelSource::CommandLine(name, _) => name,
44        }
45    }
46
47    pub fn span(&self) -> Span {
48        match *self {
49            LintLevelSource::Default => DUMMY_SP,
50            LintLevelSource::Node { span, .. } => span,
51            LintLevelSource::CommandLine(_, _) => DUMMY_SP,
52        }
53    }
54}
55
56/// Convenience helper for moving things around together that frequently are paired
57#[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 ::rustc_data_structures::stable_hasher::StableHash for
            LevelAndSource {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hasher::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __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.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                        { __binding_2.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash, 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)]
58pub struct LevelAndSource {
59    pub level: Level,
60    pub lint_id: Option<LintExpectationId>,
61    pub src: LintLevelSource,
62}
63
64/// Return type for the `shallow_lint_levels_on` query.
65///
66/// This map represents the set of allowed lints and allowance levels given
67/// by the attributes for *a single HirId*.
68#[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 ::rustc_data_structures::stable_hasher::StableHash for
            ShallowLintLevelMap {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hasher::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hasher::StableHasher) {
                match *self {
                    ShallowLintLevelMap {
                        expectations: ref __binding_0, specs: ref __binding_1 } => {
                        { __binding_0.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash)]
69pub struct ShallowLintLevelMap {
70    pub expectations: Vec<(LintExpectationId, LintExpectation)>,
71    pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelAndSource>>,
72}
73
74/// From an initial level and source, verify the effect of special annotations:
75/// `warnings` lint level and lint caps.
76///
77/// The return of this function is suitable for diagnostics.
78pub fn reveal_actual_level(
79    level: Option<(Level, Option<LintExpectationId>)>,
80    src: &mut LintLevelSource,
81    sess: &Session,
82    lint: LintId,
83    probe_for_lint_level: impl FnOnce(
84        LintId,
85    )
86        -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
87) -> (Level, Option<LintExpectationId>) {
88    // If `level` is none then we actually assume the default level for this lint.
89    let (mut level, mut lint_id) =
90        level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
91
92    // If we're about to issue a warning, check at the last minute for any
93    // directives against the `warnings` lint group. If, for example, there's an
94    // `allow(warnings)` in scope then we want to respect that instead.
95    if level == Level::Warn {
96        let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
97        if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
98            let respect_warnings_lint_group = match configured_warning_level {
99                // -Wwarnings is a no-op.
100                Level::Warn => false,
101                // Some warnings cannot be denied from the `warnings` lint group, only individually.
102                Level::Deny | Level::Forbid => !lint.lint.ignore_deny_warnings,
103                // All warnings respect -Awarnings.
104                Level::Allow => true,
105                // Not sure what the right behavior is here, but, sure, why not.
106                // See tests/ui/lint/rfc-2383-lint-reason/expect_warnings.rs.
107                Level::Expect => true,
108                Level::ForceWarn => {
109                    sess.dcx().span_delayed_bug(
110                        warnings_src.span(),
111                        "cannot --force-warn the `warnings` lint group",
112                    );
113                    false
114                }
115            };
116            if respect_warnings_lint_group {
117                level = configured_warning_level;
118                lint_id = configured_lint_id;
119                *src = warnings_src;
120            }
121        }
122    }
123
124    // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
125    level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
126        level
127    } else {
128        cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
129    };
130
131    if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
132        // Ensure that we never exceed driver level.
133        level = cmp::min(*driver_level, level);
134    }
135
136    (level, lint_id)
137}
138
139impl ShallowLintLevelMap {
140    /// Perform a deep probe in the HIR tree looking for the actual level for the lint.
141    /// This lint level is not usable for diagnostics, it needs to be corrected by
142    /// `reveal_actual_level` beforehand.
143    x;#[instrument(level = "trace", skip(self, tcx), ret)]
144    fn probe_for_lint_level(
145        &self,
146        tcx: TyCtxt<'_>,
147        id: LintId,
148        start: HirId,
149    ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
150        if let Some(map) = self.specs.get(&start.local_id)
151            && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
152        {
153            return (Some((level, lint_id)), src);
154        }
155
156        let mut owner = start.owner;
157        let mut specs = &self.specs;
158
159        for parent in tcx.hir_parent_id_iter(start) {
160            if parent.owner != owner {
161                owner = parent.owner;
162                specs = &tcx.shallow_lint_levels_on(owner).specs;
163            }
164            if let Some(map) = specs.get(&parent.local_id)
165                && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
166            {
167                return (Some((level, lint_id)), src);
168            }
169        }
170
171        (None, LintLevelSource::Default)
172    }
173
174    /// Fetch and return the user-visible lint level for the given lint at the given HirId.
175    x;#[instrument(level = "trace", skip(self, tcx), ret)]
176    pub fn lint_level_id_at_node(
177        &self,
178        tcx: TyCtxt<'_>,
179        lint: LintId,
180        cur: HirId,
181    ) -> LevelAndSource {
182        let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
183        let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
184            self.probe_for_lint_level(tcx, lint, cur)
185        });
186        LevelAndSource { level, lint_id, src }
187    }
188}
189
190impl TyCtxt<'_> {
191    /// Fetch and return the user-visible lint level for the given lint at the given HirId.
192    pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
193        self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
194    }
195}
196
197/// This struct represents a lint expectation and holds all required information
198/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after
199/// the `LateLintPass` has completed.
200#[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 ::rustc_data_structures::stable_hasher::StableHash for
            LintExpectation {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hasher::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __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.stable_hash(__hcx, __hasher); }
                        { __binding_1.stable_hash(__hcx, __hasher); }
                        { __binding_2.stable_hash(__hcx, __hasher); }
                        { __binding_3.stable_hash(__hcx, __hasher); }
                    }
                }
            }
        }
    };StableHash)]
201pub struct LintExpectation {
202    /// The reason for this expectation that can optionally be added as part of
203    /// the attribute. It will be displayed as part of the lint message.
204    pub reason: Option<Symbol>,
205    /// The [`Span`] of the attribute that this expectation originated from.
206    pub emission_span: Span,
207    /// Lint messages for the `unfulfilled_lint_expectations` lint will be
208    /// adjusted to include an additional note. Therefore, we have to track if
209    /// the expectation is for the lint.
210    pub is_unfulfilled_lint_expectations: bool,
211    /// This will hold the name of the tool that this lint belongs to. For
212    /// the lint `clippy::some_lint` the tool would be `clippy`, the same
213    /// goes for `rustdoc`. This will be `None` for rustc lints
214    pub lint_tool: Option<Symbol>,
215}
216
217impl LintExpectation {
218    pub fn new(
219        reason: Option<Symbol>,
220        emission_span: Span,
221        is_unfulfilled_lint_expectations: bool,
222        lint_tool: Option<Symbol>,
223    ) -> Self {
224        Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
225    }
226}
227
228fn explain_lint_level_source(
229    sess: &Session,
230    lint: &'static Lint,
231    level: Level,
232    src: LintLevelSource,
233    err: &mut Diag<'_, ()>,
234) {
235    // Find the name of the lint group that contains the given lint.
236    // Assumes the lint only belongs to one group.
237    let lint_group_name = |lint| {
238        let lint_groups_iter = sess.lint_groups_iter();
239        let lint_id = LintId::of(lint);
240        lint_groups_iter
241            .filter(|lint_group| !lint_group.is_externally_loaded)
242            .find(|lint_group| {
243                lint_group
244                    .lints
245                    .iter()
246                    .find(|lint_group_lint| **lint_group_lint == lint_id)
247                    .is_some()
248            })
249            .map(|lint_group| lint_group.name)
250    };
251    let name = lint.name_lower();
252    if let Level::Allow = level {
253        // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
254        // triggering. (#121009)
255        return;
256    }
257    match src {
258        LintLevelSource::Default => {
259            let level_str = level.as_str();
260            match lint_group_name(lint) {
261                Some(group_name) => {
262                    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"));
263                }
264                None => {
265                    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"));
266                }
267            }
268        }
269        LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
270            let flag = orig_level.to_cmd_flag();
271            let hyphen_case_lint_name = name.replace('_', "-");
272            if lint_flag_val.as_str() == name {
273                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!(
274                    "requested on the command line with `{flag} {hyphen_case_lint_name}`"
275                ));
276            } else {
277                let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
278                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!(
279                    "`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`"
280                ));
281                if #[allow(non_exhaustive_omitted_patterns)] match orig_level {
    Level::Warn | Level::Deny => true,
    _ => false,
}matches!(orig_level, Level::Warn | Level::Deny) {
282                    let help = if name == "dead_code" {
283                        ::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!(
284                            "to override `{flag} {hyphen_case_flag_val}` add `#[expect({name})]` or `#[allow({name})]`"
285                        )
286                    } else {
287                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("to override `{0} {1}` add `#[allow({2})]`",
                flag, hyphen_case_flag_val, name))
    })format!(
288                            "to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`"
289                        )
290                    };
291                    err.help_once(help);
292                }
293            }
294        }
295        LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
296            if let Some(rationale) = reason {
297                err.note(rationale.to_string());
298            }
299            err.span_note_once(span, "the lint level is defined here");
300            if lint_attr_name.as_str() != name {
301                let level_str = level.as_str();
302                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!(
303                    "`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`"
304                ));
305            }
306        }
307    }
308
309    if let Some(warnings_group) = sess
310        .opts
311        .lint_opts
312        .iter()
313        .find_map(|(opt, level)| (opt == "warnings").then_some(level))
314        .copied()
315        && warnings_group >= Level::Deny
316        && level < warnings_group
317    {
318        err.note_once(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the `{0}` lint ignores `-D warnings`",
                name))
    })format!("the `{name}` lint ignores `-D warnings`"));
319    }
320}
321
322/// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait.
323///
324/// If you are looking to implement a lint, look for higher level functions,
325/// for example:
326///
327/// - [`TyCtxt::emit_node_span_lint`]
328/// - `LintContext::opt_span_lint`
329#[track_caller]
330pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
331    sess: &'a Session,
332    lint: &'static Lint,
333    level: LevelAndSource,
334    span: Option<MultiSpan>,
335    decorate: D,
336) {
337    // Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
338    // the "real" work.
339    #[track_caller]
340    fn emit_lint_base_impl<'a>(
341        sess: &'a Session,
342        lint: &'static Lint,
343        level: LevelAndSource,
344        span: Option<MultiSpan>,
345        decorate: Box<
346            dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a,
347        >,
348    ) {
349        let LevelAndSource { level, lint_id, src } = level;
350
351        // Check for future incompatibility lints and issue a stronger warning.
352        let future_incompatible = lint.future_incompatible;
353
354        let has_future_breakage = future_incompatible.map_or(
355            // Default allow lints trigger too often for testing.
356            sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
357            |incompat| incompat.report_in_deps,
358        );
359
360        // Convert lint level to error level.
361        let err_level = match level {
362            Level::Allow => {
363                if has_future_breakage {
364                    rustc_errors::Level::Allow
365                } else {
366                    return;
367                }
368            }
369            Level::Expect => {
370                // This case is special as we actually allow the lint itself in this context, but
371                // we can't return early like in the case for `Level::Allow` because we still
372                // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
373                //
374                // We can also not mark the lint expectation as fulfilled here right away, as it
375                // can still be cancelled in the decorate function. All of this means that we simply
376                // create a `Diag` and continue as we would for warnings.
377                rustc_errors::Level::Expect
378            }
379            Level::ForceWarn => rustc_errors::Level::ForceWarning,
380            Level::Warn => rustc_errors::Level::Warning,
381            Level::Deny | Level::Forbid => rustc_errors::Level::Error,
382        };
383
384        let disable_suggestions = if let Some(ref span) = span
385            // If this code originates in a foreign macro, aka something that this crate
386            // did not itself author, then it's likely that there's nothing this crate
387            // can do about it. We probably want to skip the lint entirely.
388            && span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map()))
389        {
390            true
391        } else {
392            false
393        };
394
395        if disable_suggestions {
396            // If this is a future incompatible that is not an edition fixing lint
397            // it'll become a hard error, so we have to emit *something*. Also,
398            // if this lint occurs in the expansion of a macro from an external crate,
399            // allow individual lints to opt-out from being reported.
400            let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
401
402            // In rustc, for the find_attr macro, we want to always emit this.
403            // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates.
404            // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little.
405            let is_in_find_attr = sess.enable_internal_lints()
406                && span.as_ref().is_some_and(|span| {
407                    span.primary_spans().iter().any(|s| {
408                        s.source_callee().is_some_and(|i| {
409                            #[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")
410                        })
411                    })
412                });
413
414            if !incompatible && !lint.report_in_external_macro && !is_in_find_attr {
415                // Don't continue further, since we don't want to have
416                // `diag_span_note_once` called for a diagnostic that isn't emitted.
417                return;
418            }
419        }
420        // Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly),
421        // so we need to make sure when we do call `decorate` that the diagnostic is eventually
422        // emitted or we'll get a `must_produce_diag` ICE.
423        //
424        // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors:
425        // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)`
426        //    or `Error`, then the diagnostic will be emitted regardless of CLI options.
427        // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by
428        //    `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic
429        //    will be emitted if `can_emit_warnings` is true.
430        let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings();
431
432        let mut err: Diag<'_, ()> = if !skip {
433            decorate(sess.dcx(), err_level)
434        } else {
435            Diag::new(sess.dcx(), err_level, "")
436        };
437        // FIXME: Find a nicer way to expose the `DiagLocation`
438        err.emitted_at = DiagLocation::caller();
439
440        if let Some(span) = span
441            && err.span.primary_span().is_none()
442        {
443            // We can't use `err.span()` because it overwrites the labels, so we need to do it manually.
444            for primary in span.primary_spans() {
445                err.span.push_primary_span(*primary);
446            }
447            for (label_span, label) in span.span_labels_raw() {
448                err.span.push_span_diag(*label_span, label.clone());
449            }
450        }
451        if let Some(lint_id) = lint_id {
452            err.lint_id(lint_id);
453        }
454
455        if disable_suggestions {
456            // Any suggestions made here are likely to be incorrect, so anything we
457            // emit shouldn't be automatically fixed by rustfix.
458            err.disable_suggestions();
459        }
460
461        err.is_lint(lint.name_lower(), has_future_breakage);
462        // Lint diagnostics that are covered by the expect level will not be emitted outside
463        // the compiler. It is therefore not necessary to add any information for the user.
464        // This will therefore directly call the decorate function which will in turn emit
465        // the diagnostic.
466        if let Level::Expect = level {
467            err.emit();
468            return;
469        }
470
471        if let Some(future_incompatible) = future_incompatible {
472            let explanation = match future_incompatible.reason {
473                FutureIncompatibilityReason::FutureReleaseError(_) => {
474                    "this was previously accepted by the compiler but is being phased out; \
475                         it will become a hard error in a future release!"
476                        .to_owned()
477                }
478                FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => {
479                    "this will change its meaning in a future release!".to_owned()
480                }
481                FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => {
482                    let current_edition = sess.edition();
483                    ::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!(
484                        "this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!"
485                    )
486                }
487                FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw {
488                    edition, ..
489                }) => {
490                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("this changes meaning in Rust {0}",
                edition))
    })format!("this changes meaning in Rust {edition}")
491                }
492                FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw {
493                    edition,
494                    ..
495                }) => {
496                    ::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!(
497                        "this was previously accepted by the compiler but is being phased out; \
498                         it will become a hard error in Rust {edition} and in a future release in all editions!"
499                    )
500                }
501                FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(
502                    EditionFcw { edition, .. },
503                ) => {
504                    ::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!(
505                        "this changes meaning in Rust {edition} and in a future release in all editions!"
506                    )
507                }
508                FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(),
509                FutureIncompatibilityReason::Unreachable => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
510            };
511
512            if future_incompatible.explain_reason {
513                err.warn(explanation);
514            }
515
516            let citation =
517                ::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());
518            err.note(citation);
519        }
520
521        explain_lint_level_source(sess, lint, level, src, &mut err);
522        err.emit();
523    }
524    emit_lint_base_impl(
525        sess,
526        lint,
527        level,
528        span,
529        Box::new(move |dcx, level| decorate.into_diag(dcx, level)),
530    );
531}