Skip to content

Commit dd3f9ea

Browse files
Merge #5007
5007: SSR: Allow matching within macro calls r=matklad a=davidlattimore #3186 Co-authored-by: David Lattimore <[email protected]>
2 parents 656cbc6 + fc46c12 commit dd3f9ea

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
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: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ fn assert_ssr_transform(rule: &str, input: &str, result: &str) {
209209
assert_ssr_transforms(&[rule], input, result);
210210
}
211211

212+
fn normalize_code(code: &str) -> String {
213+
let (db, file_id) = single_file(code);
214+
db.file_text(file_id).to_string()
215+
}
216+
212217
fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) {
213218
let (db, file_id) = single_file(input);
214219
let mut match_finder = MatchFinder::new(&db);
@@ -217,8 +222,13 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) {
217222
match_finder.add_rule(rule);
218223
}
219224
if let Some(edits) = match_finder.edits_for_file(file_id) {
220-
let mut after = input.to_string();
225+
// Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters
226+
// stuff.
227+
let mut after = db.file_text(file_id).to_string();
221228
edits.apply(&mut after);
229+
// Likewise, we need to make sure that whatever transformations fixture parsing applies,
230+
// also get appplied to our expected result.
231+
let result = normalize_code(result);
222232
assert_eq!(after, result);
223233
} else {
224234
panic!("No edits were made");
@@ -355,6 +365,18 @@ fn match_nested_method_calls() {
355365
);
356366
}
357367

368+
// Make sure that our node matching semantics don't differ within macro calls.
369+
#[test]
370+
fn match_nested_method_calls_with_macro_call() {
371+
assert_matches(
372+
"$a.z().z().z()",
373+
r#"
374+
macro_rules! m1 { ($a:expr) => {$a}; }
375+
fn f() {m1!(h().i().j().z().z().z().d().e())}"#,
376+
&["h().i().j().z().z().z()"],
377+
);
378+
}
379+
358380
#[test]
359381
fn match_complex_expr() {
360382
let code = "fn f() -> i32 {foo(bar(40, 2), 42)}";
@@ -547,3 +569,40 @@ fn multiple_rules() {
547569
"fn f() -> i32 {add_one(add(3, 2))}",
548570
)
549571
}
572+
573+
#[test]
574+
fn match_within_macro_invocation() {
575+
let code = r#"
576+
macro_rules! foo {
577+
($a:stmt; $b:expr) => {
578+
$b
579+
};
580+
}
581+
struct A {}
582+
impl A {
583+
fn bar() {}
584+
}
585+
fn f1() {
586+
let aaa = A {};
587+
foo!(macro_ignores_this(); aaa.bar());
588+
}
589+
"#;
590+
assert_matches("$a.bar()", code, &["aaa.bar()"]);
591+
}
592+
593+
#[test]
594+
fn replace_within_macro_expansion() {
595+
assert_ssr_transform(
596+
"$a.foo() ==>> bar($a)",
597+
r#"
598+
macro_rules! macro1 {
599+
($a:expr) => {$a}
600+
}
601+
fn f() {macro1!(5.x().foo().o2())}"#,
602+
r#"
603+
macro_rules! macro1 {
604+
($a:expr) => {$a}
605+
}
606+
fn f() {macro1!(bar(5.x()).o2())}"#,
607+
)
608+
}

0 commit comments

Comments
 (0)