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::slice;
78use rustc_abias abi;
9use rustc_ast::BindingMode;
10use rustc_ast::util::parser::ExprPrecedence;
11use rustc_data_structures::fx::FxIndexMap;
12use rustc_data_structures::sync;
13use rustc_data_structures::unord::UnordMap;
14use rustc_errors::{Diagnostic, LintBuffer, MultiSpan};
15use rustc_feature::Features;
16use rustc_hiras hir;
17use rustc_hir::def::Res;
18use rustc_hir::def_id::{CrateNum, DefId};
19use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
20use rustc_hir::{Pat, PatKind};
21use rustc_lint_defs::{
22FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId,
23UnstableLintExpectationId,
24};
25use rustc_middle::lint::{LevelSpec, StableLevelSpec, UnstableLevelSpec};
26use rustc_middle::middle::privacy::EffectiveVisibilities;
27use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
28use rustc_middle::ty::print::{PrintError, PrintTraitRefExtas _, Printer, with_no_trimmed_paths};
29use rustc_middle::ty::{self, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, Unnormalized};
30use rustc_session::{DynLintStore, Session};
31use rustc_span::edit_distance::find_best_match_for_names;
32use rustc_span::{Ident, Span, Symbol, bug, sym};
33use rustc_structures::LintName;
34use tracing::debug;
3536use self::TargetLint::*;
37use crate::levels::LintLevelsBuilder;
38use crate::passes::{EarlyLintPassObject, LateLintPassObject};
3940pub(crate) type EarlyLintPassFactory =
41Box<dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync>;
42type LateLintPassFactory =
43Box<dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>;
4445/// Information about the registered lints.
46//
47// About the pass factories: these should only be called once, but since we
48// want to avoid locks or interior mutability, we don't enforce this. Lints
49// should, in theory, be compatible with being constructed more than once,
50// though not necessarily in a sane manner. This is safe though.
51pub struct LintStore {
52/// Registered lints.
53lints: Vec<&'static Lint>,
5455/// This lint pass kind is softly deprecated. It misses expanded code and has caused a few
56 /// errors in the past. Currently, it is only used in Clippy. New implementations
57 /// should avoid using this interface, as it might be removed in the future.
58 ///
59 /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838)
60 /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518)
61pub(crate) pre_expansion_lint_passes: Vec<EarlyLintPassFactory>,
6263/// These lint passes run on AST nodes.
64pub(crate) early_lint_passes: Vec<EarlyLintPassFactory>,
6566/// These lint passes run on HIR nodes. Each one processes an entire crate. They don't benefit
67 /// from incremental compilation. `late_lint_mod_passes` should be used in preference where
68 /// possible; only use `late_lint_passes` for lints that implement `check_crate` and/or
69 /// `check_crate_post` and accumulate cross-module state.
70 ///
71 /// The exception is Clippy, which uses `late_lint_passes` for all late lint passes. It needs
72 /// `check_crate`/`check_crate_post` for some of its lints and uses late lint passes throughout
73 /// for consistency. This is ok because Clippy isn't wired for incremental compilation.
74pub(crate) late_lint_passes: Vec<LateLintPassFactory>,
7576/// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times).
77 /// They benefit from incremental compilation.
78pub(crate) late_lint_mod_passes: Vec<LateLintPassFactory>,
7980/// Lints indexed by name.
81by_name: UnordMap<LintName, TargetLint>,
8283/// Map of registered lint groups to what lints they expand to.
84lint_groups: FxIndexMap<LintName, LintGroup>,
85}
8687impl DynLintStorefor LintStore {
88fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
89Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
90 rustc_session::LintGroup { name, lints, is_externally_loaded }
91 }))
92 }
93}
9495/// The target of the `by_name` map, which accounts for renaming/deprecation.
96#[derive(#[automatically_derived]impl ::core::fmt::Debug for TargetLint {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Id(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f, "Id",
&__self_0),
Self::Renamed(__self_0, __self_1) =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"Renamed", __self_0, &__self_1),
Self::Removed(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Removed", &__self_0),
Self::Ignored => ::core::fmt::Formatter::write_str(f, "Ignored"),
}
}
}Debug)]
97enum TargetLint {
98/// A direct lint target
99Id(LintId),
100101/// Temporary renaming, used for easing migration pain; see #16545
102Renamed(LintName, LintId),
103104/// Lint with this name existed previously, but has been removed/deprecated.
105 /// The string argument is the reason for removal.
106Removed(String),
107108/// A lint name that should give no warnings and have no effect.
109 ///
110 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers
111 /// them as tool lints.
112Ignored,
113}
114115struct LintAlias {
116 name: LintName,
117/// Whether deprecation warnings should be suppressed for this alias.
118silent: bool,
119}
120121struct LintGroup {
122 lint_ids: Vec<LintId>,
123 is_externally_loaded: bool,
124 depr: Option<LintAlias>,
125}
126127#[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 {
Self::Ok(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f, "Ok",
&__self_0),
Self::NoLint(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"NoLint", &__self_0),
Self::NoTool(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"NoTool", &__self_0),
Self::Renamed(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Renamed", &__self_0),
Self::Removed(__self_0) =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"Removed", &__self_0),
Self::Tool(__self_0, __self_1) =>
::core::fmt::Formatter::debug_struct_field2_finish(f, "Tool",
__self_0, &__self_1),
Self::MissingTool =>
::core::fmt::Formatter::write_str(f, "MissingTool"),
}
}
}Debug)]
128pub enum CheckLintNameResult<'a> {
129Ok(&'a [LintId]),
130/// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
131NoLint(Option<(LintName, bool)>),
132/// The lint refers to a tool that has not been registered.
133NoTool(Symbol),
134/// The lint has been renamed to a new name.
135Renamed(LintName),
136/// The lint has been removed due to the given reason.
137Removed(String),
138139/// The lint is from a tool. The `LintId` will be returned as if it were a
140 /// rustc lint. The `Option<String>` indicates if the lint has been
141 /// renamed.
142Tool(&'a [LintId], Option<LintName>),
143144/// The lint is from a tool. Either the lint does not exist in the tool or
145 /// the code was not compiled with the tool and therefore the lint was
146 /// never added to the `LintStore`.
147MissingTool,
148}
149150impl LintStore {
151pub fn new() -> LintStore {
152LintStore {
153 lints: ::alloc::vec::Vec::new()vec![],
154 pre_expansion_lint_passes: ::alloc::vec::Vec::new()vec![],
155 early_lint_passes: ::alloc::vec::Vec::new()vec![],
156 late_lint_passes: ::alloc::vec::Vec::new()vec![],
157 late_lint_mod_passes: ::alloc::vec::Vec::new()vec![],
158 by_name: Default::default(),
159 lint_groups: Default::default(),
160 }
161 }
162163pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
164&self.lints
165 }
166167pub fn get_lint_groups(&self) -> impl Iterator<Item = (LintName, Vec<LintId>, bool)> {
168self.lint_groups
169 .iter()
170 .filter(|(_, LintGroup { depr, .. })| {
171// Don't display deprecated lint groups.
172depr.is_none()
173 })
174 .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| {
175 (k.clone(), lint_ids.clone(), *is_externally_loaded)
176 })
177 }
178179/// Returns all lint group names, including deprecated/aliased groups
180pub fn get_all_group_names(&self) -> impl Iterator<Item = LintName> {
181self.lint_groups.keys().copied()
182 }
183184/// See the comment on `LintStore::pre_expansion_lint_passes`.
185pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) {
186self.pre_expansion_lint_passes.push(pass);
187 }
188189/// See the comment on `LintStore::early_lint_passes`.
190pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) {
191self.early_lint_passes.push(pass);
192 }
193194/// See the comment on `LintStore::late_lint_passes`.
195pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) {
196self.late_lint_passes.push(pass);
197 }
198199/// See the comment on `LintStore::late_lint_mod_passes`.
200pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) {
201self.late_lint_mod_passes.push(pass);
202 }
203204/// Helper method for register_early/late_pass
205pub fn register_lints(&mut self, lints: &[&'static Lint]) {
206for lint in lints {
207self.lints.push(lint);
208209let id = LintId::of(lint);
210if self.by_name.insert(LintName::from_str(&lint.name_lower()), Id(id)).is_some() {
211::rustc_span::macros::bug_impl(None,
format_args!("duplicate specification of lint {0}", lint.name_lower()),
Location::caller())bug!("duplicate specification of lint {}", lint.name_lower())212 }
213214if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
215if let Some(edition) = reason.edition() {
216self.lint_groups
217 .entry(LintName::from_str(edition.lint_name()))
218 .or_insert(LintGroup {
219 lint_ids: ::alloc::vec::Vec::new()vec![],
220 is_externally_loaded: lint.is_externally_loaded,
221 depr: None,
222 })
223 .lint_ids
224 .push(id);
225 } else {
226// Lints belonging to the `future_incompatible` lint group are lints where a
227 // future version of rustc will cause existing code to stop compiling.
228 // Lints tied to an edition don't count because they are opt-in.
229self.lint_groups
230 .entry(LintName::from_str("future_incompatible"))
231 .or_insert(LintGroup {
232 lint_ids: ::alloc::vec::Vec::new()vec![],
233 is_externally_loaded: lint.is_externally_loaded,
234 depr: None,
235 })
236 .lint_ids
237 .push(id);
238 }
239 }
240 }
241 }
242243fn insert_group(&mut self, name: LintName, group: LintGroup) {
244let previous = self.lint_groups.insert(name, group);
245if previous.is_some() {
246::rustc_span::macros::bug_impl(None,
format_args!("group {0} already exists", name), Location::caller());bug!("group {name} already exists");
247 }
248 }
249250pub fn register_group_alias(&mut self, group_name: &str, alias: &str) {
251let group_name = LintName::from_str(group_name);
252let alias = LintName::from_str(alias);
253let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(&group_name) else {
254::rustc_span::macros::bug_impl(None,
format_args!("group alias {0:?} points to unregistered group {1:?}",
alias, group_name), Location::caller())bug!("group alias {alias:?} points to unregistered group {group_name:?}")255 };
256257self.insert_group(
258alias,
259LintGroup {
260 lint_ids: lint_ids.clone(),
261 is_externally_loaded: false,
262 depr: Some(LintAlias { name: group_name, silent: true }),
263 },
264 );
265 }
266267pub fn register_group(
268&mut self,
269 is_externally_loaded: bool,
270 name: &str,
271 deprecated_name: Option<&str>,
272 to: Vec<LintId>,
273 ) {
274let name = LintName::from_str(name);
275if let Some(deprecated) = deprecated_name {
276let deprecated = LintName::from_str(deprecated);
277self.insert_group(
278deprecated,
279LintGroup {
280 lint_ids: to.clone(),
281is_externally_loaded,
282 depr: Some(LintAlias { name, silent: false }),
283 },
284 );
285 }
286self.insert_group(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None });
287 }
288289/// This lint should give no warning and have no effect.
290 ///
291 /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
292#[track_caller]
293pub fn register_ignored(&mut self, name: &str) {
294if self.by_name.insert(LintName::from_str(name), Ignored).is_some() {
295::rustc_span::macros::bug_impl(None,
format_args!("duplicate specification of lint {0}", name),
Location::caller());bug!("duplicate specification of lint {}", name);
296 }
297 }
298299/// This lint has been renamed; warn about using the new name and apply the lint.
300#[track_caller]
301pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
302let old_name = LintName::from_str(old_name);
303let new_name = LintName::from_str(new_name);
304let Some(&Id(target)) = self.by_name.get(&new_name) else {
305::rustc_span::macros::bug_impl(None,
format_args!("invalid lint renaming of {0} to {1}", old_name, new_name),
Location::caller());bug!("invalid lint renaming of {old_name} to {new_name}");
306 };
307self.by_name.insert(old_name, Renamed(new_name, target));
308 }
309310pub fn register_removed(&mut self, name: &str, reason: &str) {
311self.by_name.insert(LintName::from_str(name), Removed(reason.into()));
312 }
313314pub fn find_lints(&self, lint_name: LintName) -> Option<&[LintId]> {
315match self.by_name.get(&lint_name) {
316Some(Id(lint_id)) => Some(slice::from_ref(lint_id)),
317Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)),
318Some(Removed(_)) => None,
319Some(Ignored) => Some(&[]),
320None => match self.lint_groups.get(&lint_name) {
321Some(LintGroup { lint_ids, .. }) => Some(lint_ids),
322None => None,
323 },
324 }
325 }
326327/// True if this symbol represents a lint group name.
328pub fn is_lint_group(&self, lint_name: LintName) -> bool {
329{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/bbcdfd90a3f81ca2a5f6d69b943a6ff0d8582102/compiler/rustc_lint/src/context.rs:329",
"rustc_lint::context", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/bbcdfd90a3f81ca2a5f6d69b943a6ff0d8582102/compiler/rustc_lint/src/context.rs"),
::tracing_core::__macro_support::Option::Some(329u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::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 ::tracing::field::Value))])
});
} else { ; }
};debug!(
330"is_lint_group(lint_name={:?}, lint_groups={:?})",
331 lint_name,
332self.lint_groups.keys().collect::<Vec<_>>()
333 );
334self.lint_groups.contains_key(&lint_name) || lint_name.is(sym::warnings)
335 }
336337/// Checks the name of a lint for its existence, and whether it was
338 /// renamed or removed. Generates a `Diag` containing a
339 /// warning for renamed and removed lints. This is over both lint
340 /// names from attributes and those passed on the command line. Since
341 /// it emits non-fatal warnings and there are *two* lint passes that
342 /// inspect attributes, this is only run from the late pass to avoid
343 /// printing duplicate warnings.
344pub fn check_lint_name(
345&self,
346 lint_name: LintName,
347 registered_lint_tools: &RegisteredTools,
348 ) -> CheckLintNameResult<'_> {
349let tool_name = lint_name.tool;
350351if let Some(tool_name) = tool_name {
352// FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
353if tool_name != sym::rustc
354 && tool_name != sym::rustdoc
355 && !registered_lint_tools.contains(&Ident::with_dummy_span(tool_name))
356 {
357return CheckLintNameResult::NoTool(tool_name);
358 }
359 }
360361// If the lint was scoped with `tool::` check if the tool lint exists
362if let Some(tool_name) = tool_name {
363match self.by_name.get(&lint_name) {
364None => match self.lint_groups.get(&lint_name) {
365// If the lint isn't registered, there are two possibilities:
366None => {
367// 1. The tool is currently running, so this lint really doesn't exist.
368 // FIXME: should this handle tools that never register a lint, like rustfmt?
369{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/bbcdfd90a3f81ca2a5f6d69b943a6ff0d8582102/compiler/rustc_lint/src/context.rs:369",
"rustc_lint::context", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/bbcdfd90a3f81ca2a5f6d69b943a6ff0d8582102/compiler/rustc_lint/src/context.rs"),
::tracing_core::__macro_support::Option::Some(369u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("lints={0:?}",
self.by_name) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("lints={:?}", self.by_name);
370371return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) {
372self.no_lint_suggestion(lint_name)
373 } else {
374// 2. The tool isn't currently running, so no lints will be registered.
375 // To avoid giving a false positive, ignore all unknown lints.
376CheckLintNameResult::MissingTool377 };
378 }
379Some(LintGroup { lint_ids, depr, .. }) => {
380return if let Some(LintAlias { name, silent: false }) = depr {
381 CheckLintNameResult::Tool(lint_ids, Some(*name))
382 } else {
383 CheckLintNameResult::Tool(lint_ids, None)
384 };
385 }
386 },
387Some(Id(id)) => return CheckLintNameResult::Tool(slice::from_ref(id), None),
388// If the lint was registered as removed or renamed by the lint tool, we don't need
389 // to treat tool_lints and rustc lints different and can use the code below.
390_ => {}
391 }
392 }
393match self.by_name.get(&lint_name) {
394Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(*new_name),
395Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
396None => match self.lint_groups.get(&lint_name) {
397None => self.no_lint_suggestion(lint_name),
398Some(LintGroup { lint_ids, depr, .. }) => {
399// Check if the lint group name is deprecated
400if let Some(LintAlias { name, silent: false }) = depr {
401 CheckLintNameResult::Tool(lint_ids, Some(*name))
402 } else {
403 CheckLintNameResult::Ok(lint_ids)
404 }
405 }
406 },
407Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
408Some(&Ignored) => CheckLintNameResult::Ok(&[]),
409 }
410 }
411412fn no_lint_suggestion(&self, lint_name: LintName) -> CheckLintNameResult<'_> {
413let name_lower = lint_name.to_lowercase();
414415if self.find_lints(name_lower).is_some() {
416// First check if the lint name is (partly) in upper case instead of lower case...
417return CheckLintNameResult::NoLint(Some((name_lower, false)));
418 }
419420// ...if not, search for lints with a similar name
421 // Note: find_best_match_for_name depends on the sort order of its input vector.
422 // To ensure deterministic output, sort elements of the lint_groups hash map.
423 // Also, never suggest deprecated lint groups.
424 // We will soon sort, so the initial order does not matter.
425#[allow(rustc::potential_query_instability)]
426let mut groups: Vec<_> = self427 .lint_groups
428 .iter()
429 .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
430 .collect();
431groups.sort();
432let groups = groups.iter().map(|k| Symbol::intern(&k.to_string()));
433let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
434let names: Vec<Symbol> = groups.chain(lints).collect();
435let 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.to_string())]))vec![Symbol::intern(&name_lower.to_string())];
436if let Some(stripped) = name_lower.name.as_str().split("::").last() {
437lookups.push(Symbol::intern(stripped));
438 }
439let res = find_best_match_for_names(&names, &lookups, None);
440let res = res.map(|s| LintName::from_str(s.as_str()));
441let is_builtin =
442res.as_ref().map(|s| lint_name.tool.is_some() && s.tool.is_none()).unwrap_or(false);
443let suggestion = res.map(|s| (s, is_builtin));
444 CheckLintNameResult::NoLint(suggestion)
445 }
446}
447448/// Context for lint checking outside of type inference.
449pub struct LateContext<'tcx> {
450/// Type context we're checking in.
451pub tcx: TyCtxt<'tcx>,
452453/// Current body, or `None` if outside a body.
454pub enclosing_body: Option<hir::BodyId>,
455456/// Type-checking results for the current body.
457pub typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
458459/// Parameter environment for the item we are in.
460pub param_env: ty::ParamEnv<'tcx>,
461462/// Items accessible from the crate being checked.
463pub effective_visibilities: &'tcx EffectiveVisibilities,
464465pub last_node_with_lint_attrs: hir::HirId,
466467/// Generic type parameters in scope for the item we are in.
468pub generics: Option<&'tcx hir::Generics<'tcx>>,
469470/// We are only looking at one module
471pub only_module: bool,
472}
473474/// Context for lint checking of the AST, after expansion, before lowering to HIR.
475pub struct EarlyContext<'a> {
476pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
477pub buffered: LintBuffer,
478}
479480pub trait LintContext {
481type LintExpectationId: Copy + Into<LintExpectationId>;
482483fn sess(&self) -> &Session;
484485// FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
486 // set the span in their `decorate` function (preferably using set_span).
487/// Emit a lint at the appropriate level, with an optional associated span.
488 ///
489 /// [`emit_lint_base`]: rustc_middle::lint::emit_lint_base#decorate-signature
490#[track_caller]
491fn opt_span_lint<S: Into<MultiSpan>>(
492&self,
493 lint: &'static Lint,
494 span: Option<S>,
495 decorate: impl for<'a> Diagnostic<'a>,
496 );
497498/// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`,
499 /// typically generated by `#[derive(Diagnostic)]`).
500#[track_caller]
501fn emit_span_lint<S: Into<MultiSpan>>(
502&self,
503 lint: &'static Lint,
504 span: S,
505 decorator: impl for<'a> Diagnostic<'a>,
506 ) {
507self.opt_span_lint(lint, Some(span), decorator);
508 }
509510/// This returns the lint level spec for the given lint at the current location.
511fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<Self::LintExpectationId>;
512513/// This function can be used to manually fulfill an expectation. This can
514 /// be used for lints which contain several spans, and should be suppressed,
515 /// if either location was marked with an expectation.
516 ///
517 /// Note that this function should only be called for [`LintExpectationId`]s
518 /// retrieved from the current lint pass. Buffered or manually created ids can
519 /// cause ICEs.
520fn fulfill_expectation(&self, expectation: Self::LintExpectationId) {
521self.sess().dcx().fulfill_expectation(expectation);
522 }
523}
524525impl<'a> EarlyContext<'a> {
526pub(crate) fn new(
527 sess: &'a Session,
528 features: &'a Features,
529 lint_added_lints: bool,
530 lint_store: &'a LintStore,
531 registered_lint_tools: &'a RegisteredTools,
532 buffered: LintBuffer,
533 ) -> EarlyContext<'a> {
534EarlyContext {
535 builder: LintLevelsBuilder::new(
536sess,
537features,
538lint_added_lints,
539lint_store,
540registered_lint_tools,
541 ),
542buffered,
543 }
544 }
545}
546547impl<'tcx> LintContextfor LateContext<'tcx> {
548type LintExpectationId = StableLintExpectationId;
549550/// Gets the overall compiler `Session` object.
551fn sess(&self) -> &Session {
552self.tcx.sess
553 }
554555fn opt_span_lint<S: Into<MultiSpan>>(
556&self,
557 lint: &'static Lint,
558 span: Option<S>,
559 decorate: impl for<'a> Diagnostic<'a>,
560 ) {
561let hir_id = self.last_node_with_lint_attrs;
562563match span {
564Some(s) => self.tcx.emit_node_span_lint(lint, hir_id, s, decorate),
565None => self.tcx.emit_node_lint(lint, hir_id, decorate),
566 }
567 }
568569fn get_lint_level_spec(&self, lint: &'static Lint) -> StableLevelSpec {
570self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs)
571 }
572}
573574impl LintContextfor EarlyContext<'_> {
575type LintExpectationId = UnstableLintExpectationId;
576577/// Gets the overall compiler `Session` object.
578fn sess(&self) -> &Session {
579self.builder.sess()
580 }
581582fn opt_span_lint<S: Into<MultiSpan>>(
583&self,
584 lint: &'static Lint,
585 span: Option<S>,
586 decorator: impl for<'a> Diagnostic<'a>,
587 ) {
588self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator)
589 }
590591fn get_lint_level_spec(&self, lint: &'static Lint) -> UnstableLevelSpec {
592self.builder.lint_level_spec(lint)
593 }
594}
595596impl<'tcx> LateContext<'tcx> {
597/// The typing mode of the currently visited node. Use this when
598 /// building a new `InferCtxt`.
599pub fn typing_mode(&self) -> TypingMode<'tcx> {
600if let Some(body_id) = self.enclosing_body
601 && self.tcx.use_typing_mode_post_typeck_through_borrowck()
602 {
603let def_id = self.tcx.hir_enclosing_body_owner(body_id.hir_id);
604TypingMode::post_borrowck_analysis(self.tcx, def_id)
605 } else {
606TypingMode::non_body_analysis()
607 }
608 }
609610pub fn typing_env(&self) -> TypingEnv<'tcx> {
611TypingEnv::new(self.param_env, self.typing_mode())
612 }
613614pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
615self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
616 }
617618pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
619self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty)
620 }
621622/// Gets the type-checking results for the current body.
623 /// As this will ICE if called outside bodies, only call when working with
624 /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
625#[inline]
626 #[track_caller]
627pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
628self.typeck_results.expect("`LateContext::typeck_results` called outside of body")
629 }
630631/// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
632 /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside
633 /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
634pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
635match *qpath {
636 hir::QPath::Resolved(_, path) => path.res,
637 hir::QPath::TypeRelative(..) => self638 .typeck_results
639 .filter(|typeck_results| typeck_results.hir_owner == id.owner)
640 .or_else(|| {
641self.tcx
642 .has_typeck_results(id.owner.def_id)
643 .then(|| self.tcx.typeck(id.owner.def_id))
644 })
645 .and_then(|typeck_results| typeck_results.type_dependent_def(id))
646 .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
647 }
648 }
649650/// Gets the absolute path of `def_id` as a vector of `Symbol`.
651 ///
652 /// Note that this is kinda expensive because it has to
653 /// travel the tree and pretty-print. Use sparingly.
654 ///
655 /// If you're trying to match for an item given by its path, use a
656 /// diagnostic item. If you're only interested in given sections, use more
657 /// specific functions, such as [`TyCtxt::crate_name`]
658 ///
659 /// FIXME: It would be great if this could be optimized.
660 ///
661 /// # Examples
662 ///
663 /// ```rust,ignore (no context or def id available)
664 /// let def_path = cx.get_def_path(def_id);
665 /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
666 /// // The given `def_id` is that of an `Option` type
667 /// }
668 /// ```
669pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
670struct LintPathPrinter<'tcx> {
671 tcx: TyCtxt<'tcx>,
672 path: Vec<Symbol>,
673 }
674675impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
676fn tcx(&self) -> TyCtxt<'tcx> {
677self.tcx
678 }
679680fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
681::core::panicking::unreachable_default_msg();unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
682}
683684fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
685::core::panicking::unreachable_default_msg();unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
686}
687688fn print_dyn_existential(
689&mut self,
690 _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
691 ) -> Result<(), PrintError> {
692::core::panicking::unreachable_default_msg();unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
693}
694695fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
696::core::panicking::unreachable_default_msg();unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
697}
698699fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
700self.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)];
701Ok(())
702 }
703704fn print_path_with_qualified(
705&mut self,
706 self_ty: Ty<'tcx>,
707 trait_ref: Option<ty::TraitRef<'tcx>>,
708 ) -> Result<(), PrintError> {
709if trait_ref.is_none()
710 && let ty::Adt(def, args) = self_ty.kind()
711 {
712return self.print_def_path(def.did(), *args);
713 }
714715// This shouldn't ever be needed, but just in case:
716{
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!({
717self.path = vec![match trait_ref {
718Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
719None => Symbol::intern(&format!("<{self_ty}>")),
720 }];
721Ok(())
722 })723 }
724725fn print_path_with_impl(
726&mut self,
727 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
728 self_ty: Ty<'tcx>,
729 trait_ref: Option<ty::TraitRef<'tcx>>,
730 ) -> Result<(), PrintError> {
731 print_prefix(self)?;
732733// This shouldn't ever be needed, but just in case:
734self.path.push(match trait_ref {
735Some(trait_ref) => {
736{
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!(
737"<impl {} for {}>",
738 trait_ref.print_only_trait_path(),
739 self_ty
740 )))741 }
742None => {
743{
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}>")))744 }
745 });
746747Ok(())
748 }
749750fn print_path_with_simple(
751&mut self,
752 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
753 disambiguated_data: &DisambiguatedDefPathData,
754 ) -> Result<(), PrintError> {
755 print_prefix(self)?;
756757// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
758if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
759return Ok(());
760 }
761762self.path.push(match disambiguated_data.data.get_opt_name() {
763Some(sym) => sym,
764None => Symbol::intern(&disambiguated_data.data.to_string()),
765 });
766Ok(())
767 }
768769fn print_path_with_generic_args(
770&mut self,
771 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
772 _args: ty::GenericArgs<'tcx>,
773 ) -> Result<(), PrintError> {
774print_prefix(self)
775 }
776 }
777778let mut p = LintPathPrinter { tcx: self.tcx, path: ::alloc::vec::Vec::new()vec![] };
779p.print_def_path(def_id, ty::GenericArgs::empty()).unwrap();
780p.path
781 }
782783/// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
784 /// Do not invoke without first verifying that the type implements the trait.
785pub fn get_associated_type(
786&self,
787 self_ty: Ty<'tcx>,
788 trait_id: DefId,
789 name: Symbol,
790 ) -> Option<Ty<'tcx>> {
791let tcx = self.tcx;
792tcx.associated_items(trait_id)
793 .find_by_ident_and_kind(tcx, Ident::with_dummy_span(name), ty::AssocTag::Type, trait_id)
794 .and_then(|assoc| {
795let proj = Ty::new_projection(tcx, ty::IsRigid::No, assoc.def_id, [self_ty]);
796tcx.try_normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(proj))
797 .ok()
798 })
799 }
800801/// Returns the effective precedence of an expression for the purpose of
802 /// rendering diagnostic. This is not the same as the precedence that would
803 /// be used for pretty-printing HIR by rustc_hir_pretty.
804pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
805let has_attr = |id: hir::HirId| -> bool {
806self.tcx
807 .hir_attrs(id)
808 .iter()
809 .any(rustc_attr_ir::Attribute::is_prefix_attr_for_suggestions)
810 };
811expr.precedence(&has_attr)
812 }
813814/// If the given expression is a local binding, find the initializer expression.
815 /// If that initializer expression is another local binding, find its initializer again.
816 ///
817 /// This process repeats as long as possible (but usually no more than once).
818 /// Type-check adjustments are not taken in account in this function.
819 ///
820 /// Examples:
821 /// ```
822 /// let abc = 1;
823 /// let def = abc + 2;
824 /// // ^^^^^^^ output
825 /// let def = def;
826 /// dbg!(def);
827 /// // ^^^ input
828 /// ```
829pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
830expr = expr.peel_blocks();
831832while let hir::ExprKind::Path(ref qpath) = expr.kind
833 && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
834 Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
835_ => None,
836 }
837 && let Some(init) = match parent_node {
838 hir::Node::Expr(expr) => Some(expr),
839 hir::Node::LetStmt(hir::LetStmt {
840 init,
841// Binding is immutable, init cannot be re-assigned
842pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
843 ..
844 }) => *init,
845_ => None,
846 }
847 {
848 expr = init.peel_blocks();
849 }
850expr851 }
852853/// If the given expression is a local binding, find the initializer expression.
854 /// If that initializer expression is another local or **outside** (`const`/`static`)
855 /// binding, find its initializer again.
856 ///
857 /// This process repeats as long as possible (but usually no more than once).
858 /// Type-check adjustments are not taken in account in this function.
859 ///
860 /// Examples:
861 /// ```
862 /// const ABC: i32 = 1;
863 /// // ^ output
864 /// let def = ABC;
865 /// dbg!(def);
866 /// // ^^^ input
867 ///
868 /// // or...
869 /// let abc = 1;
870 /// let def = abc + 2;
871 /// // ^^^^^^^ output
872 /// dbg!(def);
873 /// // ^^^ input
874 /// ```
875pub fn expr_or_init_with_outside_body<'a>(
876&self,
877mut expr: &'a hir::Expr<'tcx>,
878 ) -> &'a hir::Expr<'tcx> {
879expr = expr.peel_blocks();
880881while let hir::ExprKind::Path(ref qpath) = expr.kind
882 && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
883 Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
884 Res::Def(_, def_id) => self.tcx.hir_get_if_local(def_id),
885_ => None,
886 }
887 && let Some(init) = match parent_node {
888 hir::Node::Expr(expr) => Some(expr),
889 hir::Node::LetStmt(hir::LetStmt {
890 init,
891// Binding is immutable, init cannot be re-assigned
892pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
893 ..
894 }) => *init,
895 hir::Node::Item(item) => match item.kind {
896// FIXME(mgca): figure out how to handle ConstArgKind::Path (or don't but add warning in docs here)
897hir::ItemKind::Const(.., hir::ConstItemRhs::Body(body_id))
898 | hir::ItemKind::Static(.., body_id) => Some(self.tcx.hir_body(body_id).value),
899_ => None,
900 },
901_ => None,
902 }
903 {
904 expr = init.peel_blocks();
905 }
906expr907 }
908}
909910impl<'tcx> abi::HasDataLayoutfor LateContext<'tcx> {
911#[inline]
912fn data_layout(&self) -> &abi::TargetDataLayout {
913&self.tcx.data_layout
914 }
915}
916917impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
918#[inline]
919fn tcx(&self) -> TyCtxt<'tcx> {
920self.tcx
921 }
922}
923924impl<'tcx> ty::layout::HasTypingEnv<'tcx> for LateContext<'tcx> {
925#[inline]
926fn typing_env(&self) -> ty::TypingEnv<'tcx> {
927self.typing_env()
928 }
929}
930931impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
932type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
933934#[inline]
935fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
936err937 }
938}