Skip to content

Commit e1a9461

Browse files
committed
Add identity expansion checking
1 parent 92b561b commit e1a9461

File tree

3 files changed

+55
-3
lines changed

3 files changed

+55
-3
lines changed

crates/ra_hir_expand/src/db.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use mbe::{ExpandResult, MacroRules};
66
use ra_db::{salsa, SourceDatabase};
77
use ra_parser::FragmentKind;
88
use ra_prof::profile;
9-
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
9+
use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode};
1010

1111
use crate::{
1212
ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
@@ -238,7 +238,7 @@ pub fn parse_macro_with_arg(
238238
} else {
239239
db.macro_expand(macro_call_id)
240240
};
241-
if let Some(err) = err {
241+
if let Some(err) = &err {
242242
// Note:
243243
// The final goal we would like to make all parse_macro success,
244244
// such that the following log will not call anyway.
@@ -272,7 +272,25 @@ pub fn parse_macro_with_arg(
272272
let fragment_kind = to_fragment_kind(db, macro_call_id);
273273

274274
let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
275-
Some((parse, Arc::new(rev_token_map)))
275+
276+
if err.is_none() {
277+
Some((parse, Arc::new(rev_token_map)))
278+
} else {
279+
// FIXME:
280+
// In future, we should proprate the actual error with recovery information
281+
// instead of ignore the error here.
282+
283+
// Safe check for recurisve identity macro
284+
let node = parse.syntax_node();
285+
let file: HirFileId = macro_file.into();
286+
let call_node = file.call_node(db)?;
287+
288+
if !diff(&node, &call_node.value).is_empty() {
289+
Some((parse, Arc::new(rev_token_map)))
290+
} else {
291+
None
292+
}
293+
}
276294
}
277295

278296
/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.

crates/ra_ide/src/diagnostics.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,4 +715,34 @@ fn main() {
715715
check_struct_shorthand_initialization,
716716
);
717717
}
718+
719+
#[test]
720+
fn test_bad_macro_stackover() {
721+
check_no_diagnostic(
722+
r#"
723+
//- /main.rs
724+
#[macro_export]
725+
macro_rules! match_ast {
726+
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
727+
728+
(match ($node:expr) {
729+
$( ast::$ast:ident($it:ident) => $res:expr, )*
730+
_ => $catch_all:expr $(,)?
731+
}) => {{
732+
$( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
733+
{ $catch_all }
734+
}};
735+
}
736+
737+
fn main() {
738+
let anchor = match_ast! {
739+
match parent {
740+
as => {},
741+
_ => return None
742+
}
743+
};
744+
}
745+
"#,
746+
);
747+
}
718748
}

crates/ra_syntax/src/algo.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ impl TreeDiff {
9595
builder.replace(from.text_range(), to.to_string())
9696
}
9797
}
98+
99+
pub fn is_empty(&self) -> bool {
100+
self.replacements.is_empty()
101+
}
98102
}
99103

100104
/// Finds minimal the diff, which, applied to `from`, will result in `to`.

0 commit comments

Comments
 (0)