diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 6f028e58d0cd..be244a619eb5 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -19,7 +19,7 @@ use ide_db::{ }; use itertools::{Itertools, izip}; use syntax::{ - AstNode, NodeOrToken, SyntaxKind, + AstNode, ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent, }, @@ -388,27 +388,6 @@ fn inline( } } - // We should place the following code after last usage of `usages_for_locals` - // because `ted::replace` will change the offset in syntax tree, which makes - // `FileReference` incorrect - if let Some(imp) = - sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast) - { - if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) { - if let Some(t) = imp.self_ty() { - while let Some(self_tok) = body - .syntax() - .descendants_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) - { - let replace_with = t.clone_subtree().syntax().clone_for_update(); - ted::replace(self_tok, replace_with); - } - } - } - } - let mut func_let_vars: BTreeSet = BTreeSet::new(); // grab all of the local variable declarations in the function @@ -534,11 +513,17 @@ fn inline( } } - if let Some(generic_arg_list) = generic_arg_list.clone() { - if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) - { - PathTransform::function_call(target, source, function, generic_arg_list) - .apply(body.syntax()); + // Apply PathTransform for path transformations when needed + if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { + let needs_transformation = generic_arg_list.is_some() || !target.has_same_self_type(source); + + if needs_transformation { + let path_transform = if let Some(generic_arg_list) = generic_arg_list.clone() { + PathTransform::function_call(target, source, function, generic_arg_list) + } else { + PathTransform::generic_transformation(target, source) + }; + path_transform.apply(body.syntax()); } } @@ -1832,4 +1817,72 @@ fn f() { "#, ); } + + #[test] + fn inline_call_generic_self_constructor() { + check_assist( + inline_call, + r#" +struct Generic(T); + +impl Generic { + fn new(value: T) -> Self { + Self(value) + } +} + +fn main() { + let x = Generic::::new$0(42); +} +"#, + r#" +struct Generic(T); + +impl Generic { + fn new(value: T) -> Self { + Self(value) + } +} + +fn main() { + let x = Generic(42); +} +"#, + ) + } + + #[test] + fn inline_call_generic_self_type_position() { + check_assist( + inline_call, + r#" +struct Generic(T); + +impl Generic { + fn identity(self) -> Self { + self + } +} + +fn main() { + let x = Generic(42); + let y = x.identity$0(); +} +"#, + r#" +struct Generic(T); + +impl Generic { + fn identity(self) -> Self { + self + } +} + +fn main() { + let x = Generic(42); + let y = x; +} +"#, + ) + } } diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 232648af661f..f0e0840db182 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -248,6 +248,47 @@ fn preorder_rev(item: &SyntaxNode) -> impl Iterator { x.into_iter().rev() } +fn is_path_in_expression_context(path: &ast::Path) -> bool { + let mut current = path.syntax().parent(); + while let Some(node) = current { + // Expression contexts where we need bare type names + if let Some(call_expr) = ast::CallExpr::cast(node.clone()) { + if let Some(expr) = call_expr.expr() { + if expr.syntax().text_range().contains_range(path.syntax().text_range()) { + return true; + } + } + } + if ast::PathExpr::cast(node.clone()).is_some() { + return true; + } + if let Some(record_expr) = ast::RecordExpr::cast(node.clone()) { + if let Some(record_path) = record_expr.path() { + if record_path.syntax().text_range().contains_range(path.syntax().text_range()) { + return true; + } + } + } + // Stop at type/pattern boundaries + if ast::Type::cast(node.clone()).is_some() + || ast::Pat::cast(node.clone()).is_some() + || ast::RetType::cast(node.clone()).is_some() + { + return false; + } + current = node.parent(); + } + false +} + +fn get_bare_type_name(ty_str: &str) -> String { + if let Some(angle_pos) = ty_str.find('<') { + ty_str[..angle_pos].to_owned() + } else { + ty_str.to_owned() + } +} + impl Ctx<'_> { fn apply(&self, item: &SyntaxNode) { // `transform_path` may update a node's parent and that would break the @@ -413,10 +454,17 @@ impl Ctx<'_> { true, ) .ok()?; - let ast_ty = make::ty(ty_str).clone_for_update(); + + // Context-aware replacement + let replacement = if is_path_in_expression_context(&path) { + let bare_name = get_bare_type_name(ty_str); + make::ty(&bare_name).clone_for_update() + } else { + make::ty(ty_str).clone_for_update() + }; if let Some(adt) = ty.as_adt() { - if let ast::Type::PathType(path_ty) = &ast_ty { + if let ast::Type::PathType(path_ty) = &replacement { let cfg = ImportPathConfig { prefer_no_std: false, prefer_prelude: true, @@ -439,7 +487,7 @@ impl Ctx<'_> { } } - ted::replace(path.syntax(), ast_ty.syntax()); + ted::replace(path.syntax(), replacement.syntax()); } hir::PathResolution::Local(_) | hir::PathResolution::Def(_)