Skip to content

Commit 6f1cf03

Browse files
authored
Merge pull request #21870 from Shourya742/2026-03-25-remove-generate-trait-impl-text-intransitive
Remove generate trait impl text intransitive from utils
2 parents eab192d + 80b311d commit 6f1cf03

3 files changed

Lines changed: 148 additions & 79 deletions

File tree

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

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
use std::fmt::Display;
2-
31
use hir::{ModPath, ModuleDef};
4-
use ide_db::{RootDatabase, famous_defs::FamousDefs};
2+
use ide_db::{FileId, RootDatabase, famous_defs::FamousDefs};
53
use syntax::{
6-
AstNode, Edition, SyntaxNode,
7-
ast::{self, HasName},
4+
Edition,
5+
ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
6+
syntax_editor::Position,
87
};
98

109
use crate::{
1110
AssistId,
1211
assist_context::{AssistContext, Assists, SourceChangeBuilder},
13-
utils::generate_trait_impl_text_intransitive,
12+
utils::generate_trait_impl_intransitive_with_item,
1413
};
1514

1615
// Assist: generate_deref
@@ -64,6 +63,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
6463
let field_type = field.ty()?;
6564
let field_name = field.name()?;
6665
let target = field.syntax().text_range();
66+
let file_id = ctx.vfs_file_id();
6767
acc.add(
6868
AssistId::generate("generate_deref"),
6969
format!("Generate `{deref_type_to_generate:?}` impl using `{field_name}`"),
@@ -72,9 +72,10 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
7272
generate_edit(
7373
ctx.db(),
7474
edit,
75+
file_id,
7576
strukt,
76-
field_type.syntax(),
77-
field_name.syntax(),
77+
field_type,
78+
&field_name.to_string(),
7879
deref_type_to_generate,
7980
trait_path,
8081
module.krate(ctx.db()).edition(ctx.db()),
@@ -105,6 +106,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
105106

106107
let field_type = field.ty()?;
107108
let target = field.syntax().text_range();
109+
let file_id = ctx.vfs_file_id();
108110
acc.add(
109111
AssistId::generate("generate_deref"),
110112
format!("Generate `{deref_type_to_generate:?}` impl using `{field}`"),
@@ -113,9 +115,10 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
113115
generate_edit(
114116
ctx.db(),
115117
edit,
118+
file_id,
116119
strukt,
117-
field_type.syntax(),
118-
field_list_index,
120+
field_type,
121+
&field_list_index.to_string(),
119122
deref_type_to_generate,
120123
trait_path,
121124
module.krate(ctx.db()).edition(ctx.db()),
@@ -127,35 +130,81 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
127130
fn generate_edit(
128131
db: &RootDatabase,
129132
edit: &mut SourceChangeBuilder,
133+
file_id: FileId,
130134
strukt: ast::Struct,
131-
field_type_syntax: &SyntaxNode,
132-
field_name: impl Display,
135+
field_type: ast::Type,
136+
field_name: &str,
133137
deref_type: DerefType,
134138
trait_path: ModPath,
135139
edition: Edition,
136140
) {
137-
let start_offset = strukt.syntax().text_range().end();
138-
let impl_code = match deref_type {
139-
DerefType::Deref => format!(
140-
r#" type Target = {field_type_syntax};
141-
142-
fn deref(&self) -> &Self::Target {{
143-
&self.{field_name}
144-
}}"#,
145-
),
146-
DerefType::DerefMut => format!(
147-
r#" fn deref_mut(&mut self) -> &mut Self::Target {{
148-
&mut self.{field_name}
149-
}}"#,
150-
),
141+
let make = SyntaxFactory::with_mappings();
142+
let strukt_adt = ast::Adt::Struct(strukt.clone());
143+
let trait_ty = make.ty(&trait_path.display(db, edition).to_string());
144+
145+
let assoc_items: Vec<ast::AssocItem> = match deref_type {
146+
DerefType::Deref => {
147+
let target_alias =
148+
make.ty_alias([], "Target", None, None, None, Some((field_type, None)));
149+
let ret_ty =
150+
make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), false);
151+
let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name);
152+
let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), false)));
153+
let fn_ = make
154+
.fn_(
155+
[],
156+
None,
157+
make.name("deref"),
158+
None,
159+
None,
160+
make.param_list(Some(make.self_param()), []),
161+
body,
162+
Some(make.ret_type(ret_ty)),
163+
false,
164+
false,
165+
false,
166+
false,
167+
)
168+
.indent(1.into());
169+
vec![ast::AssocItem::TypeAlias(target_alias), ast::AssocItem::Fn(fn_)]
170+
}
171+
DerefType::DerefMut => {
172+
let ret_ty =
173+
make.ty_ref(make.ty_path(make.path_from_text("Self::Target")).into(), true);
174+
let field_expr = make.expr_field(make.expr_path(make.ident_path("self")), field_name);
175+
let body = make.block_expr([], Some(make.expr_ref(field_expr.into(), true)));
176+
let fn_ = make
177+
.fn_(
178+
[],
179+
None,
180+
make.name("deref_mut"),
181+
None,
182+
None,
183+
make.param_list(Some(make.mut_self_param()), []),
184+
body,
185+
Some(make.ret_type(ret_ty)),
186+
false,
187+
false,
188+
false,
189+
false,
190+
)
191+
.indent(1.into());
192+
vec![ast::AssocItem::Fn(fn_)]
193+
}
151194
};
152-
let strukt_adt = ast::Adt::Struct(strukt);
153-
let deref_impl = generate_trait_impl_text_intransitive(
154-
&strukt_adt,
155-
&trait_path.display(db, edition).to_string(),
156-
&impl_code,
195+
196+
let body = make.assoc_item_list(assoc_items);
197+
let indent = strukt.indent_level();
198+
let impl_ = generate_trait_impl_intransitive_with_item(&make, &strukt_adt, trait_ty, body)
199+
.indent(indent);
200+
201+
let mut editor = edit.make_editor(strukt.syntax());
202+
editor.insert_all(
203+
Position::after(strukt.syntax()),
204+
vec![make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into()],
157205
);
158-
edit.insert(start_offset, deref_impl);
206+
editor.add_mappings(make.finish_with_mappings());
207+
edit.add_file_edits(file_id, editor);
159208
}
160209

