Skip to content

Commit 73acb50

Browse files
committed
Properly handle attributes on statements
We now collect tokens for the underlying node wrapped by `StmtKind` instead of storing tokens directly in `Stmt`. `LazyTokenStream` now supports capturing a trailing semicolon after it is initially constructed. This allows us to avoid refactoring statement parsing to wrap the parsing of the semicolon in `parse_tokens`. Attributes on item statements (e.g. `fn foo() { #[bar] struct MyStruct; }`) are now treated as item attributes, not statement attributes, which is consistent with how we handle attributes on other kinds of statements. The feature-gating code is adjusted so that proc-macro attributes are still allowed on item statements on stable. Two built-in macros (`#[global_allocator]` and `#[test]`) needed to be adjusted to support being passed `Annotatable::Stmt`.
1 parent c919f49 commit 73acb50

23 files changed

+723
-135
lines changed

compiler/rustc_ast/src/ast.rs

+43-5
Original file line numberDiff line numberDiff line change
@@ -901,29 +901,65 @@ pub struct Stmt {
901901
pub id: NodeId,
902902
pub kind: StmtKind,
903903
pub span: Span,
904-
pub tokens: Option<LazyTokenStream>,
905904
}
906905

907906
impl Stmt {
907+
pub fn tokens(&self) -> Option<&LazyTokenStream> {
908+
match self.kind {
909+
StmtKind::Local(ref local) => local.tokens.as_ref(),
910+
StmtKind::Item(ref item) => item.tokens.as_ref(),
911+
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.tokens.as_ref(),
912+
StmtKind::Empty => None,
913+
StmtKind::MacCall(ref mac) => mac.tokens.as_ref(),
914+
}
915+
}
916+
917+
pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> {
918+
match self.kind {
919+
StmtKind::Local(ref mut local) => local.tokens.as_mut(),
920+
StmtKind::Item(ref mut item) => item.tokens.as_mut(),
921+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(),
922+
StmtKind::Empty => None,
923+
StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(),
924+
}
925+
}
926+
927+
pub fn set_tokens(&mut self, tokens: Option<LazyTokenStream>) {
928+
match self.kind {
929+
StmtKind::Local(ref mut local) => local.tokens = tokens,
930+
StmtKind::Item(ref mut item) => item.tokens = tokens,
931+
StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens = tokens,
932+
StmtKind::Empty => {}
933+
StmtKind::MacCall(ref mut mac) => mac.tokens = tokens,
934+
}
935+
}
936+
908937
pub fn has_trailing_semicolon(&self) -> bool {
909938
match &self.kind {
910939
StmtKind::Semi(_) => true,
911940
StmtKind::MacCall(mac) => matches!(mac.style, MacStmtStyle::Semicolon),
912941
_ => false,
913942
}
914943
}
944+
945+
/// Converts a parsed `Stmt` to a `Stmt` with
946+
/// a trailing semicolon.
947+
///
948+
/// This only modifies the parsed AST struct, not the attached
949+
/// `LazyTokenStream`. The parser is responsible for calling
950+
/// `CreateTokenStream::add_trailing_semi` when there is actually
951+
/// a semicolon in the tokenstream.
915952
pub fn add_trailing_semicolon(mut self) -> Self {
916953
self.kind = match self.kind {
917954
StmtKind::Expr(expr) => StmtKind::Semi(expr),
918955
StmtKind::MacCall(mac) => {
919-
StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs }| MacCallStmt {
920-
mac,
921-
style: MacStmtStyle::Semicolon,
922-
attrs,
956+
StmtKind::MacCall(mac.map(|MacCallStmt { mac, style: _, attrs, tokens }| {
957+
MacCallStmt { mac, style: MacStmtStyle::Semicolon, attrs, tokens }
923958
}))
924959
}
925960
kind => kind,
926961
};
962+
927963
self
928964
}
929965

@@ -963,6 +999,7 @@ pub struct MacCallStmt {
963999
pub mac: MacCall,
9641000
pub style: MacStmtStyle,
9651001
pub attrs: AttrVec,
1002+
pub tokens: Option<LazyTokenStream>,
9661003
}
9671004

9681005
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug)]
@@ -988,6 +1025,7 @@ pub struct Local {
9881025
pub init: Option<P<Expr>>,
9891026
pub span: Span,
9901027
pub attrs: AttrVec,
1028+
pub tokens: Option<LazyTokenStream>,
9911029
}
9921030

