Skip to content

Move early and late lint mechanisms to librustc_lint. #67788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 4, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
829 changes: 15 additions & 814 deletions src/librustc/lint/context.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/librustc/lint/levels.rs
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ pub struct LintLevelsBuilder<'a> {

pub struct BuilderPush {
prev: u32,
pub(super) changed: bool,
pub changed: bool,
}

impl<'a> LintLevelsBuilder<'a> {
128 changes: 4 additions & 124 deletions src/librustc/lint/mod.rs
Original file line number Diff line number Diff line change
@@ -24,11 +24,8 @@ pub use self::LintSource::*;
use rustc_data_structures::sync;

use crate::hir;
use crate::hir::def_id::{CrateNum, LOCAL_CRATE};
use crate::hir::intravisit;
use crate::lint::builtin::BuiltinLintDiagnostics;
use crate::session::{DiagnosticMessageId, Session};
use crate::ty::query::Providers;
use crate::ty::TyCtxt;
use crate::util::nodemap::NodeMap;
use errors::{DiagnosticBuilder, DiagnosticId};
@@ -39,8 +36,7 @@ use syntax::source_map::{DesugaringKind, ExpnKind, MultiSpan};
use syntax::symbol::Symbol;

pub use crate::lint::context::{
check_ast_crate, check_crate, late_lint_mod, BufferedEarlyLint, CheckLintNameResult,
EarlyContext, LateContext, LintContext, LintStore,
BufferedEarlyLint, CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore,
};

pub use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintId};
@@ -376,11 +372,11 @@ mod context;
pub mod internal;
mod levels;

pub use self::levels::{LintLevelMap, LintLevelSets};
pub use self::levels::{LintLevelMap, LintLevelSets, LintLevelsBuilder};

#[derive(Default)]
pub struct LintBuffer {
map: NodeMap<Vec<BufferedEarlyLint>>,
pub map: NodeMap<Vec<BufferedEarlyLint>>,
}

impl LintBuffer {
@@ -405,7 +401,7 @@ impl LintBuffer {
}
}

fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> {
pub fn take(&mut self, id: ast::NodeId) -> Vec<BufferedEarlyLint> {
self.map.remove(&id).unwrap_or_default()
}

@@ -564,122 +560,6 @@ pub fn maybe_lint_level_root(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
attrs.iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some())
}

fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
assert_eq!(cnum, LOCAL_CRATE);
let store = &tcx.lint_store;
let mut builder = LintLevelMapBuilder {
levels: LintLevelSets::builder(tcx.sess, false, &store),
tcx: tcx,
store: store,
};
let krate = tcx.hir().krate();

let push = builder.levels.push(&krate.attrs, &store);
builder.levels.register_id(hir::CRATE_HIR_ID);
for macro_def in krate.exported_macros {
builder.levels.register_id(macro_def.hir_id);
}
intravisit::walk_crate(&mut builder, krate);
builder.levels.pop(push);

tcx.arena.alloc(builder.levels.build_map())
}

struct LintLevelMapBuilder<'a, 'tcx> {
levels: levels::LintLevelsBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
store: &'a LintStore,
}

impl LintLevelMapBuilder<'_, '_> {
fn with_lint_attrs<F>(&mut self, id: hir::HirId, attrs: &[ast::Attribute], f: F)
where
F: FnOnce(&mut Self),
{
let push = self.levels.push(attrs, self.store);
if push.changed {
self.levels.register_id(id);
}
f(self);
self.levels.pop(push);
}
}

impl intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}

fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
self.with_lint_attrs(param.hir_id, &param.attrs, |builder| {
intravisit::walk_param(builder, param);
});
}

fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
self.with_lint_attrs(it.hir_id, &it.attrs, |builder| {
intravisit::walk_item(builder, it);
});
}

fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
self.with_lint_attrs(it.hir_id, &it.attrs, |builder| {
intravisit::walk_foreign_item(builder, it);
})
}

fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
self.with_lint_attrs(e.hir_id, &e.attrs, |builder| {
intravisit::walk_expr(builder, e);
})
}

fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) {
self.with_lint_attrs(s.hir_id, &s.attrs, |builder| {
intravisit::walk_struct_field(builder, s);
})
}

fn visit_variant(
&mut self,
v: &'tcx hir::Variant<'tcx>,
g: &'tcx hir::Generics<'tcx>,
item_id: hir::HirId,
) {
self.with_lint_attrs(v.id, &v.attrs, |builder| {
intravisit::walk_variant(builder, v, g, item_id);
})
}

fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
self.with_lint_attrs(l.hir_id, &l.attrs, |builder| {
intravisit::walk_local(builder, l);
})
}

fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
self.with_lint_attrs(a.hir_id, &a.attrs, |builder| {
intravisit::walk_arm(builder, a);
})
}

fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |builder| {
intravisit::walk_trait_item(builder, trait_item);
});
}

fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
self.with_lint_attrs(impl_item.hir_id, &impl_item.attrs, |builder| {
intravisit::walk_impl_item(builder, impl_item);
});
}
}

pub fn provide(providers: &mut Providers<'_>) {
providers.lint_levels = lint_levels;
}

/// Returns whether `span` originates in a foreign crate's external macro.
///
/// This is used to test whether a lint should not even begin to figure out whether it should
7 changes: 3 additions & 4 deletions src/librustc_interface/passes.rs
Original file line number Diff line number Diff line change
@@ -231,7 +231,7 @@ fn configure_and_expand_inner<'a>(
metadata_loader: &'a MetadataLoaderDyn,
) -> Result<(ast::Crate, Resolver<'a>)> {
time(sess, "pre-AST-expansion lint checks", || {
lint::check_ast_crate(
rustc_lint::check_ast_crate(
sess,
lint_store,
&krate,
@@ -458,7 +458,7 @@ pub fn lower_to_hir<'res, 'tcx>(
});

time(sess, "early lint checks", || {
lint::check_ast_crate(
rustc_lint::check_ast_crate(
sess,
lint_store,
&krate,
@@ -692,7 +692,6 @@ pub fn default_provide(providers: &mut ty::query::Providers<'_>) {
rustc_resolve::provide(providers);
rustc_traits::provide(providers);
rustc_metadata::provide(providers);
lint::provide(providers);
rustc_lint::provide(providers);
rustc_codegen_utils::provide(providers);
rustc_codegen_ssa::provide(providers);
@@ -886,7 +885,7 @@ fn analysis(tcx: TyCtxt<'_>, cnum: CrateNum) -> Result<()> {
},
{
time(sess, "lint checking", || {
lint::check_crate(tcx, || {
rustc_lint::check_crate(tcx, || {
rustc_lint::BuiltinCombinedLateLintPass::new()
});
});
383 changes: 383 additions & 0 deletions src/librustc_lint/early.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,383 @@
//! Implementation of lint checking.
//!
//! The lint checking is mostly consolidated into one pass which runs
//! after all other analyses. Throughout compilation, lint warnings
//! can be added via the `add_lint` method on the Session structure. This
//! requires a span and an ID of the node that the lint is being added to. The
//! lint isn't actually emitted at that time because it is unknown what the
//! actual lint level at that location is.
//!
//! To actually emit lint warnings/errors, a separate pass is used.
//! A context keeps track of the current state of all lint levels.
//! Upon entering a node of the ast which can modify the lint settings, the
//! previous lint state is pushed onto a stack and the ast is then recursed
//! upon. As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
use rustc::lint::{EarlyContext, LintStore};
use rustc::lint::{EarlyLintPass, EarlyLintPassObject};
use rustc::lint::{LintBuffer, LintContext, LintPass};
use rustc::session::Session;
use rustc::util::common::time;

use rustc_span::Span;
use std::slice;
use syntax::ast;
use syntax::visit as ast_visit;

use log::debug;

macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
}) }

struct EarlyContextAndPass<'a, T: EarlyLintPass> {
context: EarlyContext<'a>,
pass: T,
}

impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
fn check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
self.context.lookup_and_emit_with_diagnostics(
early_lint.lint_id.lint,
Some(early_lint.span.clone()),
&early_lint.msg,
early_lint.diagnostic,
);
}
}

/// Merge the lints specified by any lint attributes into the
/// current lint context, call the provided function, then reset the
/// lints in effect to their previous state.
fn with_lint_attrs<F>(&mut self, id: ast::NodeId, attrs: &'a [ast::Attribute], f: F)
where
F: FnOnce(&mut Self),
{
let push = self.context.builder.push(attrs, &self.context.lint_store);
self.check_id(id);
self.enter_attrs(attrs);
f(self);
self.exit_attrs(attrs);
self.context.builder.pop(push);
}

fn enter_attrs(&mut self, attrs: &'a [ast::Attribute]) {
debug!("early context: enter_attrs({:?})", attrs);
run_early_pass!(self, enter_lint_attrs, attrs);
}

fn exit_attrs(&mut self, attrs: &'a [ast::Attribute]) {
debug!("early context: exit_attrs({:?})", attrs);
run_early_pass!(self, exit_lint_attrs, attrs);
}
}

impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
fn visit_param(&mut self, param: &'a ast::Param) {
self.with_lint_attrs(param.id, &param.attrs, |cx| {
run_early_pass!(cx, check_param, param);
ast_visit::walk_param(cx, param);
});
}

fn visit_item(&mut self, it: &'a ast::Item) {
self.with_lint_attrs(it.id, &it.attrs, |cx| {
run_early_pass!(cx, check_item, it);
ast_visit::walk_item(cx, it);
run_early_pass!(cx, check_item_post, it);
})
}

fn visit_foreign_item(&mut self, it: &'a ast::ForeignItem) {
self.with_lint_attrs(it.id, &it.attrs, |cx| {
run_early_pass!(cx, check_foreign_item, it);
ast_visit::walk_foreign_item(cx, it);
run_early_pass!(cx, check_foreign_item_post, it);
})
}

fn visit_pat(&mut self, p: &'a ast::Pat) {
run_early_pass!(self, check_pat, p);
self.check_id(p.id);
ast_visit::walk_pat(self, p);
run_early_pass!(self, check_pat_post, p);
}

fn visit_expr(&mut self, e: &'a ast::Expr) {
self.with_lint_attrs(e.id, &e.attrs, |cx| {
run_early_pass!(cx, check_expr, e);
ast_visit::walk_expr(cx, e);
})
}

fn visit_stmt(&mut self, s: &'a ast::Stmt) {
run_early_pass!(self, check_stmt, s);
self.check_id(s.id);
ast_visit::walk_stmt(self, s);
}

fn visit_fn(
&mut self,
fk: ast_visit::FnKind<'a>,
decl: &'a ast::FnDecl,
span: Span,
id: ast::NodeId,
) {
run_early_pass!(self, check_fn, fk, decl, span, id);
self.check_id(id);
ast_visit::walk_fn(self, fk, decl, span);
run_early_pass!(self, check_fn_post, fk, decl, span, id);
}

fn visit_variant_data(&mut self, s: &'a ast::VariantData) {
run_early_pass!(self, check_struct_def, s);
if let Some(ctor_hir_id) = s.ctor_id() {
self.check_id(ctor_hir_id);
}
ast_visit::walk_struct_def(self, s);
run_early_pass!(self, check_struct_def_post, s);
}

fn visit_struct_field(&mut self, s: &'a ast::StructField) {
self.with_lint_attrs(s.id, &s.attrs, |cx| {
run_early_pass!(cx, check_struct_field, s);
ast_visit::walk_struct_field(cx, s);
})
}

fn visit_variant(&mut self, v: &'a ast::Variant) {
self.with_lint_attrs(v.id, &v.attrs, |cx| {
run_early_pass!(cx, check_variant, v);
ast_visit::walk_variant(cx, v);
run_early_pass!(cx, check_variant_post, v);
})
}

fn visit_ty(&mut self, t: &'a ast::Ty) {
run_early_pass!(self, check_ty, t);
self.check_id(t.id);
ast_visit::walk_ty(self, t);
}

fn visit_ident(&mut self, ident: ast::Ident) {
run_early_pass!(self, check_ident, ident);
}

fn visit_mod(&mut self, m: &'a ast::Mod, s: Span, _a: &[ast::Attribute], n: ast::NodeId) {
run_early_pass!(self, check_mod, m, s, n);
self.check_id(n);
ast_visit::walk_mod(self, m);
run_early_pass!(self, check_mod_post, m, s, n);
}

fn visit_local(&mut self, l: &'a ast::Local) {
self.with_lint_attrs(l.id, &l.attrs, |cx| {
run_early_pass!(cx, check_local, l);
ast_visit::walk_local(cx, l);
})
}

fn visit_block(&mut self, b: &'a ast::Block) {
run_early_pass!(self, check_block, b);
self.check_id(b.id);
ast_visit::walk_block(self, b);
run_early_pass!(self, check_block_post, b);
}

fn visit_arm(&mut self, a: &'a ast::Arm) {
run_early_pass!(self, check_arm, a);
ast_visit::walk_arm(self, a);
}

fn visit_expr_post(&mut self, e: &'a ast::Expr) {
run_early_pass!(self, check_expr_post, e);
}

fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
run_early_pass!(self, check_generic_param, param);
ast_visit::walk_generic_param(self, param);
}

fn visit_generics(&mut self, g: &'a ast::Generics) {
run_early_pass!(self, check_generics, g);
ast_visit::walk_generics(self, g);
}

fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
run_early_pass!(self, check_where_predicate, p);
ast_visit::walk_where_predicate(self, p);
}

fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) {
run_early_pass!(self, check_poly_trait_ref, t, m);
ast_visit::walk_poly_trait_ref(self, t, m);
}

fn visit_trait_item(&mut self, trait_item: &'a ast::AssocItem) {
self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
run_early_pass!(cx, check_trait_item, trait_item);
ast_visit::walk_trait_item(cx, trait_item);
run_early_pass!(cx, check_trait_item_post, trait_item);
});
}

fn visit_impl_item(&mut self, impl_item: &'a ast::AssocItem) {
self.with_lint_attrs(impl_item.id, &impl_item.attrs, |cx| {
run_early_pass!(cx, check_impl_item, impl_item);
ast_visit::walk_impl_item(cx, impl_item);
run_early_pass!(cx, check_impl_item_post, impl_item);
});
}

fn visit_lifetime(&mut self, lt: &'a ast::Lifetime) {
run_early_pass!(self, check_lifetime, lt);
self.check_id(lt.id);
}

fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
run_early_pass!(self, check_path, p, id);
self.check_id(id);
ast_visit::walk_path(self, p);
}

fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
run_early_pass!(self, check_attribute, attr);
}

fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
run_early_pass!(self, check_mac_def, mac, id);
self.check_id(id);
}

fn visit_mac(&mut self, mac: &'a ast::Mac) {
// FIXME(#54110): So, this setup isn't really right. I think
// that (a) the libsyntax visitor ought to be doing this as
// part of `walk_mac`, and (b) we should be calling
// `visit_path`, *but* that would require a `NodeId`, and I
// want to get #53686 fixed quickly. -nmatsakis
ast_visit::walk_path(self, &mac.path);

run_early_pass!(self, check_mac, mac);
}
}

struct EarlyLintPassObjects<'a> {
lints: &'a mut [EarlyLintPassObject],
}

#[allow(rustc::lint_pass_impl_without_macro)]
impl LintPass for EarlyLintPassObjects<'_> {
fn name(&self) -> &'static str {
panic!()
}
}

macro_rules! expand_early_lint_pass_impl_methods {
([$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
$(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
for obj in self.lints.iter_mut() {
obj.$name(context, $($param),*);
}
})*
)
}

macro_rules! early_lint_pass_impl {
([], [$($methods:tt)*]) => (
impl EarlyLintPass for EarlyLintPassObjects<'_> {
expand_early_lint_pass_impl_methods!([$($methods)*]);
}
)
}

