Skip to content

Commit d7da82a

Browse files
committed
expand: Treat more macro calls as statement macro calls
1 parent 84b0183 commit d7da82a

File tree

4 files changed

+107
-55
lines changed

4 files changed

+107
-55
lines changed

compiler/rustc_ast/src/mut_visit.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -1371,7 +1371,17 @@ pub fn noop_flat_map_stmt<T: MutVisitor>(
13711371
) -> SmallVec<[Stmt; 1]> {
13721372
vis.visit_id(&mut id);
13731373
vis.visit_span(&mut span);
1374-
noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect()
1374+
let stmts: SmallVec<_> = noop_flat_map_stmt_kind(kind, vis)
1375+
.into_iter()
1376+
.map(|kind| Stmt { id, kind, span })
1377+
.collect();
1378+
if stmts.len() > 1 {
1379+
panic!(
1380+
"cloning statement `NodeId`s is prohibited by default, \
1381+
the visitor should implement custom statement visiting"
1382+
);
1383+
}
1384+
stmts
13751385
}
13761386

13771387
pub fn noop_flat_map_stmt_kind<T: MutVisitor>(

compiler/rustc_expand/src/expand.rs

+67-33
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_ast::ptr::P;
1212
use rustc_ast::token;
1313
use rustc_ast::tokenstream::TokenStream;
1414
use rustc_ast::visit::{self, AssocCtxt, Visitor};
15-
use rustc_ast::{AstLike, Block, Inline, ItemKind, Local, MacArgs, MacCall};
15+
use rustc_ast::{AstLike, Block, Inline, ItemKind, MacArgs, MacCall};
1616
use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem};
1717
use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe};
1818
use rustc_ast_pretty::pprust;
@@ -559,7 +559,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
559559
self.cx.force_mode = orig_force_mode;
560560

561561
// Finally incorporate all the expanded macros into the input AST fragment.
562-
let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic);
562+
let mut placeholder_expander = PlaceholderExpander::default();
563563
while let Some(expanded_fragments) = expanded_fragments.pop() {
564564
for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() {
565565
placeholder_expander
@@ -1061,13 +1061,51 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
10611061
attr
10621062
}
10631063

1064+
fn take_stmt_bang(
1065+
&mut self,
1066+
stmt: ast::Stmt,
1067+
) -> Result<(bool, MacCall, Vec<ast::Attribute>), ast::Stmt> {
1068+
match stmt.kind {
1069+
StmtKind::MacCall(mac) => {
1070+
let MacCallStmt { mac, style, attrs, .. } = mac.into_inner();
1071+
Ok((style == MacStmtStyle::Semicolon, mac, attrs.into()))
1072+
}
1073+
StmtKind::Item(ref item) if matches!(item.kind, ItemKind::MacCall(..)) => {
1074+
match stmt.kind {
1075+
StmtKind::Item(item) => match item.into_inner() {
1076+
ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => {
1077+
Ok((mac.args.need_semicolon(), mac, attrs))
1078+
}
1079+
_ => unreachable!(),
1080+
},
1081+
_ => unreachable!(),
1082+
}
1083+
}
1084+
StmtKind::Semi(ref expr) if matches!(expr.kind, ast::ExprKind::MacCall(..)) => {
1085+
match stmt.kind {
1086+
StmtKind::Semi(expr) => match expr.into_inner() {
1087+
ast::Expr { kind: ast::ExprKind::MacCall(mac), attrs, .. } => {
1088+
Ok((mac.args.need_semicolon(), mac, attrs.into()))
1089+
}
1090+
_ => unreachable!(),
1091+
},
1092+
_ => unreachable!(),
1093+
}
1094+
}
1095+
StmtKind::Local(..) | StmtKind::Empty | StmtKind::Item(..) | StmtKind::Semi(..) => {
1096+
Err(stmt)
1097+
}
1098+
StmtKind::Expr(..) => unreachable!(),
1099+
}
1100+
}
1101+
10641102
fn configure<T: AstLike>(&mut self, node: T) -> Option<T> {
10651103
self.cfg.configure(node)
10661104
}
10671105

10681106
// Detect use of feature-gated or invalid attributes on macro invocations
10691107
// since they will not be detected after macro expansion.
1070-
fn check_attributes(&mut self, attrs: &[ast::Attribute], call: &MacCall) {
1108+
fn check_attributes(&self, attrs: &[ast::Attribute], call: &MacCall) {
10711109
let features = self.cx.ecfg.features.unwrap();
10721110
let mut attrs = attrs.iter().peekable();
10731111
let mut span: Option<Span> = None;
@@ -1177,11 +1215,6 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
11771215
});
11781216
}
11791217

1180-
// This is needed in order to set `lint_node_id` for `let` statements
1181-
fn visit_local(&mut self, local: &mut P<Local>) {
1182-
assign_id!(self, &mut local.id, || noop_visit_local(local, self));
1183-
}
1184-
11851218
fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
11861219
let mut arm = configure!(self, arm);
11871220

@@ -1299,31 +1332,39 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
12991332
fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
13001333
let mut stmt = configure!(self, stmt);
13011334

