summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs')
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs133
1 files changed, 112 insertions, 21 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index ed1b8f4e2..b68c766e6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use hir::{self, HasCrate, HasSource, HasVisibility};
use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _};
@@ -63,25 +65,34 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
- let krate = sema_field_ty.krate(ctx.db());
let mut methods = vec![];
- sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| {
- if let hir::AssocItem::Function(f) = item {
- if f.self_param(ctx.db()).is_some() && f.is_visible_from(ctx.db(), current_module) {
- methods.push(f)
- }
- }
- Option::<()>::None
- });
+ let mut seen_names = HashSet::new();
- for method in methods {
+ for ty in sema_field_ty.autoderef(ctx.db()) {
+ let krate = ty.krate(ctx.db());
+ ty.iterate_assoc_items(ctx.db(), krate, |item| {
+ if let hir::AssocItem::Function(f) = item {
+ let name = f.name(ctx.db());
+ if f.self_param(ctx.db()).is_some()
+ && f.is_visible_from(ctx.db(), current_module)
+ && seen_names.insert(name.clone())
+ {
+ methods.push((name, f))
+ }
+ }
+ Option::<()>::None
+ });
+ }
+ methods.sort_by(|(a, _), (b, _)| a.cmp(b));
+ for (name, method) in methods {
let adt = ast::Adt::Struct(strukt.clone());
- let name = method.name(ctx.db()).to_string();
- let impl_def = find_struct_impl(ctx, &adt, &[name]).flatten();
+ let name = name.display(ctx.db()).to_string();
+ // if `find_struct_impl` returns None, that means that a function named `name` already exists.
+ let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; };
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
- format!("Generate delegate for `{field_name}.{}()`", method.name(ctx.db())),
+ format!("Generate delegate for `{field_name}.{name}()`",),
target,
|builder| {
// Create the function
@@ -89,9 +100,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(source) => source.value,
None => return,
};
- let method_name = method.name(ctx.db());
let vis = method_source.visibility();
- let name = make::name(&method.name(ctx.db()).to_string());
+ let fn_name = make::name(&name);
let params =
method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
let type_params = method_source.generic_param_list();
@@ -101,17 +111,30 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let tail_expr = make::expr_method_call(
make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
- make::name_ref(&method_name.to_string()),
+ make::name_ref(&name),
arg_list,
);
let ret_type = method_source.ret_type();
let is_async = method_source.async_token().is_some();
+ let is_const = method_source.const_token().is_some();
+ let is_unsafe = method_source.unsafe_token().is_some();
let tail_expr_finished =
if is_async { make::expr_await(tail_expr) } else { tail_expr };
let body = make::block_expr([], Some(tail_expr_finished));
- let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async)
- .indent(ast::edit::IndentLevel(1))
- .clone_for_update();
+ let f = make::fn_(
+ vis,
+ fn_name,
+ type_params,
+ None,
+ params,
+ body,
+ ret_type,
+ is_async,
+ is_const,
+ is_unsafe,
+ )
+ .indent(ast::edit::IndentLevel(1))
+ .clone_for_update();
let cursor = Cursor::Before(f.syntax());
@@ -143,8 +166,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let name = &strukt_name.to_string();
let params = strukt.generic_param_list();
let ty_params = params.clone();
- let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params)
- .clone_for_update();
+ let where_clause = strukt.where_clause();
+
+ let impl_def = make::impl_(
+ ty_params,
+ None,
+ make::ty_path(make::ext::ident_path(name)),
+ where_clause,
+ None,
+ )
+ .clone_for_update();
let assoc_items = impl_def.get_or_create_assoc_item_list();
assoc_items.add_item(f.clone().into());
@@ -315,6 +346,44 @@ impl<T> Person<T> {
}
#[test]
+ fn test_generates_delegate_autoderef() {
+ check_assist(
+ generate_delegate_methods,
+ r#"
+//- minicore: deref
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ ag$0e: AgeDeref,
+}
+impl Person {}"#,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ age: AgeDeref,
+}
+impl Person {
+ $0fn age(&self) -> u8 {
+ self.age.age()
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn test_generate_delegate_visibility() {
check_assist_not_applicable(
generate_delegate_methods,
@@ -333,4 +402,26 @@ struct Person {
}"#,
)
}
+
+ #[test]
+ fn test_generate_not_eligible_if_fn_exists() {
+ check_assist_not_applicable(
+ generate_delegate_methods,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+
+struct Person {
+ ag$0e: Age,
+}
+impl Person {
+ fn age(&self) -> u8 { 0 }
+}
+"#,
+ );
+ }
}