early_lint_methods!(early_lint_pass_impl, []);

fn early_lint_crate<T: EarlyLintPass>(
sess: &Session,
lint_store: &LintStore,
krate: &ast::Crate,
pass: T,
buffered: LintBuffer,
warn_about_weird_lints: bool,
) -> LintBuffer {
let mut cx = EarlyContextAndPass {
context: EarlyContext::new(sess, lint_store, krate, buffered, warn_about_weird_lints),
pass,
};

// Visit the whole crate.
cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
// since the root module isn't visited as an item (because it isn't an
// item), warn for it here.
run_early_pass!(cx, check_crate, krate);

ast_visit::walk_crate(cx, krate);

run_early_pass!(cx, check_crate_post, krate);
});
cx.context.buffered
}

pub fn check_ast_crate<T: EarlyLintPass>(
sess: &Session,
lint_store: &LintStore,
krate: &ast::Crate,
pre_expansion: bool,
lint_buffer: Option<LintBuffer>,
builtin_lints: T,
) {
let mut passes: Vec<_> = if pre_expansion {
lint_store.pre_expansion_passes.iter().map(|p| (p)()).collect()
} else {
lint_store.early_passes.iter().map(|p| (p)()).collect()
};
let mut buffered = lint_buffer.unwrap_or_default();

if !sess.opts.debugging_opts.no_interleave_lints {
buffered =
early_lint_crate(sess, lint_store, krate, builtin_lints, buffered, pre_expansion);

if !passes.is_empty() {
buffered = early_lint_crate(
sess,
lint_store,
krate,
EarlyLintPassObjects { lints: &mut passes[..] },
buffered,
pre_expansion,
);
}
} else {
for pass in &mut passes {
buffered = time(sess, &format!("running lint: {}", pass.name()), || {
early_lint_crate(
sess,
lint_store,
krate,
EarlyLintPassObjects { lints: slice::from_mut(pass) },
buffered,
pre_expansion,
)
});
}
}

// All of the buffered lints should have been emitted at this point.
// If not, that means that we somehow buffered a lint for a node id
// that was not lint-checked (perhaps it doesn't exist?). This is a bug.
//
// Rustdoc runs everybody-loops before the early lints and removes
// function bodies, so it's totally possible for linted
// node ids to not exist (e.g., macros defined within functions for the
// unused_macro lint) anymore. So we only run this check
// when we're not in rustdoc mode. (see issue #47639)
if !sess.opts.actually_rustdoc {
for (_id, lints) in buffered.map {
for early_lint in lints {
sess.delay_span_bug(early_lint.span, "failed to process buffered lint here");
}
}
}
}
473 changes: 473 additions & 0 deletions src/librustc_lint/late.rs

Large diffs are not rendered by default.

125 changes: 125 additions & 0 deletions src/librustc_lint/levels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use rustc::hir;
use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc::hir::intravisit;
use rustc::lint::{LintLevelMap, LintLevelSets, LintLevelsBuilder, LintStore};
use rustc::ty::query::Providers;
use rustc::ty::TyCtxt;
use syntax::ast;

pub use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintId};

fn lint_levels(tcx: TyCtxt<'_>, cnum: CrateNum) -> &LintLevelMap {
assert_eq!(cnum, LOCAL_CRATE);
let store = &tcx.lint_store;
let mut builder = LintLevelMapBuilder {
levels: LintLevelSets::builder(tcx.sess, false, &store),
tcx: tcx,
store: store,
};
let krate = tcx.hir().krate();

let push = builder.levels.push(&krate.attrs, &store);
builder.levels.register_id(hir::CRATE_HIR_ID);
for macro_def in krate.exported_macros {
builder.levels.register_id(macro_def.hir_id);
}
intravisit::walk_crate(&mut builder, krate);
builder.levels.pop(push);

tcx.arena.alloc(builder.levels.build_map())
}

struct LintLevelMapBuilder<'a, 'tcx> {
levels: LintLevelsBuilder<'tcx>,
tcx: TyCtxt<'tcx>,
store: &'a LintStore,
}

