Skip to content

Commit af6b942

Browse files
committed
Handle Self replacement contextually in inline assists
1 parent e2c3647 commit af6b942

File tree

1 file changed

+175
-9
lines changed

1 file changed

+175
-9
lines changed

crates/ide-assists/src/handlers/inline_call.rs

Lines changed: 175 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use ide_db::{
1919
};
2020
use itertools::{Itertools, izip};
2121
use syntax::{
22-
AstNode, NodeOrToken, SyntaxKind,
22+
AstNode, NodeOrToken, SyntaxKind, SyntaxToken,
2323
ast::{
2424
self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent,
2525
},
@@ -311,6 +311,80 @@ fn get_fn_params(
311311
Some(params)
312312
}
313313

314+
fn is_self_in_expression_context(self_token: &SyntaxToken) -> bool {
315+
let mut current = self_token.parent();
316+
while let Some(node) = current {
317+
// Check for function call expressions: Self::method() or Self(...)
318+
if let Some(call_expr) = ast::CallExpr::cast(node.clone()) {
319+
if let Some(expr) = call_expr.expr() {
320+
if expr.syntax().text_range().contains_range(self_token.text_range()) {
321+
return true;
322+
}
323+
}
324+
}
325+
326+
if let Some(method_call) = ast::MethodCallExpr::cast(node.clone()) {
327+
if let Some(receiver) = method_call.receiver() {
328+
if receiver.syntax().text_range().contains_range(self_token.text_range()) {
329+
return true;
330+
}
331+
}
332+
}
333+
334+
if let Some(_path_expr) = ast::PathExpr::cast(node.clone()) {
335+
return true;
336+
}
337+
338+
// Check for record expressions (struct construction)
339+
if let Some(record_expr) = ast::RecordExpr::cast(node.clone()) {
340+
if let Some(path) = record_expr.path() {
341+
if path.syntax().text_range().contains_range(self_token.text_range()) {
342+
return true;
343+
}
344+
}
345+
}
346+
347+
// Stop at certain boundaries (type/pattern contexts)
348+
if ast::Type::cast(node.clone()).is_some()
349+
|| ast::Pat::cast(node.clone()).is_some()
350+
|| ast::RetType::cast(node.clone()).is_some()
351+
{
352+
return false;
353+
}
354+
355+
current = node.parent();
356+
}
357+
false
358+
}
359+
360+
fn get_qualified_type_for_turbofish(ty: &ast::Type) -> String {
361+
match ty {
362+
ast::Type::PathType(path_type) => {
363+
if let Some(path) = path_type.path() {
364+
// For turbofish, we need the full path but potentially without generic args
365+
// depending on context. For now, use the bare name.
366+
if let Some(segment) = path.segments().last() {
367+
if let Some(name) = segment.name_ref() {
368+
return name.text().to_string();
369+
}
370+
}
371+
}
372+
}
373+
_ => {}
374+
}
375+
ty.syntax().text().to_string()
376+
}
377+
378+
fn have_same_self_type(source_impl: &ast::Impl, target_impl: &ast::Impl) -> bool {
379+
match (source_impl.self_ty(), target_impl.self_ty()) {
380+
(Some(source_ty), Some(target_ty)) => {
381+
// Compare the textual representation of the types
382+
source_ty.syntax().text() == target_ty.syntax().text()
383+
}
384+
_ => false,
385+
}
386+
}
387+
314388
fn inline(
315389
sema: &Semantics<'_, RootDatabase>,
316390
function_def_file_id: EditionedFileId,
@@ -391,22 +465,46 @@ fn inline(
391465
// We should place the following code after last usage of `usages_for_locals`
392466
// because `ted::replace` will change the offset in syntax tree, which makes
393467
// `FileReference` incorrect
394-
if let Some(imp) =
468+
if let Some(source_impl) =
395469
sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
396470
{
397-
if !node.syntax().ancestors().any(|anc| &anc == imp.syntax()) {
398-
if let Some(t) = imp.self_ty() {
399-
while let Some(self_tok) = body
471+
// Check if the target (call site) is also in an impl block
472+
let target_impl = node.syntax().ancestors().find_map(ast::Impl::cast);
473+
474+
let should_replace_self = match target_impl {
475+
Some(target_impl) => {
476+
// Both source and target are in impl blocks
477+
// Only replace Self if they have different Self types
478+
!have_same_self_type(&source_impl, &target_impl)
479+
}
480+
None => {
481+
// Target is not in an impl block, so we must replace Self
482+
true
483+
}
484+
};
485+
486+
if should_replace_self {
487+
if let Some(self_ty) = source_impl.self_ty() {
488+
let self_tokens: Vec<_> = body
400489
.syntax()
401490
.descendants_with_tokens()
402491
.filter_map(NodeOrToken::into_token)
403-
.find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
404-
{
405-
let replace_with = t.clone_subtree().syntax().clone_for_update();
406-
ted::replace(self_tok, replace_with);
492+
.filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
493+
.collect();
494+
495+
// Replace each Self token based on its context
496+
for self_tok in self_tokens {
497+
let replacement = if is_self_in_expression_context(&self_tok) {
498+
let qualified_name = get_qualified_type_for_turbofish(&self_ty);
499+
make::name_ref(&qualified_name).syntax().clone_for_update()
500+
} else {
501+
self_ty.clone_subtree().syntax().clone_for_update()
502+
};
503+
ted::replace(self_tok, replacement);
407504
}
408505
}
409506
}
507+
// If same Self type context, leave Self as-is (it remains valid)
410508
}
411509

412510
let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
@@ -1832,4 +1930,72 @@ fn f() {
18321930
"#,
18331931
);
18341932
}
1933+
1934+
#[test]
1935+
fn inline_call_generic_self_constructor() {
1936+
check_assist(
1937+
inline_call,
1938+
r#"
1939+
struct Generic<T>(T);
1940+
1941+
impl<T> Generic<T> {
1942+
fn new(value: T) -> Self {
1943+
Self(value)
1944+
}
1945+
}
1946+
1947+
fn main() {
1948+
let x = Generic::<i32>::new$0(42);
1949+
}
1950+
"#,
1951+
r#"
1952+
struct Generic<T>(T);
1953+
1954+
impl<T> Generic<T> {
1955+
fn new(value: T) -> Self {
1956+
Self(value)
1957+
}
1958+
}
1959+
1960+
fn main() {
1961+
let x = Generic(42);
1962+
}
1963+
"#,
1964+
)
1965+
}
1966+
1967+
#[test]
1968+
fn inline_call_generic_self_type_position() {
1969+
check_assist(
1970+
inline_call,
1971+
r#"
1972+
struct Generic<T>(T);
1973+
1974+
impl<T> Generic<T> {
1975+
fn identity(self) -> Self {
1976+
self
1977+
}
1978+
}
1979+
1980+
fn main() {
1981+
let x = Generic(42);
1982+
let y = x.identity$0();
1983+
}
1984+
"#,
1985+
r#"
1986+
struct Generic<T>(T);
1987+
1988+
impl<T> Generic<T> {
1989+
fn identity(self) -> Self {
1990+
self
1991+
}
1992+
}
1993+
1994+
fn main() {
1995+
let x = Generic(42);
1996+
let y = x;
1997+
}
1998+
"#,
1999+
)
2000+
}
18352001
}

0 commit comments

Comments
 (0)