Merge #3671
3671: Add identity expansion checking in ill-form expansion r=flodiebold a=edwin0cheng This PR try to add more checking code in error case in macro expansion. The bug in #3642 is introduced by #3580 , which allow ill-form macro expansion in *all* kind of macro expansions. In general we should separate hypothetical macro expansion and the actual macro expansion call. However, currently the `Semantic` workflow we are using only support single macro expansion type, we might want to review it and make it works in both ways. (Maybe add a field in `MacroCallLoc` for differentiation) Fix #3642 Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
50c6a315ab
@ -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 propagate 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.
|
||||
|
@ -453,3 +453,34 @@ pub mod str {
|
||||
// should be Option<char>, but currently not because of Chalk ambiguity problem
|
||||
assert_eq!("(Option<{unknown}>, Option<{unknown}>)", super::type_at_pos(&db, pos));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3642_bad_macro_stackover() {
|
||||
let (db, pos) = TestDB::with_position(
|
||||
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
|
||||
}
|
||||
};
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!("()", super::type_at_pos(&db, pos));
|
||||
}
|
||||
|
@ -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`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user