Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 7c56896

Browse files
author
Tiddo Langerak
committed
Feat: extracted method from trait impl is placed in existing impl
Previously, when triggering a method extraction from within a trait impl block, then this would always create a new impl block for the struct, even if there already is one. Now, it'll put the extracted method in the matching existing block if it exists.
1 parent d186986 commit 7c56896

File tree

1 file changed

+268
-4
lines changed

1 file changed

+268
-4
lines changed

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

Lines changed: 268 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
109109
let params =
110110
body.extracted_function_params(ctx, &container_info, locals_used.iter().copied());
111111

112-
let extracted_from_trait_impl = body.extracted_from_trait_impl();
113-
114112
let name = make_function_name(&semantics_scope);
115113

116114
let fun = Function {
@@ -129,8 +127,13 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
129127

130128
builder.replace(target_range, make_call(ctx, &fun, old_indent));
131129

130+
let has_impl_wrapper = insert_after
131+
.ancestors()
132+
.find(|a| a.kind() == SyntaxKind::IMPL && a != &insert_after)
133+
.is_some();
134+
132135
let fn_def = match fun.self_param_adt(ctx) {
133-
Some(adt) if extracted_from_trait_impl => {
136+
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
134137
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
135138
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
136139
}
@@ -271,7 +274,7 @@ enum FunType {
271274
}
272275

273276
/// Where to put extracted function definition
274-
#[derive(Debug)]
277+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
275278
enum Anchor {
276279
/// Extract free function and put right after current top-level function
277280
Freestanding,
@@ -1244,6 +1247,15 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
12441247
while let Some(next_ancestor) = ancestors.next() {
12451248
match next_ancestor.kind() {
12461249
SyntaxKind::SOURCE_FILE => break,
1250+
SyntaxKind::IMPL => {
1251+
if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) {
1252+
let impl_node = find_non_trait_impl(&next_ancestor);
1253+
let target_node = impl_node.as_ref().and_then(last_impl_member);
1254+
if target_node.is_some() {
1255+
return target_node;
1256+
}
1257+
}
1258+
}
12471259
SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
12481260
SyntaxKind::ITEM_LIST => {
12491261
if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
@@ -1264,6 +1276,28 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
12641276
last_ancestor
12651277
}
12661278

1279+
fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<SyntaxNode> {
1280+
let impl_type = Some(impl_type_name(trait_impl)?);
1281+
1282+
let mut sibblings = trait_impl.parent()?.children();
1283+
sibblings.find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
1284+
}
1285+
1286+
fn last_impl_member(impl_node: &SyntaxNode) -> Option<SyntaxNode> {
1287+
impl_node.children().find(|c| c.kind() == SyntaxKind::ASSOC_ITEM_LIST)?.last_child()
1288+
}
1289+
1290+
fn is_trait_impl(node: &SyntaxNode) -> bool {
1291+
match ast::Impl::cast(node.clone()) {
1292+
Some(c) => c.trait_().is_some(),
1293+
None => false,
1294+
}
1295+
}
1296+
1297+
fn impl_type_name(impl_node: &SyntaxNode) -> Option<String> {
1298+
Some(ast::Impl::cast(impl_node.clone())?.self_ty()?.to_string())
1299+
}
1300+
12671301
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
12681302
let ret_ty = fun.return_type(ctx);
12691303

@@ -5058,6 +5092,236 @@ impl Struct {
50585092
);
50595093
}
50605094

5095+
#[test]
5096+
fn extract_method_from_trait_with_existing_non_empty_impl_block() {
5097+
check_assist(
5098+
extract_function,
5099+
r#"
5100+
struct Struct(i32);
5101+
trait Trait {
5102+
fn bar(&self) -> i32;
5103+
}
5104+
5105+
impl Struct {
5106+
fn foo() {}
5107+
}
5108+
5109+
impl Trait for Struct {
5110+
fn bar(&self) -> i32 {
5111+
$0self.0 + 2$0
5112+
}
5113+
}
5114+
"#,
5115+
r#"
5116+
struct Struct(i32);
5117+
trait Trait {
5118+
fn bar(&self) -> i32;
5119+
}
5120+
5121+
impl Struct {
5122+
fn foo() {}
5123+
5124+
fn $0fun_name(&self) -> i32 {
5125+
self.0 + 2
5126+
}
5127+
}
5128+
5129+
impl Trait for Struct {
5130+
fn bar(&self) -> i32 {
5131+
self.fun_name()
5132+
}
5133+
}
5134+
"#,
5135+
)
5136+
}
5137+
5138+
#[test]
5139+
fn extract_function_from_trait_with_existing_non_empty_impl_block() {
5140+
check_assist(
5141+
extract_function,
5142+
r#"
5143+
struct Struct(i32);
5144+
trait Trait {
5145+
fn bar(&self) -> i32;
5146+
}
5147+
5148+
impl Struct {
5149+
fn foo() {}
5150+
}
5151+
5152+
impl Trait for Struct {
5153+
fn bar(&self) -> i32 {
5154+
let three_squared = $03 * 3$0;
5155+
self.0 + three_squared
5156+
}
5157+
}
5158+
"#,
5159+
r#"
5160+
struct Struct(i32);
5161+
trait Trait {
5162+
fn bar(&self) -> i32;
5163+
}
5164+
5165+
impl Struct {
5166+
fn foo() {}
5167+
}
5168+
5169+
impl Trait for Struct {
5170+
fn bar(&self) -> i32 {
5171+
let three_squared = fun_name();
5172+
self.0 + three_squared
5173+
}
5174+
}
5175+
5176+
fn $0fun_name() -> i32 {
5177+
3 * 3
5178+
}
5179+
"#,
5180+
)
5181+
}
5182+
5183+
#[test]
5184+
fn extract_method_from_trait_with_multiple_existing_impl_blocks() {
5185+
check_assist(
5186+
extract_function,
5187+
r#"
5188+
struct Struct(i32);
5189+
struct StructBefore(i32);
5190+
struct StructAfter(i32);
5191+
trait Trait {
5192+
fn bar(&self) -> i32;
5193+
}
5194+
5195+
impl StructBefore {
5196+
fn foo(){}
5197+
}
5198+
5199+
impl Struct {
5200+
fn foo(){}
5201+
}
5202+
5203+
impl StructAfter {
5204+
fn foo(){}
5205+
}
5206+
5207+
impl Trait for Struct {
5208+
fn bar(&self) -> i32 {
5209+
$0self.0 + 2$0
5210+
}
5211+
}
5212+
"#,
5213+
r#"
5214+
struct Struct(i32);
5215+
struct StructBefore(i32);
5216+
struct StructAfter(i32);
5217+
trait Trait {
5218+
fn bar(&self) -> i32;
5219+
}
5220+
5221+
impl StructBefore {
5222+
fn foo(){}
5223+
}
5224+
5225+
impl Struct {
5226+
fn foo(){}
5227+
5228+
fn $0fun_name(&self) -> i32 {
5229+
self.0 + 2
5230+
}
5231+
}
5232+
5233+
impl StructAfter {
5234+
fn foo(){}
5235+
}
5236+
5237+
impl Trait for Struct {
5238+
fn bar(&self) -> i32 {
5239+
self.fun_name()
5240+
}
5241+
}
5242+
"#,
5243+
)
5244+
}
5245+
5246+
#[test]
5247+
fn extract_method_from_trait_with_multiple_existing_trait_impl_blocks() {
5248+
check_assist(
5249+
extract_function,
5250+
r#"
5251+
struct Struct(i32);
5252+
trait Trait {
5253+
fn bar(&self) -> i32;
5254+
}
5255+
trait TraitBefore {
5256+
fn before(&self) -> i32;
5257+
}
5258+
trait TraitAfter {
5259+
fn after(&self) -> i32;
5260+
}
5261+
5262+
impl TraitBefore for Struct {
5263+
fn before(&self) -> i32 {
5264+
42
5265+
}
5266+
}
5267+
5268+
impl Struct {
5269+
fn foo(){}
5270+
}
5271+
5272+
impl TraitAfter for Struct {
5273+
fn after(&self) -> i32 {
5274+
42
5275+
}
5276+
}
5277+
5278+
impl Trait for Struct {
5279+
fn bar(&self) -> i32 {
5280+
$0self.0 + 2$0
5281+
}
5282+
}
5283+
"#,
5284+
r#"
5285+
struct Struct(i32);
5286+
trait Trait {
5287+
fn bar(&self) -> i32;
5288+
}
5289+
trait TraitBefore {
5290+
fn before(&self) -> i32;
5291+
}
5292+
trait TraitAfter {
5293+
fn after(&self) -> i32;
5294+
}
5295+
5296+
impl TraitBefore for Struct {
5297+
fn before(&self) -> i32 {
5298+
42
5299+
}
5300+
}
5301+
5302+
impl Struct {
5303+
fn foo(){}
5304+
5305+
fn $0fun_name(&self) -> i32 {
5306+
self.0 + 2
5307+
}
5308+
}
5309+
5310+
impl TraitAfter for Struct {
5311+
fn after(&self) -> i32 {
5312+
42
5313+
}
5314+
}
5315+
5316+
impl Trait for Struct {
5317+
fn bar(&self) -> i32 {
5318+
self.fun_name()
5319+
}
5320+
}
5321+
"#,
5322+
)
5323+
}
5324+
50615325
#[test]
50625326
fn closure_arguments() {
50635327
check_assist(

0 commit comments

Comments
 (0)