diff options
-rw-r--r-- | crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | 77 |
1 files changed, 51 insertions, 26 deletions
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 6ce417086..d3ff7b65c 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -14,16 +14,16 @@ use ide_db::{ | |||
14 | use itertools::Itertools; | 14 | use itertools::Itertools; |
15 | use rustc_hash::FxHashSet; | 15 | use rustc_hash::FxHashSet; |
16 | use syntax::{ | 16 | use syntax::{ |
17 | algo::find_node_at_offset, | ||
18 | ast::{ | 17 | ast::{ |
19 | self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner, | 18 | self, make, AstNode, AttrsOwner, GenericParamsOwner, NameOwner, TypeBoundsOwner, |
20 | VisibilityOwner, | 19 | VisibilityOwner, |
21 | }, | 20 | }, |
21 | match_ast, | ||
22 | ted::{self, Position}, | 22 | ted::{self, Position}, |
23 | SyntaxNode, T, | 23 | SyntaxNode, T, |
24 | }; | 24 | }; |
25 | 25 | ||
26 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 26 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; |
27 | 27 | ||
28 | // Assist: extract_struct_from_enum_variant | 28 | // Assist: extract_struct_from_enum_variant |
29 | // | 29 | // |
@@ -75,11 +75,10 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
75 | continue; | 75 | continue; |
76 | } | 76 | } |
77 | builder.edit_file(file_id); | 77 | builder.edit_file(file_id); |
78 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); | ||
79 | let processed = process_references( | 78 | let processed = process_references( |
80 | ctx, | 79 | ctx, |
80 | builder, | ||
81 | &mut visited_modules_set, | 81 | &mut visited_modules_set, |
82 | source_file.syntax(), | ||
83 | &enum_module_def, | 82 | &enum_module_def, |
84 | &variant_hir_name, | 83 | &variant_hir_name, |
85 | references, | 84 | references, |
@@ -89,13 +88,12 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
89 | }); | 88 | }); |
90 | } | 89 | } |
91 | builder.edit_file(ctx.frange.file_id); | 90 | builder.edit_file(ctx.frange.file_id); |
92 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); | ||
93 | let variant = builder.make_mut(variant.clone()); | 91 | let variant = builder.make_mut(variant.clone()); |
94 | if let Some(references) = def_file_references { | 92 | if let Some(references) = def_file_references { |
95 | let processed = process_references( | 93 | let processed = process_references( |
96 | ctx, | 94 | ctx, |
95 | builder, | ||
97 | &mut visited_modules_set, | 96 | &mut visited_modules_set, |
98 | source_file.syntax(), | ||
99 | &enum_module_def, | 97 | &enum_module_def, |
100 | &variant_hir_name, | 98 | &variant_hir_name, |
101 | references, | 99 | references, |
@@ -248,8 +246,8 @@ fn apply_references( | |||
248 | 246 | ||
249 | fn process_references( | 247 | fn process_references( |
250 | ctx: &AssistContext, | 248 | ctx: &AssistContext, |
249 | builder: &mut AssistBuilder, | ||
251 | visited_modules: &mut FxHashSet<Module>, | 250 | visited_modules: &mut FxHashSet<Module>, |
252 | source_file: &SyntaxNode, | ||
253 | enum_module_def: &ModuleDef, | 251 | enum_module_def: &ModuleDef, |
254 | variant_hir_name: &Name, | 252 | variant_hir_name: &Name, |
255 | refs: Vec<FileReference>, | 253 | refs: Vec<FileReference>, |
@@ -258,8 +256,9 @@ fn process_references( | |||
258 | // and corresponding nodes up front | 256 | // and corresponding nodes up front |
259 | refs.into_iter() | 257 | refs.into_iter() |
260 | .flat_map(|reference| { | 258 | .flat_map(|reference| { |
261 | let (segment, scope_node, module) = | 259 | let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?; |
262 | reference_to_node(&ctx.sema, source_file, reference)?; | 260 | let segment = builder.make_mut(segment); |
261 | let scope_node = builder.make_syntax_mut(scope_node); | ||
263 | if !visited_modules.contains(&module) { | 262 | if !visited_modules.contains(&module) { |
264 | let mod_path = module.find_use_path_prefixed( | 263 | let mod_path = module.find_use_path_prefixed( |
265 | ctx.sema.db, | 264 | ctx.sema.db, |
@@ -281,23 +280,22 @@ fn process_references( | |||
281 | 280 | ||
282 | fn reference_to_node( | 281 | fn reference_to_node( |
283 | sema: &hir::Semantics<RootDatabase>, | 282 | sema: &hir::Semantics<RootDatabase>, |
284 | source_file: &SyntaxNode, | ||
285 | reference: FileReference, | 283 | reference: FileReference, |
286 | ) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { | 284 | ) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> { |
287 | let offset = reference.range.start(); | 285 | let segment = |
288 | if let Some(path_expr) = find_node_at_offset::<ast::PathExpr>(source_file, offset) { | 286 | reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?; |
289 | // tuple variant | 287 | let parent = segment.parent_path().syntax().parent()?; |
290 | Some((path_expr.path()?.segment()?, path_expr.syntax().parent()?)) | 288 | let expr_or_pat = match_ast! { |
291 | } else if let Some(record_expr) = find_node_at_offset::<ast::RecordExpr>(source_file, offset) { | 289 | match parent { |
292 | // record variant | 290 | ast::PathExpr(_it) => parent.parent()?, |
293 | Some((record_expr.path()?.segment()?, record_expr.syntax().clone())) | 291 | ast::RecordExpr(_it) => parent, |
294 | } else { | 292 | ast::TupleStructPat(_it) => parent, |
295 | None | 293 | ast::RecordPat(_it) => parent, |
296 | } | 294 | _ => return None, |
297 | .and_then(|(segment, expr)| { | 295 | } |
298 | let module = sema.scope(&expr).module()?; | 296 | }; |
299 | Some((segment, expr, module)) | 297 | let module = sema.scope(&expr_or_pat).module()?; |
300 | }) | 298 | Some((segment, expr_or_pat, module)) |
301 | } | 299 | } |
302 | 300 | ||
303 | #[cfg(test)] | 301 | #[cfg(test)] |
@@ -558,7 +556,7 @@ enum E { | |||
558 | } | 556 | } |
559 | 557 | ||
560 | fn f() { | 558 | fn f() { |
561 | let e = E::V { i: 9, j: 2 }; | 559 | let E::V { i, j } = E::V { i: 9, j: 2 }; |
562 | } | 560 | } |
563 | "#, | 561 | "#, |
564 | r#" | 562 | r#" |
@@ -569,7 +567,34 @@ enum E { | |||
569 | } | 567 | } |
570 | 568 | ||
571 | fn f() { | 569 | fn f() { |
572 | let e = E::V(V { i: 9, j: 2 }); | 570 | let E::V(V { i, j }) = E::V(V { i: 9, j: 2 }); |
571 | } | ||
572 | "#, | ||
573 | ) | ||
574 | } | ||
575 | |||
576 | #[test] | ||
577 | fn extract_record_fix_references2() { | ||
578 | check_assist( | ||
579 | extract_struct_from_enum_variant, | ||
580 | r#" | ||
581 | enum E { | ||
582 | $0V(i32, i32) | ||
583 | } | ||
584 | |||
585 | fn f() { | ||
586 | let E::V(i, j) = E::V(9, 2); | ||
587 | } | ||
588 | "#, | ||
589 | r#" | ||
590 | struct V(pub i32, pub i32); | ||
591 | |||
592 | enum E { | ||
593 | V(V) | ||
594 | } | ||
595 | |||
596 | fn f() { | ||
597 | let E::V(V(i, j)) = E::V(V(9, 2)); | ||
573 | } | 598 | } |
574 | "#, | 599 | "#, |
575 | ) | 600 | ) |