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