about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/resolver.rs8
-rw-r--r--crates/hir/src/semantics.rs4
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_methods.rs216
-rw-r--r--crates/ide-db/src/path_transform.rs40
4 files changed, 263 insertions, 5 deletions
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 50da9ed06a0..ba0a2c0224a 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -588,6 +588,14 @@ impl Resolver {
             _ => None,
         })
     }
+
+    pub fn impl_def(&self) -> Option<ImplId> {
+        self.scopes().find_map(|scope| match scope {
+            Scope::ImplDefScope(def) => Some(*def),
+            _ => None,
+        })
+    }
+
     /// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
     #[must_use]
     pub fn update_to_inner_scope(
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 46835ec04e0..55c14312071 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -1556,6 +1556,10 @@ impl SemanticsScope<'_> {
     pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
         self.resolver.extern_crate_decls_in_scope(self.db.upcast())
     }
+
+    pub fn has_same_self_type(&self, other: &SemanticsScope<'_>) -> bool {
+        self.resolver.impl_def() == other.resolver.impl_def()
+    }
 }
 
 #[derive(Debug)]
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index bbac0a26ea4..db1e0ceaec1 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,6 +1,7 @@
 use std::collections::HashSet;
 
-use hir::{self, HasCrate, HasSource, HasVisibility};
+use hir::{self, HasCrate, HasVisibility};
+use ide_db::path_transform::PathTransform;
 use syntax::{
     ast::{
         self, edit_in_place::Indent, make, AstNode, HasGenericParams, HasName, HasVisibility as _,
@@ -105,7 +106,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
             target,
             |edit| {
                 // Create the function
-                let method_source = match method.source(ctx.db()) {
+                let method_source = match ctx.sema.source(method) {
                     Some(source) => source.value,
                     None => return,
                 };
@@ -130,7 +131,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                     vis,
                     fn_name,
                     type_params,
-                    None,
+                    method_source.where_clause(),
                     params,
                     body,
                     ret_type,
@@ -183,6 +184,12 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
                 let assoc_items = impl_def.get_or_create_assoc_item_list();
                 assoc_items.add_item(f.clone().into());
 
+                if let Some((target, source)) =
+                    ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax()))
+                {
+                    PathTransform::generic_transformation(&target, &source).apply(f.syntax());
+                }
+
                 if let Some(cap) = ctx.config.snippet_cap {
                     edit.add_tabstop_before(cap, f)
                 }
@@ -455,6 +462,209 @@ impl Person {
     }
 
     #[test]
+    fn test_preserve_where_clause() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Inner<T>(T);
+impl<T> Inner<T> {
+    fn get(&self) -> T
+    where
+        T: Copy,
+        T: PartialEq,
+    {
+        self.0
+    }
+}
+
+struct Struct<T> {
+    $0field: Inner<T>,
+}
+"#,
+            r#"
+struct Inner<T>(T);
+impl<T> Inner<T> {
+    fn get(&self) -> T
+    where
+        T: Copy,
+        T: PartialEq,
+    {
+        self.0
+    }
+}
+
+struct Struct<T> {
+    field: Inner<T>,
+}
+
+impl<T> Struct<T> {
+    $0fn get(&self) -> T where
+            T: Copy,
+            T: PartialEq, {
+        self.field.get()
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_basic_self_references() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo {
+    field: $0Bar,
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&self, other: Self) -> Self {
+        other
+    }
+}
+"#,
+            r#"
+struct Foo {
+    field: Bar,
+}
+
+impl Foo {
+    $0fn bar(&self, other: Bar) -> Bar {
+        self.field.bar(other)
+    }
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&self, other: Self) -> Self {
+        other
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_nested_self_references() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo {
+    field: $0Bar,
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
+}
+"#,
+            r#"
+struct Foo {
+    field: Bar,
+}
+
+impl Foo {
+    $0fn bar(&mut self, a: (Bar, [Bar; 4]), b: Vec<Bar>) {
+        self.field.bar(a, b)
+    }
+}
+
+struct Bar;
+
+impl Bar {
+    fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_self_references_with_lifetimes_and_generics() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+struct Foo<'a, T> {
+    $0field: Bar<'a, T>,
+}
+
+struct Bar<'a, T>(&'a T);
+
+impl<'a, T> Bar<'a, T> {
+    fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
+        b.pop().unwrap()
+    }
+}
+"#,
+            r#"
+struct Foo<'a, T> {
+    field: Bar<'a, T>,
+}
+
+impl<'a, T> Foo<'a, T> {
+    $0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> {
+        self.field.bar(b)
+    }
+}
+
+struct Bar<'a, T>(&'a T);
+
+impl<'a, T> Bar<'a, T> {
+    fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
+        b.pop().unwrap()
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_fixes_self_references_across_macros() {
+        check_assist(
+            generate_delegate_methods,
+            r#"
+//- /bar.rs
+macro_rules! test_method {
+    () => {
+        pub fn test(self, b: Bar) -> Self {
+            self
+        }
+    };
+}
+
+pub struct Bar;
+
+impl Bar {
+    test_method!();
+}
+
+//- /main.rs
+mod bar;
+
+struct Foo {
+    $0bar: bar::Bar,
+}
+"#,
+            r#"
+mod bar;
+
+struct Foo {
+    bar: bar::Bar,
+}
+
+impl Foo {
+    $0pub fn test(self,b:bar::Bar) ->bar::Bar {
+        self.bar.test(b)
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
     fn test_generate_delegate_visibility() {
         check_assist_not_applicable(
             generate_delegate_methods,
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index fa9339f30f2..fb4c0c12691 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -2,7 +2,7 @@
 
 use crate::helpers::mod_path_to_ast;
 use either::Either;
-use hir::{AsAssocItem, HirDisplay, SemanticsScope};
+use hir::{AsAssocItem, HirDisplay, ModuleDef, SemanticsScope};
 use rustc_hash::FxHashMap;
 use syntax::{
     ast::{self, make, AstNode},
@@ -183,6 +183,7 @@ impl<'a> PathTransform<'a> {
             lifetime_substs,
             target_module,
             source_scope: self.source_scope,
+            same_self_type: self.target_scope.has_same_self_type(self.source_scope),
         };
         ctx.transform_default_values(defaulted_params);
         ctx
@@ -195,6 +196,7 @@ struct Ctx<'a> {
     lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
     target_module: hir::Module,
     source_scope: &'a SemanticsScope<'a>,
+    same_self_type: bool,
 }
 
 fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
@@ -332,8 +334,42 @@ impl Ctx<'_> {
                     ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
                 }
             }
+            hir::PathResolution::SelfType(imp) => {
+                // keep Self type if it does not need to be replaced
+                if self.same_self_type {
+                    return None;
+                }
+
+                let ty = imp.self_ty(self.source_scope.db);
+                let ty_str = &ty
+                    .display_source_code(
+                        self.source_scope.db,
+                        self.source_scope.module().into(),
+                        true,
+                    )
+                    .ok()?;
+                let ast_ty = make::ty(&ty_str).clone_for_update();
+
+                if let Some(adt) = ty.as_adt() {
+                    if let ast::Type::PathType(path_ty) = &ast_ty {
+                        let found_path = self.target_module.find_use_path(
+                            self.source_scope.db.upcast(),
+                            ModuleDef::from(adt),
+                            false,
+                            true,
+                        )?;
+
+                        if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
+                            let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
+                            ted::replace(path.syntax(), res.syntax());
+                            return Some(());
+                        }
+                    }
+                }
+
+                ted::replace(path.syntax(), ast_ty.syntax());
+            }
             hir::PathResolution::Local(_)
-            | hir::PathResolution::SelfType(_)
             | hir::PathResolution::Def(_)
             | hir::PathResolution::BuiltinAttr(_)
             | hir::PathResolution::ToolModule(_)