diff options
author | Edwin Cheng <[email protected]> | 2020-03-21 14:43:48 +0000 |
---|---|---|
committer | Edwin Cheng <[email protected]> | 2020-03-21 14:43:48 +0000 |
commit | e1a9461806a4d6dd2ad58be2d6148c0e8fdb2299 (patch) | |
tree | 4bc77ef5e85876a4c5a164da97ceb0bdde9bdbb7 /crates | |
parent | 92b561b5c7da8303473792ba2bacb430614da2d1 (diff) |
Add identity expansion checking
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir_expand/src/db.rs | 24 | ||||
-rw-r--r-- | crates/ra_ide/src/diagnostics.rs | 30 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 4 |
3 files changed, 55 insertions, 3 deletions
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}; | |||
6 | use ra_db::{salsa, SourceDatabase}; | 6 | use ra_db::{salsa, SourceDatabase}; |
7 | use ra_parser::FragmentKind; | 7 | use ra_parser::FragmentKind; |
8 | use ra_prof::profile; | 8 | use ra_prof::profile; |
9 | use ra_syntax::{AstNode, Parse, SyntaxKind::*, SyntaxNode}; | 9 | use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode}; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, | 12 | ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, |
@@ -238,7 +238,7 @@ pub fn parse_macro_with_arg( | |||
238 | } else { | 238 | } else { |
239 | db.macro_expand(macro_call_id) | 239 | db.macro_expand(macro_call_id) |
240 | }; | 240 | }; |
241 | if let Some(err) = err { | 241 | if let Some(err) = &err { |
242 | // Note: | 242 | // Note: |
243 | // The final goal we would like to make all parse_macro success, | 243 | // The final goal we would like to make all parse_macro success, |
244 | // such that the following log will not call anyway. | 244 | // such that the following log will not call anyway. |
@@ -272,7 +272,25 @@ pub fn parse_macro_with_arg( | |||
272 | let fragment_kind = to_fragment_kind(db, macro_call_id); | 272 | let fragment_kind = to_fragment_kind(db, macro_call_id); |
273 | 273 | ||
274 | let (parse, rev_token_map) = mbe::token_tree_to_syntax_node(&tt, fragment_kind).ok()?; | 274 | 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 | } | ||
276 | } | 294 | } |
277 | 295 | ||
278 | /// Given a `MacroCallId`, return what `FragmentKind` it belongs to. | 296 | /// 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() { | |||
715 | check_struct_shorthand_initialization, | 715 | check_struct_shorthand_initialization, |
716 | ); | 716 | ); |
717 | } | 717 | } |
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 | } | ||
718 | } | 748 | } |
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 { | |||
95 | builder.replace(from.text_range(), to.to_string()) | 95 | builder.replace(from.text_range(), to.to_string()) |
96 | } | 96 | } |
97 | } | 97 | } |
98 | |||
99 | pub fn is_empty(&self) -> bool { | ||
100 | self.replacements.is_empty() | ||
101 | } | ||
98 | } | 102 | } |
99 | 103 | ||
100 | /// Finds minimal the diff, which, applied to `from`, will result in `to`. | 104 | /// Finds minimal the diff, which, applied to `from`, will result in `to`. |