From e1a9461806a4d6dd2ad58be2d6148c0e8fdb2299 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 21 Mar 2020 22:43:48 +0800 Subject: Add identity expansion checking --- crates/ra_hir_expand/src/db.rs | 24 +++++++++++++++++++++--- crates/ra_ide/src/diagnostics.rs | 30 ++++++++++++++++++++++++++++++ crates/ra_syntax/src/algo.rs | 4 ++++ 3 files changed, 55 insertions(+), 3 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index d171d2dfd..4421d187b 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs @@ -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. diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index a10e642db..50106e31e 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -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 + } + }; + } + "#, + ); + } } diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 344cf0fbe..ffdbdc767 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs @@ -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`. -- cgit v1.2.3