impl LintLevelMapBuilder<'_, '_> {
fn with_lint_attrs<F>(&mut self, id: hir::HirId, attrs: &[ast::Attribute], f: F)
where
F: FnOnce(&mut Self),
{
let push = self.levels.push(attrs, self.store);
if push.changed {
self.levels.register_id(id);
}
f(self);
self.levels.pop(push);
}
}

impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}

fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
self.with_lint_attrs(param.hir_id, &param.attrs, |builder| {
intravisit::walk_param(builder, param);
});
}

fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
self.with_lint_attrs(it.hir_id, &it.attrs, |builder| {
intravisit::walk_item(builder, it);
});
}

fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
self.with_lint_attrs(it.hir_id, &it.attrs, |builder| {
intravisit::walk_foreign_item(builder, it);
})
}

fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
self.with_lint_attrs(e.hir_id, &e.attrs, |builder| {
intravisit::walk_expr(builder, e);
})
}

fn visit_struct_field(&mut self, s: &'tcx hir::StructField<'tcx>) {
self.with_lint_attrs(s.hir_id, &s.attrs, |builder| {
intravisit::walk_struct_field(builder, s);
})
}

fn visit_variant(
&mut self,
v: &'tcx hir::Variant<'tcx>,
g: &'tcx hir::Generics<'tcx>,
item_id: hir::HirId,
) {
self.with_lint_attrs(v.id, &v.attrs, |builder| {
intravisit::walk_variant(builder, v, g, item_id);
})
}

fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
self.with_lint_attrs(l.hir_id, &l.attrs, |builder| {
intravisit::walk_local(builder, l);
})
}

fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
self.with_lint_attrs(a.hir_id, &a.attrs, |builder| {
intravisit::walk_arm(builder, a);
})
}

fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
self.with_lint_attrs(trait_item.hir_id, &trait_item.attrs, |builder| {
intravisit::walk_trait_item(builder, trait_item);
});
}

fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
self.with_lint_attrs(impl_item.hir_id, &impl_item.attrs, |builder| {
intravisit::walk_impl_item(builder, impl_item);
});
}
}

pub fn provide(providers: &mut Providers<'_>) {
providers.lint_levels = lint_levels;
}
8 changes: 7 additions & 1 deletion src/librustc_lint/lib.rs
Original file line number Diff line number Diff line change
@@ -24,6 +24,9 @@ extern crate rustc_session;

mod array_into_iter;
pub mod builtin;
mod early;
mod late;
mod levels;
mod non_ascii_idents;
mod nonstandard_style;
mod redundant_semicolon;
@@ -57,13 +60,16 @@ use unused::*;

/// Useful for other parts of the compiler.
pub use builtin::SoftLints;
pub use early::check_ast_crate;
pub use late::check_crate;

pub fn provide(providers: &mut Providers<'_>) {
levels::provide(providers);
*providers = Providers { lint_mod, ..*providers };
}

fn lint_mod(tcx: TyCtxt<'_>, module_def_id: DefId) {
lint::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
}

macro_rules! pre_expansion_lint_passes {