161210
fn existing_deref_impl(

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

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use hir::next_solver::{DbInterner, TypingMode};
22
use ide_db::{RootDatabase, famous_defs::FamousDefs};
3-
use syntax::ast::{self, AstNode, HasName};
3+
use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory};
4+
use syntax::syntax_editor::Position;
45

56
use crate::{
67
AssistContext, AssistId, Assists,
7-
utils::{generate_trait_impl_text_intransitive, is_selected},
8+
utils::{generate_trait_impl_intransitive_with_item, is_selected},
89
};
910

1011
// Assist: generate_from_impl_for_enum
@@ -33,39 +34,72 @@ pub(crate) fn generate_from_impl_for_enum(
3334
let variants = selected_variants(ctx, &variant)?;
3435

3536
let target = variant.syntax().text_range();
37+
let file_id = ctx.vfs_file_id();
3638
acc.add(
3739
AssistId::generate("generate_from_impl_for_enum"),
3840
"Generate `From` impl for this enum variant(s)",
3941
target,
4042
|edit| {
41-
let start_offset = variant.parent_enum().syntax().text_range().end();
42-
let from_impl = variants
43-
.into_iter()
44-
.map(|variant_info| {
45-
let from_trait = format!("From<{}>", variant_info.ty);
46-
let impl_code = generate_impl_code(variant_info);
47-
generate_trait_impl_text_intransitive(&adt, &from_trait, &impl_code)
48-
})
49-
.collect::<String>();
50-
edit.insert(start_offset, from_impl);
43+
let make = SyntaxFactory::with_mappings();
44+
let indent = adt.indent_level();
45+
let mut elements = Vec::new();
46+
47+
for variant_info in variants {
48+
let impl_ = build_from_impl(&make, &adt, variant_info).indent(indent);
49+
elements.push(make.whitespace(&format!("\n\n{indent}")).into());
50+
elements.push(impl_.syntax().clone().into());
51+
}
52+
53+
let mut editor = edit.make_editor(adt.syntax());
54+
editor.insert_all(Position::after(adt.syntax()), elements);
55+
editor.add_mappings(make.finish_with_mappings());
56+
edit.add_file_edits(file_id, editor);
5157
},
5258
)
5359
}
5460

