Skip to content

Commit d064849

Browse files
bors[bot]matklad
andauthored
Merge #9823
9823: fix: avoid pathological macro expansions r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 977fef7 + 3e5b155 commit d064849

File tree

17 files changed

+73
-60
lines changed

17 files changed

+73
-60
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cfg/src/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use expect_test::{expect, Expect};
2-
use mbe::ast_to_token_tree;
2+
use mbe::syntax_node_to_token_tree;
33
use syntax::{ast, AstNode};
44

55
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
@@ -8,7 +8,7 @@ fn assert_parse_result(input: &str, expected: CfgExpr) {
88
let (tt, _) = {
99
let source_file = ast::SourceFile::parse(input).ok().unwrap();
1010
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
11-
ast_to_token_tree(&tt)
11+
syntax_node_to_token_tree(tt.syntax())
1212
};
1313
let cfg = CfgExpr::parse(&tt);
1414
assert_eq!(cfg, expected);
@@ -18,7 +18,7 @@ fn check_dnf(input: &str, expect: Expect) {
1818
let (tt, _) = {
1919
let source_file = ast::SourceFile::parse(input).ok().unwrap();
2020
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
21-
ast_to_token_tree(&tt)
21+
syntax_node_to_token_tree(tt.syntax())
2222
};
2323
let cfg = CfgExpr::parse(&tt);
2424
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
@@ -29,7 +29,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
2929
let (tt, _) = {
3030
let source_file = ast::SourceFile::parse(input).ok().unwrap();
3131
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
32-
ast_to_token_tree(&tt)
32+
syntax_node_to_token_tree(tt.syntax())
3333
};
3434
let cfg = CfgExpr::parse(&tt);
3535
let dnf = DnfExpr::new(cfg);
@@ -42,7 +42,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
4242
let (tt, _) = {
4343
let source_file = ast::SourceFile::parse(input).ok().unwrap();
4444
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
45-
ast_to_token_tree(&tt)
45+
syntax_node_to_token_tree(tt.syntax())
4646
};
4747
let cfg = CfgExpr::parse(&tt);
4848
let dnf = DnfExpr::new(cfg);

crates/hir_def/src/attr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use either::Either;
1212
use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
1313
use itertools::Itertools;
1414
use la_arena::ArenaMap;
15-
use mbe::{ast_to_token_tree, DelimiterKind};
15+
use mbe::{syntax_node_to_token_tree, DelimiterKind};
1616
use smallvec::{smallvec, SmallVec};
1717
use syntax::{
1818
ast::{self, AstNode, AttrsOwner},
@@ -679,7 +679,7 @@ impl Attr {
679679
};
680680
Some(Interned::new(AttrInput::Literal(value)))
681681
} else if let Some(tt) = ast.token_tree() {
682-
Some(Interned::new(AttrInput::TokenTree(ast_to_token_tree(&tt).0)))
682+
Some(Interned::new(AttrInput::TokenTree(syntax_node_to_token_tree(tt.syntax()).0)))
683683
} else {
684684
None
685685
};

crates/hir_def/src/nameres/tests/macros.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
23
use crate::nameres::proc_macro::{ProcMacroDef, ProcMacroKind};
34

45
#[test]
@@ -1021,3 +1022,20 @@ pub mod prelude {
10211022
"#]],
10221023
)
10231024
}
1025+
1026+
#[test]
1027+
fn issue9358_bad_macro_stack_overflow() {
1028+
cov_mark::check!(issue9358_bad_macro_stack_overflow);
1029+
check(
1030+
r#"
1031+
macro_rules! m {
1032+
($cond:expr) => { m!($cond, stringify!($cond)) };
1033+
($cond:expr, $($arg:tt)*) => { $cond };
1034+
}
1035+
m!(
1036+
"#,
1037+
expect![[r#"
1038+
crate
1039+
"#]],
1040+
)
1041+
}

crates/hir_expand/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ edition = "2018"
99
doctest = false
1010

1111
[dependencies]
12+
cov-mark = "2.0.0-pre.1"
1213
log = "0.4.8"
1314
either = "1.5.3"
1415
rustc-hash = "1.0.0"

crates/hir_expand/src/builtin_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ mod tests {
610610

611611
let fragment = crate::to_fragment_kind(&macro_call);
612612
let args = macro_call.token_tree().unwrap();
613-
let parsed_args = mbe::ast_to_token_tree(&args).0;
613+
let parsed_args = mbe::syntax_node_to_token_tree(args.syntax()).0;
614614
let call_id = AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call));
615615

616616
let arg_id = db.intern_macro(MacroCallLoc {

crates/hir_expand/src/db.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::sync::Arc;
55
use base_db::{salsa, SourceDatabase};
66
use limit::Limit;
77
use mbe::{ExpandError, ExpandResult};
8-
use parser::FragmentKind;
8+
use parser::{FragmentKind, T};
99
use syntax::{
1010
algo::diff,
1111
ast::{self, NameOwner},
@@ -273,6 +273,23 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
273273
let loc = db.lookup_intern_macro(id);
274274
let arg = loc.kind.arg(db)?;
275275
let arg = process_macro_input(db, arg, id);
276+
if matches!(loc.kind, MacroCallKind::FnLike { .. }) {
277+
let first = arg.first_child_or_token().map_or(T![.], |it| it.kind());
278+
let last = arg.last_child_or_token().map_or(T![.], |it| it.kind());
279+
let well_formed_tt =
280+
matches!((first, last), (T!['('], T![')']) | (T!['['], T![']']) | (T!['{'], T!['}']));
281+
if !well_formed_tt {
282+
// Don't expand malformed (unbalanced) macro invocations. This is
283+
// less than ideal, but trying to expand unbalanced macro calls
284+
// sometimes produces pathological, deeply nested code which breaks
285+
// all kinds of things.
286+
//
287+
// Some day, we'll have explicit recursion counters for all
288+
// recursive things, at which point this code might be removed.
289+
cov_mark::hit!(issue9358_bad_macro_stack_overflow);
290+
return None;
291+
}
292+
}
276293
Some(arg.green().into())
277294
}
278295

@@ -281,7 +298,7 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>>
281298
MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
282299
ast::Macro::MacroRules(macro_rules) => {
283300
let arg = macro_rules.token_tree()?;
284-
let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
301+
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
285302
let mac = match mbe::MacroRules::parse(&tt) {
286303
Ok(it) => it,
287304
Err(err) => {
@@ -294,7 +311,7 @@ fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<TokenExpander>>
294311
}
295312
ast::Macro::MacroDef(macro_def) => {
296313
let arg = macro_def.body()?;
297-
let (tt, def_site_token_map) = mbe::ast_to_token_tree(&arg);
314+
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
298315
let mac = match mbe::MacroDef::parse(&tt) {
299316
Ok(it) => it,
300317
Err(err) => {

crates/hir_expand/src/eager.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub fn expand_eager_macro(
107107
mut diagnostic_sink: &mut dyn FnMut(mbe::ExpandError),
108108
) -> Result<MacroCallId, ErrorEmitted> {
109109
let parsed_args = diagnostic_sink.option_with(
110-
|| Some(mbe::ast_to_token_tree(&macro_call.value.token_tree()?).0),
110+
|| Some(mbe::syntax_node_to_token_tree(&macro_call.value.token_tree()?.syntax()).0),
111111
|| err("malformed macro invocation"),
112112
)?;
113113

crates/ide_completion/src/tests/expression.rs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -308,27 +308,7 @@ fn quux(x: i32) {
308308
m!(x$0
309309
}
310310
"#,
311-
expect![[r#"
312-
kw unsafe
313-
kw match
314-
kw while
315-
kw while let
316-
kw loop
317-
kw if
318-
kw if let
319-
kw for
320-
kw true
321-
kw false
322-
kw return
323-
kw self
324-
kw super
325-
kw crate
326-
lc y i32
327-
bt u32
328-
lc x i32
329-
fn quux(…) fn(i32)
330-
ma m!(…) macro_rules! m
331-
"#]],
311+
expect![[r#""#]],
332312
);
333313
}
334314

crates/ide_ssr/src/tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,13 +921,13 @@ fn preserves_whitespace_within_macro_expansion() {
921921
macro_rules! macro1 {
922922
($a:expr) => {$a}
923923
}
924-
fn f() {macro1!(1 * 2 + 3 + 4}
924+
fn f() {macro1!(1 * 2 + 3 + 4)}
925925
"#,
926926
expect![[r#"
927927
macro_rules! macro1 {
928928
($a:expr) => {$a}
929929
}
930-
fn f() {macro1!(4 - (3 - 1 * 2)}
930+
fn f() {macro1!(4 - (3 - 1 * 2))}
931931
"#]],
932932
)
933933
}

0 commit comments

Comments
 (0)