Skip to main content

rustc_lint/
context.rs

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