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