Skip to content

Commit 14b27cf

Browse files
authoredSep 20, 2022
Rollup merge of #100250 - cjgillot:recover-token-stream, r=Aaron1011
Manually cleanup token stream when macro expansion aborts. In case of syntax error in macro expansion, the expansion code can decide to stop processing anything. In that case, the token stream is malformed. This makes downstream users, like derive macros, ICE. In this case, this PR manually cleans up the token stream by closing all currently open delimiters. Fixes #96818. Fixes #80447. Fixes #81920. Fixes #91023.
2 parents 4136b59 + cb5ea8d commit 14b27cf

File tree

4 files changed

+84
-29
lines changed

4 files changed

+84
-29
lines changed
 

‎compiler/rustc_builtin_macros/src/cfg_eval.rs

+36-28
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_ast::visit::Visitor;
77
use rustc_ast::NodeId;
88
use rustc_ast::{mut_visit, visit};
99
use rustc_ast::{Attribute, HasAttrs, HasTokens};
10+
use rustc_errors::PResult;
1011
use rustc_expand::base::{Annotatable, ExtCtxt};
1112
use rustc_expand::config::StripUnconfigured;
1213
use rustc_expand::configure;
@@ -144,33 +145,34 @@ impl CfgEval<'_, '_> {
144145
// the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization
145146
// process is lossless, so this process is invisible to proc-macros.
146147

147-
let parse_annotatable_with: fn(&mut Parser<'_>) -> _ = match annotatable {
148-
Annotatable::Item(_) => {
149-
|parser| Annotatable::Item(parser.parse_item(ForceCollect::Yes).unwrap().unwrap())
150-
}
151-
Annotatable::TraitItem(_) => |parser| {
152-
Annotatable::TraitItem(
153-
parser.parse_trait_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
154-
)
155-
},
156-
Annotatable::ImplItem(_) => |parser| {
157-
Annotatable::ImplItem(
158-
parser.parse_impl_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
159-
)
160-
},
161-
Annotatable::ForeignItem(_) => |parser| {
162-
Annotatable::ForeignItem(
163-
parser.parse_foreign_item(ForceCollect::Yes).unwrap().unwrap().unwrap(),
164-
)
165-
},
166-
Annotatable::Stmt(_) => |parser| {
167-
Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes).unwrap().unwrap()))
168-
},
169-
Annotatable::Expr(_) => {
170-
|parser| Annotatable::Expr(parser.parse_expr_force_collect().unwrap())
171-
}
172-
_ => unreachable!(),
173-
};
148+
let parse_annotatable_with: for<'a> fn(&mut Parser<'a>) -> PResult<'a, _> =
149+
match annotatable {
150+
Annotatable::Item(_) => {
151+
|parser| Ok(Annotatable::Item(parser.parse_item(ForceCollect::Yes)?.unwrap()))
152+
}
153+
Annotatable::TraitItem(_) => |parser| {
154+
Ok(Annotatable::TraitItem(
155+
parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(),
156+
))
157+
},
158+
Annotatable::ImplItem(_) => |parser| {
159+
Ok(Annotatable::ImplItem(
160+
parser.parse_impl_item(ForceCollect::Yes)?.unwrap().unwrap(),
161+
))
162+
},
163+
Annotatable::ForeignItem(_) => |parser| {
164+
Ok(Annotatable::ForeignItem(
165+
parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(),
166+
))
167+
},
168+
Annotatable::Stmt(_) => |parser| {
169+
Ok(Annotatable::Stmt(P(parser.parse_stmt(ForceCollect::Yes)?.unwrap())))
170+
},
171+
Annotatable::Expr(_) => {
172+
|parser| Ok(Annotatable::Expr(parser.parse_expr_force_collect()?))
173+
}
174+
_ => unreachable!(),
175+
};
174176

175177
// 'Flatten' all nonterminals (i.e. `TokenKind::Interpolated`)
176178
// to `None`-delimited groups containing the corresponding tokens. This
@@ -193,7 +195,13 @@ impl CfgEval<'_, '_> {
193195
let mut parser =
194196
rustc_parse::stream_to_parser(&self.cfg.sess.parse_sess, orig_tokens, None);
195197
parser.capture_cfg = true;
196-
annotatable = parse_annotatable_with(&mut parser);
198+
match parse_annotatable_with(&mut parser) {
199+
Ok(a) => annotatable = a,
200+
Err(mut err) => {
201+
err.emit();
202+
return Some(annotatable);
203+
}
204+
}
197205

198206
// Now that we have our re-parsed `AttrTokenStream`, recursively configuring
199207
// our attribute target will correctly the tokens as well.

‎compiler/rustc_parse/src/parser/attr_wrapper.rs

-1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,5 @@ fn make_token_stream(
459459
panic!("Unexpected last token {:?}", last_token)
460460
}
461461
}
462-
assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack);
463462
AttrTokenStream::new(final_buf.inner)
464463
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
macro_rules! values {
2+
($($token:ident($value:literal) $(as $inner:ty)? => $attr:meta,)*) => {
3+
#[derive(Debug)]
4+
pub enum TokenKind {
5+
$(
6+
#[$attr]
7+
$token $($inner)? = $value,
8+
)*
9+
}
10+
};
11+
}
12+
//~^^^^^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
13+
//~| ERROR macro expansion ignores token `(String)` and any following
14+
15+
values!(STRING(1) as (String) => cfg(test),);
16+
//~^ ERROR expected one of `!` or `::`, found `<eof>`
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error: expected one of `(`, `,`, `=`, `{`, or `}`, found `(String)`
2+
--> $DIR/syntax-error-recovery.rs:7:26
3+
|
4+
LL | $token $($inner)? = $value,
5+
| ^^^^^^ expected one of `(`, `,`, `=`, `{`, or `}`
6+
...
7+
LL | values!(STRING(1) as (String) => cfg(test),);
8+
| -------------------------------------------- in this macro invocation
9+
|
10+
= note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
11+
12+
error: macro expansion ignores token `(String)` and any following
13+
--> $DIR/syntax-error-recovery.rs:7:26
14+
|
15+
LL | $token $($inner)? = $value,
16+
| ^^^^^^
17+
...
18+
LL | values!(STRING(1) as (String) => cfg(test),);
19+
| -------------------------------------------- caused by the macro expansion here
20+
|
21+
= note: the usage of `values!` is likely invalid in item context
22+
23+
error: expected one of `!` or `::`, found `<eof>`
24+
--> $DIR/syntax-error-recovery.rs:15:9
25+
|
26+
LL | values!(STRING(1) as (String) => cfg(test),);
27+
| ^^^^^^ expected one of `!` or `::`
28+
29+
error: aborting due to 3 previous errors
30+

0 commit comments

Comments
 (0)