Skip to content

Commit 6bbb2ac

Browse files
committed
Auto merge of rust-lang#15705 - rmehri01:14485_fix_delegate_self_references, r=Veykril
fix: resolve Self type references in delegate method assist This PR makes the delegate method assist resolve any `Self` type references in the parameters or return type. It also works across macros such as the `uint_impl!` macro used for `saturating_mul` in the issue example. Closes rust-lang#14485
2 parents 4f3d862 + 7e768cb commit 6bbb2ac

File tree

4 files changed

+263
-5
lines changed

4 files changed

+263
-5
lines changed

crates/hir-def/src/resolver.rs

+8
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,14 @@ impl Resolver {
588588
_ => None,
589589
})
590590
}
591+
592+
pub fn impl_def(&self) -> Option<ImplId> {
593+
self.scopes().find_map(|scope| match scope {
594+
Scope::ImplDefScope(def) => Some(*def),
595+
_ => None,
596+
})
597+
}
598+
591599
/// `expr_id` is required to be an expression id that comes after the top level expression scope in the given resolver
592600
#[must_use]
593601
pub fn update_to_inner_scope(

crates/hir/src/semantics.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,10 @@ impl SemanticsScope<'_> {
15561556
pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
15571557
self.resolver.extern_crate_decls_in_scope(self.db.upcast())
15581558
}
1559+
1560+
pub fn has_same_self_type(&self, other: &SemanticsScope<'_>) -> bool {
1561+
self.resolver.impl_def() == other.resolver.impl_def()
1562+
}
15591563
}
15601564

15611565
#[derive(Debug)]

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

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

