Skip to content

Commit 781ee7a

Browse files
committed
Use PathTransform for context-aware Self replacement in inline assists
1 parent af6b942 commit 781ee7a

File tree

2 files changed

+63
-128
lines changed

2 files changed

+63
-128
lines changed

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

Lines changed: 12 additions & 125 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, SyntaxToken,
22+
AstNode,
2323
ast::{
2424
self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent,
2525
},
@@ -311,80 +311,6 @@ 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-
388314
fn inline(
389315
sema: &Semantics<'_, RootDatabase>,
390316
function_def_file_id: EditionedFileId,
@@ -462,51 +388,6 @@ fn inline(
462388
}
463389
}
464390

465-
// We should place the following code after last usage of `usages_for_locals`
466-
// because `ted::replace` will change the offset in syntax tree, which makes
467-
// `FileReference` incorrect
468-
if let Some(source_impl) =
469-
sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
470-
{
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
489-
.syntax()
490-
.descendants_with_tokens()
491-
.filter_map(NodeOrToken::into_token)
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);
504-
}
505-
}
506-
}
507-
// If same Self type context, leave Self as-is (it remains valid)
508-
}
509-
510391
let mut func_let_vars: BTreeSet<String> = BTreeSet::new();
511392

512393
// grab all of the local variable declarations in the function
@@ -632,11 +513,17 @@ fn inline(
632513
}
633514
}
634515

635-
if let Some(generic_arg_list) = generic_arg_list.clone() {
636-
if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
637-
{
638-
PathTransform::function_call(target, source, function, generic_arg_list)
639-
.apply(body.syntax());
516+
// Apply PathTransform for path transformations when needed
517+
if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) {
518+
let needs_transformation = generic_arg_list.is_some() || !target.has_same_self_type(source);
519+
520+
if needs_transformation {
521+
let path_transform = if let Some(generic_arg_list) = generic_arg_list.clone() {
522+
PathTransform::function_call(target, source, function, generic_arg_list)
523+
} else {
524+
PathTransform::generic_transformation(target, source)
525+
};
526+
path_transform.apply(body.syntax());
640527
}
641528
}
642529

crates/ide-db/src/path_transform.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,47 @@ fn preorder_rev(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
248248
x.into_iter().rev()
249249
}
250250

251+
fn is_path_in_expression_context(path: &ast::Path) -> bool {
252+
let mut current = path.syntax().parent();
253+
while let Some(node) = current {
254+
// Expression contexts where we need bare type names
255+
if let Some(call_expr) = ast::CallExpr::cast(node.clone()) {
256+
if let Some(expr) = call_expr.expr() {
257+
if expr.syntax().text_range().contains_range(path.syntax().text_range()) {
258+
return true;
259+
}
260+
}
261+
}
262+
if ast::PathExpr::cast(node.clone()).is_some() {
263+
return true;
264+
}
265+
if let Some(record_expr) = ast::RecordExpr::cast(node.clone()) {
266+
if let Some(record_path) = record_expr.path() {
267+
if record_path.syntax().text_range().contains_range(path.syntax().text_range()) {
268+
return true;
269+
}
270+
}
271+
}
272+
// Stop at type/pattern boundaries
273+
if ast::Type::cast(node.clone()).is_some()
274+
|| ast::Pat::cast(node.clone()).is_some()
275+
|| ast::RetType::cast(node.clone()).is_some()
276+
{
277+
return false;
278+
}
279+
current = node.parent();
280+
}
281+
false
282+
}
283+
284+
fn get_bare_type_name(ty_str: &str) -> String {
285+
if let Some(angle_pos) = ty_str.find('<') {
286+
ty_str[..angle_pos].to_owned()
287+
} else {
288+
ty_str.to_owned()
289+
}
290+
}
291+
251292
impl Ctx<'_> {
252293
fn apply(&self, item: &SyntaxNode) {
253294
// `transform_path` may update a node's parent and that would break the
@@ -413,10 +454,17 @@ impl Ctx<'_> {
413454
true,
414455
)
415456
.ok()?;
416-
let ast_ty = make::ty(ty_str).clone_for_update();
457+
458+
// Context-aware replacement
459+
let replacement = if is_path_in_expression_context(&path) {
460+
let bare_name = get_bare_type_name(ty_str);
461+
make::ty(&bare_name).clone_for_update()
462+
} else {
463+
make::ty(ty_str).clone_for_update()
464+
};
417465

418466
if let Some(adt) = ty.as_adt() {
419-
if let ast::Type::PathType(path_ty) = &ast_ty {
467+
if let ast::Type::PathType(path_ty) = &replacement {
420468
let cfg = ImportPathConfig {
421469
prefer_no_std: false,
422470
prefer_prelude: true,
@@ -439,7 +487,7 @@ impl Ctx<'_> {
439487
}
440488
}
441489

442-
ted::replace(path.syntax(), ast_ty.syntax());
490+
ted::replace(path.syntax(), replacement.syntax());
443491
}
444492
hir::PathResolution::Local(_)
445493
| hir::PathResolution::Def(_)

0 commit comments

Comments
 (0)