Skip to content

Commit 934358e

Browse files
rmehri01Veykril
authored andcommitted
fix: resolve Self type references in delegate method assist
1 parent bc9c952 commit 934358e

File tree

2 files changed

+197
-3
lines changed

2 files changed

+197
-3
lines changed

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

+167-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashSet;
22

33
use hir::{self, HasCrate, HasSource, HasVisibility};
4+
use ide_db::path_transform::PathTransform;
45
use syntax::{
56
ast::{
67
self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _,
@@ -106,7 +107,10 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
106107
|edit| {
107108
// Create the function
108109
let method_source = match method.source(ctx.db()) {
109-
Some(source) => source.value,
110+
Some(source) => {
111+
ctx.sema.parse_or_expand(source.file_id);
112+
source.value
113+
}
110114
None => return,
111115
};
112116
let vis = method_source.visibility();
@@ -183,6 +187,12 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
183187
let assoc_items = impl_def.get_or_create_assoc_item_list();
184188
assoc_items.add_item(f.clone().into());
185189

190+
PathTransform::generic_transformation(
191+
&ctx.sema.scope(strukt.syntax()).unwrap(),
192+
&ctx.sema.scope(method_source.syntax()).unwrap(),
193+
)
194+
.apply(f.syntax());
195+
186196
if let Some(cap) = ctx.config.snippet_cap {
187197
edit.add_tabstop_before(cap, f)
188198
}
@@ -454,6 +464,162 @@ impl Person {
454464
);
455465
}
456466

467+
#[test]
468+
fn test_fixes_basic_self_references() {
469+
check_assist(
470+
generate_delegate_methods,
471+
r#"
472+
struct Foo {
473+
field: $0Bar,
474+
}
475+
476+
struct Bar;
477+
478+
impl Bar {
479+
fn bar(&self, other: Self) -> Self {
480+
other
481+
}
482+
}
483+
"#,
484+
r#"
485+
struct Foo {
486+
field: Bar,
487+
}
488+
489+
impl Foo {
490+
$0fn bar(&self, other: Bar) -> Bar {
491+
self.field.bar(other)
492+
}
493+
}
494+
495+
struct Bar;
496+
497+
impl Bar {
498+
fn bar(&self, other: Self) -> Self {
499+
other
500+
}
501+
}
502+
"#,
503+
);
504+
}
505+
506+
#[test]
507+
fn test_fixes_nested_self_references() {
508+
check_assist(
509+
generate_delegate_methods,
510+
r#"
511+
struct Foo {
512+
field: $0Bar,
513+
}
514+
515+
struct Bar;
516+
517+
impl Bar {
518+
fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
519+
}
520+
"#,
521+
r#"
522+
struct Foo {
523+
field: Bar,
524+
}
525+
526+
impl Foo {
527+
$0fn bar(&mut self, a: (Bar, [Bar; 4]), b: Vec<Bar>) {
528+
self.field.bar(a, b)
529+
}
530+
}
531+
532+
struct Bar;
533+
534+
impl Bar {
535+
fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
536+
}
537+
"#,
538+
);
539+
}
540+
541+
#[test]
542+
fn test_fixes_self_references_with_lifetimes_and_generics() {
543+
check_assist(
544+
generate_delegate_methods,
545+
r#"
546+
struct Foo<'a, T> {
547+
$0field: Bar<'a, T>,
548+
}
549+
550+
struct Bar<'a, T>(&'a T);
551+
552+
impl<'a, T> Bar<'a, T> {
553+
fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
554+
b.pop().unwrap()
555+
}
556+
}
557+
"#,
558+
r#"
559+
struct Foo<'a, T> {
560+
field: Bar<'a, T>,
561+
}
562+
563+
impl<'a, T> Foo<'a, T> {
564+
$0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> {
565+
self.field.bar(b)
566+
}
567+
}
568+
569+
struct Bar<'a, T>(&'a T);
570+
571+
impl<'a, T> Bar<'a, T> {
572+
fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
573+
b.pop().unwrap()
574+
}
575+
}
576+
"#,
577+
);
578+
}
579+
580+
#[test]
581+
fn test_fixes_self_references_across_macros() {
582+
check_assist(
583+
generate_delegate_methods,
584+
r#"
585+
//- /bar.rs
586+
macro_rules! test_method {
587+
() => {
588+
pub fn test(self, b: Bar) -> Self {
589+
self
590+
}
591+
};
592+
}
593+
594+
pub struct Bar;
595+
596+
impl Bar {
597+
test_method!();
598+
}
599+
600+
//- /main.rs
601+
mod bar;
602+
603+
struct Foo {
604+
$0bar: bar::Bar,
605+
}
606+
"#,
607+
r#"
608+
mod bar;
609+
610+
struct Foo {
611+
bar: bar::Bar,
612+
}
613+
614+
impl Foo {
615+
$0pub fn test(self,b:bar::Bar) ->bar::Bar {
616+
self.bar.test(b)
617+
}
618+
}
619+
"#,
620+
);
621+
}
622+
457623
#[test]
458624
fn test_generate_delegate_visibility() {
459625
check_assist_not_applicable(

crates/ide-db/src/path_transform.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::helpers::mod_path_to_ast;
44
use either::Either;
5-
use hir::{AsAssocItem, HirDisplay, SemanticsScope};
5+
use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope};
66
use rustc_hash::FxHashMap;
77
use syntax::{
88
ast::{self, make, AstNode},
@@ -332,8 +332,36 @@ impl Ctx<'_> {
332332
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
333333
}
334334
}
335+
hir::PathResolution::SelfType(imp) => {
336+
let ty = imp.self_ty(self.source_scope.db);
337+
let ty_str = &ty
338+
.display_source_code(
339+
self.source_scope.db,
340+
self.source_scope.module().into(),
341+
true,
342+
)
343+
.ok()?;
344+
let ast_ty = make::ty(&ty_str).clone_for_update();
345+
346+
if let Some(adt) = ty.as_adt() {
347+
if let ast::Type::PathType(path_ty) = &ast_ty {
348+
let found_path = self.target_module.find_use_path(
349+
self.source_scope.db.upcast(),
350+
ModuleDef::from(adt),
351+
false,
352+
)?;
353+
354+
if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
355+
let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
356+
ted::replace(path.syntax(), res.syntax());
357+
return Some(());
358+
}
359+
}
360+
}
361+
362+
ted::replace(path.syntax(), ast_ty.syntax());
363+
}
335364
hir::PathResolution::Local(_)
336-
| hir::PathResolution::SelfType(_)
337365
| hir::PathResolution::Def(_)
338366
| hir::PathResolution::BuiltinAttr(_)
339367
| hir::PathResolution::ToolModule(_)

0 commit comments

Comments
 (0)