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.rs | 139 |
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"); |