Skip to main content

rustc_lint/
context.rs

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.
5
6use std::slice;
7
8use rustc_abi as 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_hir as 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::{
22    FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId,
23    UnstableLintExpectationId,
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, PrintTraitRefExt as _, 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;
35
36use self::TargetLint::*;
37use crate::levels::LintLevelsBuilder;
38use crate::passes::{EarlyLintPassObject, LateLintPassObject};
39
40pub(crate) type EarlyLintPassFactory =
41    Box<dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync>;
42type LateLintPassFactory =
43    Box<dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync>;
44
45/// 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.
53    lints: Vec<&'static Lint>,
54
55    /// 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)
61    pub(crate) pre_expansion_lint_passes: Vec<EarlyLintPassFactory>,
62
63    /// These lint passes run on AST nodes.
64    pub(crate) early_lint_passes: Vec<EarlyLintPassFactory>,
65
66    /// 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.
74    pub(crate) late_lint_passes: Vec<LateLintPassFactory>,
75
76    /// These lint passes run on HIR nodes, and are constructed per-module (i.e. multiple times).
77    /// They benefit from incremental compilation.
78    pub(crate) late_lint_mod_passes: Vec<LateLintPassFactory>,
79
80    /// Lints indexed by name.
81    by_name: UnordMap<LintName, TargetLint>,
82
83    /// Map of registered lint groups to what lints they expand to.
84    lint_groups: FxIndexMap<LintName, LintGroup>,
85}
86
87impl DynLintStore for LintStore {
88    fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
89        Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
90            rustc_session::LintGroup { name, lints, is_externally_loaded }
91        }))
92    }
93}
94
95/// The target of the `by_name` map, which accounts for renaming/deprecation.
96#[derive(#[automatically_derived]impl ::core::fmt::Debug for TargetLint {
    #[inline]
    #[rustc_trivial_field_reads]
    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
99    Id(LintId),
100
101    /// Temporary renaming, used for easing migration pain; see #16545
102    Renamed(LintName, LintId),
103
104    /// Lint with this name existed previously, but has been removed/deprecated.
105    /// The string argument is the reason for removal.
106    Removed(String),
107
108    /// 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.
112    Ignored,
113}
114
115struct LintAlias {
116    name: LintName,
117    /// Whether deprecation warnings should be suppressed for this alias.
118    silent: bool,
119}
120
121struct LintGroup {
122    lint_ids: Vec<LintId>,
123    is_externally_loaded: bool,
124    depr: Option<LintAlias>,
125}
126
127#[derive(#[automatically_derived]impl<'a> ::core::fmt::Debug for
    CheckLintNameResult<'a> {
    #[inline]
    #[rustc_trivial_field_reads]
    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> {
129    Ok(&'a [LintId]),
130    /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
131    NoLint(Option<(LintName, bool)>),
132    /// The lint refers to a tool that has not been registered.
133    NoTool(Symbol),
134    /// The lint has been renamed to a new name.
135    Renamed(LintName),
136    /// The lint has been removed due to the given reason.
137    Removed(String),
138
139    /// 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.
142    Tool(&'a [LintId], Option<LintName>),
143
144    /// 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`.
147    MissingTool,
148}
149
150impl LintStore {
151    pub fn new() -> LintStore {
152        LintStore {
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    }
162
163    pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
164        &self.lints
165    }
166
167    pub fn get_lint_groups(&self) -> impl Iterator<Item = (LintName, Vec<LintId>, bool)> {
168        self.lint_groups
169            .iter()
170            .filter(|(_, LintGroup { depr, .. })| {
171                // Don't display deprecated lint groups.
172                depr.is_none()
173            })
174            .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| {
175                (k.clone(), lint_ids.clone(), *is_externally_loaded)
176            })
177    }
178
179    /// Returns all lint group names, including deprecated/aliased groups
180    pub fn get_all_group_names(&self) -> impl Iterator<Item = LintName> {
181        self.lint_groups.keys().copied()
182    }
183
184    /// See the comment on `LintStore::pre_expansion_lint_passes`.
185    pub fn register_pre_expansion_lint_pass(&mut self, pass: EarlyLintPassFactory) {
186        self.pre_expansion_lint_passes.push(pass);
187    }
188
189    /// See the comment on `LintStore::early_lint_passes`.
190    pub fn register_early_lint_pass(&mut self, pass: EarlyLintPassFactory) {
191        self.early_lint_passes.push(pass);
192    }
193
194    /// See the comment on `LintStore::late_lint_passes`.
195    pub fn register_late_lint_pass(&mut self, pass: LateLintPassFactory) {
196        self.late_lint_passes.push(pass);
197    }
198
199    /// See the comment on `LintStore::late_lint_mod_passes`.
200    pub fn register_late_lint_mod_pass(&mut self, pass: LateLintPassFactory) {
201        self.late_lint_mod_passes.push(pass);
202    }
203
204    /// Helper method for register_early/late_pass
205    pub fn register_lints(&mut self, lints: &[&'static Lint]) {
206        for lint in lints {
207            self.lints.push(lint);
208
209            let id = LintId::of(lint);
210            if 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            }
213
214            if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
215                if let Some(edition) = reason.edition() {
216                    self.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.
229                    self.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    }
242
243    fn insert_group(&mut self, name: LintName, group: LintGroup) {
244        let previous = self.lint_groups.insert(name, group);
245        if 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    }
249
250    pub fn register_group_alias(&mut self, group_name: &str, alias: &str) {
251        let group_name = LintName::from_str(group_name);
252        let alias = LintName::from_str(alias);
253        let 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        };
256
257        self.insert_group(
258            alias,
259            LintGroup {
260                lint_ids: lint_ids.clone(),
261                is_externally_loaded: false,
262                depr: Some(LintAlias { name: group_name, silent: true }),
263            },
264        );
265    }
266
267    pub fn register_group(
268        &mut self,
269        is_externally_loaded: bool,
270        name: &str,
271        deprecated_name: Option<&str>,
272        to: Vec<LintId>,
273    ) {
274        let name = LintName::from_str(name);
275        if let Some(deprecated) = deprecated_name {
276            let deprecated = LintName::from_str(deprecated);
277            self.insert_group(
278                deprecated,
279                LintGroup {
280                    lint_ids: to.clone(),
281                    is_externally_loaded,
282                    depr: Some(LintAlias { name, silent: false }),
283                },
284            );
285        }
286        self.insert_group(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None });
287    }
288
289    /// 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]
293    pub fn register_ignored(&mut self, name: &str) {
294        if 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    }
298
299    /// This lint has been renamed; warn about using the new name and apply the lint.
300    #[track_caller]
301    pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
302        let old_name = LintName::from_str(old_name);
303        let new_name = LintName::from_str(new_name);
304        let 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        };
307        self.by_name.insert(old_name, Renamed(new_name, target));
308    }
309
310    pub fn register_removed(&mut self, name: &str, reason: &str) {
311        self.by_name.insert(LintName::from_str(name), Removed(reason.into()));
312    }
313
314    pub fn find_lints(&self, lint_name: LintName) -> Option<&[LintId]> {
315        match self.by_name.get(&lint_name) {
316            Some(Id(lint_id)) => Some(slice::from_ref(lint_id)),
317            Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)),
318            Some(Removed(_)) => None,
319            Some(Ignored) => Some(&[]),
320            None => match self.lint_groups.get(&lint_name) {
321                Some(LintGroup { lint_ids, .. }) => Some(lint_ids),
322                None => None,
323            },
324        }
325    }
326
327    /// True if this symbol represents a lint group name.
328    pub 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/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/context.rs:329",
                        "rustc_lint::context", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/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,
