diff options
author | Lukas Wirth <[email protected]> | 2021-06-02 16:44:00 +0100 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-06-02 16:44:00 +0100 |
commit | f3dc4321c8febf5cf3a4204e2cb444fd453350e4 (patch) | |
tree | cb0571fffe755362db71e9deabc54262d725e9a3 | |
parent | dbdfeeeff91b5e42d8687df09dda1d29f99b34f8 (diff) |
Account for generics in extract_struct_from_enum_variant
-rw-r--r-- | crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | 63 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 5 |
2 files changed, 48 insertions, 20 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 007aba23d..730fc28bf 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 | |||
@@ -11,10 +11,11 @@ use ide_db::{ | |||
11 | search::FileReference, | 11 | search::FileReference, |
12 | RootDatabase, | 12 | RootDatabase, |
13 | }; | 13 | }; |
14 | use itertools::Itertools; | ||
14 | use rustc_hash::FxHashSet; | 15 | use rustc_hash::FxHashSet; |
15 | use syntax::{ | 16 | use syntax::{ |
16 | algo::find_node_at_offset, | 17 | algo::find_node_at_offset, |
17 | ast::{self, make, AstNode, NameOwner, VisibilityOwner}, | 18 | ast::{self, make, AstNode, GenericParamsOwner, NameOwner, TypeBoundsOwner, VisibilityOwner}, |
18 | ted, SyntaxNode, T, | 19 | ted, SyntaxNode, T, |
19 | }; | 20 | }; |
20 | 21 | ||
@@ -100,12 +101,12 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
100 | }); | 101 | }); |
101 | } | 102 | } |
102 | 103 | ||
103 | let def = create_struct_def(variant_name.clone(), &field_list, enum_ast.visibility()); | 104 | let def = create_struct_def(variant_name.clone(), &field_list, &enum_ast); |
104 | let start_offset = &variant.parent_enum().syntax().clone(); | 105 | let start_offset = &variant.parent_enum().syntax().clone(); |
105 | ted::insert_raw(ted::Position::before(start_offset), def.syntax()); | 106 | ted::insert_raw(ted::Position::before(start_offset), def.syntax()); |
106 | ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); | 107 | ted::insert_raw(ted::Position::before(start_offset), &make::tokens::blank_line()); |
107 | 108 | ||
108 | update_variant(&variant); | 109 | update_variant(&variant, enum_ast.generic_param_list()); |
109 | }, | 110 | }, |
110 | ) | 111 | ) |
111 | } | 112 | } |
@@ -149,7 +150,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va | |||
149 | fn create_struct_def( | 150 | fn create_struct_def( |
150 | variant_name: ast::Name, | 151 | variant_name: ast::Name, |
151 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, | 152 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, |
152 | visibility: Option<ast::Visibility>, | 153 | enum_: &ast::Enum, |
153 | ) -> ast::Struct { | 154 | ) -> ast::Struct { |
154 | let pub_vis = make::visibility_pub(); | 155 | let pub_vis = make::visibility_pub(); |
155 | 156 | ||
@@ -184,12 +185,30 @@ fn create_struct_def( | |||
184 | } | 185 | } |
185 | }; | 186 | }; |
186 | 187 | ||
187 | make::struct_(visibility, variant_name, None, field_list).clone_for_update() | 188 | // FIXME: This uses all the generic params of the enum, but the variant might not use all of them. |
189 | make::struct_(enum_.visibility(), variant_name, enum_.generic_param_list(), field_list) | ||
190 | .clone_for_update() | ||
188 | } | 191 | } |
189 | 192 | ||
190 | fn update_variant(variant: &ast::Variant) -> Option<()> { | 193 | fn update_variant(variant: &ast::Variant, generic: Option<ast::GenericParamList>) -> Option<()> { |
191 | let name = variant.name()?; | 194 | let name = variant.name()?; |
192 | let tuple_field = make::tuple_field(None, make::ty(&name.text())); | 195 | let ty = match generic { |
196 | // FIXME: This uses all the generic params of the enum, but the variant might not use all of them. | ||
197 | Some(gpl) => { | ||
198 | let gpl = gpl.clone_for_update(); | ||
199 | gpl.generic_params().for_each(|gp| { | ||
200 | match gp { | ||
201 | ast::GenericParam::LifetimeParam(it) => it.type_bound_list(), | ||
202 | ast::GenericParam::TypeParam(it) => it.type_bound_list(), | ||
203 | ast::GenericParam::ConstParam(_) => return, | ||
204 | } | ||
205 | .map(|it| it.remove()); | ||
206 | }); | ||
207 | make::ty(&format!("{}<{}>", name.text(), gpl.generic_params().join(", "))) | ||
208 | } | ||
209 | None => make::ty(&name.text()), | ||
210 | }; | ||
211 | let tuple_field = make::tuple_field(None, ty); | ||
193 | let replacement = make::variant( | 212 | let replacement = make::variant( |
194 | name, | 213 | name, |
195 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), | 214 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), |
@@ -208,10 +227,9 @@ fn apply_references( | |||
208 | if let Some((scope, path)) = import { | 227 | if let Some((scope, path)) = import { |
209 | insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); | 228 | insert_use(&scope, mod_path_to_ast(&path), insert_use_cfg); |
210 | } | 229 | } |
211 | ted::insert_raw( | 230 | // deep clone to prevent cycle |
212 | ted::Position::before(segment.syntax()), | 231 | let path = make::path_from_segments(iter::once(segment.clone_subtree()), false); |
213 | make::path_from_text(&format!("{}", segment)).clone_for_update().syntax(), | 232 | ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax()); |
214 | ); | ||
215 | ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); | 233 | ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['('])); |
216 | ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); | 234 | ted::insert_raw(ted::Position::after(&node), make::token(T![')'])); |
217 | } | 235 | } |
@@ -278,6 +296,12 @@ mod tests { | |||
278 | 296 | ||
279 | use super::*; | 297 | use super::*; |
280 | 298 | ||
299 | fn check_not_applicable(ra_fixture: &str) { | ||
300 | let fixture = | ||
301 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
302 | check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) | ||
303 | } | ||
304 | |||
281 | #[test] | 305 | #[test] |
282 | fn test_extract_struct_several_fields_tuple() { | 306 | fn test_extract_struct_several_fields_tuple() { |
283 | check_assist( | 307 | check_assist( |
@@ -312,6 +336,17 @@ enum A { One(One) }"#, | |||
312 | } | 336 | } |
313 | 337 | ||
314 | #[test] | 338 | #[test] |
339 | fn test_extract_struct_keeps_generics() { | ||
340 | check_assist( | ||
341 | extract_struct_from_enum_variant, | ||
342 | r"enum En<T> { Var { a: T$0 } }", | ||
343 | r#"struct Var<T>{ pub a: T } | ||
344 | |||
345 | enum En<T> { Var(Var<T>) }"#, | ||
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
315 | fn test_extract_struct_keep_comments_and_attrs_one_field_named() { | 350 | fn test_extract_struct_keep_comments_and_attrs_one_field_named() { |
316 | check_assist( | 351 | check_assist( |
317 | extract_struct_from_enum_variant, | 352 | extract_struct_from_enum_variant, |
@@ -610,12 +645,6 @@ fn foo() { | |||
610 | ); | 645 | ); |
611 | } | 646 | } |
612 | 647 | ||
613 | fn check_not_applicable(ra_fixture: &str) { | ||
614 | let fixture = | ||
615 | format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
616 | check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) | ||
617 | } | ||
618 | |||
619 | #[test] | 648 | #[test] |
620 | fn test_extract_enum_not_applicable_for_element_with_no_fields() { | 649 | fn test_extract_enum_not_applicable_for_element_with_no_fields() { |
621 | check_not_applicable("enum A { $0One }"); | 650 | check_not_applicable("enum A { $0One }"); |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 0cf170626..4c3c9661d 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -580,12 +580,11 @@ pub fn fn_( | |||
580 | pub fn struct_( | 580 | pub fn struct_( |
581 | visibility: Option<ast::Visibility>, | 581 | visibility: Option<ast::Visibility>, |
582 | strukt_name: ast::Name, | 582 | strukt_name: ast::Name, |
583 | type_params: Option<ast::GenericParamList>, | 583 | generic_param_list: Option<ast::GenericParamList>, |
584 | field_list: ast::FieldList, | 584 | field_list: ast::FieldList, |
585 | ) -> ast::Struct { | 585 | ) -> ast::Struct { |
586 | let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; | 586 | let semicolon = if matches!(field_list, ast::FieldList::TupleFieldList(_)) { ";" } else { "" }; |
587 | let type_params = | 587 | let type_params = generic_param_list.map_or_else(String::new, |it| it.to_string()); |
588 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | ||
589 | let visibility = match visibility { | 588 | let visibility = match visibility { |
590 | None => String::new(), | 589 | None => String::new(), |
591 | Some(it) => format!("{} ", it), | 590 | Some(it) => format!("{} ", it), |