diff options
Diffstat (limited to 'crates/assists')
-rw-r--r-- | crates/assists/src/handlers/add_custom_impl.rs | 152 | ||||
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 148 |
2 files changed, 224 insertions, 76 deletions
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs index 8757fa33f..669dd9b21 100644 --- a/crates/assists/src/handlers/add_custom_impl.rs +++ b/crates/assists/src/handlers/add_custom_impl.rs | |||
@@ -1,13 +1,16 @@ | |||
1 | use ide_db::imports_locator; | ||
1 | use itertools::Itertools; | 2 | use itertools::Itertools; |
2 | use syntax::{ | 3 | use syntax::{ |
3 | ast::{self, AstNode}, | 4 | ast::{self, make, AstNode}, |
4 | Direction, SmolStr, | 5 | Direction, SmolStr, |
5 | SyntaxKind::{IDENT, WHITESPACE}, | 6 | SyntaxKind::{IDENT, WHITESPACE}, |
6 | TextRange, TextSize, | 7 | TextRange, TextSize, |
7 | }; | 8 | }; |
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 11 | assist_config::SnippetCap, |
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
13 | utils::mod_path_to_ast, | ||
11 | AssistId, AssistKind, | 14 | AssistId, AssistKind, |
12 | }; | 15 | }; |
13 | 16 | ||
@@ -30,72 +33,116 @@ use crate::{ | |||
30 | // ``` | 33 | // ``` |
31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 34 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
32 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; | 35 | let attr = ctx.find_node_at_offset::<ast::Attr>()?; |
33 | let input = attr.token_tree()?; | ||
34 | 36 | ||
35 | let attr_name = attr | 37 | let attr_name = attr |
36 | .syntax() | 38 | .syntax() |
37 | .descendants_with_tokens() | 39 | .descendants_with_tokens() |
38 | .filter(|t| t.kind() == IDENT) | 40 | .filter(|t| t.kind() == IDENT) |
39 | .find_map(|i| i.into_token()) | 41 | .find_map(syntax::NodeOrToken::into_token) |
40 | .filter(|t| *t.text() == "derive")? | 42 | .filter(|t| t.text() == "derive")? |
41 | .text() | 43 | .text() |
42 | .clone(); | 44 | .clone(); |
43 | 45 | ||
44 | let trait_token = | 46 | let trait_token = |
45 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | 47 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; |
48 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | ||
46 | 49 | ||
47 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | 50 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; |
48 | let annotated_name = annotated.syntax().text().to_string(); | 51 | let annotated_name = annotated.syntax().text().to_string(); |
49 | let start_offset = annotated.syntax().parent()?.text_range().end(); | 52 | let insert_pos = annotated.syntax().parent()?.text_range().end(); |
53 | |||
54 | let current_module = ctx.sema.scope(annotated.syntax()).module()?; | ||
55 | let current_crate = current_module.krate(); | ||
56 | |||
57 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | ||
58 | .into_iter() | ||
59 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | ||
60 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | ||
61 | _ => None, | ||
62 | }) | ||
63 | .flat_map(|trait_| { | ||
64 | current_module | ||
65 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | ||
66 | .as_ref() | ||
67 | .map(mod_path_to_ast) | ||
68 | .zip(Some(trait_)) | ||
69 | }); | ||
50 | 70 | ||
51 | let label = | 71 | let mut no_traits_found = true; |
52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); | 72 | for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) { |
73 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
74 | } | ||
75 | if no_traits_found { | ||
76 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | ||
77 | } | ||
78 | Some(()) | ||
79 | } | ||
53 | 80 | ||
81 | fn add_assist( | ||
82 | acc: &mut Assists, | ||
83 | snippet_cap: Option<SnippetCap>, | ||
84 | attr: &ast::Attr, | ||
85 | trait_path: &ast::Path, | ||
86 | annotated_name: &str, | ||
87 | insert_pos: TextSize, | ||
88 | ) -> Option<()> { | ||
54 | let target = attr.syntax().text_range(); | 89 | let target = attr.syntax().text_range(); |
55 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { | 90 | let input = attr.token_tree()?; |
56 | let new_attr_input = input | 91 | let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name); |
57 | .syntax() | 92 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; |
58 | .descendants_with_tokens() | ||
59 | .filter(|t| t.kind() == IDENT) | ||
60 | .filter_map(|t| t.into_token().map(|t| t.text().clone())) | ||
61 | .filter(|t| t != trait_token.text()) | ||
62 | .collect::<Vec<SmolStr>>(); | ||
63 | let has_more_derives = !new_attr_input.is_empty(); | ||
64 | |||
65 | if has_more_derives { | ||
66 | let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); | ||
67 | builder.replace(input.syntax().text_range(), new_attr_input); | ||
68 | } else { | ||
69 | let attr_range = attr.syntax().text_range(); | ||
70 | builder.delete(attr_range); | ||
71 | |||
72 | let line_break_range = attr | ||
73 | .syntax() | ||
74 | .next_sibling_or_token() | ||
75 | .filter(|t| t.kind() == WHITESPACE) | ||
76 | .map(|t| t.text_range()) | ||
77 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | ||
78 | builder.delete(line_break_range); | ||
79 | } | ||
80 | 93 | ||
81 | match ctx.config.snippet_cap { | 94 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { |
95 | update_attribute(builder, &input, &trait_name, &attr); | ||
96 | match snippet_cap { | ||
82 | Some(cap) => { | 97 | Some(cap) => { |
83 | builder.insert_snippet( | 98 | builder.insert_snippet( |
84 | cap, | 99 | cap, |
85 | start_offset, | 100 | insert_pos, |
86 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_token, annotated_name), | 101 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), |
87 | ); | 102 | ); |
88 | } | 103 | } |
89 | None => { | 104 | None => { |
90 | builder.insert( | 105 | builder.insert( |
91 | start_offset, | 106 | insert_pos, |
92 | format!("\n\nimpl {} for {} {{\n\n}}", trait_token, annotated_name), | 107 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), |
93 | ); | 108 | ); |
94 | } | 109 | } |
95 | } | 110 | } |
96 | }) | 111 | }) |
97 | } | 112 | } |
98 | 113 | ||
114 | fn update_attribute( | ||
115 | builder: &mut AssistBuilder, | ||
116 | input: &ast::TokenTree, | ||
117 | trait_name: &ast::NameRef, | ||
118 | attr: &ast::Attr, | ||
119 | ) { | ||
120 | let new_attr_input = input | ||
121 | .syntax() | ||
122 | .descendants_with_tokens() | ||
123 | .filter(|t| t.kind() == IDENT) | ||
124 | .filter_map(|t| t.into_token().map(|t| t.text().clone())) | ||
125 | .filter(|t| t != trait_name.text()) | ||
126 | .collect::<Vec<SmolStr>>(); | ||
127 | let has_more_derives = !new_attr_input.is_empty(); | ||
128 | |||
129 | if has_more_derives { | ||
130 | let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); | ||
131 | builder.replace(input.syntax().text_range(), new_attr_input); | ||
132 | } else { | ||
133 | let attr_range = attr.syntax().text_range(); | ||
134 | builder.delete(attr_range); | ||
135 | |||
136 | let line_break_range = attr | ||
137 | .syntax() | ||
138 | .next_sibling_or_token() | ||
139 | .filter(|t| t.kind() == WHITESPACE) | ||
140 | .map(|t| t.text_range()) | ||
141 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | ||
142 | builder.delete(line_break_range); | ||
143 | } | ||
144 | } | ||
145 | |||
99 | #[cfg(test)] | 146 | #[cfg(test)] |
100 | mod tests { | 147 | mod tests { |
101 | use crate::tests::{check_assist, check_assist_not_applicable}; | 148 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -103,6 +150,35 @@ mod tests { | |||
103 | use super::*; | 150 | use super::*; |
104 | 151 | ||
105 | #[test] | 152 | #[test] |
153 | fn add_custom_impl_qualified() { | ||
154 | check_assist( | ||
155 | add_custom_impl, | ||
156 | " | ||
157 | mod fmt { | ||
158 | pub trait Debug {} | ||
159 | } | ||
160 | |||
161 | #[derive(Debu<|>g)] | ||
162 | struct Foo { | ||
163 | bar: String, | ||
164 | } | ||
165 | ", | ||
166 | " | ||
167 | mod fmt { | ||
168 | pub trait Debug {} | ||
169 | } | ||
170 | |||
171 | struct Foo { | ||
172 | bar: String, | ||
173 | } | ||
174 | |||
175 | impl fmt::Debug for Foo { | ||
176 | $0 | ||
177 | } | ||
178 | ", | ||
179 | ) | ||
180 | } | ||
181 | #[test] | ||
106 | fn add_custom_impl_for_unique_input() { | 182 | fn add_custom_impl_for_unique_input() { |
107 | check_assist( | 183 | check_assist( |
108 | add_custom_impl, | 184 | add_custom_impl, |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index dddab255e..14209b771 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -1,3 +1,6 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use either::Either; | ||
1 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; | 4 | use hir::{AsName, EnumVariant, Module, ModuleDef, Name}; |
2 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -31,40 +34,32 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
31 | ctx: &AssistContext, | 34 | ctx: &AssistContext, |
32 | ) -> Option<()> { | 35 | ) -> Option<()> { |
33 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | 36 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; |
34 | let field_list = match variant.kind() { | 37 | let field_list = extract_field_list_if_applicable(&variant)?; |
35 | ast::StructKind::Tuple(field_list) => field_list, | ||
36 | _ => return None, | ||
37 | }; | ||
38 | |||
39 | // skip 1-tuple variants | ||
40 | if field_list.fields().count() == 1 { | ||
41 | return None; | ||
42 | } | ||
43 | 38 | ||
44 | let variant_name = variant.name()?; | 39 | let variant_name = variant.name()?; |
45 | let variant_hir = ctx.sema.to_def(&variant)?; | 40 | let variant_hir = ctx.sema.to_def(&variant)?; |
46 | if existing_struct_def(ctx.db(), &variant_name, &variant_hir) { | 41 | if existing_definition(ctx.db(), &variant_name, &variant_hir) { |
47 | return None; | 42 | return None; |
48 | } | 43 | } |
44 | |||
49 | let enum_ast = variant.parent_enum(); | 45 | let enum_ast = variant.parent_enum(); |
50 | let visibility = enum_ast.visibility(); | ||
51 | let enum_hir = ctx.sema.to_def(&enum_ast)?; | 46 | let enum_hir = ctx.sema.to_def(&enum_ast)?; |
52 | let variant_hir_name = variant_hir.name(ctx.db()); | ||
53 | let enum_module_def = ModuleDef::from(enum_hir); | ||
54 | let current_module = enum_hir.module(ctx.db()); | ||
55 | let target = variant.syntax().text_range(); | 47 | let target = variant.syntax().text_range(); |
56 | acc.add( | 48 | acc.add( |
57 | AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite), | 49 | AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite), |
58 | "Extract struct from enum variant", | 50 | "Extract struct from enum variant", |
59 | target, | 51 | target, |
60 | |builder| { | 52 | |builder| { |
61 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | 53 | let variant_hir_name = variant_hir.name(ctx.db()); |
62 | let res = definition.usages(&ctx.sema).all(); | 54 | let enum_module_def = ModuleDef::from(enum_hir); |
55 | let usages = | ||
56 | Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)).usages(&ctx.sema).all(); | ||
63 | 57 | ||
64 | let mut visited_modules_set = FxHashSet::default(); | 58 | let mut visited_modules_set = FxHashSet::default(); |
59 | let current_module = enum_hir.module(ctx.db()); | ||
65 | visited_modules_set.insert(current_module); | 60 | visited_modules_set.insert(current_module); |
66 | let mut rewriters = FxHashMap::default(); | 61 | let mut rewriters = FxHashMap::default(); |
67 | for reference in res { | 62 | for reference in usages { |
68 | let rewriter = rewriters | 63 | let rewriter = rewriters |
69 | .entry(reference.file_range.file_id) | 64 | .entry(reference.file_range.file_id) |
70 | .or_insert_with(SyntaxRewriter::default); | 65 | .or_insert_with(SyntaxRewriter::default); |
@@ -86,26 +81,49 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
86 | builder.rewrite(rewriter); | 81 | builder.rewrite(rewriter); |
87 | } | 82 | } |
88 | builder.edit_file(ctx.frange.file_id); | 83 | builder.edit_file(ctx.frange.file_id); |
89 | update_variant(&mut rewriter, &variant_name, &field_list); | 84 | update_variant(&mut rewriter, &variant); |
90 | extract_struct_def( | 85 | extract_struct_def( |
91 | &mut rewriter, | 86 | &mut rewriter, |
92 | &enum_ast, | 87 | &enum_ast, |
93 | variant_name.clone(), | 88 | variant_name.clone(), |
94 | &field_list, | 89 | &field_list, |
95 | &variant.parent_enum().syntax().clone().into(), | 90 | &variant.parent_enum().syntax().clone().into(), |
96 | visibility, | 91 | enum_ast.visibility(), |
97 | ); | 92 | ); |
98 | builder.rewrite(rewriter); | 93 | builder.rewrite(rewriter); |
99 | }, | 94 | }, |
100 | ) | 95 | ) |
101 | } | 96 | } |
102 | 97 | ||
103 | fn existing_struct_def(db: &RootDatabase, variant_name: &ast::Name, variant: &EnumVariant) -> bool { | 98 | fn extract_field_list_if_applicable( |
99 | variant: &ast::Variant, | ||
100 | ) -> Option<Either<ast::RecordFieldList, ast::TupleFieldList>> { | ||
101 | match variant.kind() { | ||
102 | ast::StructKind::Record(field_list) if field_list.fields().next().is_some() => { | ||
103 | Some(Either::Left(field_list)) | ||
104 | } | ||
105 | ast::StructKind::Tuple(field_list) if field_list.fields().count() > 1 => { | ||
106 | Some(Either::Right(field_list)) | ||
107 | } | ||
108 | _ => None, | ||
109 | } | ||
110 | } | ||
111 | |||
112 | fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &EnumVariant) -> bool { | ||
104 | variant | 113 | variant |
105 | .parent_enum(db) | 114 | .parent_enum(db) |
106 | .module(db) | 115 | .module(db) |
107 | .scope(db, None) | 116 | .scope(db, None) |
108 | .into_iter() | 117 | .into_iter() |
118 | .filter(|(_, def)| match def { | ||
119 | // only check type-namespace | ||
120 | hir::ScopeDef::ModuleDef(def) => matches!(def, | ||
121 | ModuleDef::Module(_) | ModuleDef::Adt(_) | | ||
122 | ModuleDef::EnumVariant(_) | ModuleDef::Trait(_) | | ||
123 | ModuleDef::TypeAlias(_) | ModuleDef::BuiltinType(_) | ||
124 | ), | ||
125 | _ => false, | ||
126 | }) | ||
109 | .any(|(name, _)| name == variant_name.as_name()) | 127 | .any(|(name, _)| name == variant_name.as_name()) |
110 | } | 128 | } |
111 | 129 | ||
@@ -133,19 +151,29 @@ fn extract_struct_def( | |||
133 | rewriter: &mut SyntaxRewriter, | 151 | rewriter: &mut SyntaxRewriter, |
134 | enum_: &ast::Enum, | 152 | enum_: &ast::Enum, |
135 | variant_name: ast::Name, | 153 | variant_name: ast::Name, |
136 | variant_list: &ast::TupleFieldList, | 154 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, |
137 | start_offset: &SyntaxElement, | 155 | start_offset: &SyntaxElement, |
138 | visibility: Option<ast::Visibility>, | 156 | visibility: Option<ast::Visibility>, |
139 | ) -> Option<()> { | 157 | ) -> Option<()> { |
140 | let variant_list = make::tuple_field_list( | 158 | let pub_vis = Some(make::visibility_pub()); |
141 | variant_list | 159 | let field_list = match field_list { |
142 | .fields() | 160 | Either::Left(field_list) => { |
143 | .flat_map(|field| Some(make::tuple_field(Some(make::visibility_pub()), field.ty()?))), | 161 | make::record_field_list(field_list.fields().flat_map(|field| { |
144 | ); | 162 | Some(make::record_field(pub_vis.clone(), field.name()?, field.ty()?)) |
163 | })) | ||
164 | .into() | ||
165 | } | ||
166 | Either::Right(field_list) => make::tuple_field_list( | ||
167 | field_list | ||
168 | .fields() | ||
169 | .flat_map(|field| Some(make::tuple_field(pub_vis.clone(), field.ty()?))), | ||
170 | ) | ||
171 | .into(), | ||
172 | }; | ||
145 | 173 | ||
146 | rewriter.insert_before( | 174 | rewriter.insert_before( |
147 | start_offset, | 175 | start_offset, |
148 | make::struct_(visibility, variant_name, None, variant_list.into()).syntax(), | 176 | make::struct_(visibility, variant_name, None, field_list).syntax(), |
149 | ); | 177 | ); |
150 | rewriter.insert_before(start_offset, &make::tokens::blank_line()); | 178 | rewriter.insert_before(start_offset, &make::tokens::blank_line()); |
151 | 179 | ||
@@ -156,15 +184,14 @@ fn extract_struct_def( | |||
156 | Some(()) | 184 | Some(()) |
157 | } | 185 | } |
158 | 186 | ||
159 | fn update_variant( | 187 | fn update_variant(rewriter: &mut SyntaxRewriter, variant: &ast::Variant) -> Option<()> { |
160 | rewriter: &mut SyntaxRewriter, | 188 | let name = variant.name()?; |
161 | variant_name: &ast::Name, | 189 | let tuple_field = make::tuple_field(None, make::ty(name.text())); |
162 | field_list: &ast::TupleFieldList, | 190 | let replacement = make::variant( |
163 | ) -> Option<()> { | 191 | name, |
164 | let (l, r): (SyntaxElement, SyntaxElement) = | 192 | Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))), |
165 | (field_list.l_paren_token()?.into(), field_list.r_paren_token()?.into()); | 193 | ); |
166 | let replacement = vec![l, variant_name.syntax().clone().into(), r]; | 194 | rewriter.replace(variant.syntax(), replacement.syntax()); |
167 | rewriter.replace_with_many(field_list.syntax(), replacement); | ||
168 | Some(()) | 195 | Some(()) |
169 | } | 196 | } |
170 | 197 | ||
@@ -211,7 +238,7 @@ mod tests { | |||
211 | use super::*; | 238 | use super::*; |
212 | 239 | ||
213 | #[test] | 240 | #[test] |
214 | fn test_extract_struct_several_fields() { | 241 | fn test_extract_struct_several_fields_tuple() { |
215 | check_assist( | 242 | check_assist( |
216 | extract_struct_from_enum_variant, | 243 | extract_struct_from_enum_variant, |
217 | "enum A { <|>One(u32, u32) }", | 244 | "enum A { <|>One(u32, u32) }", |
@@ -222,6 +249,41 @@ enum A { One(One) }"#, | |||
222 | } | 249 | } |
223 | 250 | ||
224 | #[test] | 251 | #[test] |
252 | fn test_extract_struct_several_fields_named() { | ||
253 | check_assist( | ||
254 | extract_struct_from_enum_variant, | ||
255 | "enum A { <|>One { foo: u32, bar: u32 } }", | ||
256 | r#"struct One{ pub foo: u32, pub bar: u32 } | ||
257 | |||
258 | enum A { One(One) }"#, | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | #[test] | ||
263 | fn test_extract_struct_one_field_named() { | ||
264 | check_assist( | ||
265 | extract_struct_from_enum_variant, | ||
266 | "enum A { <|>One { foo: u32 } }", | ||
267 | r#"struct One{ pub foo: u32 } | ||
268 | |||
269 | enum A { One(One) }"#, | ||
270 | ); | ||
271 | } | ||
272 | |||
273 | #[test] | ||
274 | fn test_extract_enum_variant_name_value_namespace() { | ||
275 | check_assist( | ||
276 | extract_struct_from_enum_variant, | ||
277 | r#"const One: () = (); | ||
278 | enum A { <|>One(u32, u32) }"#, | ||
279 | r#"const One: () = (); | ||
280 | struct One(pub u32, pub u32); | ||
281 | |||
282 | enum A { One(One) }"#, | ||
283 | ); | ||
284 | } | ||
285 | |||
286 | #[test] | ||
225 | fn test_extract_struct_pub_visibility() { | 287 | fn test_extract_struct_pub_visibility() { |
226 | check_assist( | 288 | check_assist( |
227 | extract_struct_from_enum_variant, | 289 | extract_struct_from_enum_variant, |
@@ -298,7 +360,7 @@ fn another_fn() { | |||
298 | fn test_extract_enum_not_applicable_if_struct_exists() { | 360 | fn test_extract_enum_not_applicable_if_struct_exists() { |
299 | check_not_applicable( | 361 | check_not_applicable( |
300 | r#"struct One; | 362 | r#"struct One; |
301 | enum A { <|>One(u8) }"#, | 363 | enum A { <|>One(u8, u32) }"#, |
302 | ); | 364 | ); |
303 | } | 365 | } |
304 | 366 | ||
@@ -306,4 +368,14 @@ fn another_fn() { | |||
306 | fn test_extract_not_applicable_one_field() { | 368 | fn test_extract_not_applicable_one_field() { |
307 | check_not_applicable(r"enum A { <|>One(u32) }"); | 369 | check_not_applicable(r"enum A { <|>One(u32) }"); |
308 | } | 370 | } |
371 | |||
372 | #[test] | ||
373 | fn test_extract_not_applicable_no_field_tuple() { | ||
374 | check_not_applicable(r"enum A { <|>None() }"); | ||
375 | } | ||
376 | |||
377 | #[test] | ||
378 | fn test_extract_not_applicable_no_field_named() { | ||
379 | check_not_applicable(r"enum A { <|>None {} }"); | ||
380 | } | ||
309 | } | 381 | } |