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