Skip to content

Commit f4dc549

Browse files
SSR: Allow matching within macro calls
1 parent 9a4d02f commit f4dc549

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

crates/ra_ssr/src/lib.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ mod tests;
1212
use crate::matching::Match;
1313
use hir::Semantics;
1414
use ra_db::{FileId, FileRange};
15-
use ra_syntax::{AstNode, SmolStr, SyntaxNode};
15+
use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode};
1616
use ra_text_edit::TextEdit;
1717
use rustc_hash::FxHashMap;
1818

@@ -107,6 +107,22 @@ impl<'db> MatchFinder<'db> {
107107
return;
108108
}
109109
}
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+
}
110126
for child in code.children() {
111127
self.find_matches(&child, restrict_range, matches_out);
112128
}

crates/ra_ssr/src/matching.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
343343
}
344344

345345
/// 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.
347349
fn attempt_match_token_tree(
348350
&mut self,
349351
match_inputs: &MatchInputs,

crates/ra_ssr/src/tests.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,18 @@ fn match_nested_method_calls() {
355355
);
356356
}
357357

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+
358370
#[test]
359371
fn match_complex_expr() {
360372
let code = "fn f() -> i32 {foo(bar(40, 2), 42)}";
@@ -547,3 +559,40 @@ fn multiple_rules() {
547559
"fn f() -> i32 {add_one(add(3, 2))}",
548560
)
549561
}
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+
}

0 commit comments

Comments
 (0)