1use std::cmp::min;
23use 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::{
11FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, StableLintExpectationId,
12UnstableLintExpectationId, builtin,
13};
14use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw};
15use tracing::instrument;
1617use crate::ty::TyCtxt;
1819/// How a lint level was set.
20#[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_hash::StableHash for
LintLevelSource {
#[inline]
fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
__hcx: &mut __Hcx,
__hasher:
&mut ::rustc_data_structures::stable_hash::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)]
21pub enum LintLevelSource {
22/// Lint is at the default level as declared in rustc.
23Default,
2425/// Lint level was set by an attribute.
26Node {
27 name: Symbol,
28 span: Span,
29/// RFC 2383 reason
30reason: Option<Symbol>,
31 },
3233/// Lint level was set by a command-line flag.
34 /// The provided `Level` is the level specified on the command line.
35 /// (The actual level may be lower due to `--cap-lints`.)
36CommandLine(Symbol, Level),
37}
3839impl LintLevelSource {
40pub fn name(&self) -> Symbol {
41match *self {
42 LintLevelSource::Default => kw::Default,
43 LintLevelSource::Node { name, .. } => name,
44 LintLevelSource::CommandLine(name, _) => name,
45 }
46 }
4748pub fn span(&self) -> Span {
49match *self {
50 LintLevelSource::Default => DUMMY_SP,
51 LintLevelSource::Node { span, .. } => span,
52 LintLevelSource::CommandLine(_, _) => DUMMY_SP,
53 }
54 }
55}
5657/// Convenience helper for things that are frequently used together.
58#[derive(#[automatically_derived]
impl<Id: ::core::marker::Copy> ::core::marker::Copy for LevelSpec<Id> { }Copy, #[automatically_derived]
impl<Id: ::core::clone::Clone> ::core::clone::Clone for LevelSpec<Id> {
#[inline]
fn clone(&self) -> LevelSpec<Id> {
LevelSpec {
level: ::core::clone::Clone::clone(&self.level),
lint_id: ::core::clone::Clone::clone(&self.lint_id),
src: ::core::clone::Clone::clone(&self.src),
}
}
}Clone, #[automatically_derived]
impl<Id: ::core::fmt::Debug> ::core::fmt::Debug for LevelSpec<Id> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "LevelSpec",
"level", &self.level, "lint_id", &self.lint_id, "src", &&self.src)
}
}Debug, const _: () =
{
impl<Id> ::rustc_data_structures::stable_hash::StableHash for
LevelSpec<Id> where
Id: ::rustc_data_structures::stable_hash::StableHash {
#[inline]
fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
__hcx: &mut __Hcx,
__hasher:
&mut ::rustc_data_structures::stable_hash::StableHasher) {
match *self {
LevelSpec {
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<Id, __E: ::rustc_span::SpanEncoder>
::rustc_serialize::Encodable<__E> for LevelSpec<Id> where
Id: ::rustc_serialize::Encodable<__E> {
fn encode(&self, __encoder: &mut __E) {
match *self {
LevelSpec {
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<Id, __D: ::rustc_span::SpanDecoder>
::rustc_serialize::Decodable<__D> for LevelSpec<Id> where
Id: ::rustc_serialize::Decodable<__D> {
fn decode(__decoder: &mut __D) -> Self {
LevelSpec {
level: ::rustc_serialize::Decodable::decode(__decoder),
lint_id: ::rustc_serialize::Decodable::decode(__decoder),
src: ::rustc_serialize::Decodable::decode(__decoder),
}
}
}
};Decodable)]
59pub struct LevelSpec<Id = LintExpectationId> {
60// This field *must* be private. It must be set in tandem with `lint_id`, only in
61 // `LevelSpec::new`, because only certain `level`/`lint_id` combinations are valid. See
62 // `LevelSpec::new` for those combinations.
63 //
64 // If you are thinking right now that `level` and `lint_id` should be combined into a single
65 // type that excludes the invalid combinations, that's a reasonable thought, but in practice
66 // it's painful because `level` needs to be used by itself, without `lint_id`, in many places.
67 // Making the fields private prevents invalid combinations while retaining the flexibility of
68 // two separate fields.
69level: Level,
7071// This field *must* be private. See the comment on `level`.
72lint_id: Option<Id>,
7374pub src: LintLevelSource,
75}
7677pub type UnstableLevelSpec = LevelSpec<UnstableLintExpectationId>;
78pub type StableLevelSpec = LevelSpec<StableLintExpectationId>;
7980impl<Id: Copy> LevelSpec<Id> {
81// Panics if an invalid `level`/`lint_id` combination is given.
82pub fn new(level: Level, lint_id: Option<Id>, src: LintLevelSource) -> LevelSpec<Id> {
83match (level, lint_id) {
84 (Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None) => {}
85 (Level::Expect, Some(_)) => {}
86 (Level::ForceWarn, _) => {}
87_ => {
::core::panicking::panic_fmt(format_args!("invalid level/lint_id combination"));
}panic!("invalid level/lint_id combination"),
88 }
89LevelSpec { level, lint_id, src }
90 }
9192pub fn level(self) -> Level {
93self.level
94 }
9596pub fn is_allow(self) -> bool {
97self.level == Level::Allow98 }
99100pub fn is_expect(self) -> bool {
101self.level == Level::Expect102 }
103104pub fn lint_id(self) -> Option<Id> {
105self.lint_id
106 }
107}
108109impl From<UnstableLevelSpec> for LevelSpec {
110fn from(level: UnstableLevelSpec) -> LevelSpec {
111let LevelSpec { level, lint_id, src } = level;
112let lint_id = lint_id.map(LintExpectationId::Unstable);
113LevelSpec { level, lint_id, src }
114 }
115}
116117impl From<StableLevelSpec> for LevelSpec {
118fn from(level: StableLevelSpec) -> LevelSpec {
119let LevelSpec { level, lint_id, src } = level;
120let lint_id = lint_id.map(LintExpectationId::Stable);
121LevelSpec { level, lint_id, src }
122 }
123}
124125/// Return type for the `shallow_lint_levels_on` query.
126///
127/// This map represents lints levels given by the attributes for *a single HirId*.
128#[derive(#[automatically_derived]
impl ::core::default::Default for ShallowLintLevelMap {
#[inline]
fn default() -> ShallowLintLevelMap {
ShallowLintLevelMap {
specs: ::core::default::Default::default(),
expectations: ::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", "specs", &self.specs, "expectations",
&&self.expectations)
}
}Debug, const _: () =
{
impl ::rustc_data_structures::stable_hash::StableHash for
ShallowLintLevelMap {
#[inline]
fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
__hcx: &mut __Hcx,
__hasher:
&mut ::rustc_data_structures::stable_hash::StableHasher) {
match *self {
ShallowLintLevelMap {
specs: ref __binding_0, expectations: ref __binding_1 } => {
{ __binding_0.stable_hash(__hcx, __hasher); }
{ __binding_1.stable_hash(__hcx, __hasher); }
}
}
}
}
};StableHash)]
129pub struct ShallowLintLevelMap {
130// All the specs for this HirId. This is accessed frequently, e.g. for every lint emitted.
131pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, StableLevelSpec>>,
132133// Additional information about the `expect` specs for this HirId. This is consulted only once
134 // per compilation session, in `check_expectations`/`lint_expectations`.
135pub expectations: Vec<(StableLintExpectationId, LintExpectation)>,
136}
137138/// Verify the effect of special annotations: `warnings` lint level and lint caps.
139///
140/// The return of this function is suitable for diagnostics.
141pub fn reveal_actual_level_spec<Id: Copy>(
142 sess: &Session,
143 lint: LintId,
144 probe_for_lint_level_spec: impl Fn(LintId) -> Option<LevelSpec<Id>>,
145) -> LevelSpec<Id> {
146let level_spec = probe_for_lint_level_spec(lint);
147148// If `level` is none then we actually assume the default level for this lint.
149let mut level_spec = level_spec.unwrap_or_else(|| {
150LevelSpec::new(lint.lint.default_level(sess.edition()), None, LintLevelSource::Default)
151 });
152153// If we're about to issue a warning, check at the last minute for any
154 // directives against the `warnings` lint group. If, for example, there's an
155 // `allow(warnings)` in scope then we want to respect that instead.
156if level_spec.level == Level::Warn {
157if let Some(configured_level_spec) =
158probe_for_lint_level_spec(LintId::of(builtin::WARNINGS))
159 {
160let respect_warnings_lint_group = match configured_level_spec.level {
161// -Wwarnings is a no-op.
162Level::Warn => false,
163// Some warnings cannot be denied from the `warnings` lint group, only individually.
164Level::Deny | Level::Forbid => !lint.lint.ignore_deny_warnings,
165// All warnings respect -Awarnings.
166Level::Allow => true,
167// Not sure what the right behavior is here, but, sure, why not.
168 // See tests/ui/lint/rfc-2383-lint-reason/expect_warnings.rs.
169Level::Expect => true,
170 Level::ForceWarn => {
171sess.dcx().span_delayed_bug(
172configured_level_spec.src.span(),
173"cannot --force-warn the `warnings` lint group",
174 );
175false
176}
177 };
178if respect_warnings_lint_group {
179level_spec = configured_level_spec;
180 }
181 }
182 }
183184// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
185if !#[allow(non_exhaustive_omitted_patterns)] match level_spec.src {
LintLevelSource::CommandLine(_, Level::ForceWarn) => true,
_ => false,
}matches!(level_spec.src, LintLevelSource::CommandLine(_, Level::ForceWarn)) {
186level_spec.level = min(level_spec.level, sess.opts.lint_cap.unwrap_or(Level::Forbid));
187 };
188189// Ensure that we never exceed driver level.
190if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
191level_spec.level = min(level_spec.level, *driver_level);
192 }
193194level_spec195}
196197impl ShallowLintLevelMap {
198/// Perform a deep probe in the HIR tree looking for the actual level spec for the lint.
199 /// This lint level spec is not usable for diagnostics, it needs to be corrected by
200 /// `reveal_actual_level` beforehand.
201x;#[instrument(level = "trace", skip(self, tcx), ret)]202fn probe_for_lint_level_spec(
203&self,
204 tcx: TyCtxt<'_>,
205 id: LintId,
206 start: HirId,
207 ) -> Option<StableLevelSpec> {
208if let Some(map) = self.specs.get(&start.local_id)
209 && let Some(level_spec) = map.get(&id)
210 {
211return Some(*level_spec);
212 }
213214let mut owner = start.owner;
215let mut specs = &self.specs;
216217for parent in tcx.hir_parent_id_iter(start) {
218if parent.owner != owner {
219 owner = parent.owner;
220 specs = &tcx.shallow_lint_levels_on(owner).specs;
221 }
222if let Some(map) = specs.get(&parent.local_id)
223 && let Some(level_spec) = map.get(&id)
224 {
225return Some(*level_spec);
226 }
227 }
228229None
230}
231232/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
233x;#[instrument(level = "trace", skip(self, tcx), ret)]234pub fn lint_level_spec_at_node(
235&self,
236 tcx: TyCtxt<'_>,
237 lint: LintId,
238 cur: HirId,
239 ) -> StableLevelSpec {
240 reveal_actual_level_spec(tcx.sess, lint, |lint| {
241self.probe_for_lint_level_spec(tcx, lint, cur)
242 })
243 }
244}
245246impl TyCtxt<'_> {
247/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
248pub fn lint_level_spec_at_node(self, lint: &'static Lint, id: HirId) -> StableLevelSpec {
249self.shallow_lint_levels_on(id.owner).lint_level_spec_at_node(self, LintId::of(lint), id)
250 }
251}
252253/// This struct represents a lint expectation and holds all required information
254/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after
255/// the `LateLintPass` has completed.
256#[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_hash::StableHash for
LintExpectation {
#[inline]
fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
__hcx: &mut __Hcx,
__hasher:
&mut ::rustc_data_structures::stable_hash::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)]
257pub struct LintExpectation {
258/// The reason for this expectation that can optionally be added as part of
259 /// the attribute. It will be displayed as part of the lint message.
260pub reason: Option<Symbol>,
261/// The [`Span`] of the attribute that this expectation originated from.
262pub emission_span: Span,
263/// Lint messages for the `unfulfilled_lint_expectations` lint will be
264 /// adjusted to include an additional note. Therefore, we have to track if
265 /// the expectation is for the lint.
266pub is_unfulfilled_lint_expectations: bool,
267/// This will hold the name of the tool that this lint belongs to. For
268 /// the lint `clippy::some_lint` the tool would be `clippy`, the same
269 /// goes for `rustdoc`. This will be `None` for rustc lints
270pub lint_tool: Option<Symbol>,
271}
272273impl LintExpectation {
274pub fn new(
275 reason: Option<Symbol>,
276 emission_span: Span,
277 is_unfulfilled_lint_expectations: bool,
278 lint_tool: Option<Symbol>,
279 ) -> Self {
280Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
281 }
282}
283284fn explain_lint_level_source(
285 sess: &Session,
286 lint: &'static Lint,
287 level: Level,
288 src: LintLevelSource,
289 err: &mut Diag<'_, ()>,
290) {
291// Find the name of the lint group that contains the given lint.
292 // Assumes the lint only belongs to one group.
293let lint_group_name = |lint| {
294let lint_groups_iter = sess.lint_groups_iter();
295let lint_id = LintId::of(lint);
296lint_groups_iter297 .filter(|lint_group| !lint_group.is_externally_loaded)
298 .find(|lint_group| {
299lint_group300 .lints
301 .iter()
302 .find(|lint_group_lint| **lint_group_lint == lint_id)
303 .is_some()
304 })
305 .map(|lint_group| lint_group.name)
306 };
307let name = lint.name_lower();
308if let Level::Allow = level {
309// Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
310 // triggering. (#121009)
311return;
312 }
313match src {
314 LintLevelSource::Default => {
315let level_str = level.as_str();
316match lint_group_name(lint) {
317Some(group_name) => {
318err.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"));
319 }
320None => {
321err.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"));
322 }
323 }
324 }
325 LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
326let flag = orig_level.to_cmd_flag();
327let hyphen_case_lint_name = name.replace('_', "-");
328if lint_flag_val.as_str() == name {
329err.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!(
330"requested on the command line with `{flag} {hyphen_case_lint_name}`"
331));
332 } else {
333let hyphen_case_flag_val = lint_flag_val.as_str().replace('_', "-");
334err.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!(
335"`{flag} {hyphen_case_lint_name}` implied by `{flag} {hyphen_case_flag_val}`"
336));
337if #[allow(non_exhaustive_omitted_patterns)] match orig_level {
Level::Warn | Level::Deny => true,
_ => false,
}matches!(orig_level, Level::Warn | Level::Deny) {
338let help = if name == "dead_code" {
339::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!(
340"to override `{flag} {hyphen_case_flag_val}` add `#[expect({name})]` or `#[allow({name})]`"
341)342 } else {
343::alloc::__export::must_use({
::alloc::fmt::format(format_args!("to override `{0} {1}` add `#[allow({2})]`",
flag, hyphen_case_flag_val, name))
})format!(
344"to override `{flag} {hyphen_case_flag_val}` add `#[allow({name})]`"
345)346 };
347err.help_once(help);
348 }
349 }
350 }
351 LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
352if let Some(rationale) = reason {
353err.note(rationale.to_string());
354 }
355err.span_note_once(span, "the lint level is defined here");
356if lint_attr_name.as_str() != name {
357let level_str = level.as_str();
358err.note_once(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`#[{0}({1})]` implied by `#[{0}({2})]`",
level_str, name, lint_attr_name))
})format!(
359"`#[{level_str}({name})]` implied by `#[{level_str}({lint_attr_name})]`"
360));
361 }
362 }
363 }
364365if let Some(warnings_group) = sess366 .opts
367 .lint_opts
368 .iter()
369 .find_map(|(opt, level)| (opt == "warnings").then_some(level))
370 .copied()
371 && warnings_group >= Level::Deny372 && level < warnings_group373 {
374err.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`"));
375 }
376}
377378/// The innermost function for emitting lints implementing the [`trait@Diagnostic`] trait.
379///
380/// If you are looking to implement a lint, look for higher level functions,
381/// for example:
382///
383/// - [`TyCtxt::emit_node_span_lint`]
384/// - `LintContext::opt_span_lint`
385#[track_caller]
386pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
387 sess: &'a Session,
388 lint: &'static Lint,
389 level_spec: impl Into<LevelSpec>,
390 span: Option<MultiSpan>,
391 decorate: D,
392) {
393// Avoid codegen bloat from monomorphization by immediately doing dyn dispatch of `decorate` to
394 // the "real" work.
395#[track_caller]
396fn emit_lint_base_impl<'a>(
397 sess: &'a Session,
398 lint: &'static Lint,
399 level_spec: LevelSpec,
400 span: Option<MultiSpan>,
401 decorate: Box<
402dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a,
403 >,
404 ) {
405let LevelSpec { level, lint_id, src } = level_spec;
406407// Check for future incompatibility lints and issue a stronger warning.
408let future_incompatible = lint.future_incompatible;
409410let has_future_breakage = future_incompatible.map_or(
411// Default allow lints trigger too often for testing.
412sess.opts.unstable_opts.future_incompat_test && lint.default_level != Level::Allow,
413 |incompat| incompat.report_in_deps,
414 );
415416// Convert lint level to error level.
417let err_level = match level {
418 Level::Allow => {
419if has_future_breakage {
420 rustc_errors::Level::Allow421 } else {
422return;
423 }
424 }
425 Level::Expect => {
426// This case is special as we actually allow the lint itself in this context, but
427 // we can't return early like in the case for `Level::Allow` because we still
428 // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
429 //
430 // We can also not mark the lint expectation as fulfilled here right away, as it
431 // can still be cancelled in the decorate function. All of this means that we simply
432 // create a `Diag` and continue as we would for warnings.
433rustc_errors::Level::Expect434 }
435 Level::ForceWarn => rustc_errors::Level::ForceWarning,
436 Level::Warn => rustc_errors::Level::Warning,
437 Level::Deny | Level::Forbid => rustc_errors::Level::Error,
438 };
439440let disable_suggestions = if let Some(ref span) = span441// If this code originates in a foreign macro, aka something that this crate
442 // did not itself author, then it's likely that there's nothing this crate
443 // can do about it. We probably want to skip the lint entirely.
444&& span.primary_spans().iter().any(|s| s.in_external_macro(sess.source_map()))
445 {
446true
447} else {
448false
449};
450451if disable_suggestions {
452// If this is a future incompatible that is not an edition fixing lint
453 // it'll become a hard error, so we have to emit *something*. Also,
454 // if this lint occurs in the expansion of a macro from an external crate,
455 // allow individual lints to opt-out from being reported.
456let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
457458// In rustc, for the find_attr macro, we want to always emit this.
459 // This completely circumvents normal lint checking, which usually doesn't happen for macros from other crates.
460 // However, we kind of want that when using find_attr from another rustc crate. So we cheat a little.
461let is_in_find_attr = sess.enable_internal_lints()
462 && span.as_ref().is_some_and(|span| {
463span.primary_spans().iter().any(|s| {
464s.source_callee().is_some_and(|i| {
465#[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")466 })
467 })
468 });
469470if !incompatible && !lint.report_in_external_macro && !is_in_find_attr {
471// Don't continue further, since we don't want to have
472 // `diag_span_note_once` called for a diagnostic that isn't emitted.
473return;
474 }
475 }
476// Finally, run `decorate`. `decorate` can call `trimmed_path_str` (directly or indirectly),
477 // so we need to make sure when we do call `decorate` that the diagnostic is eventually
478 // emitted or we'll get a `must_produce_diag` ICE.
479 //
480 // When is a diagnostic *eventually* emitted? Well, that is determined by 2 factors:
481 // 1. If the corresponding `rustc_errors::Level` is beyond warning, i.e. `ForceWarning(_)`
482 // or `Error`, then the diagnostic will be emitted regardless of CLI options.
483 // 2. If the corresponding `rustc_errors::Level` is warning, then that can be affected by
484 // `-A warnings` or `--cap-lints=xxx` on the command line. In which case, the diagnostic
485 // will be emitted if `can_emit_warnings` is true.
486let skip = err_level == rustc_errors::Level::Warning && !sess.dcx().can_emit_warnings();
487488let mut err: Diag<'_, ()> = if !skip {
489decorate(sess.dcx(), err_level)
490 } else {
491Diag::new(sess.dcx(), err_level, "")
492 };
493// FIXME: Find a nicer way to expose the `DiagLocation`
494err.emitted_at = DiagLocation::caller();
495496if let Some(span) = span497 && err.span.primary_span().is_none()
498 {
499// We can't use `err.span()` because it overwrites the labels, so we need to do it manually.
500for primary in span.primary_spans() {
501 err.span.push_primary_span(*primary);
502 }
503for (label_span, label) in span.span_labels_raw() {
504 err.span.push_span_diag(*label_span, label.clone());
505 }
506 }
507if let Some(lint_id) = lint_id {
508err.lint_id(lint_id);
509 }
510511if disable_suggestions {
512// Any suggestions made here are likely to be incorrect, so anything we
513 // emit shouldn't be automatically fixed by rustfix.
514err.disable_suggestions();
515 }
516517err.is_lint(lint.name_lower(), has_future_breakage);
518// Lint diagnostics that are covered by the expect level will not be emitted outside
519 // the compiler. It is therefore not necessary to add any information for the user.
520 // This will therefore directly call the decorate function which will in turn emit
521 // the diagnostic.
522if let Level::Expect = level {
523err.emit();
524return;
525 }
526527if let Some(future_incompatible) = future_incompatible {
528let explanation = match future_incompatible.reason {
529 FutureIncompatibilityReason::FutureReleaseError(_) => {
530"this was previously accepted by the compiler but is being phased out; \
531 it will become a hard error in a future release!"
532.to_owned()
533 }
534 FutureIncompatibilityReason::FutureReleaseSemanticsChange(_) => {
535"this will change its meaning in a future release!".to_owned()
536 }
537 FutureIncompatibilityReason::EditionError(EditionFcw { edition, .. }) => {
538let current_edition = sess.edition();
539::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!(
540"this is accepted in the current edition (Rust {current_edition}) but is a hard error in Rust {edition}!"
541)542 }
543 FutureIncompatibilityReason::EditionSemanticsChange(EditionFcw {
544 edition, ..
545 }) => {
546::alloc::__export::must_use({
::alloc::fmt::format(format_args!("this changes meaning in Rust {0}",
edition))
})format!("this changes meaning in Rust {edition}")547 }
548 FutureIncompatibilityReason::EditionAndFutureReleaseError(EditionFcw {
549 edition,
550 ..
551 }) => {
552::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!(
553"this was previously accepted by the compiler but is being phased out; \
554 it will become a hard error in Rust {edition} and in a future release in all editions!"
555)556 }
557 FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(
558EditionFcw { edition, .. },
559 ) => {
560::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!(
561"this changes meaning in Rust {edition} and in a future release in all editions!"
562)563 }
564 FutureIncompatibilityReason::Custom(reason, _) => reason.to_owned(),
565 FutureIncompatibilityReason::Unreachable => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
566 };
567568if future_incompatible.explain_reason {
569err.warn(explanation);
570 }
571572let citation =
573::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());
574err.note(citation);
575 }
576577explain_lint_level_source(sess, lint, level, src, &mut err);
578err.emit();
579 }
580emit_lint_base_impl(
581sess,
582lint,
583level_spec.into(),
584span,
585Box::new(move |dcx, level| decorate.into_diag(dcx, level)),
586 );
587}