1//! Basic types for managing and implementing lints.
2//!
3//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
4//! overview of how lints are implemented.
56use std::cell::Cell;
7use std::slice;
89use rustc_abias abi;
10use rustc_ast::BindingMode;
11use rustc_ast::util::parser::ExprPrecedence;
12use rustc_data_structures::fx::FxIndexMap;
13use rustc_data_structures::sync;
14use rustc_data_structures::unord::UnordMap;
15use rustc_errors::{Diagnostic, LintBuffer, MultiSpan};
16use rustc_feature::Features;
17use rustc_hiras hir;
18use rustc_hir::def::Res;
19use rustc_hir::def_id::{CrateNum, DefId};
20use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
21use rustc_hir::{Pat, PatKind};
22use rustc_middle::bug;
23use rustc_middle::lint::{LevelSpec, StableLevelSpec, UnstableLevelSpec};
24use rustc_middle::middle::privacy::EffectiveVisibilities;
25use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
26use rustc_middle::ty::print::{PrintError, PrintTraitRefExtas _, Printer, with_no_trimmed_paths};
27use rustc_middle::ty::{
28self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, Unnormalized,
29};
30use rustc_session::lint::{
31FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId,
32UnstableLintExpectationId,
33};
34use rustc_session::{DynLintStore, Session};
35use rustc_span::edit_distance::find_best_match_for_names;
36use rustc_span::{Ident, Span, Symbol, sym};
37use tracing::debug;
3839use self::TargetLint::*;
40use crate::levels::LintLevelsBuilder;
41use crate::passes::{EarlyLintPassObject, LateLintPassObject};
4243pub(crate) type EarlyLintPassFactory =
44Box<dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync>;
45type LateLintPassFactory =
46Box<dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>;
4748/// Information about the registered lints.
49//
50// About the pass factories: these should only be called once, but since we
51// want to avoid locks or interior mutability, we don't enforce this. Lints
52// should, in theory, be compatible with being constructed more than once,
53// though not necessarily in a sane manner. This is safe though.
54pub struct LintStore {
55/// Registered lints.
56lints: Vec<&'static Lint>,
5758/// This lint pass kind is softly deprecated. It misses expanded code and has caused a few
59 /// errors in the past. Currently, it is only used in Clippy. New implementations
60 /// should avoid using this interface, as it might be removed in the future.
61 ///
62 /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838)
63 /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518)
64pub(crate) pre_expansion_lint_passes: Vec<EarlyLintPassFactory>,
6566/// These lint passes run on AST nodes.
67pub(crate) early_lint_passes: Vec<EarlyLintPassFactory>,
6869/// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit
70 /// from incremental compilation. `late_lint_mod_passes` should be used in preference where
71 /// possible; only use `late_lint_passes` for lints that implement `check_crate` and/or
72 /// `check_crate_post` and accumulate cross-module state.
73 ///
74 /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs
75 /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout
76 /// for consistency. This is ok because Clippy isn't wired for incremental compilation.
77pub(crate) late_lint_passes: Vec<LateLintPassFactory>,
7879/// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times).
80 /// They benefit from incremental compilation.
81pub(crate) late_lint_mod_passes: Vec<LateLintPassFactory>,
8283/// Lints indexed by name.
84by_name: UnordMap<String, TargetLint>,
8586/// Map of registered lint groups to what lints they expand to.
87lint_groups: FxIndexMap<&'static str, LintGroup>,
88}
8990impl DynLintStorefor LintStore {
91fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
92Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
93 rustc_session::LintGroup { name, lints, is_externally_loaded }
94 }))
95 }
96}
9798/// The target of the `by_name` map, which accounts for renaming/deprecation.
99#[derive(#[automatically_derived]
impl ::core::fmt::Debug for TargetLint {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
TargetLint::Id(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Id",
&__self_0),
TargetLint::Renamed(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f,
"Renamed", __self_0, &__self_1),
TargetLint::Removed(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Removed", &__self_0),
TargetLint::Ignored =>
::core::fmt::Formatter::write_str(f, "Ignored"),
}
}
}Debug)]
100enum TargetLint {
101/// A direct lint target
102Id(LintId),
103104/// Temporary renaming, used for easing migration pain; see #16545
105Renamed(String, LintId),
106107/// Lint with this name existed previously, but has been removed/deprecated.
108 /// The string argument is the reason for removal.
109Removed(String),
110111/// A lint name that should give no warnings and have no effect.
112 ///
113 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers
114 /// them as tool lints.
115Ignored,
116}
117118struct LintAlias {
119 name: &'static str,
120/// Whether deprecation warnings should be suppressed for this alias.
121silent: bool,
122}
123124struct LintGroup {
125 lint_ids: Vec<LintId>,
126 is_externally_loaded: bool,
127 depr: Option<LintAlias>,
128}
129130#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for CheckLintNameResult<'a> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
CheckLintNameResult::Ok(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Ok",
&__self_0),
CheckLintNameResult::NoLint(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "NoLint",
&__self_0),
CheckLintNameResult::NoTool =>
::core::fmt::Formatter::write_str(f, "NoTool"),
CheckLintNameResult::Renamed(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Renamed", &__self_0),
CheckLintNameResult::Removed(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Removed", &__self_0),
CheckLintNameResult::Tool(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f, "Tool",
__self_0, &__self_1),
CheckLintNameResult::MissingTool =>
::core::fmt::Formatter::write_str(f, "MissingTool"),
}
}
}Debug)]
131pub enum CheckLintNameResult<'a> {
132Ok(&'a [LintId]),
133/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
134NoLint(Option<(Symbol, bool)>),
135/// The lint refers to a tool that has not been registered.
136NoTool,
137/// The lint has been renamed to a new name.
138Renamed(String),
139/// The lint has been removed due to the given reason.
140Removed(String),
141142/// The lint is from a tool. The `LintId` will be returned as if it were a
143 /// rustc lint. The `Option<String>` indicates if the lint has been
144 /// renamed.
145Tool(&'a [LintId], Option<String>),
146147/// The lint is from a tool. Either the lint does not exist in the tool or
148 /// the code was not compiled with the tool and therefore the lint was
149 /// never added to the `LintStore`.
150MissingTool,
151}
152153impl LintStore {
154pub fn new() -> LintStore {
155LintStore {
156 lints: ::alloc::vec::Vec::new()vec![],
157 pre_expansion_lint_passes: ::alloc::vec::Vec::new()vec![],
158 early_lint_passes: ::alloc::vec::Vec::new()vec![],
159 late_lint_passes: ::alloc::vec::Vec::new()vec![],
160 late_lint_mod_passes: ::alloc::vec::Vec::new()vec![],
161 by_name: Default::default(),
162 lint_groups: Default::default(),
163 }
164 }
165166pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
167&self.lints
168 }
169170pub fn get_lint_groups(&self) -> impl Iterator<Item = (&'static str, Vec<LintId>, bool)> {
171self.lint_groups
172 .iter()
173 .filter(|(_, LintGroup { depr, .. })| {
174// Don't display deprecated lint groups.
175depr.is_none()
176 })
177 .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| {
178 (*k, lint_ids.clone(), *is_externally_loaded)
179 })
180 }
181182/// Returns all lint group names, including deprecated/aliased groups
183pub fn get_all_group_names(&self) -> impl Iterator<Item = &'static str> {
184self.lint_groups.keys().copied()
185 }
186187/// See the comment on `LintStore::pre_expansion_lint_passes`.
188pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) {
189self.pre_expansion_lint_passes.push(pass);
190 }
191192/// See the comment on `LintStore::early_lint_passes`.
193pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) {
194self.early_lint_passes.push(pass);
195 }
196197/// See the comment on `LintStore::late_lint_passes`.
198pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) {
199self.late_lint_passes.push(pass);
200 }
201202/// See the comment on `LintStore::late_lint_mod_passes`.
203pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) {
204self.late_lint_mod_passes.push(pass);
205 }
206207/// Helper method for register_early/late_pass
208pub fn register_lints(&mut self, lints: &[&'static Lint]) {
209for lint in lints {
210self.lints.push(lint);
211212let id = LintId::of(lint);
213if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
214::rustc_middle::util::bug::bug_fmt(format_args!("duplicate specification of lint {0}",
lint.name_lower()))bug!("duplicate specification of lint {}", lint.name_lower())215 }
216217if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
218if let Some(edition) = reason.edition() {
219self.lint_groups
220 .entry(edition.lint_name())
221 .or_insert(LintGroup {
222 lint_ids: ::alloc::vec::Vec::new()vec![],
223 is_externally_loaded: lint.is_externally_loaded,
224 depr: None,
225 })
226 .lint_ids
227 .push(id);
228 } else {
229// Lints belonging to the `future_incompatible` lint group are lints where a
230 // future version of rustc will cause existing code to stop compiling.
231 // Lints tied to an edition don't count because they are opt-in.
232self.lint_groups
233 .entry("future_incompatible")
234 .or_insert(LintGroup {
235 lint_ids: ::alloc::vec::Vec::new()vec![],
236 is_externally_loaded: lint.is_externally_loaded,
237 depr: None,
238 })
239 .lint_ids
240 .push(id);
241 }
242 }
243 }
244 }
245246fn insert_group(&mut self, name: &'static str, group: LintGroup) {
247let previous = self.lint_groups.insert(name, group);
248if previous.is_some() {
249::rustc_middle::util::bug::bug_fmt(format_args!("group {0:?} already exists",
name));bug!("group {name:?} already exists");
250 }
251 }
252253pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) {
254let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(group_name) else {
255::rustc_middle::util::bug::bug_fmt(format_args!("group alias {0:?} points to unregistered group {1:?}",
alias, group_name))bug!("group alias {alias:?} points to unregistered group {group_name:?}")256 };
257258self.insert_group(
259alias,
260LintGroup {
261 lint_ids: lint_ids.clone(),
262 is_externally_loaded: false,
263 depr: Some(LintAlias { name: group_name, silent: true }),
264 },
265 );
266 }
267268pub fn register_group(
269&mut self,
270 is_externally_loaded: bool,
271 name: &'static str,
272 deprecated_name: Option<&'static str>,
273 to: Vec<LintId>,
274 ) {
275if let Some(deprecated) = deprecated_name {
276self.insert_group(
277deprecated,
278LintGroup {
279 lint_ids: to.clone(),
280is_externally_loaded,
281 depr: Some(LintAlias { name, silent: false }),
282 },
283 );
284 }
285self.insert_group(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None });
286 }
287288/// This lint should give no warning and have no effect.
289 ///
290 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
291#[track_caller]
292pub fn register_ignored(&mut self, name: &str) {
293if self.by_name.insert(name.to_string(), Ignored).is_some() {
294::rustc_middle::util::bug::bug_fmt(format_args!("duplicate specification of lint {0}",
name));bug!("duplicate specification of lint {}", name);
295 }
296 }
297298/// This lint has been renamed; warn about using the new name and apply the lint.
299#[track_caller]
300pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
301let Some(&Id(target)) = self.by_name.get(new_name) else {
302::rustc_middle::util::bug::bug_fmt(format_args!("invalid lint renaming of {0} to {1}",
old_name, new_name));bug!("invalid lint renaming of {} to {}", old_name, new_name);
303 };
304self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
305 }
306307pub fn register_removed(&mut self, name: &str, reason: &str) {
308self.by_name.insert(name.into(), Removed(reason.into()));
309 }
310311pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> {
312match self.by_name.get(lint_name) {
313Some(Id(lint_id)) => Some(slice::from_ref(lint_id)),
314Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)),
315Some(Removed(_)) => None,
316Some(Ignored) => Some(&[]),
317None => match self.lint_groups.get(lint_name) {
318Some(LintGroup { lint_ids, .. }) => Some(lint_ids),
319None => None,
320 },
321 }
322 }
323324/// True if this symbol represents a lint group name.
325pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
326{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/context.rs:326",
"rustc_lint::context", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/context.rs"),
::tracing_core::__macro_support::Option::Some(326u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::context"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("is_lint_group(lint_name={0:?}, lint_groups={1:?})",
lint_name, self.lint_groups.keys().collect::<Vec<_>>()) as
&dyn Value))])
});
} else { ; }
};debug!(
327"is_lint_group(lint_name={:?}, lint_groups={:?})",
328 lint_name,
329self.lint_groups.keys().collect::<Vec<_>>()
330 );
331let lint_name_str = lint_name.as_str();
332self.lint_groups.contains_key(lint_name_str) || {
333let warnings_name_str = crate::WARNINGS.name_lower();
334lint_name_str == warnings_name_str335 }
336 }
337338/// Checks the name of a lint for its existence, and whether it was
339 /// renamed or removed. Generates a `Diag` containing a
340 /// warning for renamed and removed lints. This is over both lint
341 /// names from attributes and those passed on the command line. Since
342 /// it emits non-fatal warnings and there are *two* lint passes that
343 /// inspect attributes, this is only run from the late pass to avoid
344 /// printing duplicate warnings.
345pub fn check_lint_name(
346&self,
347 lint_name: &str,
348 tool_name: Option<Symbol>,
349 registered_tools: &RegisteredTools,
350 ) -> CheckLintNameResult<'_> {
351if let Some(tool_name) = tool_name {
352// FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
353if tool_name != sym::rustc354 && tool_name != sym::rustdoc355 && !registered_tools.contains(&Ident::with_dummy_span(tool_name))
356 {
357return CheckLintNameResult::NoTool;
358 }
359 }
360361let complete_name = if let Some(tool_name) = tool_name {
362::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool_name, lint_name))
})format!("{tool_name}::{lint_name}")363 } else {
364lint_name.to_string()
365 };
366// If the lint was scoped with `tool::` check if the tool lint exists
367if let Some(tool_name) = tool_name {
368match self.by_name.get(&complete_name) {
369None => match self.lint_groups.get(&*complete_name) {
370// If the lint isn't registered, there are two possibilities:
371None => {
372// 1. The tool is currently running, so this lint really doesn't exist.
373 // FIXME: should this handle tools that never register a lint, like rustfmt?
374{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/context.rs:374",
"rustc_lint::context", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/context.rs"),
::tracing_core::__macro_support::Option::Some(374u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::context"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("lints={0:?}",
self.by_name) as &dyn Value))])
});
} else { ; }
};debug!("lints={:?}", self.by_name);
375let tool_prefix = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::", tool_name))
})format!("{tool_name}::");
376377return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
378self.no_lint_suggestion(&complete_name, tool_name.as_str())
379 } else {
380// 2. The tool isn't currently running, so no lints will be registered.
381 // To avoid giving a false positive, ignore all unknown lints.
382CheckLintNameResult::MissingTool383 };
384 }
385Some(LintGroup { lint_ids, depr, .. }) => {
386return if let &Some(LintAlias { name, silent: false }) = depr {
387 CheckLintNameResult::Tool(lint_ids, Some(name.to_string()))
388 } else {
389 CheckLintNameResult::Tool(lint_ids, None)
390 };
391 }
392 },
393Some(Id(id)) => return CheckLintNameResult::Tool(slice::from_ref(id), None),
394// If the lint was registered as removed or renamed by the lint tool, we don't need
395 // to treat tool_lints and rustc lints different and can use the code below.
396_ => {}
397 }
398 }
399match self.by_name.get(&complete_name) {
400Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()),
401Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
402None => match self.lint_groups.get(&*complete_name) {
403// If neither the lint, nor the lint group exists check if there is a `clippy::`
404 // variant of this lint
405None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
406Some(LintGroup { lint_ids, depr, .. }) => {
407// Check if the lint group name is deprecated
408if let &Some(LintAlias { name, silent: false }) = depr {
409 CheckLintNameResult::Tool(lint_ids, Some(name.to_string()))
410 } else {
411 CheckLintNameResult::Ok(lint_ids)
412 }
413 }
414 },
415Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
416Some(&Ignored) => CheckLintNameResult::Ok(&[]),
417 }
418 }
419420fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> {
421let name_lower = lint_name.to_lowercase();
422423if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_some() {
424// First check if the lint name is (partly) in upper case instead of lower case...
425return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false)));
426 }
427428// ...if not, search for lints with a similar name
429 // Note: find_best_match_for_name depends on the sort order of its input vector.
430 // To ensure deterministic output, sort elements of the lint_groups hash map.
431 // Also, never suggest deprecated lint groups.
432 // We will soon sort, so the initial order does not matter.
433#[allow(rustc::potential_query_instability)]
434let mut groups: Vec<_> = self435 .lint_groups
436 .iter()
437 .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
438 .collect();
439groups.sort();
440let groups = groups.iter().map(|k| Symbol::intern(k));
441let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
442let names: Vec<Symbol> = groups.chain(lints).collect();
443let mut lookups = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Symbol::intern(&name_lower)]))vec![Symbol::intern(&name_lower)];
444if let Some(stripped) = name_lower.split("::").last() {
445lookups.push(Symbol::intern(stripped));
446 }
447let res = find_best_match_for_names(&names, &lookups, None);
448let is_rustc = res.map_or_else(
449 || false,
450 |s| name_lower.contains("::") && !s.as_str().starts_with(tool_name),
451 );
452let suggestion = res.map(|s| (s, is_rustc));
453 CheckLintNameResult::NoLint(suggestion)
454 }
455456fn check_tool_name_for_backwards_compat(
457&self,
458 lint_name: &str,
459 tool_name: &str,
460 ) -> CheckLintNameResult<'_> {
461let complete_name = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}::{1}", tool_name, lint_name))
})format!("{tool_name}::{lint_name}");
462match self.by_name.get(&complete_name) {
463None => match self.lint_groups.get(&*complete_name) {
464// Now we are sure, that this lint exists nowhere
465None => self.no_lint_suggestion(lint_name, tool_name),
466Some(LintGroup { lint_ids, .. }) => {
467 CheckLintNameResult::Tool(lint_ids, Some(complete_name))
468 }
469 },
470Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)),
471Some(other) => {
472{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_lint/src/context.rs:472",
"rustc_lint::context", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/context.rs"),
::tracing_core::__macro_support::Option::Some(472u32),
::tracing_core::__macro_support::Option::Some("rustc_lint::context"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("got renamed lint {0:?}",
other) as &dyn Value))])
});
} else { ; }
};debug!("got renamed lint {:?}", other);
473 CheckLintNameResult::NoLint(None)
474 }
475 }
476 }
477}
478479/// Context for lint checking outside of type inference.
480pub struct LateContext<'tcx> {
481/// Type context we're checking in.
482pub tcx: TyCtxt<'tcx>,
483484/// Current body, or `None` if outside a body.
485pub enclosing_body: Option<hir::BodyId>,
486487/// Type-checking results for the current body. Access using the `typeck_results`
488 /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand.
489// FIXME(eddyb) move all the code accessing internal fields like this,
490 // to this module, to avoid exposing it to lint logic.
491pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
492493/// Parameter environment for the item we are in.
494pub param_env: ty::ParamEnv<'tcx>,
495496/// Items accessible from the crate being checked.
497pub effective_visibilities: &'tcx EffectiveVisibilities,
498499pub last_node_with_lint_attrs: hir::HirId,
500501/// Generic type parameters in scope for the item we are in.
502pub generics: Option<&'tcx hir::Generics<'tcx>>,
503504/// We are only looking at one module
505pub only_module: bool,
506}
507508/// Context for lint checking of the AST, after expansion, before lowering to HIR.
509pub struct EarlyContext<'a> {
510pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
511pub buffered: LintBuffer,
512}
513514pub trait LintContext {
515type LintExpectationId: Copy + Into<LintExpectationId>;
516517fn sess(&self) -> &Session;
518519// FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
520 // set the span in their `decorate` function (preferably using set_span).
521/// Emit a lint at the appropriate level, with an optional associated span.
522 ///
523 /// [`emit_lint_base`]: rustc_middle::lint::emit_lint_base#decorate-signature
524#[track_caller]
525fn opt_span_lint<S: Into<MultiSpan>>(
526&self,
527 lint: &'static Lint,
528 span: Option<S>,
529 decorate: impl for<'a> Diagnostic<'a, ()>,
530 );
531532/// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`,
533 /// typically generated by `#[derive(Diagnostic)]`).
534#[track_caller]
535fn emit_span_lint<S: Into<MultiSpan>>(
536&self,
537 lint: &'static Lint,
538 span: S,
539 decorator: impl for<'a> Diagnostic<'a, ()>,
540 ) {
541self.opt_span_lint(lint, Some(span), decorator);
542 }
543544/// This returns the lint level spec for the given lint at the current location.
545fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<Self::LintExpectationId>;
546547/// This function can be used to manually fulfill an expectation. This can
548 /// be used for lints which contain several spans, and should be suppressed,
549 /// if either location was marked with an expectation.
550 ///
551 /// Note that this function should only be called for [`LintExpectationId`]s
552 /// retrieved from the current lint pass. Buffered or manually created ids can
553 /// cause ICEs.
554fn fulfill_expectation(&self, expectation: Self::LintExpectationId) {
555// We need to make sure that submitted expectation ids are correctly fulfilled suppressed
556 // and stored between compilation sessions. To not manually do these steps, we simply create
557 // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a
558 // normal expected lint diagnostic.
559self.sess()
560 .dcx()
561 .struct_expect(
562"this is a dummy diagnostic, to submit and store an expectation",
563expectation.into(),
564 )
565 .emit();
566 }
567}
568569impl<'a> EarlyContext<'a> {
570pub(crate) fn new(
571 sess: &'a Session,
572 features: &'a Features,
573 lint_added_lints: bool,
574 lint_store: &'a LintStore,
575 registered_tools: &'a RegisteredTools,
576 buffered: LintBuffer,
577 ) -> EarlyContext<'a> {
578EarlyContext {
579 builder: LintLevelsBuilder::new(
580sess,
581features,
582lint_added_lints,
583lint_store,
584registered_tools,
585 ),
586buffered,
587 }
588 }
589}
590591impl<'tcx> LintContextfor LateContext<'tcx> {
592type LintExpectationId = StableLintExpectationId;
593594/// Gets the overall compiler `Session` object.
595fn sess(&self) -> &Session {
596self.tcx.sess
597 }
598599fn opt_span_lint<S: Into<MultiSpan>>(
600&self,
601 lint: &'static Lint,
602 span: Option<S>,
603 decorate: impl for<'a> Diagnostic<'a, ()>,
604 ) {
605let hir_id = self.last_node_with_lint_attrs;
606607match span {
608Some(s) => self.tcx.emit_node_span_lint(lint, hir_id, s, decorate),
609None => self.tcx.emit_node_lint(lint, hir_id, decorate),
610 }
611 }
612613fn get_lint_level_spec(&self, lint: &'static Lint) -> StableLevelSpec {
614self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs)
615 }
616}
617618impl LintContextfor EarlyContext<'_> {
619type LintExpectationId = UnstableLintExpectationId;
620621/// Gets the overall compiler `Session` object.
622fn sess(&self) -> &Session {
623self.builder.sess()
624 }
625626fn opt_span_lint<S: Into<MultiSpan>>(
627&self,
628 lint: &'static Lint,
629 span: Option<S>,
630 decorator: impl for<'a> Diagnostic<'a, ()>,
631 ) {
632self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator)
633 }
634635fn get_lint_level_spec(&self, lint: &'static Lint) -> UnstableLevelSpec {
636self.builder.lint_level_spec(lint)
637 }
638}
639640impl<'tcx> LateContext<'tcx> {
641/// The typing mode of the currently visited node. Use this when
642 /// building a new `InferCtxt`.
643pub fn typing_mode(&self) -> TypingMode<'tcx> {
644if let Some(body_id) = self.enclosing_body
645 && self.tcx.use_typing_mode_post_typeck_until_borrowck()
646 {
647let def_id = self.tcx.hir_enclosing_body_owner(body_id.hir_id);
648TypingMode::borrowck(self.tcx, def_id)
649 } else {
650TypingMode::non_body_analysis()
651 }
652 }
653654pub fn typing_env(&self) -> TypingEnv<'tcx> {
655TypingEnv::new(self.param_env, self.typing_mode())
656 }
657658pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
659self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
660 }
661662pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
663self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty)
664 }
665666/// Gets the type-checking results for the current body,
667 /// or `None` if outside a body.
668pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
669self.cached_typeck_results.get().or_else(|| {
670self.enclosing_body.map(|body| {
671let typeck_results = self.tcx.typeck_body(body);
672self.cached_typeck_results.set(Some(typeck_results));
673typeck_results674 })
675 })
676 }
677678/// Gets the type-checking results for the current body.
679 /// As this will ICE if called outside bodies, only call when working with
680 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
681#[track_caller]
682pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
683self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body")
684 }
685686/// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
687 /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside
688 /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
689pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
690match *qpath {
691 hir::QPath::Resolved(_, path) => path.res,
692 hir::QPath::TypeRelative(..) => self693 .maybe_typeck_results()
694 .filter(|typeck_results| typeck_results.hir_owner == id.owner)
695 .or_else(|| {
696self.tcx
697 .has_typeck_results(id.owner.def_id)
698 .then(|| self.tcx.typeck(id.owner.def_id))
699 })
700 .and_then(|typeck_results| typeck_results.type_dependent_def(id))
701 .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
702 }
703 }
704705/// Gets the absolute path of `def_id` as a vector of `Symbol`.
706 ///
707 /// Note that this is kinda expensive because it has to
708 /// travel the tree and pretty-print. Use sparingly.
709 ///
710 /// If you're trying to match for an item given by its path, use a
711 /// diagnostic item. If you're only interested in given sections, use more
712 /// specific functions, such as [`TyCtxt::crate_name`]
713 ///
714 /// FIXME: It would be great if this could be optimized.
715 ///
716 /// # Examples
717 ///
718 /// ```rust,ignore (no context or def id available)
719 /// let def_path = cx.get_def_path(def_id);
720 /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
721 /// // The given `def_id` is that of an `Option` type
722 /// }
723 /// ```
724pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
725struct LintPathPrinter<'tcx> {
726 tcx: TyCtxt<'tcx>,
727 path: Vec<Symbol>,
728 }
729730impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
731fn tcx(&self) -> TyCtxt<'tcx> {
732self.tcx
733 }
734735fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
736::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
737}
738739fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
740::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
741}
742743fn print_dyn_existential(
744&mut self,
745 _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
746 ) -> Result<(), PrintError> {
747::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
748}
749750fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
751::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
752}
753754fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
755self.path = ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[self.tcx.crate_name(cnum)]))vec![self.tcx.crate_name(cnum)];
756Ok(())
757 }
758759fn print_path_with_qualified(
760&mut self,
761 self_ty: Ty<'tcx>,
762 trait_ref: Option<ty::TraitRef<'tcx>>,
763 ) -> Result<(), PrintError> {
764if trait_ref.is_none()
765 && let ty::Adt(def, args) = self_ty.kind()
766 {
767return self.print_def_path(def.did(), args);
768 }
769770// This shouldn't ever be needed, but just in case:
771{
let _guard = NoTrimmedGuard::new();
{
self.path =
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[match trait_ref {
Some(trait_ref) =>
Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?}", trait_ref))
})),
None =>
Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<{0}>", self_ty))
})),
}]));
Ok(())
}
}with_no_trimmed_paths!({
772self.path = vec![match trait_ref {
773Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
774None => Symbol::intern(&format!("<{self_ty}>")),
775 }];
776Ok(())
777 })778 }
779780fn print_path_with_impl(
781&mut self,
782 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
783 self_ty: Ty<'tcx>,
784 trait_ref: Option<ty::TraitRef<'tcx>>,
785 ) -> Result<(), PrintError> {
786 print_prefix(self)?;
787788// This shouldn't ever be needed, but just in case:
789self.path.push(match trait_ref {
790Some(trait_ref) => {
791{
let _guard = NoTrimmedGuard::new();
Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<impl {0} for {1}>",
trait_ref.print_only_trait_path(), self_ty))
}))
}with_no_trimmed_paths!(Symbol::intern(&format!(
792"<impl {} for {}>",
793 trait_ref.print_only_trait_path(),
794 self_ty
795 )))796 }
797None => {
798{
let _guard = NoTrimmedGuard::new();
Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("<impl {0}>", self_ty))
}))
}with_no_trimmed_paths!(Symbol::intern(&format!("<impl {self_ty}>")))799 }
800 });
801802Ok(())
803 }
804805fn print_path_with_simple(
806&mut self,
807 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
808 disambiguated_data: &DisambiguatedDefPathData,
809 ) -> Result<(), PrintError> {
810 print_prefix(self)?;
811812// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
813if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
814return Ok(());
815 }
816817self.path.push(match disambiguated_data.data.get_opt_name() {
818Some(sym) => sym,
819None => Symbol::intern(&disambiguated_data.data.to_string()),
820 });
821Ok(())
822 }
823824fn print_path_with_generic_args(
825&mut self,
826 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
827 _args: &[GenericArg<'tcx>],
828 ) -> Result<(), PrintError> {
829print_prefix(self)
830 }
831 }
832833let mut p = LintPathPrinter { tcx: self.tcx, path: ::alloc::vec::Vec::new()vec![] };
834p.print_def_path(def_id, &[]).unwrap();
835p.path
836 }
837838/// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
839 /// Do not invoke without first verifying that the type implements the trait.
840pub fn get_associated_type(
841&self,
842 self_ty: Ty<'tcx>,
843 trait_id: DefId,
844 name: Symbol,
845 ) -> Option<Ty<'tcx>> {
846let tcx = self.tcx;
847tcx.associated_items(trait_id)
848 .find_by_ident_and_kind(tcx, Ident::with_dummy_span(name), ty::AssocTag::Type, trait_id)
849 .and_then(|assoc| {
850let proj = Ty::new_projection(tcx, ty::IsRigid::No, assoc.def_id, [self_ty]);
851tcx.try_normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(proj))
852 .ok()
853 })
854 }
855856/// Returns the effective precedence of an expression for the purpose of
857 /// rendering diagnostic. This is not the same as the precedence that would
858 /// be used for pretty-printing HIR by rustc_hir_pretty.
859pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
860let has_attr = |id: hir::HirId| -> bool {
861self.tcx.hir_attrs(id).iter().any(hir::Attribute::is_prefix_attr_for_suggestions)
862 };
863expr.precedence(&has_attr)
864 }
865866/// If the given expression is a local binding, find the initializer expression.
867 /// If that initializer expression is another local binding, find its initializer again.
868 ///
869 /// This process repeats as long as possible (but usually no more than once).
870 /// Type-check adjustments are not taken in account in this function.
871 ///
872 /// Examples:
873 /// ```
874 /// let abc = 1;
875 /// let def = abc + 2;
876 /// // ^^^^^^^ output
877 /// let def = def;
878 /// dbg!(def);
879 /// // ^^^ input
880 /// ```
881pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
882expr = expr.peel_blocks();
883884while let hir::ExprKind::Path(ref qpath) = expr.kind
885 && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
886 Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
887_ => None,
888 }
889 && let Some(init) = match parent_node {
890 hir::Node::Expr(expr) => Some(expr),
891 hir::Node::LetStmt(hir::LetStmt {
892 init,
893// Binding is immutable, init cannot be re-assigned
894pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
895 ..
896 }) => *init,
897_ => None,
898 }
899 {
900 expr = init.peel_blocks();
901 }
902expr903 }
904905/// If the given expression is a local binding, find the initializer expression.
906 /// If that initializer expression is another local or **outside** (`const`/`static`)
907 /// binding, find its initializer again.
908 ///
909 /// This process repeats as long as possible (but usually no more than once).
910 /// Type-check adjustments are not taken in account in this function.
911 ///
912 /// Examples:
913 /// ```
914 /// const ABC: i32 = 1;
915 /// // ^ output
916 /// let def = ABC;
917 /// dbg!(def);
918 /// // ^^^ input
919 ///
920 /// // or...
921 /// let abc = 1;
922 /// let def = abc + 2;
923 /// // ^^^^^^^ output
924 /// dbg!(def);
925 /// // ^^^ input
926 /// ```
927pub fn expr_or_init_with_outside_body<'a>(
928&self,
929mut expr: &'a hir::Expr<'tcx>,
930 ) -> &'a hir::Expr<'tcx> {
931expr = expr.peel_blocks();
932933while let hir::ExprKind::Path(ref qpath) = expr.kind
934 && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
935 Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
936 Res::Def(_, def_id) => self.tcx.hir_get_if_local(def_id),
937_ => None,
938 }
939 && let Some(init) = match parent_node {
940 hir::Node::Expr(expr) => Some(expr),
941 hir::Node::LetStmt(hir::LetStmt {
942 init,
943// Binding is immutable, init cannot be re-assigned
944pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
945 ..
946 }) => *init,
947 hir::Node::Item(item) => match item.kind {
948// FIXME(mgca): figure out how to handle ConstArgKind::Path (or don't but add warning in docs here)
949hir::ItemKind::Const(.., hir::ConstItemRhs::Body(body_id))
950 | hir::ItemKind::Static(.., body_id) => Some(self.tcx.hir_body(body_id).value),
951_ => None,
952 },
953_ => None,
954 }
955 {
956 expr = init.peel_blocks();
957 }
958expr959 }
960}
961962impl<'tcx> abi::HasDataLayoutfor LateContext<'tcx> {
963#[inline]
964fn data_layout(&self) -> &abi::TargetDataLayout {
965&self.tcx.data_layout
966 }
967}
968969impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
970#[inline]
971fn tcx(&self) -> TyCtxt<'tcx> {
972self.tcx
973 }
974}
975976impl<'tcx> ty::layout::HasTypingEnv<'tcx> for LateContext<'tcx> {
977#[inline]
978fn typing_env(&self) -> ty::TypingEnv<'tcx> {
979self.typing_env()
980 }
981}
982983impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
984type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
985986#[inline]
987fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
988err989 }
990}