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