1302-
// we'll expand attributes on expressions separately
1303-
if !stmt.is_expr() {
1335+
// We pull macro invocations (both attributes and fn-like macro calls) out of their
1336+
// `StmtKind`s and treat them as statement macro invocations, not as items or expressions.
1337+
// FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
1338+
// changing that requires some compatibility measures.
1339+
let mut stmt = if !stmt.is_expr() {
13041340
if let Some(attr) = self.take_first_attr(&mut stmt) {
13051341
return self
13061342
.collect_attr(attr, Annotatable::Stmt(P(stmt)), AstFragmentKind::Stmts)
13071343
.make_stmts();
13081344
}
1309-
}
13101345

1311-
if let StmtKind::MacCall(mac) = stmt.kind {
1312-
let MacCallStmt { mac, style, attrs, tokens: _ } = mac.into_inner();
1313-
self.check_attributes(&attrs, &mac);
1314-
let mut placeholder =
1315-
self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts();
1316-
1317-
// If this is a macro invocation with a semicolon, then apply that
1318-
// semicolon to the final statement produced by expansion.
1319-
if style == MacStmtStyle::Semicolon {
1320-
if let Some(stmt) = placeholder.pop() {
1321-
placeholder.push(stmt.add_trailing_semicolon());
1346+
let span = stmt.span;
1347+
match self.take_stmt_bang(stmt) {
1348+
Ok((add_semicolon, mac, attrs)) => {
1349+
self.check_attributes(&attrs, &mac);
1350+
let mut stmts =
1351+
self.collect_bang(mac, span, AstFragmentKind::Stmts).make_stmts();
1352+
1353+
// If this is a macro invocation with a semicolon, then apply that
1354+
// semicolon to the final statement produced by expansion.
1355+
if add_semicolon {
1356+
if let Some(stmt) = stmts.pop() {
1357+
stmts.push(stmt.add_trailing_semicolon());
1358+
}
1359+
}
1360+
1361+
return stmts;
13221362
}
1363+
Err(stmt) => stmt,
13231364
}
1324-
1325-
return placeholder;
1326-
}
1365+
} else {
1366+
stmt
1367+
};
13271368

13281369
// The only way that we can end up with a `MacCall` expression statement,
13291370
// (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
@@ -1338,14 +1379,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
13381379
}
13391380
}
13401381

1341-
// The placeholder expander gives ids to statements, so we avoid folding the id here.
1342-
// We don't use `assign_id!` - it will be called when we visit statement's contents
1343-
// (e.g. an expression, item, or local)
1344-
let ast::Stmt { id, kind, span } = stmt;
1345-
let res = noop_flat_map_stmt_kind(kind, self)
1346-
.into_iter()
1347-
.map(|kind| ast::Stmt { id, kind, span })
1348-
.collect();
1382+
let res = assign_id!(self, &mut stmt.id, || noop_flat_map_stmt(stmt, self));
13491383

13501384
self.cx.current_expansion.is_trailing_mac = false;
13511385
res

compiler/rustc_expand/src/placeholders.rs

+4-21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::base::ExtCtxt;
21
use crate::expand::{AstFragment, AstFragmentKind};
32

43
use rustc_ast as ast;
@@ -175,17 +174,12 @@ pub fn placeholder(
175174
}
176175
}
177176

178-
pub struct PlaceholderExpander<'a, 'b> {
177+
#[derive(Default)]
178+
pub struct PlaceholderExpander {
179179
expanded_fragments: FxHashMap<ast::NodeId, AstFragment>,
180-
cx: &'a mut ExtCtxt<'b>,
181-
monotonic: bool,
182180
}
183181

184-
impl<'a, 'b> PlaceholderExpander<'a, 'b> {
185-
pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
186-
PlaceholderExpander { cx, expanded_fragments: FxHashMap::default(), monotonic }
187-
}
188-
182+
impl PlaceholderExpander {
189183
pub fn add(&mut self, id: ast::NodeId, mut fragment: AstFragment) {
190184
fragment.mut_visit_with(self);
191185
self.expanded_fragments.insert(id, fragment);
@@ -196,7 +190,7 @@ impl<'a, 'b> PlaceholderExpander<'a, 'b> {
196190
}
197191
}
198192

199-
impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
193+
impl MutVisitor for PlaceholderExpander {
200194
fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
201195
if arm.is_placeholder {
202196
self.remove(arm.id).make_arms()
@@ -360,15 +354,4 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> {
360354
_ => noop_visit_ty(ty, self),
361355
}
362356
}
363-
364-
fn visit_block(&mut self, block: &mut P<ast::Block>) {
365-
noop_visit_block(block, self);
366-
367-
for stmt in block.stmts.iter_mut() {
368-
if self.monotonic {
369-
assert_eq!(stmt.id, ast::DUMMY_NODE_ID);
370-
stmt.id = self.cx.resolver.next_node_id();
371-
}
372-
}
373-
}
374357
}

src/test/ui/macros/issue-87877.rs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// check-pass
2+
3+
macro_rules! two_items {
4+
() => {
5+
extern "C" {}
6+
extern "C" {}
7+
};
8+
}
9+
10+
macro_rules! single_expr_funneler {
11+
($expr:expr) => {
12+
$expr; // note the semicolon, it changes the statement kind during parsing
13+
};
14+
}
15+
16+
macro_rules! single_item_funneler {
17+
($item:item) => {
18+
$item
19+
};
20+
}
21+
22+
fn main() {
23+
single_expr_funneler! { two_items! {} }
24+
single_item_funneler! { two_items! {} }
25+
}

0 commit comments

Comments
 (0)