Skip to content

Commit d3d3806

Browse files
committed
Auto merge of rust-lang#12991 - TiddoLangerak:extract-method-from-trait-into-impl-root, r=Veykril
Feat: extracted method from trait impl is placed in existing impl **Before** https://user-images.githubusercontent.com/1759192/183872883-3b0eafd2-d1dc-440e-9e66-38e3372f8b64.mp4 **After** https://user-images.githubusercontent.com/1759192/183875769-87f34c7d-52f0-4dfc-9766-f591ee738ebb.mp4 Previously, when triggering a method extraction from within an impl trait block, then this would always create a new impl block for the struct, even if there already is one. Now, if there is already an existing trait-less impl block, then it'll put the extracted method in there. **Caveats**: - It currently requires the target impl block to be non-empty. This limitation is because the current architecture takes a `node_to_insert_after` as reference for where to insert the extracted function. An empty impl block doesn't have such a reference node, since it's empty. It seems that supporting this requires a much larger and more complex change. - This is my first contribution in rust, so apologies for any beginner mistakes.
2 parents 364820a + f24fbc2 commit d3d3806

File tree

1 file changed

+266
-4
lines changed

1 file changed

+266
-4
lines changed

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

+266-4
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,11 @@ 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 =
131+
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
132+
132133
let fn_def = match fun.self_param_adt(ctx) {
133-
Some(adt) if extracted_from_trait_impl => {
134+
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
134135
let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1);
135136
generate_impl_text(&adt, &fn_def).replace("{\n\n", "{")
136137
}
@@ -272,7 +273,7 @@ enum FunType {
272273
}
273274

274275
/// Where to put extracted function definition
275-
#[derive(Debug)]
276+
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
276277
enum Anchor {
277278
/// Extract free function and put right after current top-level function
278279
Freestanding,
@@ -1245,6 +1246,14 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
12451246
while let Some(next_ancestor) = ancestors.next() {
12461247
match next_ancestor.kind() {
12471248
SyntaxKind::SOURCE_FILE => break,
1249+
SyntaxKind::IMPL => {
1250+
if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) {
1251+
let impl_node = find_non_trait_impl(&next_ancestor);
1252+
if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) {
1253+
return target_node;
1254+
}
1255+
}
1256+
}
12481257
SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
12491258
SyntaxKind::ITEM_LIST => {
12501259
if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
@@ -1265,6 +1274,29 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
12651274
last_ancestor
12661275
}
12671276

1277+
fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> {
1278+
let as_impl = ast::Impl::cast(trait_impl.clone())?;
1279+
let impl_type = Some(impl_type_name(&as_impl)?);
1280+
1281+
let sibblings = trait_impl.parent()?.children();
1282+
sibblings
1283+
.filter_map(ast::Impl::cast)
1284+
.find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
1285+
}
1286+
1287+
fn last_impl_member(impl_node: &ast::Impl) -> Option<SyntaxNode> {
1288+
let last_child = impl_node.assoc_item_list()?.assoc_items().last()?;
1289+
Some(last_child.syntax().clone())
1290+
}
1291+
1292+
fn is_trait_impl(node: &ast::Impl) -> bool {
1293+
node.trait_().is_some()
1294+
}
1295+
1296+
fn impl_type_name(impl_node: &ast::Impl) -> Option<String> {
1297+
Some(impl_node.self_ty()?.to_string())
1298+
}
1299+
12681300
fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String {
12691301
let ret_ty = fun.return_type(ctx);
12701302

@@ -5051,6 +5083,236 @@ impl Struct {
50515083
);
50525084
}
50535085

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

0 commit comments

Comments
 (0)