diff options
author | David Lattimore <[email protected]> | 2020-06-23 09:59:18 +0100 |
---|---|---|
committer | David Lattimore <[email protected]> | 2020-06-27 02:33:00 +0100 |
commit | f4dc54958257ad33fe182d600c418591341c86dd (patch) | |
tree | a250def8c836e089a6b296db9502f24a45c92061 | |
parent | 9a4d02faf9c47f401b8756c3f7fcab2198f5f9cd (diff) |
SSR: Allow matching within macro calls
-rw-r--r-- | crates/ra_ssr/src/lib.rs | 18 | ||||
-rw-r--r-- | crates/ra_ssr/src/matching.rs | 4 | ||||
-rw-r--r-- | crates/ra_ssr/src/tests.rs | 49 |
3 files changed, 69 insertions, 2 deletions
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index da26ee669..8f149e3db 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -12,7 +12,7 @@ mod tests; | |||
12 | use crate::matching::Match; | 12 | use crate::matching::Match; |
13 | use hir::Semantics; | 13 | use hir::Semantics; |
14 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
15 | use ra_syntax::{AstNode, SmolStr, SyntaxNode}; | 15 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode}; |
16 | use ra_text_edit::TextEdit; | 16 | use ra_text_edit::TextEdit; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | 18 | ||
@@ -107,6 +107,22 @@ impl<'db> MatchFinder<'db> { | |||
107 | return; | 107 | return; |
108 | } | 108 | } |
109 | } | 109 | } |
110 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | ||
111 | // way to match the whole macro, now try expanding it and matching the expansion. | ||
112 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | ||
113 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
114 | if let Some(tt) = macro_call.token_tree() { | ||
115 | // When matching within a macro expansion, we only want to allow matches of | ||
116 | // nodes that originated entirely from within the token tree of the macro call. | ||
117 | // i.e. we don't want to match something that came from the macro itself. | ||
118 | self.find_matches( | ||
119 | &expanded, | ||
120 | &Some(self.sema.original_range(tt.syntax())), | ||
121 | matches_out, | ||
122 | ); | ||
123 | } | ||
124 | } | ||
125 | } | ||
110 | for child in code.children() { | 126 | for child in code.children() { |
111 | self.find_matches(&child, restrict_range, matches_out); | 127 | self.find_matches(&child, restrict_range, matches_out); |
112 | } | 128 | } |
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs index bdaba9f1b..85420ed3c 100644 --- a/crates/ra_ssr/src/matching.rs +++ b/crates/ra_ssr/src/matching.rs | |||
@@ -343,7 +343,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
343 | } | 343 | } |
344 | 344 | ||
345 | /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token | 345 | /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token |
346 | /// tree it can match a sequence of tokens. | 346 | /// tree it can match a sequence of tokens. Note, that this code will only be used when the |
347 | /// pattern matches the macro invocation. For matches within the macro call, we'll already have | ||
348 | /// expanded the macro. | ||
347 | fn attempt_match_token_tree( | 349 | fn attempt_match_token_tree( |
348 | &mut self, | 350 | &mut self, |
349 | match_inputs: &MatchInputs, | 351 | match_inputs: &MatchInputs, |
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 3ee1e74e9..d7e6d817a 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -355,6 +355,18 @@ fn match_nested_method_calls() { | |||
355 | ); | 355 | ); |
356 | } | 356 | } |
357 | 357 | ||
358 | // Make sure that our node matching semantics don't differ within macro calls. | ||
359 | #[test] | ||
360 | fn match_nested_method_calls_with_macro_call() { | ||
361 | assert_matches( | ||
362 | "$a.z().z().z()", | ||
363 | r#" | ||
364 | macro_rules! m1 { ($a:expr) => {$a}; } | ||
365 | fn f() {m1!(h().i().j().z().z().z().d().e())}"#, | ||
366 | &["h().i().j().z().z().z()"], | ||
367 | ); | ||
368 | } | ||
369 | |||
358 | #[test] | 370 | #[test] |
359 | fn match_complex_expr() { | 371 | fn match_complex_expr() { |
360 | let code = "fn f() -> i32 {foo(bar(40, 2), 42)}"; | 372 | let code = "fn f() -> i32 {foo(bar(40, 2), 42)}"; |
@@ -547,3 +559,40 @@ fn multiple_rules() { | |||
547 | "fn f() -> i32 {add_one(add(3, 2))}", | 559 | "fn f() -> i32 {add_one(add(3, 2))}", |
548 | ) | 560 | ) |
549 | } | 561 | } |
562 | |||
563 | #[test] | ||
564 | fn match_within_macro_invocation() { | ||
565 | let code = r#" | ||
566 | macro_rules! foo { | ||
567 | ($a:stmt; $b:expr) => { | ||
568 | $b | ||
569 | }; | ||
570 | } | ||
571 | struct A {} | ||
572 | impl A { | ||
573 | fn bar() {} | ||
574 | } | ||
575 | fn f1() { | ||
576 | let aaa = A {}; | ||
577 | foo!(macro_ignores_this(); aaa.bar()); | ||
578 | } | ||
579 | "#; | ||
580 | assert_matches("$a.bar()", code, &["aaa.bar()"]); | ||
581 | } | ||
582 | |||
583 | #[test] | ||
584 | fn replace_within_macro_expansion() { | ||
585 | assert_ssr_transform( | ||
586 | "$a.foo() ==>> bar($a)", | ||
587 | r#" | ||
588 | macro_rules! macro1 { | ||
589 | ($a:expr) => {$a} | ||
590 | } | ||
591 | fn f() {macro1!(5.x().foo().o2())}"#, | ||
592 | r#" | ||
593 | macro_rules! macro1 { | ||
594 | ($a:expr) => {$a} | ||
595 | } | ||
596 | fn f() {macro1!(bar(5.x()).o2())}"#, | ||
597 | ) | ||
598 | } | ||