9931031
/// An arm of a 'match'.

compiler/rustc_ast/src/mut_visit.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -576,13 +576,14 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
576576
}
577577

578578
pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
579-
let Local { id, pat, ty, init, span, attrs } = local.deref_mut();
579+
let Local { id, pat, ty, init, span, attrs, tokens } = local.deref_mut();
580580
vis.visit_id(id);
581581
vis.visit_pat(pat);
582582
visit_opt(ty, |ty| vis.visit_ty(ty));
583583
visit_opt(init, |init| vis.visit_expr(init));
584584
vis.visit_span(span);
585585
visit_thin_attrs(attrs, vis);
586+
visit_lazy_tts(tokens, vis);
586587
}
587588

588589
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
@@ -1325,16 +1326,12 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio
13251326
}
13261327

13271328
pub fn noop_flat_map_stmt<T: MutVisitor>(
1328-
Stmt { kind, mut span, mut id, mut tokens }: Stmt,
1329+
Stmt { kind, mut span, mut id }: Stmt,
13291330
vis: &mut T,
13301331
) -> SmallVec<[Stmt; 1]> {
13311332
vis.visit_id(&mut id);
13321333
vis.visit_span(&mut span);
1333-
visit_lazy_tts(&mut tokens, vis);
1334-
noop_flat_map_stmt_kind(kind, vis)
1335-
.into_iter()
1336-
.map(|kind| Stmt { id, kind, span, tokens: tokens.clone() })
1337-
.collect()
1334+
noop_flat_map_stmt_kind(kind, vis).into_iter().map(|kind| Stmt { id, kind, span }).collect()
13381335
}
13391336

13401337
pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
@@ -1351,9 +1348,10 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
13511348
StmtKind::Semi(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Semi).collect(),
13521349
StmtKind::Empty => smallvec![StmtKind::Empty],
13531350
StmtKind::MacCall(mut mac) => {
1354-
let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut();
1351+
let MacCallStmt { mac: mac_, style: _, attrs, tokens } = mac.deref_mut();
13551352
vis.visit_mac_call(mac_);
13561353
visit_thin_attrs(attrs, vis);
1354+
visit_lazy_tts(tokens, vis);
13571355
smallvec![StmtKind::MacCall(mac)]
13581356
}
13591357
}

compiler/rustc_ast/src/tokenstream.rs

+11
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,14 @@ where
121121
}
122122

123123
pub trait CreateTokenStream: sync::Send + sync::Sync {
124+
fn add_trailing_semi(&self) -> Box<dyn CreateTokenStream>;
124125
fn create_token_stream(&self) -> TokenStream;
125126
}
126127

