Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/extract_function.rs')
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs139
1 files changed, 96 insertions, 43 deletions
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 6f4b886a28..046af71a9d 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -7,33 +7,34 @@ use hir::{
TypeInfo, TypeParam,
};
use ide_db::{
+ FxIndexSet, RootDatabase,
assists::GroupLabel,
defs::{Definition, NameRefClass},
famous_defs::FamousDefs,
helpers::mod_path_to_ast,
- imports::insert_use::{insert_use, ImportScope},
+ imports::insert_use::{ImportScope, insert_use},
search::{FileReference, ReferenceCategory, SearchScope},
source_change::SourceChangeBuilder,
syntax_helpers::node_ext::{
for_each_tail_expr, preorder_expr, walk_expr, walk_pat, walk_patterns_in_expr,
},
- FxIndexSet, RootDatabase,
};
use itertools::Itertools;
use syntax::{
+ Edition, SyntaxElement,
+ SyntaxKind::{self, COMMENT},
+ SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent,
ast::{
- self, edit::IndentLevel, edit_in_place::Indent, AstNode, AstToken, HasGenericParams,
- HasName,
+ self, AstNode, AstToken, HasGenericParams, HasName, edit::IndentLevel,
+ edit_in_place::Indent,
},
- match_ast, ted, Edition, SyntaxElement,
- SyntaxKind::{self, COMMENT},
- SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
+ match_ast, ted,
};
use crate::{
+ AssistId,
assist_context::{AssistContext, Assists, TreeMutator},
utils::generate_impl,
- AssistId,
};
// Assist: extract_function
@@ -107,7 +108,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
acc.add_group(
&GroupLabel("Extract into...".to_owned()),
- AssistId("extract_function", crate::AssistKind::RefactorExtract),
+ AssistId::refactor_extract("extract_function"),
"Extract into function",
target_range,
move |builder| {
@@ -247,11 +248,8 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
let mut names_in_scope = vec![];
semantics_scope.process_all_names(&mut |name, _| {
names_in_scope.push(
- name.display(
- semantics_scope.db.upcast(),
- semantics_scope.krate().edition(semantics_scope.db),
- )
- .to_string(),
+ name.display(semantics_scope.db, semantics_scope.krate().edition(semantics_scope.db))
+ .to_string(),
)
});
@@ -750,7 +748,7 @@ impl FunctionBody {
ast::Stmt::Item(_) => (),
ast::Stmt::LetStmt(stmt) => {
if let Some(pat) = stmt.pat() {
- let _ = walk_pat(&pat, &mut |pat| {
+ _ = walk_pat(&pat, &mut |pat| {
cb(pat);
std::ops::ControlFlow::<(), ()>::Continue(())
});
@@ -799,15 +797,21 @@ impl FunctionBody {
) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
let mut self_param = None;
let mut res = FxIndexSet::default();
- let mut add_name_if_local = |name_ref: Option<_>| {
- let local_ref =
- match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
- Some(
- NameRefClass::Definition(Definition::Local(local_ref), _)
- | NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ },
- ) => local_ref,
- _ => return,
- };
+
+ fn local_from_name_ref(
+ sema: &Semantics<'_, RootDatabase>,
+ name_ref: ast::NameRef,
+ ) -> Option<hir::Local> {
+ match NameRefClass::classify(sema, &name_ref) {
+ Some(
+ NameRefClass::Definition(Definition::Local(local_ref), _)
+ | NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ },
+ ) => Some(local_ref),
+ _ => None,
+ }
+ }
+
+ let mut add_name_if_local = |local_ref: Local| {
let InFile { file_id, value } = local_ref.primary_source(sema.db).source;
// locals defined inside macros are not relevant to us
if !file_id.is_macro() {
@@ -823,13 +827,20 @@ impl FunctionBody {
};
self.walk_expr(&mut |expr| match expr {
ast::Expr::PathExpr(path_expr) => {
- add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref()))
+ if let Some(local) = path_expr
+ .path()
+ .and_then(|it| it.as_single_name_ref())
+ .and_then(|name_ref| local_from_name_ref(sema, name_ref))
+ {
+ add_name_if_local(local);
+ }
}
ast::Expr::ClosureExpr(closure_expr) => {
if let Some(body) = closure_expr.body() {
body.syntax()
.descendants()
- .map(ast::NameRef::cast)
+ .filter_map(ast::NameRef::cast)
+ .filter_map(|name_ref| local_from_name_ref(sema, name_ref))
.for_each(&mut add_name_if_local);
}
}
@@ -838,9 +849,31 @@ impl FunctionBody {
tt.syntax()
.descendants_with_tokens()
.filter_map(SyntaxElement::into_token)
- .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
- .flat_map(|t| sema.descend_into_macros_exact(t))
- .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
+ .filter(|it| {
+ matches!(it.kind(), SyntaxKind::STRING | SyntaxKind::IDENT | T![self])
+ })
+ .for_each(|t| {
+ if ast::String::can_cast(t.kind()) {
+ if let Some(parts) =
+ ast::String::cast(t).and_then(|s| sema.as_format_args_parts(&s))
+ {
+ parts
+ .into_iter()
+ .filter_map(|(_, value)| value.and_then(|it| it.left()))
+ .filter_map(|path| match path {
+ PathResolution::Local(local) => Some(local),
+ _ => None,
+ })
+ .for_each(&mut add_name_if_local);
+ }
+ } else {
+ sema.descend_into_macros_exact(t)
+ .into_iter()
+ .filter_map(|t| t.parent().and_then(ast::NameRef::cast))
+ .filter_map(|name_ref| local_from_name_ref(sema, name_ref))
+ .for_each(&mut add_name_if_local);
+ }
+ });
}
}
_ => (),
@@ -1428,10 +1461,10 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> Sy
let name = fun.name.clone();
let mut call_expr = if fun.self_param.is_some() {
let self_arg = make::expr_path(make::ext::ident_path("self"));
- make::expr_method_call(self_arg, name, args)
+ make::expr_method_call(self_arg, name, args).into()
} else {
let func = make::expr_path(make::path_unqualified(make::path_segment(name)));
- make::expr_call(func, args)
+ make::expr_call(func, args).into()
};
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
@@ -1689,11 +1722,7 @@ fn make_where_clause(
})
.peekable();
- if predicates.peek().is_some() {
- Some(make::where_clause(predicates))
- } else {
- None
- }
+ if predicates.peek().is_some() { Some(make::where_clause(predicates)) } else { None }
}
fn pred_is_required(
@@ -1917,14 +1946,15 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
};
let func = make::expr_path(make::ext::ident_path(constructor));
let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(func, args)
+ make::expr_call(func, args).into()
})
}
FlowHandler::If { .. } => {
let controlflow_continue = make::expr_call(
make::expr_path(make::path_from_text("ControlFlow::Continue")),
make::arg_list([make::ext::expr_unit()]),
- );
+ )
+ .into();
with_tail_expr(block, controlflow_continue)
}
FlowHandler::IfOption { .. } => {
@@ -1934,12 +1964,12 @@ fn make_body(ctx: &AssistContext<'_>, old_indent: IndentLevel, fun: &Function) -
FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
let some = make::expr_path(make::ext::ident_path("Some"));
let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(some, args)
+ make::expr_call(some, args).into()
}),
FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
let ok = make::expr_path(make::ext::ident_path("Ok"));
let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(ok, args)
+ make::expr_call(ok, args).into()
}),
}
}
@@ -2127,17 +2157,18 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
FlowHandler::If { .. } => make::expr_call(
make::expr_path(make::path_from_text("ControlFlow::Break")),
make::arg_list([make::ext::expr_unit()]),
- ),
+ )
+ .into(),
FlowHandler::IfOption { .. } => {
let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
let args = make::arg_list([expr]);
- make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
+ make::expr_call(make::expr_path(make::ext::ident_path("Some")), args).into()
}
FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
FlowHandler::MatchResult { .. } => {
let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
let args = make::arg_list([expr]);
- make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
+ make::expr_call(make::expr_path(make::ext::ident_path("Err")), args).into()
}
};
Some(make::expr_return(Some(value)).clone_for_update())
@@ -6135,6 +6166,28 @@ fn $0fun_name(a: i32, b: i32, c: i32, x: i32) -> i32 {
}
#[test]
+ fn fmt_macro_argument() {
+ check_assist(
+ extract_function,
+ r#"
+//- minicore: fmt
+fn existing(a: i32, b: i32, c: i32) {
+ $0print!("{a}{}{}", b, "{c}");$0
+}
+"#,
+ r#"
+fn existing(a: i32, b: i32, c: i32) {
+ fun_name(a, b);
+}
+
+fn $0fun_name(a: i32, b: i32) {
+ print!("{a}{}{}", b, "{c}");
+}
+"#,
+ );
+ }
+
+ #[test]
fn in_left_curly_is_not_applicable() {
cov_mark::check!(extract_function_in_braces_is_not_applicable);
check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");