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::cell::Cell;
7use std::slice;
8
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::{Diag, Diagnostic, LintBuffer, LintDiagnostic, MultiSpan};
15use rustc_feature::Features;
16use rustc_hir::def::Res;
17use rustc_hir::def_id::{CrateNum, DefId};
18use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
19use rustc_hir::{Pat, PatKind};
20use rustc_middle::bug;
21use rustc_middle::lint::LevelAndSource;
22use rustc_middle::middle::privacy::EffectiveVisibilities;
23use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
24use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
25use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
26use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId};
27use rustc_session::{DynLintStore, Session};
28use rustc_span::edit_distance::find_best_match_for_names;
29use rustc_span::{Ident, Span, Symbol, sym};
30use tracing::debug;
31use {rustc_abi as abi, rustc_hir as hir};
32
33use self::TargetLint::*;
34use crate::levels::LintLevelsBuilder;
35use crate::passes::{EarlyLintPassObject, LateLintPassObject};
36
37type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
38type LateLintPassFactory =
39    dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
40
41/// Information about the registered lints.
42pub struct LintStore {
43    /// Registered lints.
44    lints: Vec<&'static Lint>,
45
46    /// Constructor functions for each variety of lint pass.
47    ///
48    /// These should only be called once, but since we want to avoid locks or
49    /// interior mutability, we don't enforce this (and lints should, in theory,
50    /// be compatible with being constructed more than once, though not
51    /// necessarily in a sane manner. This is safe though.)
52    pub pre_expansion_passes: Vec<Box<EarlyLintPassFactory>>,
53    pub early_passes: Vec<Box<EarlyLintPassFactory>>,
54    pub late_passes: Vec<Box<LateLintPassFactory>>,
55    /// This is unique in that we construct them per-module, so not once.
56    pub late_module_passes: Vec<Box<LateLintPassFactory>>,
57
58    /// Lints indexed by name.
59    by_name: UnordMap<String, TargetLint>,
60
61    /// Map of registered lint groups to what lints they expand to.
62    lint_groups: FxIndexMap<&'static str, LintGroup>,
63}
64
65impl DynLintStore for LintStore {
66    fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
67        Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
68            rustc_session::LintGroup { name, lints, is_externally_loaded }
69        }))
70    }
71}
72
73/// The target of the `by_name` map, which accounts for renaming/deprecation.
74#[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)]
75enum TargetLint {
76    /// A direct lint target
77    Id(LintId),
78
79    /// Temporary renaming, used for easing migration pain; see #16545
80    Renamed(String, LintId),
81
82    /// Lint with this name existed previously, but has been removed/deprecated.
83    /// The string argument is the reason for removal.
84    Removed(String),
85
86    /// A lint name that should give no warnings and have no effect.
87    ///
88    /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers
89    /// them as tool lints.
90    Ignored,
91}
92
93struct LintAlias {
94    name: &'static str,
95    /// Whether deprecation warnings should be suppressed for this alias.
96    silent: bool,
97}
98
99struct LintGroup {
100    lint_ids: Vec<LintId>,
101    is_externally_loaded: bool,
102    depr: Option<LintAlias>,
103}
104
105#[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)]
106pub enum CheckLintNameResult<'a> {
107    Ok(&'a [LintId]),
108    /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
109    NoLint(Option<(Symbol, bool)>),
110    /// The lint refers to a tool that has not been registered.
111    NoTool,
112    /// The lint has been renamed to a new name.
113    Renamed(String),
114    /// The lint has been removed due to the given reason.
115    Removed(String),
116
117    /// The lint is from a tool. The `LintId` will be returned as if it were a
118    /// rustc lint. The `Option<String>` indicates if the lint has been
119    /// renamed.
120    Tool(&'a [LintId], Option<String>),
121
122    /// The lint is from a tool. Either the lint does not exist in the tool or
123    /// the code was not compiled with the tool and therefore the lint was
124    /// never added to the `LintStore`.
125    MissingTool,
126}
127
128impl LintStore {
129    pub fn new() -> LintStore {
130        LintStore {
131            lints: ::alloc::vec::Vec::new()vec![],
132            pre_expansion_passes: ::alloc::vec::Vec::new()vec![],
133            early_passes: ::alloc::vec::Vec::new()vec![],
134            late_passes: ::alloc::vec::Vec::new()vec![],
135            late_module_passes: ::alloc::vec::Vec::new()vec![],
136            by_name: Default::default(),
137            lint_groups: Default::default(),
138        }
139    }
140
141    pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
142        &self.lints
143    }
144
145    pub fn get_lint_groups(&self) -> impl Iterator<Item = (&'static str, Vec<LintId>, bool)> {
146        self.lint_groups
147            .iter()
148            .filter(|(_, LintGroup { depr, .. })| {
149                // Don't display deprecated lint groups.
150                depr.is_none()
151            })
152            .map(|(k, LintGroup { lint_ids, is_externally_loaded, .. })| {
153                (*k, lint_ids.clone(), *is_externally_loaded)
154            })
155    }
156
157    /// Returns all lint group names, including deprecated/aliased groups
158    pub fn get_all_group_names(&self) -> impl Iterator<Item = &'static str> {
159        self.lint_groups.keys().copied()
160    }
161
162    pub fn register_early_pass(
163        &mut self,
164        pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
165    ) {
166        self.early_passes.push(Box::new(pass));
167    }
168
169    /// This lint pass is softly deprecated. It misses expanded code and has caused a few
170    /// errors in the past. Currently, it is only used in Clippy. New implementations
171    /// should avoid using this interface, as it might be removed in the future.
172    ///
173    /// * See [rust#69838](https://github.com/rust-lang/rust/pull/69838)
174    /// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518)
175    pub fn register_pre_expansion_pass(
176        &mut self,
177        pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
178    ) {
179        self.pre_expansion_passes.push(Box::new(pass));
180    }
181
182    pub fn register_late_pass(
183        &mut self,
184        pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
185        + 'static
186        + sync::DynSend
187        + sync::DynSync,
188    ) {
189        self.late_passes.push(Box::new(pass));
190    }
191
192    pub fn register_late_mod_pass(
193        &mut self,
194        pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
195        + 'static
196        + sync::DynSend
197        + sync::DynSync,
198    ) {
199        self.late_module_passes.push(Box::new(pass));
200    }
201
202    /// Helper method for register_early/late_pass
203    pub fn register_lints(&mut self, lints: &[&'static Lint]) {
204        for lint in lints {
205            self.lints.push(lint);
206
207            let id = LintId::of(lint);
208            if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
209                ::rustc_middle::util::bug::bug_fmt(format_args!("duplicate specification of lint {0}",
        lint.name_lower()))bug!("duplicate specification of lint {}", lint.name_lower())
210            }
211
212            if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
213                if let Some(edition) = reason.edition() {
214                    self.lint_groups
215                        .entry(edition.lint_name())
216                        .or_insert(LintGroup {
217                            lint_ids: ::alloc::vec::Vec::new()vec![],
218                            is_externally_loaded: lint.is_externally_loaded,
219                            depr: None,
220                        })
221                        .lint_ids
222                        .push(id);
223                } else {
224                    // Lints belonging to the `future_incompatible` lint group are lints where a
225                    // future version of rustc will cause existing code to stop compiling.
226                    // Lints tied to an edition don't count because they are opt-in.
227                    self.lint_groups
228                        .entry("future_incompatible")
229                        .or_insert(LintGroup {
230                            lint_ids: ::alloc::vec::Vec::new()vec![],
231                            is_externally_loaded: lint.is_externally_loaded,
232                            depr: None,
233                        })
234                        .lint_ids
235                        .push(id);
236                }
237            }
238        }
239    }
240
241    fn insert_group(&mut self, name: &'static str, group: LintGroup) {
242        let previous = self.lint_groups.insert(name, group);
243        if previous.is_some() {
244            ::rustc_middle::util::bug::bug_fmt(format_args!("group {0:?} already exists",
        name));bug!("group {name:?} already exists");
245        }
246    }
247
248    pub fn register_group_alias(&mut self, group_name: &'static str, alias: &'static str) {
249        let Some(LintGroup { lint_ids, .. }) = self.lint_groups.get(group_name) else {
250            ::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:?}")
251        };
252
253        self.insert_group(
254            alias,
255            LintGroup {
256                lint_ids: lint_ids.clone(),
257                is_externally_loaded: false,
258                depr: Some(LintAlias { name: group_name, silent: true }),
259            },
260        );
261    }
262
263    pub fn register_group(
264        &mut self,
265        is_externally_loaded: bool,
266        name: &'static str,
267        deprecated_name: Option<&'static str>,
268        to: Vec<LintId>,
269    ) {
270        if let Some(deprecated) = deprecated_name {
271            self.insert_group(
272                deprecated,
273                LintGroup {
274                    lint_ids: to.clone(),
275                    is_externally_loaded,
276                    depr: Some(LintAlias { name, silent: false }),
277                },
278            );
279        }
280        self.insert_group(name, LintGroup { lint_ids: to, is_externally_loaded, depr: None });
281    }
282
283    /// This lint should give no warning and have no effect.
284    ///
285    /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
286    #[track_caller]
287    pub fn register_ignored(&mut self, name: &str) {
288        if self.by_name.insert(name.to_string(), Ignored).is_some() {
289            ::rustc_middle::util::bug::bug_fmt(format_args!("duplicate specification of lint {0}",
        name));bug!("duplicate specification of lint {}", name);
290        }
291    }
292
293    /// This lint has been renamed; warn about using the new name and apply the lint.
294    #[track_caller]
295    pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
296        let Some(&Id(target)) = self.by_name.get(new_name) else {
297            ::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);
298        };
299        self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
300    }
301
302    pub fn register_removed(&mut self, name: &str, reason: &str) {
303        self.by_name.insert(name.into(), Removed(reason.into()));
304    }
305
306    pub fn find_lints(&self, lint_name: &str) -> Option<&[LintId]> {
307        match self.by_name.get(lint_name) {
308            Some(Id(lint_id)) => Some(slice::from_ref(lint_id)),
309            Some(Renamed(_, lint_id)) => Some(slice::from_ref(lint_id)),
310            Some(Removed(_)) => None,
311            Some(Ignored) => Some(&[]),
312            None => match self.lint_groups.get(lint_name) {
313                Some(LintGroup { lint_ids, .. }) => Some(lint_ids),
314                None => None,
315            },
316        }
317    }
318
319    /// True if this symbol represents a lint group name.
320    pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
321        {
    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:321",
                        "rustc_lint::context", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/context.rs"),
                        ::tracing_core::__macro_support::Option::Some(321u32),
                        ::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!(
322            "is_lint_group(lint_name={:?}, lint_groups={:?})",
323            lint_name,
324            self.lint_groups.keys().collect::<Vec<_>>()
325        );
326        let lint_name_str = lint_name.as_str();
327        self.lint_groups.contains_key(lint_name_str) || {
328            let warnings_name_str = crate::WARNINGS.name_lower();
329            lint_name_str == warnings_name_str
330        }
331    }
332
333    /// Checks the name of a lint for its existence, and whether it was
334    /// renamed or removed. Generates a `Diag` containing a
335    /// warning for renamed and removed lints. This is over both lint
336    /// names from attributes and those passed on the command line. Since
337    /// it emits non-fatal warnings and there are *two* lint passes that
338    /// inspect attributes, this is only run from the late pass to avoid
339    /// printing duplicate warnings.
340    pub fn check_lint_name(
341        &self,
342        lint_name: &str,
343        tool_name: Option<Symbol>,
344        registered_tools: &RegisteredTools,
345    ) -> CheckLintNameResult<'_> {
346        if let Some(tool_name) = tool_name {
347            // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
348            if tool_name != sym::rustc
349                && tool_name != sym::rustdoc
350                && !registered_tools.contains(&Ident::with_dummy_span(tool_name))
351            {
352                return CheckLintNameResult::NoTool;
353            }
354        }
355
356        let complete_name = if let Some(tool_name) = tool_name {
357            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::{1}", tool_name, lint_name))
    })format!("{tool_name}::{lint_name}")
