Add identity expansion checking

This commit is contained in:
Edwin Cheng 2020-03-21 22:43:48 +08:00
parent 92b561b5c7
commit e1a9461806
3 changed files with 55 additions and 3 deletions

View file

@ -6,7 +6,7 @@ use mbe::{ExpandResult, MacroRules};
use ra_db::{salsa, SourceDatabase};
use ra_parser::FragmentKind;
use ra_prof::profile;
use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode};
use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode};
use crate::{
ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
@ -238,7 +238,7 @@ pub fn parse_macro_with_arg(
} else {
db.macro_expand(macro_call_id)
};
if let Some(err) = err {
if let Some(err) = &err {
// Note:
// The final goal we would like to make all parse_macro success,
// such that the following log will not call anyway.
@ -272,7 +272,25 @@ pub fn parse_macro_with_arg(
let fragment_kind = to_fragment_kind(db, macro_call_id);
let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?;
Some((parse, Arc::new(rev_token_map)))
if err.is_none() {
Some((parse, Arc::new(rev_token_map)))
} else {
// FIXME:
// In future, we should proprate the actual error with recovery information
// instead of ignore the error here.
// Safe check for recurisve identity macro
let node = parse.syntax_node();
let file: HirFileId = macro_file.into();
let call_node = file.call_node(db)?;
if !diff(&node, &call_node.value).is_empty() {
Some((parse, Arc::new(rev_token_map)))
} else {
None
}
}
}
/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.

View file

@ -715,4 +715,34 @@ fn main() {
check_struct_shorthand_initialization,
);
}
#[test]
fn test_bad_macro_stackover() {
check_no_diagnostic(
r#"
//- /main.rs
#[macro_export]
macro_rules! match_ast {
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
(match ($node:expr) {
$( ast::$ast:ident($it:ident) => $res:expr, )*
_ => $catch_all:expr $(,)?
}) => {{
$( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
{ $catch_all }
}};
}
fn main() {
let anchor = match_ast! {
match parent {
as => {},
_ => return None
}
};
}
"#,
);
}
}

View file

@ -95,6 +95,10 @@ impl TreeDiff {
builder.replace(from.text_range(), to.to_string())
}
}
pub fn is_empty(&self) -> bool {
self.replacements.is_empty()
}
}
/// Finds minimal the diff, which, applied to `from`, will result in `to`.