mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
SSR: Allow matching within macro calls
This commit is contained in:
parent
9a4d02faf9
commit
f4dc549582
3 changed files with 69 additions and 2 deletions
|
@ -12,7 +12,7 @@ mod tests;
|
|||
use crate::matching::Match;
|
||||
use hir::Semantics;
|
||||
use ra_db::{FileId, FileRange};
|
||||
use ra_syntax::{AstNode, SmolStr, SyntaxNode};
|
||||
use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode};
|
||||
use ra_text_edit::TextEdit;
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
|
@ -107,6 +107,22 @@ impl<'db> MatchFinder<'db> {
|
|||
return;
|
||||
}
|
||||
}
|
||||
// If we've got a macro call, we already tried matching it pre-expansion, which is the only
|
||||
// way to match the whole macro, now try expanding it and matching the expansion.
|
||||
if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
|
||||
if let Some(expanded) = self.sema.expand(¯o_call) {
|
||||
if let Some(tt) = macro_call.token_tree() {
|
||||
// When matching within a macro expansion, we only want to allow matches of
|
||||
// nodes that originated entirely from within the token tree of the macro call.
|
||||
// i.e. we don't want to match something that came from the macro itself.
|
||||
self.find_matches(
|
||||
&expanded,
|
||||
&Some(self.sema.original_range(tt.syntax())),
|
||||
matches_out,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
for child in code.children() {
|
||||
self.find_matches(&child, restrict_range, matches_out);
|
||||
}
|
||||
|
|
|
@ -343,7 +343,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> {
|
|||
}
|
||||
|
||||
/// Outside of token trees, a placeholder can only match a single AST node, whereas in a token
|
||||
/// tree it can match a sequence of tokens.
|
||||
/// tree it can match a sequence of tokens. Note, that this code will only be used when the
|
||||
/// pattern matches the macro invocation. For matches within the macro call, we'll already have
|
||||
/// expanded the macro.
|
||||
fn attempt_match_token_tree(
|
||||
&mut self,
|
||||
match_inputs: &MatchInputs,
|
||||
|
|
|
@ -355,6 +355,18 @@ fn match_nested_method_calls() {
|
|||
);
|
||||
}
|
||||
|
||||
// Make sure that our node matching semantics don't differ within macro calls.
|
||||
#[test]
|
||||
fn match_nested_method_calls_with_macro_call() {
|
||||
assert_matches(
|
||||
"$a.z().z().z()",
|
||||
r#"
|
||||
macro_rules! m1 { ($a:expr) => {$a}; }
|
||||
fn f() {m1!(h().i().j().z().z().z().d().e())}"#,
|
||||
&["h().i().j().z().z().z()"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_complex_expr() {
|
||||
let code = "fn f() -> i32 {foo(bar(40, 2), 42)}";
|
||||
|
@ -547,3 +559,40 @@ fn multiple_rules() {
|
|||
"fn f() -> i32 {add_one(add(3, 2))}",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_within_macro_invocation() {
|
||||
let code = r#"
|
||||
macro_rules! foo {
|
||||
($a:stmt; $b:expr) => {
|
||||
$b
|
||||
};
|
||||
}
|
||||
struct A {}
|
||||
impl A {
|
||||
fn bar() {}
|
||||
}
|
||||
fn f1() {
|
||||
let aaa = A {};
|
||||
foo!(macro_ignores_this(); aaa.bar());
|
||||
}
|
||||
"#;
|
||||
assert_matches("$a.bar()", code, &["aaa.bar()"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_within_macro_expansion() {
|
||||
assert_ssr_transform(
|
||||
"$a.foo() ==>> bar($a)",
|
||||
r#"
|
||||
macro_rules! macro1 {
|
||||
($a:expr) => {$a}
|
||||
}
|
||||
fn f() {macro1!(5.x().foo().o2())}"#,
|
||||
r#"
|
||||
macro_rules! macro1 {
|
||||
($a:expr) => {$a}
|
||||
}
|
||||
fn f() {macro1!(bar(5.x()).o2())}"#,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue