Skip to content

Commit

Permalink
Auto merge of #87146 - Aaron1011:better-macro-lint, r=petrochenkov
Browse files Browse the repository at this point in the history
Compute a better `lint_node_id` during expansion

When we need to emit a lint at a macro invocation, we currently use the
`NodeId` of its parent definition (e.g. the enclosing function). This
means that any `#[allow]` / `#[deny]` attributes placed 'closer' to the
macro (e.g. on an enclosing block or statement) will have no effect.

This commit computes a better `lint_node_id` in `InvocationCollector`.
When we visit/flat_map an AST node, we assign it a `NodeId` (earlier
than we normally would), and store than `NodeId` in current
`ExpansionData`. When we collect a macro invocation, the current
`lint_node_id` gets cloned along with our `ExpansionData`, allowing it
to be used if we need to emit a lint later on.

This improves the handling of `#[allow]` / `#[deny]` for
`SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` and some `asm!`-related lints.
The 'legacy derive helpers' lint retains its current behavior
(I've inlined the now-removed `lint_node_id` function), since
there isn't an `ExpansionData` readily available.
  • Loading branch information
bors committed Jul 19, 2021
2 parents 10c0b00 + 1c1c794 commit 0ecff8c
Show file tree
Hide file tree
Showing 13 changed files with 226 additions and 59 deletions.
4 changes: 2 additions & 2 deletions compiler/rustc_builtin_macros/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,15 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
ecx.parse_sess().buffer_lint(
lint::builtin::BAD_ASM_STYLE,
find_span(".intel_syntax"),
ecx.resolver.lint_node_id(ecx.current_expansion.id),
ecx.current_expansion.lint_node_id,
"avoid using `.intel_syntax`, Intel syntax is the default",
);
}
if template_str.contains(".att_syntax") {
ecx.parse_sess().buffer_lint(
lint::builtin::BAD_ASM_STYLE,
find_span(".att_syntax"),
ecx.resolver.lint_node_id(ecx.current_expansion.id),
ecx.current_expansion.lint_node_id,
"avoid using `.att_syntax`, prefer using `options(att_syntax)` instead",
);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/source_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn expand_include<'cx>(
}
}

Box::new(ExpandResult { p, node_id: cx.resolver.lint_node_id(cx.current_expansion.id) })
Box::new(ExpandResult { p, node_id: cx.current_expansion.lint_node_id })
}

// include_str! : read the given file, insert it as a literal string expr
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ use std::rc::Rc;

crate use rustc_span::hygiene::MacroKind;

// When adding new variants, make sure to
// adjust the `visit_*` / `flat_map_*` calls in `InvocationCollector`
// to use `assign_id!`
#[derive(Debug, Clone)]
pub enum Annotatable {
Item(P<ast::Item>),
Expand Down Expand Up @@ -869,9 +872,6 @@ pub trait ResolverExpand {

fn check_unused_macros(&mut self);

/// Some parent node that is close enough to the given macro call.
fn lint_node_id(&self, expn_id: LocalExpnId) -> NodeId;

// Resolver interfaces for specific built-in macros.
/// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it?
fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool;
Expand Down Expand Up @@ -926,6 +926,8 @@ pub struct ExpansionData {
pub module: Rc<ModuleData>,
pub dir_ownership: DirOwnership,
pub prior_type_ascription: Option<(Span, bool)>,
/// Some parent node that is close to this macro call
pub lint_node_id: NodeId,
}

type OnExternModLoaded<'a> =
Expand Down Expand Up @@ -971,6 +973,7 @@ impl<'a> ExtCtxt<'a> {
module: Default::default(),
dir_ownership: DirOwnership::Owned { relative: None },
prior_type_ascription: None,
lint_node_id: ast::CRATE_NODE_ID,
},
force_mode: false,
expansions: FxHashMap::default(),
Expand Down
103 changes: 83 additions & 20 deletions compiler/rustc_expand/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs};
use rustc_ast::{AstLike, Block, Inline, ItemKind, Local, MacArgs};
use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
use rustc_ast_pretty::pprust;
Expand Down Expand Up @@ -1098,6 +1098,43 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
}
}

