1use std::cmp;
23use rustc_data_structures::fx::FxIndexMap;
4use rustc_data_structures::sorted_map::SortedMap;
5use rustc_errors::{Diag, 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, Span, Symbol, kw};
13use tracing::instrument;
1415use crate::ty::TyCtxt;
1617/// 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 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_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_query_system::ich::StableHashingContext<'__ctx>>
for LintLevelSource {
#[inline]
fn hash_stable(&self,
__hcx:
&mut ::rustc_query_system::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.
21Default,
2223/// Lint level was set by an attribute.
24Node {
25 name: Symbol,
26 span: Span,
27/// RFC 2383 reason
28reason: Option<Symbol>,
29 },
3031/// 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`.)
34CommandLine(Symbol, Level),
35}
3637impl LintLevelSource {
38pub fn name(&self) -> Symbol {
39match *self {
40 LintLevelSource::Default => kw::Default,
41 LintLevelSource::Node { name, .. } => name,
42 LintLevelSource::CommandLine(name, _) => name,
43 }
44 }
4546pub fn span(&self) -> Span {
47match *self {
48 LintLevelSource::Default => DUMMY_SP,
49 LintLevelSource::Node { span, .. } => span,
50 LintLevelSource::CommandLine(_, _) => DUMMY_SP,
51 }
52 }
53}
5455/// 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_query_system::ich::StableHashingContext<'__ctx>>
for LevelAndSource {
#[inline]
fn hash_stable(&self,
__hcx:
&mut ::rustc_query_system::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 {
58pub level: Level,
59pub lint_id: Option<LintExpectationId>,
60pub src: LintLevelSource,
61}
6263/// 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_query_system::ich::StableHashingContext<'__ctx>>
for ShallowLintLevelMap {
#[inline]
fn hash_stable(&self,
__hcx:
&mut ::rustc_query_system::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 {
69pub expectations: Vec<(LintExpectationId, LintExpectation)>,
70pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelAndSource>>,
71}
7273/// 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(
83LintId,
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.
88let (mut level, mut lint_id) =
89level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
9091// 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.
99if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
100let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
101if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
102if configured_warning_level != Level::Warn {
103level = configured_warning_level;
104lint_id = configured_lint_id;
105*src = warnings_src;
106 }
107 }
108 }
109110// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
111level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
112level113 } else {
114 cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
115 };
116117if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
118// Ensure that we never exceed driver level.
119level = cmp::min(*driver_level, level);
120 }
121122 (level, lint_id)
123}
124125impl 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.
129x;#[instrument(level = "trace", skip(self, tcx), ret)]130fn probe_for_lint_level(
131&self,
132 tcx: TyCtxt<'_>,
133 id: LintId,
134 start: HirId,
135 ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
136if let Some(map) = self.specs.get(&start.local_id)
137 && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
138 {
139return (Some((level, lint_id)), src);
140 }
141142let mut owner = start.owner;
143let mut specs = &self.specs;
144145for parent in tcx.hir_parent_id_iter(start) {
146if parent.owner != owner {
147 owner = parent.owner;
148 specs = &tcx.shallow_lint_levels_on(owner).specs;
149 }
150if let Some(map) = specs.get(&parent.local_id)
151 && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
152 {
153return (Some((level, lint_id)), src);
154 }
155 }
156157 (None, LintLevelSource::Default)
158 }
159160/// Fetch and return the user-visible lint level for the given lint at the given HirId.
161x;#[instrument(level = "trace", skip(self, tcx), ret)]162pub fn lint_level_id_at_node(
163&self,
164 tcx: TyCtxt<'_>,
165 lint: LintId,
166 cur: HirId,
167 ) -> LevelAndSource {
168let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
169let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
170self.probe_for_lint_level(tcx, lint, cur)
171 });
172 LevelAndSource { level, lint_id, src }
173 }
174}
175176impl TyCtxt<'_> {
177/// Fetch and return the user-visible lint level for the given lint at the given HirId.
178pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> LevelAndSource {
179self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
180 }
181}
182183/// 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_query_system::ich::StableHashingContext<'__ctx>>
for LintExpectation {
#[inline]
fn hash_stable(&self,
__hcx:
&mut ::rustc_query_system::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.
190pub reason: Option<Symbol>,
191/// The [`Span`] of the attribute that this expectation originated from.
192pub 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.
196pub 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
200pub lint_tool: Option<Symbol>,
201}
202203impl LintExpectation {
204pub fn new(
205 reason: Option<Symbol>,
206 emission_span: Span,
207 is_unfulfilled_lint_expectations: bool,
208 lint_tool: Option<Symbol>,
209 ) -> Self {
210Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
211 }
212}
213214fn 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.
223let lint_group_name = |lint| {
224let lint_groups_iter = sess.lint_groups_iter();
225let lint_id = LintId::of(lint);
226lint_groups_iter227 .filter(|lint_group| !lint_group.is_externally_loaded)
228 .find(|lint_group| {
229lint_group230 .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 };
237let name = lint.name_lower();
238if let Level::Allow = level {
239// Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
240 // triggering. (#121009)
241return;
242 }
243match src {
244 LintLevelSource::Default => {
245let level_str = level.as_str();
246match lint_group_name(lint) {
247Some(group_name) => {
248err.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 }
250None => {
251err.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) => {
256let flag = orig_level.to_cmd_flag();
257let hyphen_case_lint_name = name.replace('_', "-");
258if lint_flag_val.as_str() == name {
259err.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 {
263let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
264err.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));
267if #[allow(non_exhaustive_omitted_patterns)] match orig_level {
Level::Warn | Level::Deny => true,
_ => false,
}matches!(orig_level, Level::Warn | Level::Deny) {
268let 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 };
277err.help_once(help);
278 }
279 }
280 }
281 LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
282if let Some(rationale) = reason {
283err.note(rationale.to_string());
284 }
285err.span_note_once(span, "the lint level is defined here");
286if lint_attr_name.as_str() != name {
287let level_str = level.as_str();
288err.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}
295296/// 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::emit_node_lint`]
303/// - [`TyCtxt::node_lint`]
304/// - `LintContext::opt_span_lint`
305///
306/// ## `decorate`
307///
308/// It is not intended to call `emit`/`cancel` on the `Diag` passed in the `decorate` callback.
309#[track_caller]
310pub fn lint_level(
311 sess: &Session,
312 lint: &'static Lint,
313 level: LevelAndSource,
314 span: Option<MultiSpan>,
315 decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
316) {
317// Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
318 // the "real" work.
319#[track_caller]
320fn lint_level_impl(
321 sess: &Session,
322 lint: &'static Lint,
323 level: LevelAndSource,
324 span: Option<MultiSpan>,
325 decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
326 ) {
327let LevelAndSource { level, lint_id, src } = level;
328329// Check for future incompatibility lints and issue a stronger warning.
330let future_incompatible = lint.future_incompatible;
331332let has_future_breakage = future_incompatible.map_or(
333// Default allow lints trigger too often for testing.
334sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
335 |incompat| incompat.report_in_deps,
336 );
337338// Convert lint level to error level.
339let err_level = match level {
340 Level::Allow => {
341if has_future_breakage {
342 rustc_errors::Level::Allow343 } else {
344return;
345 }
346 }
347 Level::Expect => {
348// This case is special as we actually allow the lint itself in this context, but
349 // we can't return early like in the case for `Level::Allow` because we still
350 // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
351 //
352 // We can also not mark the lint expectation as fulfilled here right away, as it
353 // can still be cancelled in the decorate function. All of this means that we simply
354 // create a `Diag` and continue as we would for warnings.
355rustc_errors::Level::Expect356 }
357 Level::ForceWarn => rustc_errors::Level::ForceWarning,
358 Level::Warn => rustc_errors::Level::Warning,
359 Level::Deny | Level::Forbid => rustc_errors::Level::Error,
360 };
361let mut err = Diag::new(sess.dcx(), err_level, "");
362if let Some(span) = span {
363err.span(span);
364 }
365if let Some(lint_id) = lint_id {
366err.lint_id(lint_id);
367 }
368369// If this code originates in a foreign macro, aka something that this crate
370 // did not itself author, then it's likely that there's nothing this crate
371 // can do about it. We probably want to skip the lint entirely.
372if err.span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map())) {
373// Any suggestions made here are likely to be incorrect, so anything we
374 // emit shouldn't be automatically fixed by rustfix.
375err.disable_suggestions();
376377// If this is a future incompatible that is not an edition fixing lint
378 // it'll become a hard error, so we have to emit *something*. Also,
379 // if this lint occurs in the expansion of a macro from an external crate,
380 // allow individual lints to opt-out from being reported.
381let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
382383if !incompatible && !lint.report_in_external_macro {
384err.cancel();
385386// Don't continue further, since we don't want to have
387 // `diag_span_note_once` called for a diagnostic that isn't emitted.
388return;
389 }
390 }
391392err.is_lint(lint.name_lower(), has_future_breakage);
393394// Lint diagnostics that are covered by the expect level will not be emitted outside
395 // the compiler. It is therefore not necessary to add any information for the user.
396 // This will therefore directly call the decorate function which will in turn emit
397 // the diagnostic.
398if let Level::Expect = level {
399decorate(&mut err);
400err.emit();
401return;
402 }
403404if let Some(future_incompatible) = future_incompatible {
405let explanation = match future_incompatible.reason {
406 FutureIncompatibilityReason::FutureReleaseError(_) => {
407"this was previously accepted by the compiler but is being phased out; \
408 it will become a hard error in a future release!"
409.to_owned()
410 }
411 FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => {
412"this will change its meaning in a future release!".to_owned()
413 }
414 FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => {
415let current_edition = sess.edition();
416::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!(
417"this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!"
418)419 }
420 FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw {
421 edition, ..
422 }) => {
423::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this changes meaning in Rust {0}",
edition))
})format!("this changes meaning in Rust {edition}")424 }
425 FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw {
426 edition,
427 ..
428 }) => {
429::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!(
430"this was previously accepted by the compiler but is being phased out; \
431 it will become a hard error in Rust {edition} and in a future release in all editions!"
432)433 }
434 FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(
435EditionFcw { edition, .. },
436 ) => {
437::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!(
438"this changes meaning in Rust {edition} and in a future release in all editions!"
439)440 }
441 FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(),
442 FutureIncompatibilityReason::Unreachable => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
443 };
444445if future_incompatible.explain_reason {
446err.warn(explanation);
447 }
448449let citation =
450::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());
451err.note(citation);
452 }
453454// Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly),
455 // so we need to make sure when we do call `decorate` that the diagnostic is eventually
456 // emitted or we'll get a `must_produce_diag` ICE.
457 //
458 // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors:
459 // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)`
460 // or `Error`, then the diagnostic will be emitted regardless of CLI options.
461 // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by
462 // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic
463 // will be emitted if `can_emit_warnings` is true.
464let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings();
465466if !skip {
467decorate(&mut err);
468 }
469470explain_lint_level_source(sess, lint, level, src, &mut err);
471err.emit()
472 }
473lint_level_impl(sess, lint, level, span, Box::new(decorate))
474}