1 //! Implementation of lint checking.
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.
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.
17 use self::TargetLint::*;
20 use crate::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
21 use crate::hir::intravisit as hir_visit;
22 use crate::hir::intravisit::Visitor;
23 use crate::hir::map::{definitions::DisambiguatedDefPathData, DefPathData};
24 use crate::lint::{EarlyLintPass, LateLintPass, EarlyLintPassObject, LateLintPassObject};
25 use crate::lint::{Level, Lint, LintId, LintPass, LintBuffer, FutureIncompatibleInfo};
26 use crate::lint::builtin::BuiltinLintDiagnostics;
27 use crate::lint::levels::{LintLevelSets, LintLevelsBuilder};
28 use crate::middle::privacy::AccessLevels;
29 use crate::session::Session;
30 use crate::ty::{self, print::Printer, subst::GenericArg, TyCtxt, Ty};
31 use crate::ty::layout::{LayoutError, LayoutOf, TyLayout};
32 use crate::util::nodemap::FxHashMap;
33 use crate::util::common::time;
35 use errors::DiagnosticBuilder;
37 use rustc_data_structures::sync::{self, ParallelIterator, join, par_iter};
39 use syntax::util::lev_distance::find_best_match_for_name;
40 use syntax::visit as ast_visit;
41 use syntax_pos::{MultiSpan, Span, symbol::Symbol};
43 use rustc_error_codes::*;
45 /// Information about the registered lints.
47 /// This is basically the subset of `Context` that we can
48 /// build early in the compile pipeline.
49 pub struct LintStore {
51 lints: Vec<&'static Lint>,
53 /// Constructor functions for each variety of lint pass.
55 /// These should only be called once, but since we want to avoid locks or
56 /// interior mutability, we don't enforce this (and lints should, in theory,
57 /// be compatible with being constructed more than once, though not
58 /// necessarily in a sane manner. This is safe though.)
59 pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
60 early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
61 late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
62 /// This is unique in that we construct them per-module, so not once.
63 late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
65 /// Lints indexed by name.
66 by_name: FxHashMap<String, TargetLint>,
68 /// Map of registered lint groups to what lints they expand to.
69 lint_groups: FxHashMap<&'static str, LintGroup>,
72 /// Lints that are buffered up early on in the `Session` before the
73 /// `LintLevels` is calculated
74 #[derive(PartialEq, Debug)]
75 pub struct BufferedEarlyLint {
77 pub ast_id: ast::NodeId,
80 pub diagnostic: BuiltinLintDiagnostics,
83 /// The target of the `by_name` map, which accounts for renaming/deprecation.
85 /// A direct lint target
88 /// Temporary renaming, used for easing migration pain; see #16545
89 Renamed(String, LintId),
91 /// Lint with this name existed previously, but has been removed/deprecated.
92 /// The string argument is the reason for removal.
96 pub enum FindLintError {
103 /// Whether deprecation warnings should be suppressed for this alias.
108 lint_ids: Vec<LintId>,
110 depr: Option<LintAlias>,
113 pub enum CheckLintNameResult<'a> {
115 /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name.
116 NoLint(Option<Symbol>),
117 /// The lint is either renamed or removed. This is the warning
118 /// message, and an optional new name (`None` if removed).
119 Warning(String, Option<String>),
120 /// The lint is from a tool. If the Option is None, then either
121 /// the lint does not exist in the tool or the code was not
122 /// compiled with the tool and therefore the lint was never
123 /// added to the `LintStore`. Otherwise the `LintId` will be
124 /// returned as if it where a rustc lint.
125 Tool(Result<&'a [LintId], (Option<&'a [LintId]>, String)>),
129 pub fn new() -> LintStore {
132 pre_expansion_passes: vec![],
133 early_passes: vec![],
135 late_module_passes: vec![],
136 by_name: Default::default(),
137 lint_groups: Default::default(),
141 pub fn get_lints<'t>(&'t self) -> &'t [&'static Lint] {
145 pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
146 self.lint_groups.iter()
147 .filter(|(_, LintGroup { depr, .. })| {
148 // Don't display deprecated lint groups.
151 .map(|(k, LintGroup { lint_ids, from_plugin, .. })| {
152 (*k, lint_ids.clone(), *from_plugin)
157 pub fn register_early_pass(
159 pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync
161 self.early_passes.push(Box::new(pass));
164 pub fn register_pre_expansion_pass(
166 pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
168 self.pre_expansion_passes.push(Box::new(pass));
171 pub fn register_late_pass(
173 pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
175 self.late_passes.push(Box::new(pass));
178 pub fn register_late_mod_pass(
180 pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
182 self.late_module_passes.push(Box::new(pass));
185 // Helper method for register_early/late_pass
186 pub fn register_lints(&mut self, lints: &[&'static Lint]) {
188 self.lints.push(lint);
190 let id = LintId::of(lint);
191 if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
192 bug!("duplicate specification of lint {}", lint.name_lower())
195 if let Some(FutureIncompatibleInfo { edition, .. }) = lint.future_incompatible {
196 if let Some(edition) = edition {
197 self.lint_groups.entry(edition.lint_name())
198 .or_insert(LintGroup {
200 from_plugin: lint.is_plugin,
206 self.lint_groups.entry("future_incompatible")
207 .or_insert(LintGroup {
209 from_plugin: lint.is_plugin,
217 pub fn register_group_alias(
219 lint_name: &'static str,
222 self.lint_groups.insert(alias, LintGroup {
225 depr: Some(LintAlias { name: lint_name, silent: true }),
229 pub fn register_group(
233 deprecated_name: Option<&'static str>,
238 .insert(name, LintGroup {
244 if let Some(deprecated) = deprecated_name {
245 self.lint_groups.insert(deprecated, LintGroup {
248 depr: Some(LintAlias { name, silent: false }),
253 bug!("duplicate specification of lint group {}", name);
257 pub fn register_renamed(&mut self, old_name: &str, new_name: &str) {
258 let target = match self.by_name.get(new_name) {
259 Some(&Id(lint_id)) => lint_id.clone(),
260 _ => bug!("invalid lint renaming of {} to {}", old_name, new_name)
262 self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
265 pub fn register_removed(&mut self, name: &str, reason: &str) {
266 self.by_name.insert(name.into(), Removed(reason.into()));
269 pub fn find_lints(&self, mut lint_name: &str) -> Result<Vec<LintId>, FindLintError> {
270 match self.by_name.get(lint_name) {
271 Some(&Id(lint_id)) => Ok(vec![lint_id]),
272 Some(&Renamed(_, lint_id)) => {
275 Some(&Removed(_)) => {
276 Err(FindLintError::Removed)
280 return match self.lint_groups.get(lint_name) {
281 Some(LintGroup {lint_ids, depr, .. }) => {
282 if let Some(LintAlias { name, .. }) = depr {
288 None => Err(FindLintError::Removed)
295 /// Checks the validity of lint names derived from the command line
296 pub fn check_lint_name_cmdline(&self,
300 let db = match self.check_lint_name(lint_name, None) {
301 CheckLintNameResult::Ok(_) => None,
302 CheckLintNameResult::Warning(ref msg, _) => {
303 Some(sess.struct_warn(msg))
305 CheckLintNameResult::NoLint(suggestion) => {
306 let mut err = struct_err!(sess, E0602, "unknown lint: `{}`", lint_name);
308 if let Some(suggestion) = suggestion {
309 err.help(&format!("did you mean: `{}`", suggestion));
314 CheckLintNameResult::Tool(result) => match result {
315 Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
316 "lint name `{}` is deprecated \
317 and does not have an effect anymore. \
325 if let Some(mut db) = db {
326 let msg = format!("requested on the command line with `{} {}`",
328 Level::Allow => "-A",
331 Level::Forbid => "-F",
339 /// Checks the name of a lint for its existence, and whether it was
340 /// renamed or removed. Generates a DiagnosticBuilder containing a
341 /// warning for renamed and removed lints. This is over both lint
342 /// names from attributes and those passed on the command line. Since
343 /// it emits non-fatal warnings and there are *two* lint passes that
344 /// inspect attributes, this is only run from the late pass to avoid
345 /// printing duplicate warnings.
346 pub fn check_lint_name(
349 tool_name: Option<Symbol>,
350 ) -> CheckLintNameResult<'_> {
351 let complete_name = if let Some(tool_name) = tool_name {
352 format!("{}::{}", tool_name, lint_name)
354 lint_name.to_string()
356 // If the lint was scoped with `tool::` check if the tool lint exists
357 if let Some(_) = tool_name {
358 match self.by_name.get(&complete_name) {
359 None => match self.lint_groups.get(&*complete_name) {
360 None => return CheckLintNameResult::Tool(Err((None, String::new()))),
361 Some(LintGroup { lint_ids, .. }) => {
362 return CheckLintNameResult::Tool(Ok(&lint_ids));
365 Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
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.
371 match self.by_name.get(&complete_name) {
372 Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
374 "lint `{}` has been renamed to `{}`",
375 complete_name, new_name
377 Some(new_name.to_owned()),
379 Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
380 format!("lint `{}` has been removed: `{}`", complete_name, reason),
383 None => match self.lint_groups.get(&*complete_name) {
384 // If neither the lint, nor the lint group exists check if there is a `clippy::`
385 // variant of this lint
386 None => self.check_tool_name_for_backwards_compat(&complete_name, "clippy"),
387 Some(LintGroup { lint_ids, depr, .. }) => {
388 // Check if the lint group name is deprecated
389 if let Some(LintAlias { name, silent }) = depr {
390 let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
392 CheckLintNameResult::Ok(&lint_ids)
394 CheckLintNameResult::Tool(Err((
400 CheckLintNameResult::Ok(&lint_ids)
403 Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
407 fn check_tool_name_for_backwards_compat(
411 ) -> CheckLintNameResult<'_> {
412 let complete_name = format!("{}::{}", tool_name, lint_name);
413 match self.by_name.get(&complete_name) {
414 None => match self.lint_groups.get(&*complete_name) {
415 // Now we are sure, that this lint exists nowhere
417 let symbols = self.by_name.keys()
418 .map(|name| Symbol::intern(&name))
419 .collect::<Vec<_>>();
422 find_best_match_for_name(symbols.iter(), &lint_name.to_lowercase(), None);
424 CheckLintNameResult::NoLint(suggestion)
426 Some(LintGroup { lint_ids, depr, .. }) => {
427 // Reaching this would be weird, but let's cover this case anyway
428 if let Some(LintAlias { name, silent }) = depr {
429 let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap();
431 CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
433 CheckLintNameResult::Tool(Err((
439 CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
442 Some(&Id(ref id)) => {
443 CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
445 _ => CheckLintNameResult::NoLint(None),
450 /// Context for lint checking after type checking.
451 pub struct LateContext<'a, 'tcx> {
452 /// Type context we're checking in.
453 pub tcx: TyCtxt<'tcx>,
455 /// Side-tables for the body we are in.
456 // FIXME: Make this lazy to avoid running the TypeckTables query?
457 pub tables: &'a ty::TypeckTables<'tcx>,
459 /// Parameter environment for the item we are in.
460 pub param_env: ty::ParamEnv<'tcx>,
462 /// Items accessible from the crate being checked.
463 pub access_levels: &'a AccessLevels,
465 /// The store of registered lints and the lint levels.
466 lint_store: &'tcx LintStore,
468 last_node_with_lint_attrs: hir::HirId,
470 /// Generic type parameters in scope for the item we are in.
471 pub generics: Option<&'tcx hir::Generics>,
473 /// We are only looking at one module
477 pub struct LateContextAndPass<'a, 'tcx, T: LateLintPass<'a, 'tcx>> {
478 context: LateContext<'a, 'tcx>,
482 /// Context for lint checking of the AST, after expansion, before lowering to
484 pub struct EarlyContext<'a> {
485 /// Type context we're checking in.
486 pub sess: &'a Session,
488 /// The crate being checked.
489 pub krate: &'a ast::Crate,
491 builder: LintLevelsBuilder<'a>,
493 /// The store of registered lints and the lint levels.
494 lint_store: &'a LintStore,
496 buffered: LintBuffer,
499 pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
500 context: EarlyContext<'a>,
504 pub trait LintPassObject: Sized {}
506 impl LintPassObject for EarlyLintPassObject {}
508 impl LintPassObject for LateLintPassObject {}
510 pub trait LintContext: Sized {
511 type PassObject: LintPassObject;
513 fn sess(&self) -> &Session;
514 fn lints(&self) -> &LintStore;
516 fn lookup_and_emit<S: Into<MultiSpan>>(&self,
520 self.lookup(lint, span, msg).emit();
523 fn lookup_and_emit_with_diagnostics<S: Into<MultiSpan>>(&self,
527 diagnostic: BuiltinLintDiagnostics) {
528 let mut db = self.lookup(lint, span, msg);
529 diagnostic.run(self.sess(), &mut db);
533 fn lookup<S: Into<MultiSpan>>(&self,
537 -> DiagnosticBuilder<'_>;
539 /// Emit a lint at the appropriate level, for a particular span.
540 fn span_lint<S: Into<MultiSpan>>(&self, lint: &'static Lint, span: S, msg: &str) {
541 self.lookup_and_emit(lint, Some(span), msg);
544 fn struct_span_lint<S: Into<MultiSpan>>(&self,
548 -> DiagnosticBuilder<'_> {
549 self.lookup(lint, Some(span), msg)
552 /// Emit a lint and note at the appropriate level, for a particular span.
553 fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str,
554 note_span: Span, note: &str) {
555 let mut err = self.lookup(lint, Some(span), msg);
556 if note_span == span {
559 err.span_note(note_span, note);
564 /// Emit a lint and help at the appropriate level, for a particular span.
565 fn span_lint_help(&self, lint: &'static Lint, span: Span,
566 msg: &str, help: &str) {
567 let mut err = self.lookup(lint, Some(span), msg);
568 self.span_lint(lint, span, msg);
569 err.span_help(span, help);
573 /// Emit a lint at the appropriate level, with no associated span.
574 fn lint(&self, lint: &'static Lint, msg: &str) {
575 self.lookup_and_emit(lint, None as Option<Span>, msg);
580 impl<'a> EarlyContext<'a> {
583 lint_store: &'a LintStore,
584 krate: &'a ast::Crate,
585 buffered: LintBuffer,
586 warn_about_weird_lints: bool,
587 ) -> EarlyContext<'a> {
592 builder: LintLevelSets::builder(sess, warn_about_weird_lints, lint_store),
598 macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
599 $cx.pass.$f(&$cx.context, $($args),*);
602 macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
603 $cx.pass.$f(&$cx.context, $($args),*);
606 impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
607 fn check_id(&mut self, id: ast::NodeId) {
608 for early_lint in self.context.buffered.take(id) {
609 self.context.lookup_and_emit_with_diagnostics(
610 early_lint.lint_id.lint,
611 Some(early_lint.span.clone()),
613 early_lint.diagnostic
618 /// Merge the lints specified by any lint attributes into the
619 /// current lint context, call the provided function, then reset the
620 /// lints in effect to their previous state.
621 fn with_lint_attrs<F>(&mut self,
623 attrs: &'a [ast::Attribute],
625 where F: FnOnce(&mut Self)
627 let push = self.context.builder.push(attrs, &self.context.lint_store);
629 self.enter_attrs(attrs);
631 self.exit_attrs(attrs);
632 self.context.builder.pop(push);
635 fn enter_attrs(&mut self, attrs: &'a [ast::Attribute]) {
636 debug!("early context: enter_attrs({:?})", attrs);
637 run_early_pass!(self, enter_lint_attrs, attrs);
640 fn exit_attrs(&mut self, attrs: &'a [ast::Attribute]) {
641 debug!("early context: exit_attrs({:?})", attrs);
642 run_early_pass!(self, exit_lint_attrs, attrs);
646 impl LintContext for LateContext<'_, '_> {
647 type PassObject = LateLintPassObject;
649 /// Gets the overall compiler `Session` object.
650 fn sess(&self) -> &Session {
654 fn lints(&self) -> &LintStore {
658 fn lookup<S: Into<MultiSpan>>(&self,
662 -> DiagnosticBuilder<'_> {
663 let hir_id = self.last_node_with_lint_attrs;
666 Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg),
668 self.tcx.struct_lint_node(lint, hir_id, msg)
674 impl LintContext for EarlyContext<'_> {
675 type PassObject = EarlyLintPassObject;
677 /// Gets the overall compiler `Session` object.
678 fn sess(&self) -> &Session {
682 fn lints(&self) -> &LintStore {
686 fn lookup<S: Into<MultiSpan>>(&self,
690 -> DiagnosticBuilder<'_> {
691 self.builder.struct_lint(lint, span.map(|s| s.into()), msg)
695 impl<'a, 'tcx> LateContext<'a, 'tcx> {
696 pub fn current_lint_root(&self) -> hir::HirId {
697 self.last_node_with_lint_attrs
700 /// Check if a `DefId`'s path matches the given absolute type path usage.
702 /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`;
703 /// inherent `impl` blocks are matched with the name of the type.
707 /// ```rust,ignore (no context or def id available)
708 /// if cx.match_def_path(def_id, &[sym::core, sym::option, sym::Option]) {
709 /// // The given `def_id` is that of an `Option` type
712 pub fn match_def_path(&self, def_id: DefId, path: &[Symbol]) -> bool {
713 let names = self.get_def_path(def_id);
715 names.len() == path.len() && names.into_iter().zip(path.iter()).all(|(a, &b)| a == b)
718 /// Gets the absolute path of `def_id` as a vector of `Symbol`.
722 /// ```rust,ignore (no context or def id available)
723 /// let def_path = cx.get_def_path(def_id);
724 /// if let &[sym::core, sym::option, sym::Option] = &def_path[..] {
725 /// // The given `def_id` is that of an `Option` type
728 pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
729 pub struct AbsolutePathPrinter<'tcx> {
730 pub tcx: TyCtxt<'tcx>,
733 impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
736 type Path = Vec<Symbol>;
739 type DynExistential = ();
742 fn tcx(&self) -> TyCtxt<'tcx> {
746 fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
750 fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
754 fn print_dyn_existential(
756 _predicates: &'tcx ty::List<ty::ExistentialPredicate<'tcx>>,
757 ) -> Result<Self::DynExistential, Self::Error> {
763 _ct: &'tcx ty::Const<'tcx>,
764 ) -> Result<Self::Const, Self::Error> {
768 fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
769 Ok(vec![self.tcx.original_crate_name(cnum)])
775 trait_ref: Option<ty::TraitRef<'tcx>>,
776 ) -> Result<Self::Path, Self::Error> {
777 if trait_ref.is_none() {
778 if let ty::Adt(def, substs) = self_ty.kind {
779 return self.print_def_path(def.did, substs);
783 // This shouldn't ever be needed, but just in case:
784 Ok(vec![match trait_ref {
785 Some(trait_ref) => Symbol::intern(&format!("{:?}", trait_ref)),
786 None => Symbol::intern(&format!("<{}>", self_ty)),
792 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
793 _disambiguated_data: &DisambiguatedDefPathData,
795 trait_ref: Option<ty::TraitRef<'tcx>>,
796 ) -> Result<Self::Path, Self::Error> {
797 let mut path = print_prefix(self)?;
799 // This shouldn't ever be needed, but just in case:
800 path.push(match trait_ref {
805 trait_ref.print_only_trait_path(),
810 None => Symbol::intern(&format!("<impl {}>", self_ty)),
818 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
819 disambiguated_data: &DisambiguatedDefPathData,
820 ) -> Result<Self::Path, Self::Error> {
821 let mut path = print_prefix(self)?;
823 // Skip `::{{constructor}}` on tuple/unit structs.
824 match disambiguated_data.data {
825 DefPathData::Ctor => return Ok(path),
829 path.push(disambiguated_data.data.as_symbol());
833 fn path_generic_args(
835 print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
836 _args: &[GenericArg<'tcx>],
837 ) -> Result<Self::Path, Self::Error> {
842 AbsolutePathPrinter { tcx: self.tcx }
843 .print_def_path(def_id, &[])
848 impl<'a, 'tcx> LayoutOf for LateContext<'a, 'tcx> {
850 type TyLayout = Result<TyLayout<'tcx>, LayoutError<'tcx>>;
852 fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyLayout {
853 self.tcx.layout_of(self.param_env.and(ty))
857 impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> LateContextAndPass<'a, 'tcx, T> {
858 /// Merge the lints specified by any lint attributes into the
859 /// current lint context, call the provided function, then reset the
860 /// lints in effect to their previous state.
861 fn with_lint_attrs<F>(&mut self,
863 attrs: &'tcx [ast::Attribute],
865 where F: FnOnce(&mut Self)
867 let prev = self.context.last_node_with_lint_attrs;
868 self.context.last_node_with_lint_attrs = id;
869 self.enter_attrs(attrs);
871 self.exit_attrs(attrs);
872 self.context.last_node_with_lint_attrs = prev;
875 fn with_param_env<F>(&mut self, id: hir::HirId, f: F)
876 where F: FnOnce(&mut Self),
878 let old_param_env = self.context.param_env;
879 self.context.param_env = self.context.tcx.param_env(
880 self.context.tcx.hir().local_def_id(id)
883 self.context.param_env = old_param_env;
886 fn process_mod(&mut self, m: &'tcx hir::Mod, s: Span, n: hir::HirId) {
887 lint_callback!(self, check_mod, m, s, n);
888 hir_visit::walk_mod(self, m, n);
889 lint_callback!(self, check_mod_post, m, s, n);
892 fn enter_attrs(&mut self, attrs: &'tcx [ast::Attribute]) {
893 debug!("late context: enter_attrs({:?})", attrs);
894 lint_callback!(self, enter_lint_attrs, attrs);
897 fn exit_attrs(&mut self, attrs: &'tcx [ast::Attribute]) {
898 debug!("late context: exit_attrs({:?})", attrs);
899 lint_callback!(self, exit_lint_attrs, attrs);
903 impl<'a, 'tcx, T: LateLintPass<'a, 'tcx>> hir_visit::Visitor<'tcx>
904 for LateContextAndPass<'a, 'tcx, T> {
905 /// Because lints are scoped lexically, we want to walk nested
906 /// items in the context of the outer item, so enable
908 fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'tcx> {
909 hir_visit::NestedVisitorMap::All(&self.context.tcx.hir())
912 fn visit_nested_body(&mut self, body: hir::BodyId) {
913 let old_tables = self.context.tables;
914 self.context.tables = self.context.tcx.body_tables(body);
915 let body = self.context.tcx.hir().body(body);
916 self.visit_body(body);
917 self.context.tables = old_tables;
920 fn visit_param(&mut self, param: &'tcx hir::Param) {
921 self.with_lint_attrs(param.hir_id, ¶m.attrs, |cx| {
922 lint_callback!(cx, check_param, param);
923 hir_visit::walk_param(cx, param);
927 fn visit_body(&mut self, body: &'tcx hir::Body) {
928 lint_callback!(self, check_body, body);
929 hir_visit::walk_body(self, body);
930 lint_callback!(self, check_body_post, body);
933 fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
934 let generics = self.context.generics.take();
935 self.context.generics = it.kind.generics();
936 self.with_lint_attrs(it.hir_id, &it.attrs, |cx| {
937 cx.with_param_env(it.hir_id, |cx| {
938 lint_callback!(cx, check_item, it);
939 hir_visit::walk_item(cx, it);
940 lint_callback!(cx, check_item_post, it);
943 self.context.generics = generics;
946 fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem) {
947 self.with_lint_attrs(it.hir_id, &it.attrs, |cx| {
948 cx.with_param_env(it.hir_id, |cx| {
949 lint_callback!(cx, check_foreign_item, it);
950 hir_visit::walk_foreign_item(cx, it);
951 lint_callback!(cx, check_foreign_item_post, it);
956 fn visit_pat(&mut self, p: &'tcx hir::Pat) {
957 lint_callback!(self, check_pat, p);
958 hir_visit::walk_pat(self, p);
961 fn visit_expr(&mut self, e: &'tcx hir::Expr) {
962 self.with_lint_attrs(e.hir_id, &e.attrs, |cx| {
963 lint_callback!(cx, check_expr, e);
964 hir_visit::walk_expr(cx, e);
965 lint_callback!(cx, check_expr_post, e);
969 fn visit_stmt(&mut self, s: &'tcx hir::Stmt) {
970 // statement attributes are actually just attributes on one of
974 // so we keep track of lint levels there
975 lint_callback!(self, check_stmt, s);
976 hir_visit::walk_stmt(self, s);
979 fn visit_fn(&mut self, fk: hir_visit::FnKind<'tcx>, decl: &'tcx hir::FnDecl,
980 body_id: hir::BodyId, span: Span, id: hir::HirId) {
981 // Wrap in tables here, not just in visit_nested_body,
982 // in order for `check_fn` to be able to use them.
983 let old_tables = self.context.tables;
984 self.context.tables = self.context.tcx.body_tables(body_id);
985 let body = self.context.tcx.hir().body(body_id);
986 lint_callback!(self, check_fn, fk, decl, body, span, id);
987 hir_visit::walk_fn(self, fk, decl, body_id, span, id);
988 lint_callback!(self, check_fn_post, fk, decl, body, span, id);
989 self.context.tables = old_tables;
992 fn visit_variant_data(&mut self,
993 s: &'tcx hir::VariantData,
995 _: &'tcx hir::Generics,
998 lint_callback!(self, check_struct_def, s);
999 hir_visit::walk_struct_def(self, s);
1000 lint_callback!(self, check_struct_def_post, s);
1003 fn visit_struct_field(&mut self, s: &'tcx hir::StructField) {
1004 self.with_lint_attrs(s.hir_id, &s.attrs, |cx| {
1005 lint_callback!(cx, check_struct_field, s);
1006 hir_visit::walk_struct_field(cx, s);
1010 fn visit_variant(&mut self,
1011 v: &'tcx hir::Variant,
1012 g: &'tcx hir::Generics,
1013 item_id: hir::HirId) {
1014 self.with_lint_attrs(v.id, &v.attrs, |cx| {
1015 lint_callback!(cx, check_variant, v);
1016 hir_visit::walk_variant(cx, v, g, item_id);
1017 lint_callback!(cx, check_variant_post, v);
1021 fn visit_ty(&mut self, t: &'tcx hir::Ty) {
1022 lint_callback!(self, check_ty, t);
1023 hir_visit::walk_ty(self, t);
1026 fn visit_name(&mut self, sp: Span, name: ast::Name) {
1027 lint_callback!(self, check_name, sp, name);
1030 fn visit_mod(&mut self, m: &'tcx hir::Mod, s: Span, n: hir::HirId) {
1031 if !self.context.only_module {
1032 self.process_mod(m, s, n);
1036 fn visit_local(&mut self, l: &'tcx hir::Local) {
1037 self.with_lint_attrs(l.hir_id, &l.attrs, |cx| {
1038 lint_callback!(cx, check_local, l);
1039 hir_visit::walk_local(cx, l);
1043 fn visit_block(&mut self, b: &'tcx hir::Block) {
1044 lint_callback!(self, check_block, b);
1045 hir_visit::walk_block(self, b);
1046 lint_callback!(self, check_block_post, b);
1049 fn visit_arm(&mut self, a: &'tcx hir::Arm) {
1050 lint_callback!(self, check_arm, a);
1051 hir_visit::walk_arm(self, a);
1054 fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam) {
1055 lint_callback!(self, check_generic_param, p);
1056 hir_visit::walk_generic_param(self, p);
1059 fn visit_generics(&mut self, g: &'tcx hir::Generics) {
1060 lint_callback!(self, check_generics, g);
1061 hir_visit::walk_generics(self, g);
1064 fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate) {
1065 lint_callback!(self, check_where_predicate, p);
1066 hir_visit::walk_where_predicate(self, p);
1069 fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef,
1070 m: hir::TraitBoundModifier) {
1071 lint_callback!(self, check_poly_trait_ref, t, m);
1072 hir_visit::walk_poly_trait_ref(self, t, m);
1075 fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
1076 let generics = self.context.generics.take();
1077 self.context.generics = Some(&trait_item.generics);
1078 self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |cx| {
1079 cx.with_param_env(trait_item.hir_id, |cx| {
1080 lint_callback!(cx, check_trait_item, trait_item);
1081 hir_visit::walk_trait_item(cx, trait_item);
1082 lint_callback!(cx, check_trait_item_post, trait_item);
1085 self.context.generics = generics;
1088 fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) {
1089 let generics = self.context.generics.take();
1090 self.context.generics = Some(&impl_item.generics);
1091 self.with_lint_attrs(impl_item.hir_id, &impl_item.attrs, |cx| {
1092 cx.with_param_env(impl_item.hir_id, |cx| {
1093 lint_callback!(cx, check_impl_item, impl_item);
1094 hir_visit::walk_impl_item(cx, impl_item);
1095 lint_callback!(cx, check_impl_item_post, impl_item);
1098 self.context.generics = generics;
1101 fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
1102 lint_callback!(self, check_lifetime, lt);
1103 hir_visit::walk_lifetime(self, lt);
1106 fn visit_path(&mut self, p: &'tcx hir::Path, id: hir::HirId) {
1107 lint_callback!(self, check_path, p, id);
1108 hir_visit::walk_path(self, p);
1111 fn visit_attribute(&mut self, attr: &'tcx ast::Attribute) {
1112 lint_callback!(self, check_attribute, attr);
1116 impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
1117 fn visit_param(&mut self, param: &'a ast::Param) {
1118 self.with_lint_attrs(param.id, ¶m.attrs, |cx| {
1119 run_early_pass!(cx, check_param, param);
1120 ast_visit::walk_param(cx, param);
1124 fn visit_item(&mut self, it: &'a ast::Item) {
1125 self.with_lint_attrs(it.id, &it.attrs, |cx| {
1126 run_early_pass!(cx, check_item, it);
1127 ast_visit::walk_item(cx, it);
1128 run_early_pass!(cx, check_item_post, it);
1132 fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) {
1133 self.with_lint_attrs(it.id, &it.attrs, |cx| {
1134 run_early_pass!(cx, check_foreign_item, it);
1135 ast_visit::walk_foreign_item(cx, it);
1136 run_early_pass!(cx, check_foreign_item_post, it);
1140 fn visit_pat(&mut self, p: &'a ast::Pat) {
1141 run_early_pass!(self, check_pat, p);
1142 self.check_id(p.id);
1143 ast_visit::walk_pat(self, p);
1144 run_early_pass!(self, check_pat_post, p);
1147 fn visit_expr(&mut self, e: &'a ast::Expr) {
1148 self.with_lint_attrs(e.id, &e.attrs, |cx| {
1149 run_early_pass!(cx, check_expr, e);
1150 ast_visit::walk_expr(cx, e);
1154 fn visit_stmt(&mut self, s: &'a ast::Stmt) {
1155 run_early_pass!(self, check_stmt, s);
1156 self.check_id(s.id);
1157 ast_visit::walk_stmt(self, s);
1160 fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, decl: &'a ast::FnDecl,
1161 span: Span, id: ast::NodeId) {
1162 run_early_pass!(self, check_fn, fk, decl, span, id);
1164 ast_visit::walk_fn(self, fk, decl, span);
1165 run_early_pass!(self, check_fn_post, fk, decl, span, id);
1168 fn visit_variant_data(&mut self, s: &'a ast::VariantData) {
1169 run_early_pass!(self, check_struct_def, s);
1170 if let Some(ctor_hir_id) = s.ctor_id() {
1171 self.check_id(ctor_hir_id);
1173 ast_visit::walk_struct_def(self, s);
1174 run_early_pass!(self, check_struct_def_post, s);
1177 fn visit_struct_field(&mut self, s: &'a ast::StructField) {
1178 self.with_lint_attrs(s.id, &s.attrs, |cx| {
1179 run_early_pass!(cx, check_struct_field, s);
1180 ast_visit::walk_struct_field(cx, s);
1184 fn visit_variant(&mut self, v: &'a ast::Variant) {
1185 self.with_lint_attrs(v.id, &v.attrs, |cx| {
1186 run_early_pass!(cx, check_variant, v);
1187 ast_visit::walk_variant(cx, v);
1188 run_early_pass!(cx, check_variant_post, v);
1192 fn visit_ty(&mut self, t: &'a ast::Ty) {
1193 run_early_pass!(self, check_ty, t);
1194 self.check_id(t.id);
1195 ast_visit::walk_ty(self, t);
1198 fn visit_ident(&mut self, ident: ast::Ident) {
1199 run_early_pass!(self, check_ident, ident);
1202 fn visit_mod(&mut self, m: &'a ast::Mod, s: Span, _a: &[ast::Attribute], n: ast::NodeId) {
1203 run_early_pass!(self, check_mod, m, s, n);
1205 ast_visit::walk_mod(self, m);
1206 run_early_pass!(self, check_mod_post, m, s, n);
1209 fn visit_local(&mut self, l: &'a ast::Local) {
1210 self.with_lint_attrs(l.id, &l.attrs, |cx| {
1211 run_early_pass!(cx, check_local, l);
1212 ast_visit::walk_local(cx, l);
1216 fn visit_block(&mut self, b: &'a ast::Block) {
1217 run_early_pass!(self, check_block, b);
1218 self.check_id(b.id);
1219 ast_visit::walk_block(self, b);
1220 run_early_pass!(self, check_block_post, b);
1223 fn visit_arm(&mut self, a: &'a ast::Arm) {
1224 run_early_pass!(self, check_arm, a);
1225 ast_visit::walk_arm(self, a);
1228 fn visit_expr_post(&mut self, e: &'a ast::Expr) {
1229 run_early_pass!(self, check_expr_post, e);
1232 fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
1233 run_early_pass!(self, check_generic_param, param);
1234 ast_visit::walk_generic_param(self, param);
1237 fn visit_generics(&mut self, g: &'a ast::Generics) {
1238 run_early_pass!(self, check_generics, g);
1239 ast_visit::walk_generics(self, g);
1242 fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
1243 run_early_pass!(self, check_where_predicate, p);
1244 ast_visit::walk_where_predicate(self, p);
1247 fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) {
1248 run_early_pass!(self, check_poly_trait_ref, t, m);
1249 ast_visit::walk_poly_trait_ref(self, t, m);
1252 fn visit_trait_item(&mut self, trait_item: &'a ast::AssocItem) {
1253 self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
1254 run_early_pass!(cx, check_trait_item, trait_item);
1255 ast_visit::walk_trait_item(cx, trait_item);
1256 run_early_pass!(cx, check_trait_item_post, trait_item);
1260 fn visit_impl_item(&mut self, impl_item: &'a ast::AssocItem) {
1261 self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| {
1262 run_early_pass!(cx, check_impl_item, impl_item);
1263 ast_visit::walk_impl_item(cx, impl_item);
1264 run_early_pass!(cx, check_impl_item_post, impl_item);
1268 fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
1269 run_early_pass!(self, check_lifetime, lt);
1270 self.check_id(lt.id);
1273 fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
1274 run_early_pass!(self, check_path, p, id);
1276 ast_visit::walk_path(self, p);
1279 fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
1280 run_early_pass!(self, check_attribute, attr);
1283 fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
1284 run_early_pass!(self, check_mac_def, mac, id);
1288 fn visit_mac(&mut self, mac: &'a ast::Mac) {
1289 // FIXME(#54110): So, this setup isn't really right. I think
1290 // that (a) the libsyntax visitor ought to be doing this as
1291 // part of `walk_mac`, and (b) we should be calling
1292 // `visit_path`, *but* that would require a `NodeId`, and I
1293 // want to get #53686 fixed quickly. -nmatsakis
1294 ast_visit::walk_path(self, &mac.path);
1296 run_early_pass!(self, check_mac, mac);
1300 struct LateLintPassObjects<'a> {
1301 lints: &'a mut [LateLintPassObject],
1304 #[allow(rustc::lint_pass_impl_without_macro)]
1305 impl LintPass for LateLintPassObjects<'_> {
1306 fn name(&self) -> &'static str {
1311 macro_rules! expand_late_lint_pass_impl_methods {
1312 ([$a:tt, $hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
1313 $(fn $name(&mut self, context: &LateContext<$a, $hir>, $($param: $arg),*) {
1314 for obj in self.lints.iter_mut() {
1315 obj.$name(context, $($param),*);
1321 macro_rules! late_lint_pass_impl {
1322 ([], [$hir:tt], $methods:tt) => (
1323 impl LateLintPass<'a, $hir> for LateLintPassObjects<'_> {
1324 expand_late_lint_pass_impl_methods!(['a, $hir], $methods);
1329 late_lint_methods!(late_lint_pass_impl, [], ['tcx]);
1331 fn late_lint_mod_pass<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
1333 module_def_id: DefId,
1336 let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
1338 let context = LateContext {
1340 tables: &ty::TypeckTables::empty(None),
1341 param_env: ty::ParamEnv::empty(),
1343 lint_store: &tcx.lint_store,
1344 last_node_with_lint_attrs: tcx.hir().as_local_hir_id(module_def_id).unwrap(),
1349 let mut cx = LateContextAndPass {
1354 let (module, span, hir_id) = tcx.hir().get_module(module_def_id);
1355 cx.process_mod(module, span, hir_id);
1357 // Visit the crate attributes
1358 if hir_id == hir::CRATE_HIR_ID {
1359 walk_list!(cx, visit_attribute, tcx.hir().attrs(hir::CRATE_HIR_ID));
1363 pub fn late_lint_mod<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
1365 module_def_id: DefId,
1368 if tcx.sess.opts.debugging_opts.no_interleave_lints {
1369 // These passes runs in late_lint_crate with -Z no_interleave_lints
1373 late_lint_mod_pass(tcx, module_def_id, builtin_lints);
1375 let mut passes: Vec<_> = tcx.lint_store.late_module_passes
1376 .iter().map(|pass| (pass)()).collect();
1378 if !passes.is_empty() {
1379 late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] });
1383 fn late_lint_pass_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tcx>, pass: T) {
1384 let access_levels = &tcx.privacy_access_levels(LOCAL_CRATE);
1386 let krate = tcx.hir().krate();
1388 let context = LateContext {
1390 tables: &ty::TypeckTables::empty(None),
1391 param_env: ty::ParamEnv::empty(),
1393 lint_store: &tcx.lint_store,
1394 last_node_with_lint_attrs: hir::CRATE_HIR_ID,
1399 let mut cx = LateContextAndPass {
1404 // Visit the whole crate.
1405 cx.with_lint_attrs(hir::CRATE_HIR_ID, &krate.attrs, |cx| {
1406 // since the root module isn't visited as an item (because it isn't an
1407 // item), warn for it here.
1408 lint_callback!(cx, check_crate, krate);
1410 hir_visit::walk_crate(cx, krate);
1412 lint_callback!(cx, check_crate_post, krate);
1416 fn late_lint_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) {
1417 let mut passes = tcx.lint_store
1418 .late_passes.iter().map(|p| (p)()).collect::<Vec<_>>();
1420 if !tcx.sess.opts.debugging_opts.no_interleave_lints {
1421 if !passes.is_empty() {
1422 late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] });
1425 late_lint_pass_crate(tcx, builtin_lints);
1427 for pass in &mut passes {
1428 time(tcx.sess, &format!("running late lint: {}", pass.name()), || {
1429 late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
1433 let mut passes: Vec<_> = tcx.lint_store.late_module_passes
1434 .iter().map(|pass| (pass)()).collect();
1436 for pass in &mut passes {
1437 time(tcx.sess, &format!("running late module lint: {}", pass.name()), || {
1438 late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
1444 /// Performs lint checking on a crate.
1445 pub fn check_crate<'tcx, T: for<'a> LateLintPass<'a, 'tcx>>(
1447 builtin_lints: impl FnOnce() -> T + Send,
1450 time(tcx.sess, "crate lints", || {
1451 // Run whole crate non-incremental lints
1452 late_lint_crate(tcx, builtin_lints());
1455 time(tcx.sess, "module lints", || {
1456 // Run per-module lints
1457 par_iter(&tcx.hir().krate().modules).for_each(|(&module, _)| {
1458 tcx.ensure().lint_mod(tcx.hir().local_def_id(module));
1464 struct EarlyLintPassObjects<'a> {
1465 lints: &'a mut [EarlyLintPassObject],
1468 #[allow(rustc::lint_pass_impl_without_macro)]
1469 impl LintPass for EarlyLintPassObjects<'_> {
1470 fn name(&self) -> &'static str {
1475 macro_rules! expand_early_lint_pass_impl_methods {
1476 ([$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
1477 $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
1478 for obj in self.lints.iter_mut() {
1479 obj.$name(context, $($param),*);
1485 macro_rules! early_lint_pass_impl {
1486 ([], [$($methods:tt)*]) => (
1487 impl EarlyLintPass for EarlyLintPassObjects<'_> {
1488 expand_early_lint_pass_impl_methods!([$($methods)*]);
1493 early_lint_methods!(early_lint_pass_impl, []);
1495 fn early_lint_crate<T: EarlyLintPass>(
1497 lint_store: &LintStore,
1500 buffered: LintBuffer,
1501 warn_about_weird_lints: bool,
1503 let mut cx = EarlyContextAndPass {
1504 context: EarlyContext::new(sess, lint_store, krate, buffered, warn_about_weird_lints),
1508 // Visit the whole crate.
1509 cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
1510 // since the root module isn't visited as an item (because it isn't an
1511 // item), warn for it here.
1512 run_early_pass!(cx, check_crate, krate);
1514 ast_visit::walk_crate(cx, krate);
1516 run_early_pass!(cx, check_crate_post, krate);
1521 pub fn check_ast_crate<T: EarlyLintPass>(
1523 lint_store: &LintStore,
1525 pre_expansion: bool,
1526 lint_buffer: Option<LintBuffer>,
1529 let mut passes: Vec<_> = if pre_expansion {
1530 lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect()
1532 lint_store.early_passes.iter().map(|p| (p)()).collect()
1534 let mut buffered = lint_buffer.unwrap_or_default();
1536 if !sess.opts.debugging_opts.no_interleave_lints {
1537 buffered = early_lint_crate(sess, lint_store, krate, builtin_lints, buffered,
1540 if !passes.is_empty() {
1541 buffered = early_lint_crate(
1545 EarlyLintPassObjects { lints: &mut passes[..] },
1551 for pass in &mut passes {
1552 buffered = time(sess, &format!("running lint: {}", pass.name()), || {
1557 EarlyLintPassObjects { lints: slice::from_mut(pass) },
1565 // All of the buffered lints should have been emitted at this point.
1566 // If not, that means that we somehow buffered a lint for a node id
1567 // that was not lint-checked (perhaps it doesn't exist?). This is a bug.
1569 // Rustdoc runs everybody-loops before the early lints and removes
1570 // function bodies, so it's totally possible for linted
1571 // node ids to not exist (e.g., macros defined within functions for the
1572 // unused_macro lint) anymore. So we only run this check
1573 // when we're not in rustdoc mode. (see issue #47639)
1574 if !sess.opts.actually_rustdoc {
1575 for (_id, lints) in buffered.map {
1576 for early_lint in lints {
1577 sess.delay_span_bug(early_lint.span, "failed to process buffered lint here");