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 | 185 |
1 files changed, 168 insertions, 17 deletions
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 0b90c9ba34..2a67909e63 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let node = ctx.covering_element(); + if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) { + cov_mark::hit!(extract_function_in_braces_is_not_applicable); + return None; + } + if node.kind() == COMMENT { cov_mark::hit!(extract_function_in_comment_is_not_applicable); return None; @@ -178,7 +183,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op 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.to_string())); + semantics_scope.process_all_names(&mut |name, _| { + names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string()) + }); let default_name = "fun_name"; @@ -369,7 +376,7 @@ struct OutlivedLocal { /// Container of local variable usages /// -/// Semanticall same as `UsageSearchResult`, but provides more convenient interface +/// Semantically same as `UsageSearchResult`, but provides more convenient interface struct LocalUsages(ide_db::search::UsageSearchResult); impl LocalUsages { @@ -438,7 +445,7 @@ impl Param { } fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param { - let var = self.var.name(ctx.db()).to_string(); + let var = self.var.name(ctx.db()).display(ctx.db()).to_string(); let var_name = make::name(&var); let pat = match self.kind() { ParamKind::MutValue => make::ident_pat(false, true, var_name), @@ -468,7 +475,8 @@ impl TryKind { let name = adt.name(ctx.db()); // FIXME: use lang items to determine if it is std type or user defined // E.g. if user happens to define type named `Option`, we would have false positive - match name.to_string().as_str() { + let name = &name.display(ctx.db()).to_string(); + match name.as_str() { "Option" => Some(TryKind::Option), "Result" => Some(TryKind::Result { ty }), _ => None, @@ -702,7 +710,7 @@ impl FunctionBody { ) -> (FxIndexSet<Local>, Option<ast::SelfParam>) { let mut self_param = None; let mut res = FxIndexSet::default(); - let mut cb = |name_ref: Option<_>| { + 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( @@ -726,21 +734,24 @@ impl FunctionBody { }; self.walk_expr(&mut |expr| match expr { ast::Expr::PathExpr(path_expr) => { - cb(path_expr.path().and_then(|it| it.as_single_name_ref())) + add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref())) } ast::Expr::ClosureExpr(closure_expr) => { if let Some(body) = closure_expr.body() { - body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it)); + body.syntax() + .descendants() + .map(ast::NameRef::cast) + .for_each(&mut add_name_if_local); } } ast::Expr::MacroExpr(expr) => { if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) { tt.syntax() - .children_with_tokens() - .flat_map(SyntaxElement::into_token) - .filter(|it| it.kind() == SyntaxKind::IDENT) + .descendants_with_tokens() + .filter_map(SyntaxElement::into_token) + .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self])) .flat_map(|t| sema.descend_into_macros(t)) - .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast))); + .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast))); } } _ => (), @@ -1286,8 +1297,8 @@ fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> { let as_impl = ast::Impl::cast(trait_impl.clone())?; let impl_type = Some(impl_type_name(&as_impl)?); - let sibblings = trait_impl.parent()?.children(); - sibblings + let siblings = trait_impl.parent()?.children(); + siblings .filter_map(ast::Impl::cast) .find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s)) } @@ -1333,14 +1344,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St [var] => { let modifier = mut_modifier(var); let name = var.local.name(ctx.db()); - format_to!(buf, "let {modifier}{name} = ") + format_to!(buf, "let {modifier}{} = ", name.display(ctx.db())) } vars => { buf.push_str("let ("); let bindings = vars.iter().format_with(", ", |local, f| { let modifier = mut_modifier(local); let name = local.local.name(ctx.db()); - f(&format_args!("{modifier}{name}")) + f(&format_args!("{modifier}{}", name.display(ctx.db())))?; + Ok(()) }); format_to!(buf, "{bindings}"); buf.push_str(") = "); @@ -1479,7 +1491,7 @@ impl FlowHandler { } fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr { - let name = var.name(ctx.db()).to_string(); + let name = var.name(ctx.db()).display(ctx.db()).to_string(); make::expr_path(make::ext::ident_path(&name)) } @@ -1879,7 +1891,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr } fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String { - ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string()) + ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string()) } fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { @@ -4340,6 +4352,82 @@ fn $0fun_name(n: i32) -> i32 { } #[test] + fn param_usage_in_macro_with_nested_tt() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + $0let k = n * m!((n) + { t });$0 + let m = k + 1; +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +fn foo() { + let n = 1; + let t = 1; + let k = fun_name(n, t); + let m = k + 1; +} + +fn $0fun_name(n: i32, t: i32) -> i32 { + let k = n * m!((n) + { t }); + k +} +"#, + ) + } + + #[test] + fn param_usage_in_macro_with_nested_tt_2() { + check_assist( + extract_function, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + $0let k = n * m!((n) + { self.0 });$0 + let m = k + 1; + } +} +"#, + r#" +macro_rules! m { + ($val:expr) => { $val }; +} + +struct S(i32); +impl S { + fn foo(&self) { + let n = 1; + let k = self.fun_name(n); + let m = k + 1; + } + + fn $0fun_name(&self, n: i32) -> i32 { + let k = n * m!((n) + { self.0 }); + k + } +} +"#, + ) + } + + #[test] fn extract_with_await() { check_assist( extract_function, @@ -4640,6 +4728,7 @@ const fn $0fun_name() { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { let mut x = 5; for _ in 0..10 { @@ -4663,6 +4752,7 @@ fn $0fun_name(x: &mut i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { for _ in 0..10 { let mut x = 5; @@ -4686,6 +4776,7 @@ fn $0fun_name(mut x: i32) { check_assist( extract_function, r#" +//- minicore: iterator fn foo() { loop { let mut x = 5; @@ -5388,6 +5479,30 @@ fn $0fun_name<T: Debug>(i: T) { } #[test] + fn dont_emit_type_with_hidden_lifetime_parameter() { + // FIXME: We should emit a `<T: Debug>` generic argument for the generated function + check_assist( + extract_function, + r#" +struct Struct<'a, T>(&'a T); +fn func<T: Debug>(i: Struct<'_, T>) { + $0foo(i);$0 +} +"#, + r#" +struct Struct<'a, T>(&'a T); +fn func<T: Debug>(i: Struct<'_, T>) { + fun_name(i); +} + +fn $0fun_name(i: Struct<'_, T>) { + foo(i); +} +"#, + ); + } + + #[test] fn preserve_generics_from_body() { check_assist( extract_function, @@ -5800,4 +5915,40 @@ fn $0fun_name() -> ControlFlow<()> { "#, ); } + + #[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"); + } + + #[test] + fn in_right_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 }"); + } + + #[test] + fn in_left_paren_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }"); + } + + #[test] + fn in_right_paren_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }"); + } + + #[test] + fn in_left_brack_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}"); + } + + #[test] + fn in_right_brack_is_not_applicable() { + cov_mark::check!(extract_function_in_braces_is_not_applicable); + check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}"); + } } |