/// Wraps a call to `noop_visit_*` / `noop_flat_map_*`
/// for an AST node that supports attributes
/// (see the `Annotatable` enum)
/// This method assigns a `NodeId`, and sets that `NodeId`
/// as our current 'lint node id'. If a macro call is found
/// inside this AST node, we will use this AST node's `NodeId`
/// to emit lints associated with that macro (allowing
/// `#[allow]` / `#[deny]` to be applied close to
/// the macro invocation).
///
/// Do *not* call this for a macro AST node
/// (e.g. `ExprKind::MacCall`) - we cannot emit lints
/// at these AST nodes, since they are removed and
/// replaced with the result of macro expansion.
///
/// All other `NodeId`s are assigned by `visit_id`.
/// * `self` is the 'self' parameter for the current method,
/// * `id` is a mutable reference to the `NodeId` field
/// of the current AST node.
/// * `closure` is a closure that executes the
/// `noop_visit_*` / `noop_flat_map_*` method
/// for the current AST node.
macro_rules! assign_id {
($self:ident, $id:expr, $closure:expr) => {{
let old_id = $self.cx.current_expansion.lint_node_id;
if $self.monotonic {
debug_assert_eq!(*$id, ast::DUMMY_NODE_ID);
let new_id = $self.cx.resolver.next_node_id();
*$id = new_id;
$self.cx.current_expansion.lint_node_id = new_id;
}
let ret = ($closure)();
$self.cx.current_expansion.lint_node_id = old_id;
ret
}};
}

impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
self.cfg.configure_expr(expr);
Expand All @@ -1118,12 +1155,19 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
self.check_attributes(&expr.attrs);
self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner()
} else {
ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
assign_id!(self, &mut expr.id, || {
ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self));
});
expr
}
});
}

// This is needed in order to set `lint_node_id` for `let` statements
fn visit_local(&mut self, local: &mut P<Local>) {
assign_id!(self, &mut local.id, || noop_visit_local(local, self));
}

fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
let mut arm = configure!(self, arm);

Expand All @@ -1133,7 +1177,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_arms();
}

noop_flat_map_arm(arm, self)
assign_id!(self, &mut arm.id, || noop_flat_map_arm(arm, self))
}

fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
Expand All @@ -1145,7 +1189,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_expr_fields();
}

noop_flat_map_expr_field(field, self)
assign_id!(self, &mut field.id, || noop_flat_map_expr_field(field, self))
}

fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
Expand All @@ -1157,7 +1201,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_pat_fields();
}

noop_flat_map_pat_field(fp, self)
assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self))
}

fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> {
Expand All @@ -1169,7 +1213,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_params();
}

noop_flat_map_param(p, self)
assign_id!(self, &mut p.id, || noop_flat_map_param(p, self))
}

fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
Expand All @@ -1181,7 +1225,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_field_defs();
}

noop_flat_map_field_def(sf, self)
assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self))
}

fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
Expand All @@ -1193,7 +1237,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_variants();
}

noop_flat_map_variant(variant, self)
assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self))
}

fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
Expand All @@ -1214,9 +1258,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_opt_expr()
.map(|expr| expr.into_inner())
} else {
Some({
noop_visit_expr(&mut expr, self);
expr
assign_id!(self, &mut expr.id, || {
Some({
noop_visit_expr(&mut expr, self);
expr
})
})
}
})
Expand Down Expand Up @@ -1266,6 +1312,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
}

// The placeholder expander gives ids to statements, so we avoid folding the id here.
// We don't use `assign_id!` - it will be called when we visit statement's contents
// (e.g. an expression, item, or local)
let ast::Stmt { id, kind, span } = stmt;
noop_flat_map_stmt_kind(kind, self)
.into_iter()
Expand Down Expand Up @@ -1377,7 +1425,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
let orig_dir_ownership =
mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership);

let result = noop_flat_map_item(item, self);
let result = assign_id!(self, &mut item.id, || noop_flat_map_item(item, self));