127128
impl CreateTokenStream for TokenStream {
129+
fn add_trailing_semi(&self) -> Box<dyn CreateTokenStream> {
130+
panic!("Cannot call `add_trailing_semi` on a `TokenStream`!");
131+
}
128132
fn create_token_stream(&self) -> TokenStream {
129133
self.clone()
130134
}
@@ -141,6 +145,13 @@ impl LazyTokenStream {
141145
LazyTokenStream(Lrc::new(Box::new(inner)))
142146
}
143147

148+
/// Extends the captured stream by one token,
149+
/// which must be a trailing semicolon. This
150+
/// affects the `TokenStream` created by `make_tokenstream`.
151+
pub fn add_trailing_semi(&self) -> LazyTokenStream {
152+
LazyTokenStream(Lrc::new(self.0.add_trailing_semi()))
153+
}
154+
144155
pub fn create_token_stream(&self) -> TokenStream {
145156
self.0.create_token_stream()
146157
}

compiler/rustc_ast/src/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -686,7 +686,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
686686
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => visitor.visit_expr(expr),
687687
StmtKind::Empty => {}
688688
StmtKind::MacCall(ref mac) => {
689-
let MacCallStmt { ref mac, style: _, ref attrs } = **mac;
689+
let MacCallStmt { ref mac, style: _, ref attrs, tokens: _ } = **mac;
690690
visitor.visit_mac_call(mac);
691691
for attr in attrs.iter() {
692692
visitor.visit_attribute(attr);

compiler/rustc_builtin_macros/src/deriving/debug.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
132132
id: ast::DUMMY_NODE_ID,
133133
span: sp,
134134
attrs: ast::AttrVec::new(),
135+
tokens: None,
135136
});
136-
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp, tokens: None }
137+
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
137138
}

compiler/rustc_builtin_macros/src/global_allocator.rs

+19-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_ast::expand::allocator::{
44
AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
55
};
66
use rustc_ast::ptr::P;
7-
use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param};
7+
use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param, StmtKind};
88
use rustc_ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe};
99
use rustc_expand::base::{Annotatable, ExtCtxt};
1010
use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -14,14 +14,25 @@ pub fn expand(
1414
ecx: &mut ExtCtxt<'_>,
1515
_span: Span,
1616
meta_item: &ast::MetaItem,
17-
item: Annotatable,
17+
mut item: Annotatable,
1818
) -> Vec<Annotatable> {
1919
check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
2020

2121
let not_static = |item: Annotatable| {
2222
ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
2323
vec![item]
2424
};
25+
let orig_item = item.clone();
26+
let mut is_stmt = false;
27+
28+
// Allow using `#[global_allocator]` on an item statement
29+
if let Annotatable::Stmt(stmt) = &item {
30+
if let StmtKind::Item(item_) = &stmt.kind {
31+
item = Annotatable::Item(item_.clone());
32+
is_stmt = true;
33+
}
34+
}
35+
2536
let item = match item {
2637
Annotatable::Item(item) => match item.kind {
2738
ItemKind::Static(..) => item,
@@ -41,9 +52,14 @@ pub fn expand(
4152
let const_ty = ecx.ty(span, TyKind::Tup(Vec::new()));
4253
let const_body = ecx.expr_block(ecx.block(span, stmts));
4354
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
55+
let const_item = if is_stmt {
56+
Annotatable::Stmt(P(ecx.stmt_item(span, const_item)))
57+
} else {
58+
Annotatable::Item(const_item)
59+
};
4460

4561
// Return the original item and the new methods.
46-
vec![Annotatable::Item(item), Annotatable::Item(const_item)]
62+
vec![orig_item, const_item]
4763
}
4864

4965
struct AllocFnFactory<'a, 'b> {

compiler/rustc_builtin_macros/src/test.rs

+30-10
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::util::check_builtin_macro_attribute;
44

55
use rustc_ast as ast;
66
use rustc_ast::attr;
7+
use rustc_ast::ptr::P;
78
use rustc_ast_pretty::pprust;
89
use rustc_expand::base::*;
910
use rustc_session::Session;
@@ -78,8 +79,16 @@ pub fn expand_test_or_bench(
7879
return vec![];
7980
}
8081

81-
let item = match item {
82-
Annotatable::Item(i) => i,
82+
let (item, is_stmt) = match item {
83+
Annotatable::Item(i) => (i, false),
84+
Annotatable::Stmt(stmt) if matches!(stmt.kind, ast::StmtKind::Item(_)) => {
85+
// FIXME: Use an 'if let' guard once they are implemented
86+
if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
87+
(i, true)
88+
} else {
89+
unreachable!()
90+
}
91+
}
8392
other => {
8493
cx.struct_span_err(
8594
other.span(),
@@ -304,14 +313,25 @@ pub fn expand_test_or_bench(
304313

305314
tracing::debug!("synthetic test item:\n{}\n", pprust::item_to_string(&test_const));
306315

307-
vec![
308-
// Access to libtest under a hygienic name
309-
Annotatable::Item(test_extern),
310-
// The generated test case
311-
Annotatable::Item(test_const),
312-
// The original item
313-
Annotatable::Item(item),
314-
]
316+
if is_stmt {
317+
vec![
318+
// Access to libtest under a hygienic name
319+
Annotatable::Stmt(P(cx.stmt_item(sp, test_extern))),
320+
// The generated test case
321+
Annotatable::Stmt(P(cx.stmt_item(sp, test_const))),
322+
// The original item
323+
Annotatable::Stmt(P(cx.stmt_item(sp, item))),
324+
]
325+
} else {
326+
vec![
327+
// Access to libtest under a hygienic name
328+
Annotatable::Item(test_extern),
329+
// The generated test case
330+
Annotatable::Item(test_const),
331+
// The original item
332+
Annotatable::Item(item),
333+
]
334+
}
315335
}
316336

317337
fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {

compiler/rustc_expand/src/base.rs

-2
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,6 @@ macro_rules! make_stmts_default {
366366
id: ast::DUMMY_NODE_ID,
367367
span: e.span,
368368
kind: ast::StmtKind::Expr(e),
369-
tokens: None
370369
}]
371370
})
372371
};
@@ -609,7 +608,6 @@ impl MacResult for DummyResult {
609608
id: ast::DUMMY_NODE_ID,
610609
kind: ast::StmtKind::Expr(DummyResult::raw_expr(self.span, self.is_error)),
611610
span: self.span,
612-
tokens: None
613611
}])
614612
}
615613

compiler/rustc_expand/src/build.rs

+6-20
Original file line numberDiff line numberDiff line change
@@ -140,12 +140,7 @@ impl<'a> ExtCtxt<'a> {
140140
}
141141

142142
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
143-
ast::Stmt {
144-
id: ast::DUMMY_NODE_ID,
145-
span: expr.span,
146-
kind: ast::StmtKind::Expr(expr),
147-
tokens: None,
148-
}
143+
ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) }
149144
}
150145

