]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint/src/context.rs
Auto merge of #84267 - dtolnay:ptrunit, r=nagisa
[rust.git] / compiler / rustc_lint / src / context.rs
1 //! Implementation of lint checking.
2 //!
3 //! The lint checking is mostly consolidated into one pass which runs
4 //! after all other analyses. Throughout compilation, lint warnings
5 //! can be added via the `add_lint` method on the Session structure. This
6 //! requires a span and an ID of the node that the lint is being added to. The
7 //! lint isn't actually emitted at that time because it is unknown what the
8 //! actual lint level at that location is.
9 //!
10 //! To actually emit lint warnings/errors, a separate pass is used.
11 //! A context keeps track of the current state of all lint levels.
12 //! Upon entering a node of the ast which can modify the lint settings, the
13 //! previous lint state is pushed onto a stack and the ast is then recursed
14 //! upon. As the ast is traversed, this keeps track of the current lint level
15 //! for all lint attributes.
16
17 use self::TargetLint::*;
18
19 use crate::levels::{is_known_lint_tool, LintLevelsBuilder};
20 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
21 use rustc_ast as ast;
22 use rustc_data_structures::fx::FxHashMap;
23 use rustc_data_structures::sync;
24 use rustc_errors::{
25     add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability, SuggestionStyle,
26 };
27 use rustc_hir as hir;
28 use rustc_hir::def::Res;
29 use rustc_hir::def_id::{CrateNum, DefId};
30 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
31 use rustc_middle::lint::LintDiagnosticBuilder;
32 use rustc_middle::middle::privacy::AccessLevels;
33 use rustc_middle::middle::stability;
34 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
35 use rustc_middle::ty::print::with_no_trimmed_paths;
36 use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
37 use rustc_serialize::json::Json;
38 use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec};
39 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
40 use rustc_session::Session;
41 use rustc_span::lev_distance::find_best_match_for_name;
42 use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
43 use rustc_target::abi;
44 use tracing::debug;
45
46 use std::cell::Cell;
47 use std::iter;
48 use std::slice;
49
50 /// Information about the registered lints.
51 ///
52 /// This is basically the subset of `Context` that we can
53 /// build early in the compile pipeline.
54 pub struct LintStore {
55     /// Registered lints.
56     lints: Vec<&'static Lint>,
57
58     /// Constructor functions for each variety of lint pass.
59     ///
60     /// These should only be called once, but since we want to avoid locks or
61     /// interior mutability, we don't enforce this (and lints should, in theory,
62     /// be compatible with being constructed more than once, though not
63     /// necessarily in a sane manner. This is safe though.)
64     pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
65     pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
66     pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
67     /// This is unique in that we construct them per-module, so not once.
68     pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
69
70     /// Lints indexed by name.
71     by_name: FxHashMap<String, TargetLint>,
72
73     /// Map of registered lint groups to what lints they expand to.
74     lint_groups: FxHashMap<&'static str, LintGroup>,
75 }
76
77 /// The target of the `by_name` map, which accounts for renaming/deprecation.
78 #[derive(Debug)]
79 enum TargetLint {
80     /// A direct lint target
81     Id(LintId),
82
83     /// Temporary renaming, used for easing migration pain; see #16545
84     Renamed(String, LintId),
85
86     /// Lint with this name existed previously, but has been removed/deprecated.
87     /// The string argument is the reason for removal.
88     Removed(String),
89
90     /// A lint name that should give no warnings and have no effect.
91     ///
92     /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
93     Ignored,
94 }
95
96 pub enum FindLintError {
97     NotFound,
98     Removed,
99 }
100
101 struct LintAlias {
102     name: &'static str,
103     /// Whether deprecation warnings should be suppressed for this alias.
104     silent: bool,
105 }
106
107 struct LintGroup {
108     lint_ids: Vec<LintId>,
109     from_plugin: bool,
110     depr: Option<LintAlias>,
111 }
112
113 pub enum CheckLintNameResult<'a> {
114     Ok(&'a [LintId]),
115     /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
116     NoLint(Option<Symbol>),
117     /// The lint refers to a tool that has not been registered.
118     NoTool,
119     /// The lint is either renamed or removed. This is the warning
120     /// message, and an optional new name (`None` if removed).
121     Warning(String, Option<String>),
122     /// The lint is from a tool. If the Option is None, then either
123     /// the lint does not exist in the tool or the code was not
124     /// compiled with the tool and therefore the lint was never
125     /// added to the `LintStore`. Otherwise the `LintId` will be
126     /// returned as if it where a rustc lint.
127     Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>),
128 }
129
130 impl LintStore {
131     pub fn new() -> LintStore {
132         LintStore {
133             lints: vec![],
134             pre_expansion_passes: vec![],
135             early_passes: vec![],
136             late_passes: vec![],
137             late_module_passes: vec![],
138             by_name: Default::default(),
139             lint_groups: Default::default(),
140         }
141     }
142
143     pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
144         &self.lints
145     }
146
147     pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
148         self.lint_groups
149             .iter()
150             .filter(|(_, LintGroup { depr, .. })| {
151                 // Don't display deprecated lint groups.
152                 depr.is_none()
153             })
154             .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
155                 (*k, lint_ids.clone(), *from_plugin)
156             })
157             .collect()
158     }
159
160     pub fn register_early_pass(
161         &mut self,
162         pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
163     ) {
164         self.early_passes.push(Box::new(pass));
165     }
166
167     /// Used by clippy.
168     pub fn register_pre_expansion_pass(
169         &mut self,
170         pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
171     ) {
172         self.pre_expansion_passes.push(Box::new(pass));
173     }
174
175     pub fn register_late_pass(
176         &mut self,
177         pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
178     ) {
179         self.late_passes.push(Box::new(pass));
180     }
181
182     pub fn register_late_mod_pass(
183         &mut self,
184         pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
185     ) {
186         self.late_module_passes.push(Box::new(pass));
187     }
188
189     // Helper method for register_early/late_pass
190     pub fn register_lints(&mut self, lints: &[&'static Lint]) {
191         for lint in lints {
192             self.lints.push(lint);
193
194             let id = LintId::of(lint);
195             if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
196                 bug!("duplicate specification of lint {}", lint.name_lower())
197             }
198
199             if let Some(FutureIncompatibleInfo { reason, .. }) = lint.future_incompatible {
200                 if let Some(edition) = reason.edition() {
201                     self.lint_groups
202                         .entry(edition.lint_name())
203                         .or_insert(LintGroup {
204                             lint_ids: vec![],
205                             from_plugin: lint.is_plugin,
206                             depr: None,
207                         })
208                         .lint_ids
209                         .push(id);
210                 } else {
211                     // Lints belonging to the `future_incompatible` lint group are lints where a
212                     // future version of rustc will cause existing code to stop compiling.
213                     // Lints tied to an edition don't count because they are opt-in.
214                     self.lint_groups
215                         .entry("future_incompatible")
216                         .or_insert(LintGroup {
217                             lint_ids: vec![],
218                             from_plugin: lint.is_plugin,
219                             depr: None,
220                         })
221                         .lint_ids
222                         .push(id);
223                 }
224             }
225         }
226     }
227
228     pub fn register_group_alias(&mut self, lint_name: &'static str, alias: &'static str) {
229         self.lint_groups.insert(
230             alias,
231             LintGroup {
232                 lint_ids: vec![],
233                 from_plugin: false,
234                 depr: Some(LintAlias { name: lint_name, silent: true }),
235             },
236         );
237     }
238
239     pub fn register_group(
240         &mut self,
241         from_plugin: bool,
242         name: &'static str,
243         deprecated_name: Option<&'static str>,
244         to: Vec<LintId>,
245     ) {
246         let new = self
247             .lint_groups
248             .insert(name, LintGroup { lint_ids: to, from_plugin, depr: None })
249             .is_none();
250         if let Some(deprecated) = deprecated_name {
251             self.lint_groups.insert(
252                 deprecated,
253                 LintGroup {
254                     lint_ids: vec![],
255                     from_plugin,
256                     depr: Some(LintAlias { name, silent: false }),
257                 },
258             );
259         }
260
261         if !new {
262             bug!("duplicate specification of lint group {}", name);
263         }
264     }
265
266     /// This lint should give no warning and have no effect.
267     ///
268     /// This is used by rustc to avoid warning about old rustdoc lints before rustdoc registers them as tool lints.
269     #[track_caller]
270     pub fn register_ignored(&mut self, name: &str) {
271         if self.by_name.insert(name.to_string(), Ignored).is_some() {
272             bug!("duplicate specification of lint {}", name);
273         }
274     }
275
276     /// This lint has been renamed; warn about using the new name and apply the lint.
277     #[track_caller]
278     pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
279         let target = match self.by_name.get(new_name) {
280             Some(&Id(lint_id)) => lint_id,
281             _ => bug!("invalid lint renaming of {} to {}", old_name, new_name),
282         };
283         self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
284     }
285
286     pub fn register_removed(&mut self, name: &str, reason: &str) {
287         self.by_name.insert(name.into(), Removed(reason.into()));
288     }
289
290     pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
291         match self.by_name.get(lint_name) {
292             Some(&Id(lint_id)) => Ok(vec![lint_id]),
293             Some(&Renamed(_, lint_id)) => Ok(vec![lint_id]),
294             Some(&Removed(_)) => Err(FindLintError::Removed),
295             Some(&Ignored) => Ok(vec![]),
296             None => loop {
297                 return match self.lint_groups.get(lint_name) {
298                     Some(LintGroup { lint_ids, depr, .. }) => {
299                         if let Some(LintAlias { name, .. }) = depr {
300                             lint_name = name;
301                             continue;
302                         }
303                         Ok(lint_ids.clone())
304                     }
305                     None => Err(FindLintError::Removed),
306                 };
307             },
308         }
309     }
310
311     /// Checks the validity of lint names derived from the command line.
312     pub fn check_lint_name_cmdline(
313         &self,
314         sess: &Session,
315         lint_name: &str,
316         level: Level,
317         crate_attrs: &[ast::Attribute],
318     ) {
319         let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
320         if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn {
321             return struct_span_err!(
322                 sess,
323                 DUMMY_SP,
324                 E0602,
325                 "`{}` lint group is not supported with Â´--force-warn´",
326                 crate::WARNINGS.name_lower()
327             )
328             .emit();
329         }
330         let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) {
331             CheckLintNameResult::Ok(_) => None,
332             CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
333             CheckLintNameResult::NoLint(suggestion) => {
334                 let mut err =
335                     struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name);
336
337                 if let Some(suggestion) = suggestion {
338                     err.help(&format!("did you mean: `{}`", suggestion));
339                 }
340
341                 Some(err)
342             }
343             CheckLintNameResult::Tool(result) => match result {
344                 Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
345                     "lint name `{}` is deprecated \
346                      and does not have an effect anymore. \
347                      Use: {}",
348                     lint_name, new_name
349                 ))),
350                 _ => None,
351             },
352             CheckLintNameResult::NoTool => Some(struct_span_err!(
353                 sess,
354                 DUMMY_SP,
355                 E0602,
356                 "unknown lint tool: `{}`",
357                 tool_name.unwrap()
358             )),
359         };
360
361         if let Some(mut db) = db {
362             let msg = format!(
363                 "requested on the command line with `{} {}`",
364                 match level {
365                     Level::Allow => "-A",
366                     Level::Warn => "-W",
367                     Level::ForceWarn => "--force-warn",
368                     Level::Deny => "-D",
369                     Level::Forbid => "-F",
370                 },
371                 lint_name
372             );
373             db.note(&msg);
374             db.emit();
375         }
376     }
377
378     /// True if this symbol represents a lint group name.
379     pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
380         debug!(
381             "is_lint_group(lint_name={:?}, lint_groups={:?})",
382             lint_name,
383             self.lint_groups.keys().collect::<Vec<_>>()
384         );
385         let lint_name_str = &*lint_name.as_str();
386         self.lint_groups.contains_key(&lint_name_str) || {
387             let warnings_name_str = crate::WARNINGS.name_lower();
388             lint_name_str == &*warnings_name_str
389         }
390     }
391
392     /// Checks the name of a lint for its existence, and whether it was
393     /// renamed or removed. Generates a DiagnosticBuilder containing a
394     /// warning for renamed and removed lints. This is over both lint
395     /// names from attributes and those passed on the command line. Since
396     /// it emits non-fatal warnings and there are *two* lint passes that
397     /// inspect attributes, this is only run from the late pass to avoid
398     /// printing duplicate warnings.
399     pub fn check_lint_name(
400         &self,
401         sess: &Session,
402         lint_name: &str,
403         tool_name: Option<Symbol>,
404         crate_attrs: &[ast::Attribute],
405     ) -> CheckLintNameResult<'_> {
406         if let Some(tool_name) = tool_name {
407             if !is_known_lint_tool(tool_name, sess, crate_attrs) {
408                 return CheckLintNameResult::NoTool;
409             }
410         }
411
412         let complete_name = if let Some(tool_name) = tool_name {
413             format!("{}::{}", tool_name, lint_name)
414         } else {
415             lint_name.to_string()
416         };
417         // If the lint was scoped with `tool::` check if the tool lint exists
418         if let Some(tool_name) = tool_name {
419             match self.by_name.get(&complete_name) {
420                 None => match self.lint_groups.get(&*complete_name) {
421                     // If the lint isn't registered, there are two possibilities:
422                     None => {
423                         // 1. The tool is currently running, so this lint really doesn't exist.
424                         // FIXME: should this handle tools that never register a lint, like rustfmt?
425                         tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
426                         let tool_prefix = format!("{}::", tool_name);
427                         return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
428                             self.no_lint_suggestion(&complete_name)
429                         } else {
430                             // 2. The tool isn't currently running, so no lints will be registered.
431                             // To avoid giving a false positive, ignore all unknown lints.
432                             CheckLintNameResult::Tool(Err((None, String::new())))
433                         };
434                     }
435                     Some(LintGroup { lint_ids, .. }) => {
436                         return CheckLintNameResult::Tool(Ok(&lint_ids));
437                     }
438                 },
439                 Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
440                 // If the lint was registered as removed or renamed by the lint tool, we don't need
441                 // to treat tool_lints and rustc lints different and can use the code below.
442                 _ => {}
443             }
444         }
445         match self.by_name.get(&complete_name) {
446             Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
447                 format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
448                 Some(new_name.to_owned()),
449             ),
450             Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
451                 format!("lint `{}` has been removed: {}", complete_name, reason),
452                 None,
453             ),
454             None => match self.lint_groups.get(&*complete_name) {
455                 // If neither the lint, nor the lint group exists check if there is a `clippy::`
456                 // variant of this lint
457                 None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
458                 Some(LintGroup { lint_ids, depr, .. }) => {
459                     // Check if the lint group name is deprecated
460                     if let Some(LintAlias { name, silent }) = depr {
461                         let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
462                         return if *silent {
463                             CheckLintNameResult::Ok(&lint_ids)
464                         } else {
465                             CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string())))
466                         };
467                     }
468                     CheckLintNameResult::Ok(&lint_ids)
469                 }
470             },
471             Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
472             Some(&Ignored) => CheckLintNameResult::Ok(&[]),
473         }
474     }
475
476     fn no_lint_suggestion(&self, lint_name: &str) -> CheckLintNameResult<'_> {
477         let name_lower = lint_name.to_lowercase();
478
479         if lint_name.chars().any(char::is_uppercase) && self.find_lints(&name_lower).is_ok() {
480             // First check if the lint name is (partly) in upper case instead of lower case...
481             return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower)));
482         }
483         // ...if not, search for lints with a similar name
484         let groups = self.lint_groups.keys().copied().map(Symbol::intern);
485         let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
486         let names: Vec<Symbol> = groups.chain(lints).collect();
487         let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None);
488         CheckLintNameResult::NoLint(suggestion)
489     }
490
491     fn check_tool_name_for_backwards_compat(
492         &self,
493         lint_name: &str,
494         tool_name: &str,
495     ) -> CheckLintNameResult<'_> {
496         let complete_name = format!("{}::{}", tool_name, lint_name);
497         match self.by_name.get(&complete_name) {
498             None => match self.lint_groups.get(&*complete_name) {
499                 // Now we are sure, that this lint exists nowhere
500                 None => self.no_lint_suggestion(lint_name),
501                 Some(LintGroup { lint_ids, depr, .. }) => {
502                     // Reaching this would be weird, but let's cover this case anyway
503                     if let Some(LintAlias { name, silent }) = depr {
504                         let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
505                         return if *silent {
506                             CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
507                         } else {
508                             CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string())))
509                         };
510                     }
511                     CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
512                 }
513             },
514             Some(&Id(ref id)) => {
515                 CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
516             }
517             Some(other) => {
518                 tracing::debug!("got renamed lint {:?}", other);
519                 CheckLintNameResult::NoLint(None)
520             }
521         }
522     }
523 }
524
525 /// Context for lint checking after type checking.
526 pub struct LateContext<'tcx> {
527     /// Type context we're checking in.
528     pub tcx: TyCtxt<'tcx>,
529
530     /// Current body, or `None` if outside a body.
531     pub enclosing_body: Option<hir::BodyId>,
532
533     /// Type-checking results for the current body. Access using the `typeck_results`
534     /// and `maybe_typeck_results` methods, which handle querying the typeck results on demand.
535     // FIXME(eddyb) move all the code accessing internal fields like this,
536     // to this module, to avoid exposing it to lint logic.
537     pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
538
539     /// Parameter environment for the item we are in.
540     pub param_env: ty::ParamEnv<'tcx>,
541
542     /// Items accessible from the crate being checked.
543     pub access_levels: &'tcx AccessLevels,
544
545     /// The store of registered lints and the lint levels.
546     pub lint_store: &'tcx LintStore,
547
548     pub last_node_with_lint_attrs: hir::HirId,
549
550     /// Generic type parameters in scope for the item we are in.
551     pub generics: Option<&'tcx hir::Generics<'tcx>>,
552
553     /// We are only looking at one module
554     pub only_module: bool,
555 }
556
557 /// Context for lint checking of the AST, after expansion, before lowering to
558 /// HIR.
559 pub struct EarlyContext<'a> {
560     /// Type context we're checking in.
561     pub sess: &'a Session,
562
563     /// The crate being checked.
564     pub krate: &'a ast::Crate,
565
566     pub builder: LintLevelsBuilder<'a>,
567
568     /// The store of registered lints and the lint levels.
569     pub lint_store: &'a LintStore,
570
571     pub buffered: LintBuffer,
572 }
573
574 pub trait LintPassObject: Sized {}
575
576 impl LintPassObject for EarlyLintPassObject {}
577
578 impl LintPassObject for LateLintPassObject {}
579
580 pub trait LintContext: Sized {
581     type PassObject: LintPassObject;
582
583     fn sess(&self) -> &Session;
584     fn lints(&self) -> &LintStore;
585
586     fn lookup_with_diagnostics(
587         &self,
588         lint: &'static Lint,
589         span: Option<impl Into<MultiSpan>>,
590         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
591         diagnostic: BuiltinLintDiagnostics,
592     ) {
593         self.lookup(lint, span, |lint| {
594             // We first generate a blank diagnostic.
595             let mut db = lint.build("");
596
597             // Now, set up surrounding context.
598             let sess = self.sess();
599             match diagnostic {
600                 BuiltinLintDiagnostics::Normal => (),
601                 BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
602                     let (sugg, app) = match sess.source_map().span_to_snippet(span) {
603                         Ok(s) if is_global => {
604                             (format!("dyn ({})", s), Applicability::MachineApplicable)
605                         }
606                         Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
607                         Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
608                     };
609                     db.span_suggestion(span, "use `dyn`", sugg, app);
610                 }
611                 BuiltinLintDiagnostics::AbsPathWithModule(span) => {
612                     let (sugg, app) = match sess.source_map().span_to_snippet(span) {
613                         Ok(ref s) => {
614                             // FIXME(Manishearth) ideally the emitting code
615                             // can tell us whether or not this is global
616                             let opt_colon =
617                                 if s.trim_start().starts_with("::") { "" } else { "::" };
618
619                             (format!("crate{}{}", opt_colon, s), Applicability::MachineApplicable)
620                         }
621                         Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
622                     };
623                     db.span_suggestion(span, "use `crate`", sugg, app);
624                 }
625                 BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
626                     db.span_label(
627                         span,
628                         "names from parent modules are not accessible without an explicit import",
629                     );
630                 }
631                 BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
632                     span_def,
633                 ) => {
634                     db.span_note(span_def, "the macro is defined here");
635                 }
636                 BuiltinLintDiagnostics::ElidedLifetimesInPaths(
637                     n,
638                     path_span,
639                     incl_angl_brckt,
640                     insertion_span,
641                     anon_lts,
642                 ) => {
643                     add_elided_lifetime_in_path_suggestion(
644                         sess.source_map(),
645                         &mut db,
646                         n,
647                         path_span,
648                         incl_angl_brckt,
649                         insertion_span,
650                         anon_lts,
651                     );
652                 }
653                 BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
654                     db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
655                 }
656                 BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
657                     if !replaces.is_empty() {
658                         db.tool_only_multipart_suggestion(
659                             &message,
660                             replaces,
661                             Applicability::MachineApplicable,
662                         );
663                     }
664                 }
665                 BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
666                     for (span, is_imported) in spans {
667                         let introduced = if is_imported { "imported" } else { "defined" };
668                         db.span_label(
669                             span,
670                             format!("the item `{}` is already {} here", ident, introduced),
671                         );
672                     }
673                 }
674                 BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
675                     stability::deprecation_suggestion(&mut db, "macro", suggestion, span)
676                 }
677                 BuiltinLintDiagnostics::UnusedDocComment(span) => {
678                     db.span_label(span, "rustdoc does not generate documentation for macro invocations");
679                     db.help("to document an item produced by a macro, \
680                                   the macro must produce the documentation as part of its expansion");
681                 }
682                 BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
683                     db.span_suggestion(span, "remove `mut` from the parameter", ident.to_string(), Applicability::MachineApplicable);
684                 }
685                 BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
686                     db.span_label(span, "ABI should be specified here");
687                     db.help(&format!("the default ABI is {}", default_abi.name()));
688                 }
689                 BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
690                     db.span_label(span, "the attribute is introduced here");
691                 }
692                 BuiltinLintDiagnostics::ExternDepSpec(krate, loc) => {
693                     let json = match loc {
694                         ExternDepSpec::Json(json) => {
695                             db.help(&format!("remove unnecessary dependency `{}`", krate));
696                             json
697                         }
698                         ExternDepSpec::Raw(raw) => {
699                             db.help(&format!("remove unnecessary dependency `{}` at `{}`", krate, raw));
700                             db.span_suggestion_with_style(
701                                 DUMMY_SP,
702                                 "raw extern location",
703                                 raw.clone(),
704                                 Applicability::Unspecified,
705                                 SuggestionStyle::CompletelyHidden,
706                             );
707                             Json::String(raw)
708                         }
709                     };
710                     db.tool_only_suggestion_with_metadata(
711                         "json extern location",
712                         Applicability::Unspecified,
713                         json
714                     );
715                 }
716                 BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
717                     db.note(&note);
718                 }
719                 BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
720                     db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable);
721                 }
722                 BuiltinLintDiagnostics::ReservedPrefix(span) => {
723                     db.span_label(span, "unknown prefix");
724                     db.span_suggestion_verbose(
725                         span.shrink_to_hi(),
726                         "insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
727                         " ".into(),
728                         Applicability::MachineApplicable,
729                     );
730                 }
731                 BuiltinLintDiagnostics::UnusedBuiltinAttribute {
732                     attr_name,
733                     macro_name,
734                     invoc_span
735                 } => {
736                     db.span_note(
737                         invoc_span,
738                         &format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
739                     );
740                 }
741                 BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
742                     if is_trailing {
743                         db.note("macro invocations at the end of a block are treated as expressions");
744                         db.note(&format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
745                     }
746                 }
747                 BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
748                     db.multipart_suggestion(
749                         "wrap this expression in parentheses",
750                         vec![(span.shrink_to_lo(), "(".to_string()),
751                              (span.shrink_to_hi(), ")".to_string())],
752                         Applicability::MachineApplicable
753                     );
754                 }
755                 BuiltinLintDiagnostics::NamedAsmLabel(help) => {
756                     db.help(&help);
757                     db.note("see the asm section of the unstable book <https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels> for more information");
758                 }
759             }
760             // Rewrap `db`, and pass control to the user.
761             decorate(LintDiagnosticBuilder::new(db));
762         });
763     }
764
765     // FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
766     // set the span in their `decorate` function (preferably using set_span).
767     fn lookup<S: Into<MultiSpan>>(
768         &self,
769         lint: &'static Lint,
770         span: Option<S>,
771         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
772     );
773
774     fn struct_span_lint<S: Into<MultiSpan>>(
775         &self,
776         lint: &'static Lint,
777         span: S,
778         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
779     ) {
780         self.lookup(lint, Some(span), decorate);
781     }
782     /// Emit a lint at the appropriate level, with no associated span.
783     fn lint(&self, lint: &'static Lint, decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>)) {
784         self.lookup(lint, None as Option<Span>, decorate);
785     }
786 }
787
788 impl<'a> EarlyContext<'a> {
789     pub fn new(
790         sess: &'a Session,
791         lint_store: &'a LintStore,
792         krate: &'a ast::Crate,
793         crate_attrs: &'a [ast::Attribute],
794         buffered: LintBuffer,
795         warn_about_weird_lints: bool,
796     ) -> EarlyContext<'a> {
797         EarlyContext {
798             sess,
799             krate,
800             lint_store,
801             builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, crate_attrs),
802             buffered,
803         }
804     }
805 }
806
807 impl LintContext for LateContext<'_> {
808     type PassObject = LateLintPassObject;
809
810     /// Gets the overall compiler `Session` object.
811     fn sess(&self) -> &Session {
812         &self.tcx.sess
813     }
814
815     fn lints(&self) -> &LintStore {
816         &*self.lint_store
817     }
818
819     fn lookup<S: Into<MultiSpan>>(
820         &self,
821         lint: &'static Lint,
822         span: Option<S>,
823         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
824     ) {
825         let hir_id = self.last_node_with_lint_attrs;
826
827         match span {
828             Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate),
829             None => self.tcx.struct_lint_node(lint, hir_id, decorate),
830         }
831     }
832 }
833
834 impl LintContext for EarlyContext<'_> {
835     type PassObject = EarlyLintPassObject;
836
837     /// Gets the overall compiler `Session` object.
838     fn sess(&self) -> &Session {
839         &self.sess
840     }
841
842     fn lints(&self) -> &LintStore {
843         &*self.lint_store
844     }
845
846     fn lookup<S: Into<MultiSpan>>(
847         &self,
848         lint: &'static Lint,
849         span: Option<S>,
850         decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a>),
851     ) {
852         self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
853     }
854 }
855
856 impl<'tcx> LateContext<'tcx> {
857     /// Gets the type-checking results for the current body,
858     /// or `None` if outside a body.
859     pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
860         self.cached_typeck_results.get().or_else(|| {
861             self.enclosing_body.map(|body| {
862                 let typeck_results = self.tcx.typeck_body(body);
863                 self.cached_typeck_results.set(Some(typeck_results));
864                 typeck_results
865             })
866         })
867     }
868
869     /// Gets the type-checking results for the current body.
870     /// As this will ICE if called outside bodies, only call when working with
871     /// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
872     #[track_caller]
873     pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
874         self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body")
875     }
876
877     /// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.
878     /// Unlike `.typeck_results().qpath_res(qpath, id)`, this can be used even outside
879     /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing.
880     pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
881         match *qpath {
882             hir::QPath::Resolved(_, ref path) => path.res,
883             hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
884                 .maybe_typeck_results()
885                 .filter(|typeck_results| typeck_results.hir_owner == id.owner)
886                 .or_else(|| {
887                     if self.tcx.has_typeck_results(id.owner.to_def_id()) {
888                         Some(self.tcx.typeck(id.owner))
889                     } else {
890                         None
891                     }
892                 })
893                 .and_then(|typeck_results| typeck_results.type_dependent_def(id))
894                 .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
895         }
896     }
897
898     /// Check if a `DefId`'s path matches the given absolute type path usage.
899     ///
900     /// Anonymous scopes such as `extern` imports are matched with `kw::Empty`;
901     /// inherent `impl` blocks are matched with the name of the type.
902     ///
903     /// Instead of using this method, it is often preferable to instead use
904     /// `rustc_diagnostic_item` or a `lang_item`. This is less prone to errors
905     /// as paths get invalidated if the target definition moves.
906     ///
907     /// # Examples
908     ///
909     /// ```rust,ignore (no context or def id available)
910     /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
911     ///     // The given `def_id` is that of an `Option` type
912     /// }
913     /// ```
914     ///
915     /// Used by clippy, but should be replaced by diagnostic items eventually.
916     pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
917         let names = self.get_def_path(def_id);
918
919         names.len() == path.len() && iter::zip(names, path).all(|(a, &b)| a == b)
920     }
921
922     /// Gets the absolute path of `def_id` as a vector of `Symbol`.
923     ///
924     /// # Examples
925     ///
926     /// ```rust,ignore (no context or def id available)
927     /// let def_path = cx.get_def_path(def_id);
928     /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
929     ///     // The given `def_id` is that of an `Option` type
930     /// }
931     /// ```
932     pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
933         pub struct AbsolutePathPrinter<'tcx> {
934             pub tcx: TyCtxt<'tcx>,
935         }
936
937         impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
938             type Error = !;
939
940             type Path = Vec<Symbol>;
941             type Region = ();
942             type Type = ();
943             type DynExistential = ();
944             type Const = ();
945
946             fn tcx(&self) -> TyCtxt<'tcx> {
947                 self.tcx
948             }
949
950             fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
951                 Ok(())
952             }
953
954             fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
955                 Ok(())
956             }
957
958             fn print_dyn_existential(
959                 self,
960                 _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
961             ) -> Result<Self::DynExistential, Self::Error> {
962                 Ok(())
963             }
964
965             fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
966                 Ok(())
967             }
968
969             fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
970                 Ok(vec![self.tcx.crate_name(cnum)])
971             }
972
973             fn path_qualified(
974                 self,
975                 self_ty: Ty<'tcx>,
976                 trait_ref: Option<ty::TraitRef<'tcx>>,
977             ) -> Result<Self::Path, Self::Error> {
978                 if trait_ref.is_none() {
979                     if let ty::Adt(def, substs) = self_ty.kind() {
980                         return self.print_def_path(def.did, substs);
981                     }
982                 }
983
984                 // This shouldn't ever be needed, but just in case:
985                 with_no_trimmed_paths(|| {
986                     Ok(vec![match trait_ref {
987                         Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
988                         None => Symbol::intern(&format!("<{}>", self_ty)),
989                     }])
990                 })
991             }
992
993             fn path_append_impl(
994                 self,
995                 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
996                 _disambiguated_data: &DisambiguatedDefPathData,
997                 self_ty: Ty<'tcx>,
998                 trait_ref: Option<ty::TraitRef<'tcx>>,
999             ) -> Result<Self::Path, Self::Error> {
1000                 let mut path = print_prefix(self)?;
1001
1002                 // This shouldn't ever be needed, but just in case:
1003                 path.push(match trait_ref {
1004                     Some(trait_ref) => with_no_trimmed_paths(|| {
1005                         Symbol::intern(&format!(
1006                             "<impl {} for {}>",
1007                             trait_ref.print_only_trait_path(),
1008                             self_ty
1009                         ))
1010                     }),
1011                     None => {
1012                         with_no_trimmed_paths(|| Symbol::intern(&format!("<impl {}>", self_ty)))
1013                     }
1014                 });
1015
1016                 Ok(path)
1017             }
1018
1019             fn path_append(
1020                 self,
1021                 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
1022                 disambiguated_data: &DisambiguatedDefPathData,
1023             ) -> Result<Self::Path, Self::Error> {
1024                 let mut path = print_prefix(self)?;
1025
1026                 // Skip `::{{constructor}}` on tuple/unit structs.
1027                 if let DefPathData::Ctor = disambiguated_data.data {
1028                     return Ok(path);
1029                 }
1030
1031                 path.push(Symbol::intern(&disambiguated_data.data.to_string()));
1032                 Ok(path)
1033             }
1034
1035             fn path_generic_args(
1036                 self,
1037                 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
1038                 _args: &[GenericArg<'tcx>],
1039             ) -> Result<Self::Path, Self::Error> {
1040                 print_prefix(self)
1041             }
1042         }
1043
1044         AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
1045     }
1046 }
1047
1048 impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
1049     #[inline]
1050     fn data_layout(&self) -> &abi::TargetDataLayout {
1051         &self.tcx.data_layout
1052     }
1053 }
1054
1055 impl<'tcx> ty::layout::HasTyCtxt<'tcx> for LateContext<'tcx> {
1056     #[inline]
1057     fn tcx(&self) -> TyCtxt<'tcx> {
1058         self.tcx
1059     }
1060 }
1061
1062 impl<'tcx> ty::layout::HasParamEnv<'tcx> for LateContext<'tcx> {
1063     #[inline]
1064     fn param_env(&self) -> ty::ParamEnv<'tcx> {
1065         self.param_env
1066     }
1067 }
1068
1069 impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
1070     type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
1071
1072     #[inline]
1073     fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
1074         err
1075     }
1076 }
1077
1078 pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
1079     match lint_name.split_once("::") {
1080         Some((tool_name, lint_name)) => {
1081             let tool_name = Symbol::intern(tool_name);
1082
1083             (Some(tool_name), lint_name)
1084         }
1085         None => (None, lint_name),
1086     }
1087 }