// Restore the module info.
self.cx.current_expansion.dir_ownership = orig_dir_ownership;
Expand All @@ -1387,7 +1435,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
}
_ => {
item.attrs = attrs;
noop_flat_map_item(item, self)
// The crate root is special - don't assign an ID to it.
if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::invalid()) {
assign_id!(self, &mut item.id, || noop_flat_map_item(item, self))
} else {
noop_flat_map_item(item, self)
}
}
}
}
Expand All @@ -1411,7 +1464,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
_ => unreachable!(),
})
}
_ => noop_flat_map_assoc_item(item, self),
_ => {
assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
}
}
}

Expand All @@ -1434,7 +1489,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
_ => unreachable!(),
})
}
_ => noop_flat_map_assoc_item(item, self),
_ => {
assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self))
}
}
}

Expand Down Expand Up @@ -1478,7 +1535,12 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
_ => unreachable!(),
})
}
_ => noop_flat_map_foreign_item(foreign_item, self),
_ => {
assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item(
foreign_item,
self
))
}
}
}

Expand All @@ -1498,13 +1560,14 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
.make_generic_params();
}

noop_flat_map_generic_param(param, self)
assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self))
}

fn visit_id(&mut self, id: &mut ast::NodeId) {
if self.monotonic {
debug_assert_eq!(*id, ast::DUMMY_NODE_ID);
*id = self.cx.resolver.next_node_id()
// We may have already assigned a `NodeId`
// by calling `assign_id`
if self.monotonic && *id == ast::DUMMY_NODE_ID {
*id = self.cx.resolver.next_node_id();
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions compiler/rustc_expand/src/mbe/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,6 @@ fn generic_extension<'cx>(

let mut p = Parser::new(sess, tts, false, None);
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
let lint_node_id = cx.resolver.lint_node_id(cx.current_expansion.id);

// Let the context choose how to interpret the result.
// Weird, but useful for X-macros.
Expand All @@ -301,7 +300,7 @@ fn generic_extension<'cx>(
// macro leaves unparsed tokens.
site_span: sp,
macro_ident: name,
lint_node_id,
lint_node_id: cx.current_expansion.lint_node_id,
arm_span,
});
}
Expand Down
22 changes: 18 additions & 4 deletions compiler/rustc_lint/src/early.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
})
}

fn visit_expr_field(&mut self, f: &'a ast::ExprField) {
self.with_lint_attrs(f.id, &f.attrs, |cx| {
ast_visit::walk_expr_field(cx, f);
})
}

fn visit_stmt(&mut self, s: &'a ast::Stmt) {
// Add the statement's lint attributes to our
// current state when checking the statement itself.
Expand Down Expand Up @@ -204,8 +210,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
}

fn visit_arm(&mut self, a: &'a ast::Arm) {
run_early_pass!(self, check_arm, a);
ast_visit::walk_arm(self, a);
self.with_lint_attrs(a.id, &a.attrs, |cx| {
run_early_pass!(cx, check_arm, a);
ast_visit::walk_arm(cx, a);
})
}

fn visit_expr_post(&mut self, e: &'a ast::Expr) {
Expand Down Expand Up @@ -389,9 +397,15 @@ pub fn check_ast_crate<T: EarlyLintPass>(
// 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.
for (_id, lints) in buffered.map {
for (id, lints) in buffered.map {
for early_lint in lints {
sess.delay_span_bug(early_lint.span, "failed to process buffered lint here");
sess.delay_span_bug(
early_lint.span,
&format!(
"failed to process buffered lint here (dummy = {})",
id == ast::DUMMY_NODE_ID
),
);
}
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,15 @@ impl<HCX> ToStableHashKey<HCX> for LintId {
}

// Duplicated from rustc_session::config::ExternDepSpec to avoid cyclic dependency
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum ExternDepSpec {
Json(Json),
Raw(String),
}

// This could be a closure, but then implementing derive trait
// becomes hacky (and it gets allocated).
#[derive(PartialEq)]
#[derive(PartialEq, Debug)]
pub enum BuiltinLintDiagnostics {
Normal,
BareTraitObject(Span, /* is_global */ bool),
Expand Down
Loading

0 comments on commit 0ecff8c

Please sign in to comment.