aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorDavid Lattimore <[email protected]>2020-06-23 09:59:18 +0100
committerDavid Lattimore <[email protected]>2020-06-27 02:33:00 +0100
commitf4dc54958257ad33fe182d600c418591341c86dd (patch)
treea250def8c836e089a6b296db9502f24a45c92061 /crates
parent9a4d02faf9c47f401b8756c3f7fcab2198f5f9cd (diff)
SSR: Allow matching within macro calls
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ssr/src/lib.rs18
-rw-r--r--crates/ra_ssr/src/matching.rs4
-rw-r--r--crates/ra_ssr/src/tests.rs49
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;
12use crate::matching::Match; 12use crate::matching::Match;
13use hir::Semantics; 13use hir::Semantics;
14use ra_db::{FileId, FileRange}; 14use ra_db::{FileId, FileRange};
15use ra_syntax::{AstNode, SmolStr, SyntaxNode}; 15use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode};
16use ra_text_edit::TextEdit; 16use ra_text_edit::TextEdit;
17use rustc_hash::FxHashMap; 17use 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(&macro_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]
360fn 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]
359fn match_complex_expr() { 371fn 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]
564fn 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]
584fn 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}