Fix extract_function with macro arg

This commit is contained in:
Daiki Ihara 2021-06-25 22:14:42 +09:00 committed by Lukas Wirth
parent 535f0726f1
commit 4d005e529b
2 changed files with 69 additions and 28 deletions

View file

@ -2,7 +2,7 @@ use std::{hash::BuildHasherDefault, iter};
use ast::make; use ast::make;
use either::Either; use either::Either;
use hir::{HirDisplay, Local, Semantics, TypeInfo}; use hir::{HirDisplay, InFile, Local, Semantics, TypeInfo};
use ide_db::{ use ide_db::{
defs::{Definition, NameRefClass}, defs::{Definition, NameRefClass},
search::{FileReference, ReferenceAccess, SearchScope}, search::{FileReference, ReferenceAccess, SearchScope},
@ -17,7 +17,7 @@ use syntax::{
edit::{AstNodeEdit, IndentLevel}, edit::{AstNodeEdit, IndentLevel},
AstNode, AstNode,
}, },
match_ast, ted, match_ast, ted, SyntaxElement,
SyntaxKind::{self, COMMENT}, SyntaxKind::{self, COMMENT},
SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
}; };
@ -582,38 +582,45 @@ impl FunctionBody {
&self, &self,
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
) -> (FxIndexSet<Local>, Option<ast::SelfParam>) { ) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
// FIXME: currently usages inside macros are not found
let mut self_param = None; let mut self_param = None;
let mut res = FxIndexSet::default(); let mut res = FxIndexSet::default();
self.walk_expr(&mut |expr| { let mut cb = |name_ref: Option<_>| {
let name_ref = match expr { let local_ref =
ast::Expr::PathExpr(path_expr) => { match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
path_expr.path().and_then(|it| it.as_single_name_ref()) Some(
}
_ => return,
};
if let Some(name_ref) = name_ref {
if let Some(
NameRefClass::Definition(Definition::Local(local_ref)) NameRefClass::Definition(Definition::Local(local_ref))
| NameRefClass::FieldShorthand { local_ref, field_ref: _ }, | NameRefClass::FieldShorthand { local_ref, field_ref: _ },
) = NameRefClass::classify(sema, &name_ref) ) => local_ref,
{ _ => return,
if local_ref.is_self(sema.db) { };
match local_ref.source(sema.db).value { let InFile { file_id, value } = local_ref.source(sema.db);
// locals defined inside macros are not relevant to us
if !file_id.is_macro() {
match value {
Either::Right(it) => { Either::Right(it) => {
self_param.replace(it); self_param.replace(it);
} }
Either::Left(_) => { Either::Left(_) => {
stdx::never!(
"Local::is_self returned true, but source is IdentPat"
);
}
}
} else {
res.insert(local_ref); res.insert(local_ref);
} }
} }
} }
};
self.walk_expr(&mut |expr| match expr {
ast::Expr::PathExpr(path_expr) => {
cb(path_expr.path().and_then(|it| it.as_single_name_ref()))
}
ast::Expr::MacroCall(call) => {
if let Some(tt) = call.token_tree() {
tt.syntax()
.children_with_tokens()
.flat_map(SyntaxElement::into_token)
.filter(|it| it.kind() == SyntaxKind::IDENT)
.flat_map(|t| sema.descend_into_macros_many(t))
.for_each(|t| cb(t.parent().and_then(ast::NameRef::cast)));
}
}
_ => (),
}); });
(res, self_param) (res, self_param)
} }
@ -4155,6 +4162,35 @@ fn foo() {
fn $0fun_name(y: &mut Foo) { fn $0fun_name(y: &mut Foo) {
y.foo(); y.foo();
} }
"#,
);
}
#[test]
fn extract_with_macro_arg() {
check_assist(
extract_function,
r#"
macro_rules! m {
($val:expr) => { $val };
}
fn main() {
let bar = "bar";
$0m!(bar);$0
}
"#,
r#"
macro_rules! m {
($val:expr) => { $val };
}
fn main() {
let bar = "bar";
fun_name(bar);
}
fn $0fun_name(bar: &str) {
m!(bar);
}
"#, "#,
); );
} }

View file

@ -77,6 +77,11 @@ impl ast::Expr {
} }
// Don't skip subtree since we want to process the expression child next // Don't skip subtree since we want to process the expression child next
Some(ast::Stmt::ExprStmt(_)) => (), Some(ast::Stmt::ExprStmt(_)) => (),
// This might be an expression
Some(ast::Stmt::Item(ast::Item::MacroCall(mcall))) => {
cb(WalkEvent::Enter(ast::Expr::MacroCall(mcall)));
preorder.skip_subtree();
}
// skip inner items which might have their own expressions // skip inner items which might have their own expressions
Some(ast::Stmt::Item(_)) => preorder.skip_subtree(), Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
None => { None => {