332            self.lint_groups.keys().collect::<Vec<_>>()
333        );
334        self.lint_groups.contains_key(&lint_name) || lint_name.is(sym::warnings)
335    }
336
337    /// 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.
344    pub fn check_lint_name(
345        &self,
346        lint_name: LintName,
347        registered_lint_tools: &RegisteredTools,
348    ) -> CheckLintNameResult<'_> {
349        let tool_name = lint_name.tool;
350
351        if let Some(tool_name) = tool_name {
352            // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
353            if tool_name != sym::rustc
354                && tool_name != sym::rustdoc
355                && !registered_lint_tools.contains(&Ident::with_dummy_span(tool_name))
356            {
357                return CheckLintNameResult::NoTool(tool_name);
358            }
359        }
360
361        // If the lint was scoped with `tool::` check if the tool lint exists
362        if let Some(tool_name) = tool_name {
363            match self.by_name.get(&lint_name) {
364                None => match self.lint_groups.get(&lint_name) {
365                    // If the lint isn't registered, there are two possibilities:
366                    None => {
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/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/context.rs:369",
                        "rustc_lint::context", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/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);
370
371                        return if self.by_name.keys().any(|lint| lint.tool == Some(tool_name)) {
372                            self.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.
376                            CheckLintNameResult::MissingTool
377                        };
378                    }
379                    Some(LintGroup { lint_ids, depr, .. }) => {
380                        return 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                },
387                Some(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        }
393        match self.by_name.get(&lint_name) {
394            Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(*new_name),
395            Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
396            None => match self.lint_groups.get(&lint_name) {
397                None => self.no_lint_suggestion(lint_name),
398                Some(LintGroup { lint_ids, depr, .. }) => {
399                    // Check if the lint group name is deprecated
400                    if let Some(LintAlias { name, silent: false }) = depr {
401                        CheckLintNameResult::Tool(lint_ids, Some(*name))
402                    } else {
403                        CheckLintNameResult::Ok(lint_ids)
404                    }
405                }
406            },
407            Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
408            Some(&Ignored) => CheckLintNameResult::Ok(&[]),
409        }
410    }
411
412    fn no_lint_suggestion(&self, lint_name: LintName) -> CheckLintNameResult<'_> {
413        let name_lower = lint_name.to_lowercase();
414
415        if self.find_lints(name_lower).is_some() {
416            // First check if the lint name is (partly) in upper case instead of lower case...
417            return CheckLintNameResult::NoLint(Some((name_lower, false)));
418        }
419
420        // ...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)]
426        let mut groups: Vec<_> = self
427            .lint_groups
428            .iter()
429            .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
430            .collect();
431        groups.sort();
432        let groups = groups.iter().map(|k| Symbol::intern(&k.to_string()));
433        let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
434        let names: Vec<Symbol> = groups.chain(lints).collect();
435        let 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())];
436        if let Some(stripped) = name_lower.name.as_str().split("::").last() {
437            lookups.push(Symbol::intern(stripped));
438        }
439        let res = find_best_match_for_names(&names, &lookups, None);
440        let res = res.map(|s| LintName::from_str(s.as_str()));
441        let is_builtin =
442            res.as_ref().map(|s| lint_name.tool.is_some() && s.tool.is_none()).unwrap_or(false);
443        let suggestion = res.map(|s| (s, is_builtin));
444        CheckLintNameResult::NoLint(suggestion)
445    }
446}
447
448/// Context for lint checking outside of type inference.
449pub struct LateContext<'tcx> {
450    /// Type context we're checking in.
451    pub tcx: TyCtxt<'tcx>,
452
453    /// Current body, or `None` if outside a body.
454    pub enclosing_body: Option<hir::BodyId>,
455
456    /// Type-checking results for the current body.
457    pub typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
458
459    /// Parameter environment for the item we are in.
460    pub param_env: ty::ParamEnv<'tcx>,
461
462    /// Items accessible from the crate being checked.
463    pub effective_visibilities: &'tcx EffectiveVisibilities,
464
465    pub last_node_with_lint_attrs: hir::HirId,
466
467    /// Generic type parameters in scope for the item we are in.
468    pub generics: Option<&'tcx hir::Generics<'tcx>>,
469
470    /// We are only looking at one module
471    pub only_module: bool,
472}
473
474/// Context for lint checking of the AST, after expansion, before lowering to HIR.
475pub struct EarlyContext<'a> {
476    pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
477    pub buffered: LintBuffer,
478}
479
480pub trait LintContext {
481    type LintExpectationId: Copy + Into<LintExpectationId>;
482
483    fn sess(&self) -> &Session;
484
485    // 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]
491    fn opt_span_lint<S: Into<MultiSpan>>(
492        &self,
493        lint: &'static Lint,
494        span: Option<S>,
495        decorate: impl for<'a> Diagnostic<'a>,
496    );
497
498    /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`,
499    /// typically generated by `#[derive(Diagnostic)]`).
500    #[track_caller]
501    fn emit_span_lint<S: Into<MultiSpan>>(
502        &self,
503        lint: &'static Lint,
504        span: S,
505        decorator: impl for<'a> Diagnostic<'a>,
506    ) {
507        self.opt_span_lint(lint, Some(span), decorator);
508    }
509
510    /// This returns the lint level spec for the given lint at the current location.
511    fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<Self::LintExpectationId>;
512
513    /// 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.
520    fn fulfill_expectation(&self, expectation: Self::LintExpectationId) {
521        self.sess().dcx().fulfill_expectation(expectation);
522    }
523}
524
525impl<'a> EarlyContext<'a> {
526    pub(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> {
534        EarlyContext {
535            builder: LintLevelsBuilder::new(
536                sess,
537                features,
538                lint_added_lints,
539                lint_store,
540                registered_lint_tools,
541            ),
542            buffered,
543        }
544    }
545}
546
547impl<'tcx> LintContext for LateContext<'tcx> {
548    type LintExpectationId = StableLintExpectationId;
549
550    /// Gets the overall compiler `Session` object.
551    fn sess(&self) -> &Session {
552        self.tcx.sess
553    }
554
555    fn opt_span_lint<S: Into<MultiSpan>>(
556        &self,
557        lint: &'static Lint,
558        span: Option<S>,
559        decorate: impl for<'a> Diagnostic<'a>,
560    ) {
561        let hir_id = self.last_node_with_lint_attrs;
562
563        match span {
564            Some(s) => self.tcx.emit_node_span_lint(lint, hir_id, s, decorate),
565            None => self.tcx.emit_node_lint(lint, hir_id, decorate),
566        }
567    }
568
569    fn get_lint_level_spec(&self, lint: &'static Lint) -> StableLevelSpec {
570        self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs)
571    }
572}
573
574impl LintContext for EarlyContext<'_> {
575    type LintExpectationId = UnstableLintExpectationId;
576
577    /// Gets the overall compiler `Session` object.
578    fn sess(&self) -> &Session {
579        self.builder.sess()
580    }
581
582    fn opt_span_lint<S: Into<MultiSpan>>(
583        &self,
584        lint: &'static Lint,
585        span: Option<S>,
586        decorator: impl for<'a> Diagnostic<'a>,
587    ) {
588        self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator)
589    }
590
591    fn get_lint_level_spec(&self, lint: &'static Lint) -> UnstableLevelSpec {
592        self.builder.lint_level_spec(lint)
593    }
594}
595
596impl<'tcx> LateContext<'tcx> {
597    /// The typing mode of the currently visited node. Use this when
598    /// building a new `InferCtxt`.
599    pub fn typing_mode(&self) -> TypingMode<'tcx> {
600        if let Some(body_id) = self.enclosing_body
601            && self.tcx.use_typing_mode_post_typeck_through_borrowck()
602        {
603            let def_id = self.tcx.hir_enclosing_body_owner(body_id.hir_id);
604            TypingMode::post_borrowck_analysis(self.tcx, def_id)
605        } else {
606            TypingMode::non_body_analysis()
607        }
608    }
609
610    pub fn typing_env(&self) -> TypingEnv<'tcx> {
611        TypingEnv::new(self.param_env, self.typing_mode())
612    }
613
614    pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
615        self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
616    }
617
618    pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
619        self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty)
620    }
621
622    /// 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]
627    pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
628        self.typeck_results.expect("`LateContext::typeck_results` called outside of body")
629    }
630
631    /// 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.
634    pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
635        match *qpath {
636            hir::QPath::Resolved(_, path) => path.res,
637            hir::QPath::TypeRelative(..) => self
638                .typeck_results
639                .filter(|typeck_results| typeck_results.hir_owner == id.owner)
640                .or_else(|| {
641                    self.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    }
649
650    /// 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    /// ```
669    pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
670        struct LintPathPrinter<'tcx> {
671            tcx: TyCtxt<'tcx>,
672            path: Vec<Symbol>,
673        }
674
675        impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
676            fn tcx(&self) -> TyCtxt<'tcx> {
677                self.tcx
678            }
679
680            fn 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            }
683
684            fn 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            }
687
688            fn 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            }
694
695            fn 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            }
698
699            fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
700                self.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)];
701                Ok(())
702            }
703
704            fn print_path_with_qualified(
705                &mut self,
706                self_ty: Ty<'tcx>,
707                trait_ref: Option<ty::TraitRef<'tcx>>,
708            ) -> Result<(), PrintError> {
709                if trait_ref.is_none()
710                    && let ty::Adt(def, args) = self_ty.kind()
711                {
712                    return self.print_def_path(def.did(), *args);
713                }
714
715                // 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!({
717                    self.path = vec![match trait_ref {
718                        Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
719                        None => Symbol::intern(&format!("<{self_ty}>")),
720                    }];
721                    Ok(())
722                })
723            }
724
725            fn 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)?;
732
733                // This shouldn't ever be needed, but just in case:
734                self.path.push(match trait_ref {
735                    Some(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                    }
742                    None => {
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                });
746
747                Ok(())
748            }
749
750            fn 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)?;
756
757                // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
758                if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
759                    return Ok(());
760                }
761
762                self.path.push(match disambiguated_data.data.get_opt_name() {
763                    Some(sym) => sym,
764                    None => Symbol::intern(&disambiguated_data.data.to_string()),
765                });
766                Ok(())
767            }
768
769            fn 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> {
774                print_prefix(self)
775            }
776        }
777
778        let mut p = LintPathPrinter { tcx: self.tcx, path: ::alloc::vec::Vec::new()vec![] };
779        p.print_def_path(def_id, ty::GenericArgs::empty()).unwrap();
780        p.path
781    }
782
783    /// 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.
785    pub fn get_associated_type(
786        &self,
787        self_ty: Ty<'tcx>,
788        trait_id: DefId,
789        name: Symbol,
790    ) -> Option<Ty<'tcx>> {
791        let tcx = self.tcx;
792        tcx.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| {
795                let proj = Ty::new_projection(tcx, ty::IsRigid::No, assoc.def_id, [self_ty]);
796                tcx.try_normalize_erasing_regions(self.typing_env(), Unnormalized::new_wip(proj))
797                    .ok()
798            })
799    }
800
801    /// 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.
804    pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
805        let has_attr = |id: hir::HirId| -> bool {
806            self.tcx
807                .hir_attrs(id)
808                .iter()
809                .any(rustc_attr_ir::Attribute::is_prefix_attr_for_suggestions)
810        };
811        expr.precedence(&has_attr)
812    }
813
814    /// 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    /// ```
829    pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
830        expr = expr.peel_blocks();
831
832        while 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
842                    pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
843                    ..
844                }) => *init,
845                _ => None,
846            }
847        {
848            expr = init.peel_blocks();
849        }
850        expr
851    }
852
853    /// 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    /// ```
875    pub fn expr_or_init_with_outside_body<'a>(
876        &self,
877        mut expr: &'a hir::Expr<'tcx>,
878    ) -> &'a hir::Expr<'tcx> {
879        expr = expr.peel_blocks();
880
881        while 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
892                    pat: 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)
897                    hir::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        }
906        expr
907    }
908}
909
910impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
911    #[inline]
912    fn data_layout(&self) -> &abi::TargetDataLayout {
913        &self.tcx.data_layout
914    }
915}
916
917impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
918    #[inline]
919    fn tcx(&self) -> TyCtxt<'tcx> {
920        self.tcx
921    }
922}
923
924impl<'tcx> ty::layout::HasTypingEnv<'tcx> for LateContext<'tcx> {
925    #[inline]
926    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
927        self.typing_env()
928    }
929}
930
931impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
932    type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
933
934    #[inline]
935    fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
936        err
937    }
938}