Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #21870 from Shourya742/2026-03-25-remove-generate-trait-impl-text-intransitive
Remove generate trait impl text intransitive from utils
Shoyu Vanilla (Flint) 7 weeks ago
parent eab192d · parent 80b311d · commit 6f1cf03
-rw-r--r--crates/ide-assists/src/handlers/generate_deref.rs113
-rw-r--r--crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs82
-rw-r--r--crates/ide-assists/src/utils.rs32
3 files changed, 148 insertions, 79 deletions
diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs
index 494c87e6d1..5534dc1cd3 100644
--- a/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/crates/ide-assists/src/handlers/generate_deref.rs
@@ -1,16 +1,15 @@
-use std::fmt::Display;
-
use hir::{ModPath, ModuleDef};
-use ide_db::{RootDatabase, famous_defs::FamousDefs};
+use ide_db::{FileId, RootDatabase, famous_defs::FamousDefs};
use syntax::{
- AstNode, Edition, SyntaxNode,
- ast::{self, HasName},
+ Edition,
+ ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+ syntax_editor::Position,
};
use crate::{
AssistId,
assist_context::{AssistContext, Assists, SourceChangeBuilder},
- utils::generate_trait_impl_text_intransitive,
+ utils::generate_trait_impl_intransitive_with_item,
};
// Assist: generate_deref
@@ -64,6 +63,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let field_type = field.ty()?;
let field_name = field.name()?;
let target = field.syntax().text_range();
+ let file_id = ctx.vfs_file_id();
acc.add(
AssistId::generate("generate_deref"),
format!("Generate `{deref_type_to_generate:?}` impl using `{field_name}`"),
@@ -72,9 +72,10 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
generate_edit(
ctx.db(),
edit,
+ file_id,
strukt,
- field_type.syntax(),
- field_name.syntax(),
+ field_type,
+ &field_name.to_string(),
deref_type_to_generate,
trait_path,
module.krate(ctx.db()).edition(ctx.db()),
@@ -105,6 +106,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
let field_type = field.ty()?;
let target = field.syntax().text_range();
+ let file_id = ctx.vfs_file_id();
acc.add(
AssistId::generate("generate_deref"),
format!("Generate `{deref_type_to_generate:?}` impl using `{field}`"),
@@ -113,9 +115,10 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
generate_edit(
ctx.db(),
edit,
+ file_id,
strukt,
- field_type.syntax(),
- field_list_index,
+ field_type,
+ &field_list_index.to_string(),
deref_type_to_generate,
trait_path,
module.krate(ctx.db()).edition(ctx.db()),
@@ -127,35 +130,81 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
fn generate_edit(
db: &RootDatabase,
edit: &mut SourceChangeBuilder,
+ file_id: FileId,
strukt: ast::Struct,
- field_type_syntax: &SyntaxNode,
- field_name: impl Display,
+ field_type: ast::Type,
+ field_name: &str,
deref_type: DerefType,
trait_path: ModPath,
edition: Edition,
) {
- let start_offset = strukt.syntax().text_range().end();
- let impl_code = match deref_type {
- DerefType::Deref => format!(
- r#" type Target = {field_type_syntax};
-
- fn deref(&self) -> &Self::Target {{
- &self.{field_name}
- }}"#,
- ),
- DerefType::DerefMut => format!(
- r#" fn deref_mut(&mut self) -> &mut Self::Target {{
- &mut self.{field_name}
- }}"#,
- ),
+ let make = SyntaxFactory::with_mappings();
+ let strukt_adt = ast::Adt::Struct(strukt.clone());
+ let trait_ty = make.ty(&trait_path.display(db, edition).to_string());
+
+ let assoc_items: Vec<ast::AssocItem> = match deref_type {
+ DerefType::Deref => {
+ let target_alias =
+ make.ty_alias([], "Target", None, None, None, Some((field_type, None)));
+ let ret_ty =
+ make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), false);
+ let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name);
+ let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), false)));
+ let fn_ = make
+ .fn_(
+ [],
+ None,
+ make.name("deref"),
+ None,
+ None,
+ make.param_list(Some(make.self_param()), []),
+ body,
+ Some(make.ret_type(ret_ty)),
+ false,
+ false,
+ false,
+ false,
+ )
+ .indent(1.into());
+ vec![ast::AssocItem::TypeAlias(target_alias), ast::AssocItem::Fn(fn_)]
+ }
+ DerefType::DerefMut => {
+ let ret_ty =
+ make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), true);
+ let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name);
+ let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), true)));
+ let fn_ = make
+ .fn_(
+ [],
+ None,
+ make.name("deref_mut"),
+ None,
+ None,
+ make.param_list(Some(make.mut_self_param()), []),
+ body,
+ Some(make.ret_type(ret_ty)),
+ false,
+ false,
+ false,
+ false,
+ )
+ .indent(1.into());
+ vec![ast::AssocItem::Fn(fn_)]
+ }
};
- let strukt_adt = ast::Adt::Struct(strukt);
- let deref_impl = generate_trait_impl_text_intransitive(
- &strukt_adt,
- &trait_path.display(db, edition).to_string(),
- &impl_code,
+
+ let body = make.assoc_item_list(assoc_items);
+ let indent = strukt.indent_level();
+ let impl_ = generate_trait_impl_intransitive_with_item(&make, &strukt_adt, trait_ty, body)
+ .indent(indent);
+
+ let mut editor = edit.make_editor(strukt.syntax());
+ editor.insert_all(
+ Position::after(strukt.syntax()),
+ vec![make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into()],
);
- edit.insert(start_offset, deref_impl);
+ editor.add_mappings(make.finish_with_mappings());
+ edit.add_file_edits(file_id, editor);
}
fn existing_deref_impl(
diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
index 24f271ded8..1adb3f4fe4 100644
--- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,10 +1,11 @@
use hir::next_solver::{DbInterner, TypingMode};
use ide_db::{RootDatabase, famous_defs::FamousDefs};
-use syntax::ast::{self, AstNode, HasName};
+use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory};
+use syntax::syntax_editor::Position;
use crate::{
AssistContext, AssistId, Assists,
- utils::{generate_trait_impl_text_intransitive, is_selected},
+ utils::{generate_trait_impl_intransitive_with_item, is_selected},
};
// Assist: generate_from_impl_for_enum
@@ -33,39 +34,72 @@ pub(crate) fn generate_from_impl_for_enum(
let variants = selected_variants(ctx, &variant)?;
let target = variant.syntax().text_range();
+ let file_id = ctx.vfs_file_id();
acc.add(
AssistId::generate("generate_from_impl_for_enum"),
"Generate `From` impl for this enum variant(s)",
target,
|edit| {
- let start_offset = variant.parent_enum().syntax().text_range().end();
- let from_impl = variants
- .into_iter()
- .map(|variant_info| {
- let from_trait = format!("From<{}>", variant_info.ty);
- let impl_code = generate_impl_code(variant_info);
- generate_trait_impl_text_intransitive(&adt, &from_trait, &impl_code)
- })
- .collect::<String>();
- edit.insert(start_offset, from_impl);
+ let make = SyntaxFactory::with_mappings();
+ let indent = adt.indent_level();
+ let mut elements = Vec::new();
+
+ for variant_info in variants {
+ let impl_ = build_from_impl(&make, &adt, variant_info).indent(indent);
+ elements.push(make.whitespace(&format!("\n\n{indent}")).into());
+ elements.push(impl_.syntax().clone().into());
+ }
+
+ let mut editor = edit.make_editor(adt.syntax());
+ editor.insert_all(Position::after(adt.syntax()), elements);
+ editor.add_mappings(make.finish_with_mappings());
+ edit.add_file_edits(file_id, editor);
},
)
}
-fn generate_impl_code(VariantInfo { name, field_name, ty }: VariantInfo) -> String {
- if let Some(field) = field_name {
- format!(
- r#" fn from({field}: {ty}) -> Self {{
- Self::{name} {{ {field} }}
- }}"#
- )
+fn build_from_impl(make: &SyntaxFactory, adt: &ast::Adt, variant_info: VariantInfo) -> ast::Impl {
+ let VariantInfo { name, field_name, ty } = variant_info;
+ let trait_ty = make.ty(&format!("From<{ty}>"));
+ let ret_ty = make.ret_type(make.ty_path(make.ident_path("Self")).into());
+
+ let (params, body_expr) = if let Some(field) = field_name {
+ let field_str = field.to_string();
+ let param = make.param(make.ident_pat(false, false, make.name(&field_str)).into(), ty);
+ let field_item = make.record_expr_field(make.name_ref(&field_str), None);
+ let record = make.record_expr(
+ make.path_from_text(&format!("Self::{name}")),
+ make.record_expr_field_list([field_item]),
+ );
+ (make.param_list(None, [param]), ast::Expr::from(record))
} else {
- format!(
- r#" fn from(v: {ty}) -> Self {{
- Self::{name}(v)
- }}"#
+ let param = make.param(make.ident_pat(false, false, make.name("v")).into(), ty);
+ let call = make.expr_call(
+ make.expr_path(make.path_from_text(&format!("Self::{name}"))),
+ make.arg_list([make.expr_path(make.ident_path("v"))]),
+ );
+ (make.param_list(None, [param]), ast::Expr::from(call))
+ };
+
+ let from_fn = make
+ .fn_(
+ [],
+ None,
+ make.name("from"),
+ None,
+ None,
+ params,
+ make.block_expr([], Some(body_expr)),
+ Some(ret_ty),
+ false,
+ false,
+ false,
+ false,
)
- }
+ .indent(1.into());
+
+ let body = make.assoc_item_list([ast::AssocItem::Fn(from_fn)]);
+ generate_trait_impl_intransitive_with_item(make, adt, trait_ty, body)
}
struct VariantInfo {
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index cf1c66ab5e..96e1bbdc7e 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -630,29 +630,6 @@ pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
generate_impl_text_inner(adt, None, true, code)
}
-/// Generates the surrounding `impl <trait> for Type { <code> }` including type
-/// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds.
-///
-/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
-// FIXME: migrate remaining uses to `generate_trait_impl`
-#[allow(dead_code)]
-pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
- generate_impl_text_inner(adt, Some(trait_text), true, code)
-}
-
-/// Generates the surrounding `impl <trait> for Type { <code> }` including type
-/// and lifetime parameters, with `impl`'s generic parameters' bounds kept as-is.
-///
-/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
-// FIXME: migrate remaining uses to `generate_trait_impl_intransitive`
-pub(crate) fn generate_trait_impl_text_intransitive(
- adt: &ast::Adt,
- trait_text: &str,
- code: &str,
-) -> String {
- generate_impl_text_inner(adt, Some(trait_text), false, code)
-}
-
fn generate_impl_text_inner(
adt: &ast::Adt,
trait_text: Option<&str>,
@@ -773,6 +750,15 @@ pub(crate) fn generate_trait_impl_intransitive(
generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, None)
}
+pub(crate) fn generate_trait_impl_intransitive_with_item(
+ make: &SyntaxFactory,
+ adt: &ast::Adt,
+ trait_: ast::Type,
+ body: ast::AssocItemList,
+) -> ast::Impl {
+ generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body))
+}
+
fn generate_impl_inner(
is_unsafe: bool,
adt: &ast::Adt,