358        } else {
359            lint_name.to_string()
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(&complete_name) {
364                None => match self.lint_groups.get(&*complete_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 compiler/rustc_lint/src/context.rs:369",
                        "rustc_lint::context", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("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};
                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);
370                        let tool_prefix = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::", tool_name))
    })format!("{tool_name}::");
371
372                        return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
373                            self.no_lint_suggestion(&complete_name, tool_name.as_str())
374                        } else {
375                            // 2. The tool isn't currently running, so no lints will be registered.
376                            // To avoid giving a false positive, ignore all unknown lints.
377                            CheckLintNameResult::MissingTool
378                        };
379                    }
380                    Some(LintGroup { lint_ids, depr, .. }) => {
381                        return if let &Some(LintAlias { name, silent: false }) = depr {
382                            CheckLintNameResult::Tool(lint_ids, Some(name.to_string()))
383                        } else {
384                            CheckLintNameResult::Tool(lint_ids, None)
385                        };
386                    }
387                },
388                Some(Id(id)) => return CheckLintNameResult::Tool(slice::from_ref(id), None),
389                // If the lint was registered as removed or renamed by the lint tool, we don't need
390                // to treat tool_lints and rustc lints different and can use the code below.
391                _ => {}
392            }
393        }
394        match self.by_name.get(&complete_name) {
395            Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()),
396            Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
397            None => match self.lint_groups.get(&*complete_name) {
398                // If neither the lint, nor the lint group exists check if there is a `clippy::`
399                // variant of this lint
400                None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
401                Some(LintGroup { lint_ids, depr, .. }) => {
402                    // Check if the lint group name is deprecated
403                    if let &Some(LintAlias { name, silent: false }) = depr {
404                        CheckLintNameResult::Tool(lint_ids, Some(name.to_string()))
405                    } else {
406                        CheckLintNameResult::Ok(lint_ids)
407                    }
408                }
409            },
410            Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
411            Some(&Ignored) => CheckLintNameResult::Ok(&[]),
412        }
413    }
414
415    fn no_lint_suggestion(&self, lint_name: &str, tool_name: &str) -> CheckLintNameResult<'_> {
416        let name_lower = lint_name.to_lowercase();
417
418        if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_some() {
419            // First check if the lint name is (partly) in upper case instead of lower case...
420            return CheckLintNameResult::NoLint(Some((Symbol::intern(&name_lower), false)));
421        }
422
423        // ...if not, search for lints with a similar name
424        // Note: find_best_match_for_name depends on the sort order of its input vector.
425        // To ensure deterministic output, sort elements of the lint_groups hash map.
426        // Also, never suggest deprecated lint groups.
427        // We will soon sort, so the initial order does not matter.
428        #[allow(rustc::potential_query_instability)]
429        let mut groups: Vec<_> = self
430            .lint_groups
431            .iter()
432            .filter_map(|(k, LintGroup { depr, .. })| depr.is_none().then_some(k))
433            .collect();
434        groups.sort();
435        let groups = groups.iter().map(|k| Symbol::intern(k));
436        let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
437        let names: Vec<Symbol> = groups.chain(lints).collect();
438        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)]))vec![Symbol::intern(&name_lower)];
439        if let Some(stripped) = name_lower.split("::").last() {
440            lookups.push(Symbol::intern(stripped));
441        }
442        let res = find_best_match_for_names(&names, &lookups, None);
443        let is_rustc = res.map_or_else(
444            || false,
445            |s| name_lower.contains("::") && !s.as_str().starts_with(tool_name),
446        );
447        let suggestion = res.map(|s| (s, is_rustc));
448        CheckLintNameResult::NoLint(suggestion)
449    }
450
451    fn check_tool_name_for_backwards_compat(
452        &self,
453        lint_name: &str,
454        tool_name: &str,
455    ) -> CheckLintNameResult<'_> {
456        let complete_name = ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::{1}", tool_name, lint_name))
    })format!("{tool_name}::{lint_name}");