55-
fn generate_impl_code(VariantInfo { name, field_name, ty }: VariantInfo) -> String {
56-
if let Some(field) = field_name {
57-
format!(
58-
r#" fn from({field}: {ty}) -> Self {{
59-
Self::{name} {{ {field} }}
60-
}}"#
61-
)
61+
fn build_from_impl(make: &SyntaxFactory, adt: &ast::Adt, variant_info: VariantInfo) -> ast::Impl {
62+
let VariantInfo { name, field_name, ty } = variant_info;
63+
let trait_ty = make.ty(&format!("From<{ty}>"));
64+
let ret_ty = make.ret_type(make.ty_path(make.ident_path("Self")).into());
65+
66+
let (params, body_expr) = if let Some(field) = field_name {
67+
let field_str = field.to_string();
68+
let param = make.param(make.ident_pat(false, false, make.name(&field_str)).into(), ty);
69+
let field_item = make.record_expr_field(make.name_ref(&field_str), None);
70+
let record = make.record_expr(
71+
make.path_from_text(&format!("Self::{name}")),
72+
make.record_expr_field_list([field_item]),
73+
);
74+
(make.param_list(None, [param]), ast::Expr::from(record))
6275
} else {
63-
format!(
64-
r#" fn from(v: {ty}) -> Self {{
65-
Self::{name}(v)
66-
}}"#
76+
let param = make.param(make.ident_pat(false, false, make.name("v")).into(), ty);
77+
let call = make.expr_call(
78+
make.expr_path(make.path_from_text(&format!("Self::{name}"))),
79+
make.arg_list([make.expr_path(make.ident_path("v"))]),
80+
);
81+
(make.param_list(None, [param]), ast::Expr::from(call))
82+
};
83+
84+
let from_fn = make
85+
.fn_(
86+
[],
87+
None,
88+
make.name("from"),
89+
None,
90+
None,
91+
params,
92+
make.block_expr([], Some(body_expr)),
93+
Some(ret_ty),
94+
false,
95+
false,
96+
false,
97+
false,
6798
)
68-
}
99+
.indent(1.into());
100+
101+
let body = make.assoc_item_list([ast::AssocItem::Fn(from_fn)]);
102+
generate_trait_impl_intransitive_with_item(make, adt, trait_ty, body)
69103
}
70104

71105
struct VariantInfo {

crates/ide-assists/src/utils.rs

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -630,29 +630,6 @@ pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
630630
generate_impl_text_inner(adt, None, true, code)
631631
}
632632

633-
/// Generates the surrounding `impl <trait> for Type { <code> }` including type
634-
/// and lifetime parameters, with `<trait>` appended to `impl`'s generic parameters' bounds.
635-
///
636-
/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
637-
// FIXME: migrate remaining uses to `generate_trait_impl`
638-
#[allow(dead_code)]
639-
pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
640-
generate_impl_text_inner(adt, Some(trait_text), true, code)
641-
}
642-
643-
/// Generates the surrounding `impl <trait> for Type { <code> }` including type
644-
/// and lifetime parameters, with `impl`'s generic parameters' bounds kept as-is.
645-
///
646-
/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
647-
// FIXME: migrate remaining uses to `generate_trait_impl_intransitive`
648-
pub(crate) fn generate_trait_impl_text_intransitive(
649-
adt: &ast::Adt,
650-
trait_text: &str,
651-
code: &str,
652-
) -> String {
653-
generate_impl_text_inner(adt, Some(trait_text), false, code)
654-
}
655-
656633
fn generate_impl_text_inner(
657634
adt: &ast::Adt,
658635
trait_text: Option<&str>,
@@ -773,6 +750,15 @@ pub(crate) fn generate_trait_impl_intransitive(
773750
generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, None)
774751
}
775752

753+
pub(crate) fn generate_trait_impl_intransitive_with_item(
754+
make: &SyntaxFactory,
755+
adt: &ast::Adt,
756+
trait_: ast::Type,
757+
body: ast::AssocItemList,
758+
) -> ast::Impl {
759+
generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body))
760+
}
761+
776762
fn generate_impl_inner(
777763
is_unsafe: bool,
778764
adt: &ast::Adt,

0 commit comments

Comments
 (0)