Skip to content

Commit 6cad4c7

Browse files
Rollup merge of rust-lang#158417 - TaKO8Ki:fix-cfg-eval-derive-reparse-ice, r=petrochenkov
Avoid ICE when cfg_eval recovers no item from derive input Fixes rust-lang#148891 `cfg_eval` reparses derive input when it contains `#[cfg]` or `#[cfg_attr]` so it can capture cfg positions in the token stream. That reparse can emit syntax errors and return `Ok(None)` when parser recovery cannot reconstruct an item. This pr changes the reparse path to return `Option<Annotatable>` and fall back to the original annotatable when recovery produces no node.
2 parents 6f0e170 + 3f88370 commit 6cad4c7

4 files changed

Lines changed: 64 additions & 31 deletions

File tree

compiler/rustc_builtin_macros/src/cfg_eval.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,40 +107,42 @@ impl CfgEval<'_> {
107107
// our attribute target will correctly configure the tokens as well.
108108
let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None);
109109
parser.capture_cfg = true;
110-
let res: PResult<'_, Annotatable> = try {
111-
match annotatable {
112-
Annotatable::Item(_) => {
113-
let item =
114-
parser.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?.unwrap();
115-
Annotatable::Item(self.flat_map_item(item).pop().unwrap())
116-
}
110+
let res: PResult<'_, Option<Annotatable>> = try {
111+
match &annotatable {
112+
Annotatable::Item(_) => parser
113+
.parse_item(ForceCollect::Yes, AllowConstBlockItems::Yes)?
114+
.and_then(|item| self.flat_map_item(item).pop().map(Annotatable::Item)),
117115
Annotatable::AssocItem(_, ctxt) => {
118-
let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap();
119-
Annotatable::AssocItem(
120-
self.flat_map_assoc_item(item, ctxt).pop().unwrap(),
121-
ctxt,
122-
)
116+
parser.parse_trait_item(ForceCollect::Yes)?.flatten().and_then(|item| {
117+
self.flat_map_assoc_item(item, *ctxt)
118+
.pop()
119+
.map(|item| Annotatable::AssocItem(item, *ctxt))
120+
})
123121
}
124122
Annotatable::ForeignItem(_) => {
125-
let item = parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap();
126-
Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap())
123+
parser.parse_foreign_item(ForceCollect::Yes)?.flatten().and_then(|item| {
124+
self.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
125+
})
127126
}
128127
Annotatable::Stmt(_) => {
129128
let stmt =
130129
parser.parse_stmt_without_recovery(false, ForceCollect::Yes, false)?;
131-
Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap()))
130+
self.flat_map_stmt(stmt).pop().map(|stmt| Annotatable::Stmt(Box::new(stmt)))
132131
}
133132
Annotatable::Expr(_) => {
134133
let mut expr = parser.parse_expr_force_collect()?;
135134
self.visit_expr(&mut expr);
136-
Annotatable::Expr(expr)
135+
Some(Annotatable::Expr(expr))
137136
}
138137
_ => unreachable!(),
139138
}
140139
};
141140

142141
match res {
143-
Ok(ann) => ann,
142+
Ok(Some(ann)) => ann,
143+
// Parser recovery may emit errors without reconstructing an annotatable.
144+
// Keep the original node so cfg-eval stays best-effort instead of ICEing.
145+
Ok(None) => annotatable,
144146
Err(err) => {
145147
err.emit();
146148
annotatable

tests/crashes/148891.rs

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Regression test for https://github.com/rust-lang/rust/issues/148891.
2+
3+
macro_rules! values {
4+
($inner:ty) => {
5+
#[derive(Debug)]
6+
pub enum TokenKind {
7+
#[cfg(test)]
8+
STRING([u8; $inner]),
9+
//~^ ERROR expected expression, found `ty` metavariable
10+
//~| ERROR macro expansion ignores `)` and any tokens following
11+
}
12+
};
13+
}
14+
15+
values!(String);
16+
17+
fn main() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: expected expression, found `ty` metavariable
2+
--> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:25
3+
|
4+
LL | pub enum TokenKind {
5+
| --------- while parsing this enum
6+
LL | #[cfg(test)]
7+
LL | STRING([u8; $inner]),
8+
| ^^^^^^ expected expression
9+
...
10+
LL | values!(String);
11+
| --------------- in this macro invocation
12+
|
13+
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
14+
= note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
16+
error: macro expansion ignores `)` and any tokens following
17+
--> $DIR/cfg-eval-derive-invalid-reparse-no-ice.rs:8:32
18+
|
19+
LL | STRING([u8; $inner]),
20+
| ^
21+
...
22+
LL | values!(String);
23+
| --------------- caused by the macro expansion here
24+
|
25+
= note: the usage of `values!` is likely invalid in item context
26+
27+
error: aborting due to 2 previous errors
28+

0 commit comments

Comments
 (0)