457        match self.by_name.get(&complete_name) {
458            None => match self.lint_groups.get(&*complete_name) {
459                // Now we are sure, that this lint exists nowhere
460                None => self.no_lint_suggestion(lint_name, tool_name),
461                Some(LintGroup { lint_ids, .. }) => {
462                    CheckLintNameResult::Tool(lint_ids, Some(complete_name))
463                }
464            },
465            Some(Id(id)) => CheckLintNameResult::Tool(slice::from_ref(id), Some(complete_name)),
466            Some(other) => {
467                {
    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:467",
                        "rustc_lint::context", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_lint/src/context.rs"),
                        ::tracing_core::__macro_support::Option::Some(467u32),
                        ::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);
468                CheckLintNameResult::NoLint(None)
469            }
470        }
471    }
472}
473
474/// Context for lint checking outside of type inference.
475pub struct LateContext<'tcx> {
476    /// Type context we're checking in.
477    pub tcx: TyCtxt<'tcx>,
478
479    /// Current body, or `None` if outside a body.
480    pub enclosing_body: Option<hir::BodyId>,
481
482    /// Type-checking results for the current body. Access using the `typeck_results`
483    /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand.
484    // FIXME(eddyb) move all the code accessing internal fields like this,
485    // to this module, to avoid exposing it to lint logic.
486    pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
487
488    /// Parameter environment for the item we are in.
489    pub param_env: ty::ParamEnv<'tcx>,
490
491    /// Items accessible from the crate being checked.
492    pub effective_visibilities: &'tcx EffectiveVisibilities,
493
494    pub last_node_with_lint_attrs: hir::HirId,
495
496    /// Generic type parameters in scope for the item we are in.
497    pub generics: Option<&'tcx hir::Generics<'tcx>>,
498
499    /// We are only looking at one module
500    pub only_module: bool,
501}
502
503/// Context for lint checking of the AST, after expansion, before lowering to HIR.
504pub struct EarlyContext<'a> {
505    pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
506    pub buffered: LintBuffer,
507}
508
509pub trait LintContext {
510    fn sess(&self) -> &Session;
511
512    // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
513    // set the span in their `decorate` function (preferably using set_span).
514    /// Emit a lint at the appropriate level, with an optional associated span.
515    ///
516    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
517    #[track_caller]
518    fn opt_span_lint<S: Into<MultiSpan>>(
519        &self,
520        lint: &'static Lint,
521        span: Option<S>,
522        decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
523    );
524
525    // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
526    // set the span in their `decorate` function (preferably using set_span).
527    /// Emit a lint at the appropriate level, with an optional associated span.
528    ///
529    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
530    #[track_caller]
531    fn opt_span_diag_lint<S: Into<MultiSpan>>(
532        &self,
533        lint: &'static Lint,
534        span: Option<S>,
535        decorate: impl for<'a> Diagnostic<'a, ()>,
536    );
537
538    /// Emit a lint at `span` from a lint struct (some type that implements `LintDiagnostic`,
539    /// typically generated by `#[derive(LintDiagnostic)]`).
540    fn emit_span_lint<S: Into<MultiSpan>>(
541        &self,
542        lint: &'static Lint,
543        span: S,
544        decorator: impl for<'a> Diagnostic<'a, ()>,
545    ) {
546        self.opt_span_diag_lint(lint, Some(span), decorator);
547    }
548
549    /// Emit a lint at `span` from a lazily-constructed lint struct (some type that implements
550    /// `LintDiagnostic`, typically generated by `#[derive(LintDiagnostic)]`).
551    fn emit_span_lint_lazy<S: Into<MultiSpan>, L: for<'a> LintDiagnostic<'a, ()>>(
552        &self,
553        lint: &'static Lint,
554        span: S,
555        decorator: impl FnOnce() -> L,
556    ) {
557        self.opt_span_lint(lint, Some(span), |lint| {
558            let decorator = decorator();
559            decorator.decorate_lint(lint);
560        });
561    }
562
563    /// Emit a lint at the appropriate level, with an associated span.
564    ///
565    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
566    #[track_caller]
567    fn span_lint<S: Into<MultiSpan>>(
568        &self,
569        lint: &'static Lint,
570        span: S,
571        decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
572    ) {
573        self.opt_span_lint(lint, Some(span), decorate);
574    }
575
576    /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
577    /// generated by `#[derive(LintDiagnostic)]`).
578    fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> LintDiagnostic<'a, ()>) {
579        self.opt_span_lint(lint, None as Option<Span>, |lint| {
580            decorator.decorate_lint(lint);
581        });
582    }
583
584    /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
585    /// generated by `#[derive(LintDiagnostic)]`).
586    fn emit_diag_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) {
587        self.opt_span_diag_lint(lint, None as Option<Span>, decorator);
588    }
589
590    /// Emit a lint at the appropriate level, with no associated span.
591    ///
592    /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature
593    fn lint(&self, lint: &'static Lint, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)) {
594        self.opt_span_lint(lint, None as Option<Span>, decorate);
595    }
596
597    /// This returns the lint level for the given lint at the current location.
598    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
599
600    /// This function can be used to manually fulfill an expectation. This can
601    /// be used for lints which contain several spans, and should be suppressed,
602    /// if either location was marked with an expectation.
603    ///
604    /// Note that this function should only be called for [`LintExpectationId`]s
605    /// retrieved from the current lint pass. Buffered or manually created ids can
606    /// cause ICEs.
607    fn fulfill_expectation(&self, expectation: LintExpectationId) {
608        // We need to make sure that submitted expectation ids are correctly fulfilled suppressed
609        // and stored between compilation sessions. To not manually do these steps, we simply create
610        // a dummy diagnostic and emit it as usual, which will be suppressed and stored like a
611        // normal expected lint diagnostic.
612        self.sess()
613            .dcx()
614            .struct_expect(
615                "this is a dummy diagnostic, to submit and store an expectation",
616                expectation,
617            )
618            .emit();
619    }
620}
621
622impl<'a> EarlyContext<'a> {
623    pub(crate) fn new(
624        sess: &'a Session,
625        features: &'a Features,
626        lint_added_lints: bool,
627        lint_store: &'a LintStore,
628        registered_tools: &'a RegisteredTools,
629        buffered: LintBuffer,
630    ) -> EarlyContext<'a> {
631        EarlyContext {
632            builder: LintLevelsBuilder::new(
633                sess,
634                features,
635                lint_added_lints,
636                lint_store,
637                registered_tools,
638            ),
639            buffered,
640        }
641    }
642}
643
644impl<'tcx> LintContext for LateContext<'tcx> {
645    /// Gets the overall compiler `Session` object.
646    fn sess(&self) -> &Session {
647        self.tcx.sess
648    }
649
650    fn opt_span_lint<S: Into<MultiSpan>>(
651        &self,
652        lint: &'static Lint,
653        span: Option<S>,
654        decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
655    ) {
656        let hir_id = self.last_node_with_lint_attrs;
657
658        match span {
659            Some(s) => self.tcx.node_span_lint(lint, hir_id, s, decorate),
660            None => self.tcx.node_lint(lint, hir_id, decorate),
661        }
662    }
663
664    fn opt_span_diag_lint<S: Into<MultiSpan>>(
665        &self,
666        lint: &'static Lint,
667        span: Option<S>,
668        decorate: impl for<'a> Diagnostic<'a, ()>,
669    ) {
670        let hir_id = self.last_node_with_lint_attrs;
671
672        match span {
673            Some(s) => self.tcx.emit_node_span_lint(lint, hir_id, s, decorate),
674            None => self.tcx.emit_node_lint(lint, hir_id, decorate),
675        }
676    }
677
678    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
679        self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
680    }
681}
682
683impl LintContext for EarlyContext<'_> {
684    /// Gets the overall compiler `Session` object.
685    fn sess(&self) -> &Session {
686        self.builder.sess()
687    }
688
689    fn opt_span_lint<S: Into<MultiSpan>>(
690        &self,
691        lint: &'static Lint,
692        span: Option<S>,
693        decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
694    ) {
695        self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
696    }
697
698    fn opt_span_diag_lint<S: Into<MultiSpan>>(
699        &self,
700        lint: &'static Lint,
701        span: Option<S>,
702        decorator: impl for<'a> Diagnostic<'a, ()>,
703    ) {
704        self.builder.opt_span_diag_lint(lint, span.map(|s| s.into()), decorator)
705    }
706
707    fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
708        self.builder.lint_level(lint)
709    }
710}
711
712impl<'tcx> LateContext<'tcx> {
713    /// The typing mode of the currently visited node. Use this when
714    /// building a new `InferCtxt`.
715    pub fn typing_mode(&self) -> TypingMode<'tcx> {
716        // FIXME(#132279): In case we're in a body, we should use a typing
717        // mode which reveals the opaque types defined by that body.
718        TypingMode::non_body_analysis()
719    }
720
721    pub fn typing_env(&self) -> TypingEnv<'tcx> {
722        TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env }
723    }
724
725    pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
726        self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty)
727    }
728
729    pub fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
730        self.tcx.type_is_use_cloned_modulo_regions(self.typing_env(), ty)
731    }
732
733    /// Gets the type-checking results for the current body,
734    /// or `None` if outside a body.
735    pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
736        self.cached_typeck_results.get().or_else(|| {
737            self.enclosing_body.map(|body| {
738                let typeck_results = self.tcx.typeck_body(body);
739                self.cached_typeck_results.set(Some(typeck_results));
740                typeck_results
741            })
742        })
743    }
744
745    /// Gets the type-checking results for the current body.
746    /// As this will ICE if called outside bodies, only call when working with
747    /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
748    #[track_caller]
749    pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
750        self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body")
751    }
752
753    /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
754    /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside
755    /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
756    pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
757        match *qpath {
758            hir::QPath::Resolved(_, path) => path.res,
759            hir::QPath::TypeRelative(..) => self
760                .maybe_typeck_results()
761                .filter(|typeck_results| typeck_results.hir_owner == id.owner)
762                .or_else(|| {
763                    self.tcx
764                        .has_typeck_results(id.owner.def_id)
765                        .then(|| self.tcx.typeck(id.owner.def_id))
766                })
767                .and_then(|typeck_results| typeck_results.type_dependent_def(id))
768                .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
769        }
770    }
771
772    /// Gets the absolute path of `def_id` as a vector of `Symbol`.
773    ///
774    /// Note that this is kinda expensive because it has to
775    /// travel the tree and pretty-print. Use sparingly.
776    ///
777    /// If you're trying to match for an item given by its path, use a
778    /// diagnostic item. If you're only interested in given sections, use more
779    /// specific functions, such as [`TyCtxt::crate_name`]
780    ///
781    /// FIXME: It would be great if this could be optimized.
782    ///
783    /// # Examples
784    ///
785    /// ```rust,ignore (no context or def id available)
786    /// let def_path = cx.get_def_path(def_id);
787    /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
788    ///     // The given `def_id` is that of an `Option` type
789    /// }
790    /// ```
791    pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
792        struct LintPathPrinter<'tcx> {
793            tcx: TyCtxt<'tcx>,
794            path: Vec<Symbol>,
795        }
796
797        impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
798            fn tcx(&self) -> TyCtxt<'tcx> {
799                self.tcx
800            }
801
802            fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
803                ::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
804            }
805
806            fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
807                ::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
808            }
809
810            fn print_dyn_existential(
811                &mut self,
812                _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
813            ) -> Result<(), PrintError> {
814                ::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
815            }
816
817            fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
818                ::core::panicking::panic("internal error: entered unreachable code");unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
819            }
820
821            fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
822                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)];
823                Ok(())
824            }
825
826            fn print_path_with_qualified(
827                &mut self,
828                self_ty: Ty<'tcx>,
829                trait_ref: Option<ty::TraitRef<'tcx>>,
830            ) -> Result<(), PrintError> {
831                if trait_ref.is_none()
832                    && let ty::Adt(def, args) = self_ty.kind()
833                {
834                    return self.print_def_path(def.did(), args);
835                }
836
837                // This shouldn't ever be needed, but just in case:
838                {
    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!({
839                    self.path = vec![match trait_ref {
840                        Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
841                        None => Symbol::intern(&format!("<{self_ty}>")),
842                    }];
843                    Ok(())
844                })
845            }
846
847            fn print_path_with_impl(
848                &mut self,
849                print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
850                self_ty: Ty<'tcx>,
851                trait_ref: Option<ty::TraitRef<'tcx>>,
852            ) -> Result<(), PrintError> {
853                print_prefix(self)?;
854
855                // This shouldn't ever be needed, but just in case:
856                self.path.push(match trait_ref {
857                    Some(trait_ref) => {
858                        {
    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!(
859                            "<impl {} for {}>",
860                            trait_ref.print_only_trait_path(),
861                            self_ty
862                        )))
863                    }
864                    None => {
865                        {
    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}>")))
866                    }
867                });
868
869                Ok(())
870            }
871
872            fn print_path_with_simple(
873                &mut self,
874                print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
875                disambiguated_data: &DisambiguatedDefPathData,
876            ) -> Result<(), PrintError> {
877                print_prefix(self)?;
878
879                // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
880                if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
881                    return Ok(());
882                }
883
884                self.path.push(match disambiguated_data.data.get_opt_name() {
885                    Some(sym) => sym,
886                    None => Symbol::intern(&disambiguated_data.data.to_string()),
887                });
888                Ok(())
889            }
890
891            fn print_path_with_generic_args(
892                &mut self,
893                print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
894                _args: &[GenericArg<'tcx>],
895            ) -> Result<(), PrintError> {
896                print_prefix(self)
897            }
898        }
899
900        let mut p = LintPathPrinter { tcx: self.tcx, path: ::alloc::vec::Vec::new()vec![] };
901        p.print_def_path(def_id, &[]).unwrap();
902        p.path
903    }
904
905    /// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
906    /// Do not invoke without first verifying that the type implements the trait.
907    pub fn get_associated_type(
908        &self,
909        self_ty: Ty<'tcx>,
910        trait_id: DefId,
911        name: Symbol,
912    ) -> Option<Ty<'tcx>> {
913        let tcx = self.tcx;
914        tcx.associated_items(trait_id)
915            .find_by_ident_and_kind(tcx, Ident::with_dummy_span(name), ty::AssocTag::Type, trait_id)
916            .and_then(|assoc| {
917                let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]);
918                tcx.try_normalize_erasing_regions(self.typing_env(), proj).ok()
919            })
920    }
921
922    /// Returns the effective precedence of an expression for the purpose of
923    /// rendering diagnostic. This is not the same as the precedence that would
924    /// be used for pretty-printing HIR by rustc_hir_pretty.
925    pub fn precedence(&self, expr: &hir::Expr<'_>) -> ExprPrecedence {
926        let has_attr = |id: hir::HirId| -> bool {
927            for attr in self.tcx.hir_attrs(id) {
928                if attr.span().desugaring_kind().is_none() {
929                    return true;
930                }
931            }
932            false
933        };
934        expr.precedence(&has_attr)
935    }
936
937    /// If the given expression is a local binding, find the initializer expression.
938    /// If that initializer expression is another local binding, find its initializer again.
939    ///
940    /// This process repeats as long as possible (but usually no more than once).
941    /// Type-check adjustments are not taken in account in this function.
942    ///
943    /// Examples:
944    /// ```
945    /// let abc = 1;
946    /// let def = abc + 2;
947    /// //        ^^^^^^^ output
948    /// let def = def;
949    /// dbg!(def);
950    /// //   ^^^ input
951    /// ```
952    pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
953        expr = expr.peel_blocks();
954
955        while let hir::ExprKind::Path(ref qpath) = expr.kind
956            && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
957                Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
958                _ => None,
959            }
960            && let Some(init) = match parent_node {
961                hir::Node::Expr(expr) => Some(expr),
962                hir::Node::LetStmt(hir::LetStmt {
963                    init,
964                    // Binding is immutable, init cannot be re-assigned
965                    pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
966                    ..
967                }) => *init,
968                _ => None,
969            }
970        {
971            expr = init.peel_blocks();
972        }
973        expr
974    }
975
976    /// If the given expression is a local binding, find the initializer expression.
977    /// If that initializer expression is another local or **outside** (`const`/`static`)
978    /// binding, find its initializer again.
979    ///
980    /// This process repeats as long as possible (but usually no more than once).
981    /// Type-check adjustments are not taken in account in this function.
982    ///
983    /// Examples:
984    /// ```
985    /// const ABC: i32 = 1;
986    /// //               ^ output
987    /// let def = ABC;
988    /// dbg!(def);
989    /// //   ^^^ input
990    ///
991    /// // or...
992    /// let abc = 1;
993    /// let def = abc + 2;
994    /// //        ^^^^^^^ output
995    /// dbg!(def);
996    /// //   ^^^ input
997    /// ```
998    pub fn expr_or_init_with_outside_body<'a>(
999        &self,
1000        mut expr: &'a hir::Expr<'tcx>,
1001    ) -> &'a hir::Expr<'tcx> {
1002        expr = expr.peel_blocks();
1003
1004        while let hir::ExprKind::Path(ref qpath) = expr.kind
1005            && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
1006                Res::Local(hir_id) => Some(self.tcx.parent_hir_node(hir_id)),
1007                Res::Def(_, def_id) => self.tcx.hir_get_if_local(def_id),
1008                _ => None,
1009            }
1010            && let Some(init) = match parent_node {
1011                hir::Node::Expr(expr) => Some(expr),
1012                hir::Node::LetStmt(hir::LetStmt {
1013                    init,
1014                    // Binding is immutable, init cannot be re-assigned
1015                    pat: Pat { kind: PatKind::Binding(BindingMode::NONE, ..), .. },
1016                    ..
1017                }) => *init,
1018                hir::Node::Item(item) => match item.kind {
1019                    // FIXME(mgca): figure out how to handle ConstArgKind::Path (or don't but add warning in docs here)
1020                    hir::ItemKind::Const(.., hir::ConstItemRhs::Body(body_id))
1021                    | hir::ItemKind::Static(.., body_id) => Some(self.tcx.hir_body(body_id).value),
1022                    _ => None,
1023                },
1024                _ => None,
1025            }
1026        {
1027            expr = init.peel_blocks();
1028        }
1029        expr
1030    }
1031}
1032
1033impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
1034    #[inline]
1035    fn data_layout(&self) -> &abi::TargetDataLayout {
1036        &self.tcx.data_layout
1037    }
1038}
1039
1040impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
1041    #[inline]
1042    fn tcx(&self) -> TyCtxt<'tcx> {
1043        self.tcx
1044    }
1045}
1046
1047impl<'tcx> ty::layout::HasTypingEnv<'tcx> for LateContext<'tcx> {
1048    #[inline]
1049    fn typing_env(&self) -> ty::TypingEnv<'tcx> {
1050        self.typing_env()
1051    }
1052}
1053
1054impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
1055    type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
1056
1057    #[inline]
1058    fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
1059        err
1060    }
1061}