3-
use hir::{self, HasCrate, HasSource, HasVisibility};
3+
use hir::{self, HasCrate, 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 _,
@@ -105,7 +106,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
105106
target,
106107
|edit| {
107108
// Create the function
108-
let method_source = match method.source(ctx.db()) {
109+
let method_source = match ctx.sema.source(method) {
109110
Some(source) => source.value,
110111
None => return,
111112
};
@@ -130,7 +131,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
130131
vis,
131132
fn_name,
132133
type_params,
133-
None,
134+
method_source.where_clause(),
134135
params,
135136
body,
136137
ret_type,
@@ -183,6 +184,12 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
183184
let assoc_items = impl_def.get_or_create_assoc_item_list();
184185
assoc_items.add_item(f.clone().into());
185186

187+
if let Some((target, source)) =
188+
ctx.sema.scope(strukt.syntax()).zip(ctx.sema.scope(method_source.syntax()))
189+
{
190+
PathTransform::generic_transformation(&target, &source).apply(f.syntax());
191+
}
192+
186193
if let Some(cap) = ctx.config.snippet_cap {
187194
edit.add_tabstop_before(cap, f)
188195
}
@@ -454,6 +461,209 @@ impl Person {
454461
);
455462
}
456463

464+
#[test]
465+
fn test_preserve_where_clause() {
466+
check_assist(
467+
generate_delegate_methods,
468+
r#"
469+
struct Inner<T>(T);
470+
impl<T> Inner<T> {
471+
fn get(&self) -> T
472+
where
473+
T: Copy,
474+
T: PartialEq,
475+
{
476+
self.0
477+
}
478+
}
479+
480+
struct Struct<T> {
481+
$0field: Inner<T>,
482+
}
483+
"#,
484+
r#"
485+
struct Inner<T>(T);
486+
impl<T> Inner<T> {
487+
fn get(&self) -> T
488+
where
489+
T: Copy,
490+
T: PartialEq,
491+
{
492+
self.0
493+
}
494+
}
495+
496+
struct Struct<T> {
497+
field: Inner<T>,
498+
}
499+
500+
impl<T> Struct<T> {
501+
$0fn get(&self) -> T where
502+
T: Copy,
503+
T: PartialEq, {
504+
self.field.get()
505+
}
506+
}
507+
"#,
508+
);
509+
}
510+
511+
#[test]
512+
fn test_fixes_basic_self_references() {
513+
check_assist(
514+
generate_delegate_methods,
515+
r#"
516+
struct Foo {
517+
field: $0Bar,
518+
}
519+
520+
struct Bar;
521+
522+
impl Bar {
523+
fn bar(&self, other: Self) -> Self {
524+
other
525+
}
526+
}
527+
"#,
528+
r#"
529+
struct Foo {
530+
field: Bar,
531+
}
532+
533+
impl Foo {
534+
$0fn bar(&self, other: Bar) -> Bar {
535+
self.field.bar(other)
536+
}
537+
}
538+
539+
struct Bar;
540+
541+
impl Bar {
542+
fn bar(&self, other: Self) -> Self {
543+
other
544+
}
545+
}
546+
"#,
547+
);
548+
}
549+
550+
#[test]
551+
fn test_fixes_nested_self_references() {
552+
check_assist(
553+
generate_delegate_methods,
554+
r#"
555+
struct Foo {
556+
field: $0Bar,
557+
}
558+
559+
struct Bar;
560+
561+
impl Bar {
562+
fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
563+
}
564+
"#,
565+
r#"
566+
struct Foo {
567+
field: Bar,
568+
}
569+
570+
impl Foo {
571+
$0fn bar(&mut self, a: (Bar, [Bar; 4]), b: Vec<Bar>) {
572+
self.field.bar(a, b)
573+
}
574+
}
575+
576+
struct Bar;
577+
578+
impl Bar {
579+
fn bar(&mut self, a: (Self, [Self; 4]), b: Vec<Self>) {}
580+
}
581+
"#,
582+
);
583+
}
584+
585+
#[test]
586+
fn test_fixes_self_references_with_lifetimes_and_generics() {
587+
check_assist(
588+
generate_delegate_methods,
589+
r#"
590+
struct Foo<'a, T> {
591+
$0field: Bar<'a, T>,
592+
}
593+
594+
struct Bar<'a, T>(&'a T);
595+
596+
impl<'a, T> Bar<'a, T> {
597+
fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
598+
b.pop().unwrap()
599+
}
600+
}
601+
"#,
602+
r#"
603+
struct Foo<'a, T> {
604+
field: Bar<'a, T>,
605+
}
606+
607+
impl<'a, T> Foo<'a, T> {
608+
$0fn bar(self, mut b: Vec<&'a Bar<'_, T>>) -> &'a Bar<'_, T> {
609+
self.field.bar(b)
610+
}
611+
}
612+
613+
struct Bar<'a, T>(&'a T);
614+
615+
impl<'a, T> Bar<'a, T> {
616+
fn bar(self, mut b: Vec<&'a Self>) -> &'a Self {
617+
b.pop().unwrap()
618+
}
619+
}
620+
"#,
621+
);
622+
}
623+
624+
#[test]
625+
fn test_fixes_self_references_across_macros() {
626+
check_assist(
627+
generate_delegate_methods,
628+
r#"
629+
//- /bar.rs
630+
macro_rules! test_method {
631+
() => {
632+
pub fn test(self, b: Bar) -> Self {
633+
self
634+
}
635+
};
636+
}
637+
638+
pub struct Bar;
639+
640+
impl Bar {
641+
test_method!();
642+
}
643+
644+
//- /main.rs
645+
mod bar;
646+
647+
struct Foo {
648+
$0bar: bar::Bar,
649+
}
650+
"#,
651+
r#"
652+
mod bar;
653+
654+
struct Foo {
655+
bar: bar::Bar,
656+
}
657+
658+
impl Foo {
659+
$0pub fn test(self,b:bar::Bar) ->bar::Bar {
660+
self.bar.test(b)
661+
}
662+
}
663+
"#,
664+
);
665+
}
666+
457667
#[test]
458668
fn test_generate_delegate_visibility() {
459669
check_assist_not_applicable(

crates/ide-db/src/path_transform.rs

+38-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},
@@ -183,6 +183,7 @@ impl<'a> PathTransform<'a> {
183183
lifetime_substs,
184184
target_module,
185185
source_scope: self.source_scope,
186+
same_self_type: self.target_scope.has_same_self_type(self.source_scope),
186187
};
187188
ctx.transform_default_values(defaulted_params);
188189
ctx
@@ -195,6 +196,7 @@ struct Ctx<'a> {
195196
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
196197
target_module: hir::Module,
197198
source_scope: &'a SemanticsScope<'a>,
199+
same_self_type: bool,
198200
}
199201

200202
fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
@@ -332,8 +334,42 @@ impl Ctx<'_> {
332334
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
333335
}
334336
}
337+
hir::PathResolution::SelfType(imp) => {
338+
// keep Self type if it does not need to be replaced
339+
if self.same_self_type {
340+
return None;
341+
}
342+
343+
let ty = imp.self_ty(self.source_scope.db);
344+
let ty_str = &ty
345+
.display_source_code(
346+
self.source_scope.db,
347+
self.source_scope.module().into(),
348+
true,
349+
)
350+
.ok()?;
351+
let ast_ty = make::ty(&ty_str).clone_for_update();
352+
353+
if let Some(adt) = ty.as_adt() {
354+
if let ast::Type::PathType(path_ty) = &ast_ty {
355+
let found_path = self.target_module.find_use_path(
356+
self.source_scope.db.upcast(),
357+
ModuleDef::from(adt),
358+
false,
359+
true,
360+
)?;
361+
362+
if let Some(qual) = mod_path_to_ast(&found_path).qualifier() {
363+
let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
364+
ted::replace(path.syntax(), res.syntax());
365+
return Some(());
366+
}
367+
}
368+
}
369+
370+
ted::replace(path.syntax(), ast_ty.syntax());
371+
}
335372
hir::PathResolution::Local(_)
336-
| hir::PathResolution::SelfType(_)
337373
| hir::PathResolution::Def(_)
338374
| hir::PathResolution::BuiltinAttr(_)
339375
| hir::PathResolution::ToolModule(_)

0 commit comments

Comments
 (0)