151146
pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P<ast::Expr>) -> ast::Stmt {
@@ -162,13 +157,9 @@ impl<'a> ExtCtxt<'a> {
162157
id: ast::DUMMY_NODE_ID,
163158
span: sp,
164159
attrs: AttrVec::new(),
165-
});
166-
ast::Stmt {
167-
id: ast::DUMMY_NODE_ID,
168-
kind: ast::StmtKind::Local(local),
169-
span: sp,
170160
tokens: None,
171-
}
161+
});
162+
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span: sp }
172163
}
173164

174165
// Generates `let _: Type;`, which is usually used for type assertions.
@@ -180,17 +171,13 @@ impl<'a> ExtCtxt<'a> {
180171
id: ast::DUMMY_NODE_ID,
181172
span,
182173
attrs: AttrVec::new(),
174+
tokens: None,
183175
});
184-
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span, tokens: None }
176+
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Local(local), span }
185177
}
186178

187179
pub fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
188-
ast::Stmt {
189-
id: ast::DUMMY_NODE_ID,
190-
kind: ast::StmtKind::Item(item),
191-
span: sp,
192-
tokens: None,
193-
}
180+
ast::Stmt { id: ast::DUMMY_NODE_ID, kind: ast::StmtKind::Item(item), span: sp }
194181
}
195182

196183
pub fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> {
@@ -200,7 +187,6 @@ impl<'a> ExtCtxt<'a> {
200187
id: ast::DUMMY_NODE_ID,
201188
span: expr.span,
202189
kind: ast::StmtKind::Expr(expr),
203-
tokens: None,
204190
}],
205191
)
206192
}

0 commit comments

Comments
 (0)