aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists')
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_is_method.rs (renamed from crates/ide_assists/src/handlers/generate_enum_match_method.rs)128
-rw-r--r--crates/ide_assists/src/handlers/generate_enum_projection_method.rs331
-rw-r--r--crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs (renamed from crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs)38
-rw-r--r--crates/ide_assists/src/handlers/replace_let_with_if_let.rs2
-rw-r--r--crates/ide_assists/src/lib.rs17
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs108
-rw-r--r--crates/ide_assists/src/utils.rs24
8 files changed, 547 insertions, 103 deletions
diff --git a/crates/ide_assists/src/handlers/generate_enum_match_method.rs b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
index aeb887e71..7e181a480 100644
--- a/crates/ide_assists/src/handlers/generate_enum_match_method.rs
+++ b/crates/ide_assists/src/handlers/generate_enum_is_method.rs
@@ -1,14 +1,13 @@
1use stdx::{format_to, to_lower_snake_case}; 1use stdx::to_lower_snake_case;
2use syntax::ast::VisibilityOwner; 2use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner}; 3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5 4
6use crate::{ 5use crate::{
7 utils::{find_impl_block_end, find_struct_impl, generate_impl_text}, 6 utils::{add_method_to_adt, find_struct_impl},
8 AssistContext, AssistId, AssistKind, Assists, 7 AssistContext, AssistId, AssistKind, Assists,
9}; 8};
10 9
11// Assist: generate_enum_match_method 10// Assist: generate_enum_is_method
12// 11//
13// Generate an `is_` method for an enum variant. 12// Generate an `is_` method for an enum variant.
14// 13//
@@ -34,79 +33,52 @@ use crate::{
34// } 33// }
35// } 34// }
36// ``` 35// ```
37pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 36pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 let variant = ctx.find_node_at_offset::<ast::Variant>()?; 37 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
39 let variant_name = variant.name()?; 38 let variant_name = variant.name()?;
40 let parent_enum = variant.parent_enum(); 39 let parent_enum = ast::Adt::Enum(variant.parent_enum());
41 if !matches!(variant.kind(), ast::StructKind::Unit) { 40 let pattern_suffix = match variant.kind() {
42 mark::hit!(test_gen_enum_match_on_non_unit_variant_not_implemented); 41 ast::StructKind::Record(_) => " { .. }",
43 return None; 42 ast::StructKind::Tuple(_) => "(..)",
44 } 43 ast::StructKind::Unit => "",
44 };
45 45
46 let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string()); 46 let enum_lowercase_name = to_lower_snake_case(&parent_enum.name()?.to_string());
47 let fn_name = to_lower_snake_case(&variant_name.to_string()); 47 let fn_name = format!("is_{}", &to_lower_snake_case(variant_name.text()));
48 48
49 // Return early if we've found an existing new fn 49 // Return early if we've found an existing new fn
50 let impl_def = find_struct_impl( 50 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?;
51 &ctx,
52 &ast::Adt::Enum(parent_enum.clone()),
53 format!("is_{}", fn_name).as_str(),
54 )?;
55 51
56 let target = variant.syntax().text_range(); 52 let target = variant.syntax().text_range();
57 acc.add( 53 acc.add(
58 AssistId("generate_enum_match_method", AssistKind::Generate), 54 AssistId("generate_enum_is_method", AssistKind::Generate),
59 "Generate an `is_` method for an enum variant", 55 "Generate an `is_` method for an enum variant",
60 target, 56 target,
61 |builder| { 57 |builder| {
62 let mut buf = String::with_capacity(512);
63
64 if impl_def.is_some() {
65 buf.push('\n');
66 }
67
68 let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); 58 let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
69 format_to!( 59 let method = format!(
70 buf,
71 " /// Returns `true` if the {} is [`{}`]. 60 " /// Returns `true` if the {} is [`{}`].
72 {}fn is_{}(&self) -> bool {{ 61 {}fn {}(&self) -> bool {{
73 matches!(self, Self::{}) 62 matches!(self, Self::{}{})
74 }}", 63 }}",
75 enum_lowercase_name, 64 enum_lowercase_name, variant_name, vis, fn_name, variant_name, pattern_suffix,
76 variant_name,
77 vis,
78 fn_name,
79 variant_name
80 ); 65 );
81 66
82 let start_offset = impl_def 67 add_method_to_adt(builder, &parent_enum, impl_def, &method);
83 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
84 .unwrap_or_else(|| {
85 buf = generate_impl_text(&ast::Adt::Enum(parent_enum.clone()), &buf);
86 parent_enum.syntax().text_range().end()
87 });
88
89 builder.insert(start_offset, buf);
90 }, 68 },
91 ) 69 )
92} 70}
93 71
94#[cfg(test)] 72#[cfg(test)]
95mod tests { 73mod tests {
96 use test_utils::mark;
97
98 use crate::tests::{check_assist, check_assist_not_applicable}; 74 use crate::tests::{check_assist, check_assist_not_applicable};
99 75
100 use super::*; 76 use super::*;
101 77
102 fn check_not_applicable(ra_fixture: &str) {
103 check_assist_not_applicable(generate_enum_match_method, ra_fixture)
104 }
105
106 #[test] 78 #[test]
107 fn test_generate_enum_match_from_variant() { 79 fn test_generate_enum_is_from_variant() {
108 check_assist( 80 check_assist(
109 generate_enum_match_method, 81 generate_enum_is_method,
110 r#" 82 r#"
111enum Variant { 83enum Variant {
112 Undefined, 84 Undefined,
@@ -129,8 +101,9 @@ impl Variant {
129 } 101 }
130 102
131 #[test] 103 #[test]
132 fn test_generate_enum_match_already_implemented() { 104 fn test_generate_enum_is_already_implemented() {
133 check_not_applicable( 105 check_assist_not_applicable(
106 generate_enum_is_method,
134 r#" 107 r#"
135enum Variant { 108enum Variant {
136 Undefined, 109 Undefined,
@@ -147,22 +120,59 @@ impl Variant {
147 } 120 }
148 121
149 #[test] 122 #[test]
150 fn test_add_from_impl_no_element() { 123 fn test_generate_enum_is_from_tuple_variant() {
151 mark::check!(test_gen_enum_match_on_non_unit_variant_not_implemented); 124 check_assist(
152 check_not_applicable( 125 generate_enum_is_method,
153 r#" 126 r#"
154enum Variant { 127enum Variant {
155 Undefined, 128 Undefined,
156 Minor(u32)$0, 129 Minor(u32)$0,
157 Major, 130 Major,
158}"#, 131}"#,
132 r#"enum Variant {
133 Undefined,
134 Minor(u32),
135 Major,
136}
137
138impl Variant {
139 /// Returns `true` if the variant is [`Minor`].
140 fn is_minor(&self) -> bool {
141 matches!(self, Self::Minor(..))
142 }
143}"#,
144 );
145 }
146
147 #[test]
148 fn test_generate_enum_is_from_record_variant() {
149 check_assist(
150 generate_enum_is_method,
151 r#"
152enum Variant {
153 Undefined,
154 Minor { foo: i32 }$0,
155 Major,
156}"#,
157 r#"enum Variant {
158 Undefined,
159 Minor { foo: i32 },
160 Major,
161}
162
163impl Variant {
164 /// Returns `true` if the variant is [`Minor`].
165 fn is_minor(&self) -> bool {
166 matches!(self, Self::Minor { .. })
167 }
168}"#,
159 ); 169 );
160 } 170 }
161 171
162 #[test] 172 #[test]
163 fn test_generate_enum_match_from_variant_with_one_variant() { 173 fn test_generate_enum_is_from_variant_with_one_variant() {
164 check_assist( 174 check_assist(
165 generate_enum_match_method, 175 generate_enum_is_method,
166 r#"enum Variant { Undefi$0ned }"#, 176 r#"enum Variant { Undefi$0ned }"#,
167 r#" 177 r#"
168enum Variant { Undefined } 178enum Variant { Undefined }
@@ -177,9 +187,9 @@ impl Variant {
177 } 187 }
178 188
179 #[test] 189 #[test]
180 fn test_generate_enum_match_from_variant_with_visibility_marker() { 190 fn test_generate_enum_is_from_variant_with_visibility_marker() {
181 check_assist( 191 check_assist(
182 generate_enum_match_method, 192 generate_enum_is_method,
183 r#" 193 r#"
184pub(crate) enum Variant { 194pub(crate) enum Variant {
185 Undefined, 195 Undefined,
@@ -202,9 +212,9 @@ impl Variant {
202 } 212 }
203 213
204 #[test] 214 #[test]
205 fn test_multiple_generate_enum_match_from_variant() { 215 fn test_multiple_generate_enum_is_from_variant() {
206 check_assist( 216 check_assist(
207 generate_enum_match_method, 217 generate_enum_is_method,
208 r#" 218 r#"
209enum Variant { 219enum Variant {
210 Undefined, 220 Undefined,
diff --git a/crates/ide_assists/src/handlers/generate_enum_projection_method.rs b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
new file mode 100644
index 000000000..871bcab50
--- /dev/null
+++ b/crates/ide_assists/src/handlers/generate_enum_projection_method.rs
@@ -0,0 +1,331 @@
1use itertools::Itertools;
2use stdx::to_lower_snake_case;
3use syntax::ast::VisibilityOwner;
4use syntax::ast::{self, AstNode, NameOwner};
5
6use crate::{
7 utils::{add_method_to_adt, find_struct_impl},
8 AssistContext, AssistId, AssistKind, Assists,
9};
10
11// Assist: generate_enum_try_into_method
12//
13// Generate an `try_into_` method for an enum variant.
14//
15// ```
16// enum Value {
17// Number(i32),
18// Text(String)$0,
19// }
20// ```
21// ->
22// ```
23// enum Value {
24// Number(i32),
25// Text(String),
26// }
27//
28// impl Value {
29// fn try_into_text(self) -> Result<String, Self> {
30// if let Self::Text(v) = self {
31// Ok(v)
32// } else {
33// Err(self)
34// }
35// }
36// }
37// ```
38pub(crate) fn generate_enum_try_into_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 generate_enum_projection_method(
40 acc,
41 ctx,
42 "generate_enum_try_into_method",
43 "Generate an `try_into_` method for an enum variant",
44 ProjectionProps {
45 fn_name_prefix: "try_into",
46 self_param: "self",
47 return_prefix: "Result<",
48 return_suffix: ", Self>",
49 happy_case: "Ok",
50 sad_case: "Err(self)",
51 },
52 )
53}
54
55// Assist: generate_enum_as_method
56//
57// Generate an `as_` method for an enum variant.
58//
59// ```
60// enum Value {
61// Number(i32),
62// Text(String)$0,
63// }
64// ```
65// ->
66// ```
67// enum Value {
68// Number(i32),
69// Text(String),
70// }
71//
72// impl Value {
73// fn as_text(&self) -> Option<&String> {
74// if let Self::Text(v) = self {
75// Some(v)
76// } else {
77// None
78// }
79// }
80// }
81// ```
82pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
83 generate_enum_projection_method(
84 acc,
85 ctx,
86 "generate_enum_as_method",
87 "Generate an `as_` method for an enum variant",
88 ProjectionProps {
89 fn_name_prefix: "as",
90 self_param: "&self",
91 return_prefix: "Option<&",
92 return_suffix: ">",
93 happy_case: "Some",
94 sad_case: "None",
95 },
96 )
97}
98
99struct ProjectionProps {
100 fn_name_prefix: &'static str,
101 self_param: &'static str,
102 return_prefix: &'static str,
103 return_suffix: &'static str,
104 happy_case: &'static str,
105 sad_case: &'static str,
106}
107
108fn generate_enum_projection_method(
109 acc: &mut Assists,
110 ctx: &AssistContext,
111 assist_id: &'static str,
112 assist_description: &str,
113 props: ProjectionProps,
114) -> Option<()> {
115 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
116 let variant_name = variant.name()?;
117 let parent_enum = ast::Adt::Enum(variant.parent_enum());
118
119 let (pattern_suffix, field_type, bound_name) = match variant.kind() {
120 ast::StructKind::Record(record) => {
121 let (field,) = record.fields().collect_tuple()?;
122 let name = field.name()?.to_string();
123 let ty = field.ty()?;
124 let pattern_suffix = format!(" {{ {} }}", name);
125 (pattern_suffix, ty, name)
126 }
127 ast::StructKind::Tuple(tuple) => {
128 let (field,) = tuple.fields().collect_tuple()?;
129 let ty = field.ty()?;
130 ("(v)".to_owned(), ty, "v".to_owned())
131 }
132 ast::StructKind::Unit => return None,
133 };
134
135 let fn_name = format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(variant_name.text()));
136
137 // Return early if we've found an existing new fn
138 let impl_def = find_struct_impl(&ctx, &parent_enum, &fn_name)?;
139
140 let target = variant.syntax().text_range();
141 acc.add(AssistId(assist_id, AssistKind::Generate), assist_description, target, |builder| {
142 let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
143 let method = format!(
144 " {0}fn {1}({2}) -> {3}{4}{5} {{
145 if let Self::{6}{7} = self {{
146 {8}({9})
147 }} else {{
148 {10}
149 }}
150 }}",
151 vis,
152 fn_name,
153 props.self_param,
154 props.return_prefix,
155 field_type.syntax(),
156 props.return_suffix,
157 variant_name,
158 pattern_suffix,
159 props.happy_case,
160 bound_name,
161 props.sad_case,
162 );
163
164 add_method_to_adt(builder, &parent_enum, impl_def, &method);
165 })
166}
167
168#[cfg(test)]
169mod tests {
170 use crate::tests::{check_assist, check_assist_not_applicable};
171
172 use super::*;
173
174 #[test]
175 fn test_generate_enum_try_into_tuple_variant() {
176 check_assist(
177 generate_enum_try_into_method,
178 r#"
179enum Value {
180 Number(i32),
181 Text(String)$0,
182}"#,
183 r#"enum Value {
184 Number(i32),
185 Text(String),
186}
187
188impl Value {
189 fn try_into_text(self) -> Result<String, Self> {
190 if let Self::Text(v) = self {
191 Ok(v)
192 } else {
193 Err(self)
194 }
195 }
196}"#,
197 );
198 }
199
200 #[test]
201 fn test_generate_enum_try_into_already_implemented() {
202 check_assist_not_applicable(
203 generate_enum_try_into_method,
204 r#"enum Value {
205 Number(i32),
206 Text(String)$0,
207}
208
209impl Value {
210 fn try_into_text(self) -> Result<String, Self> {
211 if let Self::Text(v) = self {
212 Ok(v)
213 } else {
214 Err(self)
215 }
216 }
217}"#,
218 );
219 }
220
221 #[test]
222 fn test_generate_enum_try_into_unit_variant() {
223 check_assist_not_applicable(
224 generate_enum_try_into_method,
225 r#"enum Value {
226 Number(i32),
227 Text(String),
228 Unit$0,
229}"#,
230 );
231 }
232
233 #[test]
234 fn test_generate_enum_try_into_record_with_multiple_fields() {
235 check_assist_not_applicable(
236 generate_enum_try_into_method,
237 r#"enum Value {
238 Number(i32),
239 Text(String),
240 Both { first: i32, second: String }$0,
241}"#,
242 );
243 }
244
245 #[test]
246 fn test_generate_enum_try_into_tuple_with_multiple_fields() {
247 check_assist_not_applicable(
248 generate_enum_try_into_method,
249 r#"enum Value {
250 Number(i32),
251 Text(String, String)$0,
252}"#,
253 );
254 }
255
256 #[test]
257 fn test_generate_enum_try_into_record_variant() {
258 check_assist(
259 generate_enum_try_into_method,
260 r#"enum Value {
261 Number(i32),
262 Text { text: String }$0,
263}"#,
264 r#"enum Value {
265 Number(i32),
266 Text { text: String },
267}
268
269impl Value {
270 fn try_into_text(self) -> Result<String, Self> {
271 if let Self::Text { text } = self {
272 Ok(text)
273 } else {
274 Err(self)
275 }
276 }
277}"#,
278 );
279 }
280
281 #[test]
282 fn test_generate_enum_as_tuple_variant() {
283 check_assist(
284 generate_enum_as_method,
285 r#"
286enum Value {
287 Number(i32),
288 Text(String)$0,
289}"#,
290 r#"enum Value {
291 Number(i32),
292 Text(String),
293}
294
295impl Value {
296 fn as_text(&self) -> Option<&String> {
297 if let Self::Text(v) = self {
298 Some(v)
299 } else {
300 None
301 }
302 }
303}"#,
304 );
305 }
306
307 #[test]
308 fn test_generate_enum_as_record_variant() {
309 check_assist(
310 generate_enum_as_method,
311 r#"enum Value {
312 Number(i32),
313 Text { text: String }$0,
314}"#,
315 r#"enum Value {
316 Number(i32),
317 Text { text: String },
318}
319
320impl Value {
321 fn as_text(&self) -> Option<&String> {
322 if let Self::Text { text } = self {
323 Some(text)
324 } else {
325 None
326 }
327 }
328}"#,
329 );
330 }
331}
diff --git a/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
index 9fddf889c..27da28bc0 100644
--- a/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs
+++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs
@@ -3,17 +3,18 @@ use hir::known;
3use ide_db::helpers::FamousDefs; 3use ide_db::helpers::FamousDefs;
4use stdx::format_to; 4use stdx::format_to;
5use syntax::{ast, AstNode}; 5use syntax::{ast, AstNode};
6use test_utils::mark;
6 7
7use crate::{AssistContext, AssistId, AssistKind, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
8 9
9// Assist: convert_for_to_iter_for_each 10// Assist: replace_for_loop_with_for_each
10// 11//
11// Converts a for loop into a for_each loop on the Iterator. 12// Converts a for loop into a for_each loop on the Iterator.
12// 13//
13// ``` 14// ```
14// fn main() { 15// fn main() {
15// let x = vec![1, 2, 3]; 16// let x = vec![1, 2, 3];
16// for $0v in x { 17// for$0 v in x {
17// let y = v * 2; 18// let y = v * 2;
18// } 19// }
19// } 20// }
@@ -27,15 +28,19 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
27// }); 28// });
28// } 29// }
29// ``` 30// ```
30pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; 32 let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
32 let iterable = for_loop.iterable()?; 33 let iterable = for_loop.iterable()?;
33 let pat = for_loop.pat()?; 34 let pat = for_loop.pat()?;
34 let body = for_loop.loop_body()?; 35 let body = for_loop.loop_body()?;
36 if body.syntax().text_range().start() < ctx.offset() {
37 mark::hit!(not_available_in_body);
38 return None;
39 }
35 40
36 acc.add( 41 acc.add(
37 AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite), 42 AssistId("replace_for_loop_with_for_each", AssistKind::RefactorRewrite),
38 "Convert a for loop into an Iterator::for_each", 43 "Replace this for loop with `Iterator::for_each`",
39 for_loop.syntax().text_range(), 44 for_loop.syntax().text_range(),
40 |builder| { 45 |builder| {
41 let mut buf = String::new(); 46 let mut buf = String::new();
@@ -145,13 +150,13 @@ pub struct NoIterMethod;
145 FamousDefs::FIXTURE, 150 FamousDefs::FIXTURE,
146 EMPTY_ITER_FIXTURE 151 EMPTY_ITER_FIXTURE
147 ); 152 );
148 check_assist(convert_for_to_iter_for_each, before, after); 153 check_assist(replace_for_loop_with_for_each, before, after);
149 } 154 }
150 155
151 #[test] 156 #[test]
152 fn test_not_for() { 157 fn test_not_for() {
153 check_assist_not_applicable( 158 check_assist_not_applicable(
154 convert_for_to_iter_for_each, 159 replace_for_loop_with_for_each,
155 r" 160 r"
156let mut x = vec![1, 2, 3]; 161let mut x = vec![1, 2, 3];
157x.iter_mut().$0for_each(|v| *v *= 2); 162x.iter_mut().$0for_each(|v| *v *= 2);
@@ -162,7 +167,7 @@ x.iter_mut().$0for_each(|v| *v *= 2);
162 #[test] 167 #[test]
163 fn test_simple_for() { 168 fn test_simple_for() {
164 check_assist( 169 check_assist(
165 convert_for_to_iter_for_each, 170 replace_for_loop_with_for_each,
166 r" 171 r"
167fn main() { 172fn main() {
168 let x = vec![1, 2, 3]; 173 let x = vec![1, 2, 3];
@@ -181,6 +186,21 @@ fn main() {
181 } 186 }
182 187
183 #[test] 188 #[test]
189 fn not_available_in_body() {
190 mark::check!(not_available_in_body);
191 check_assist_not_applicable(
192 replace_for_loop_with_for_each,
193 r"
194fn main() {
195 let x = vec![1, 2, 3];
196 for v in x {
197 $0v *= 2;
198 }
199}",
200 )
201 }
202
203 #[test]
184 fn test_for_borrowed() { 204 fn test_for_borrowed() {
185 check_assist_with_fixtures( 205 check_assist_with_fixtures(
186 r" 206 r"
@@ -255,7 +275,7 @@ fn main() {
255 #[test] 275 #[test]
256 fn test_for_borrowed_mut_behind_var() { 276 fn test_for_borrowed_mut_behind_var() {
257 check_assist( 277 check_assist(
258 convert_for_to_iter_for_each, 278 replace_for_loop_with_for_each,
259 r" 279 r"
260fn main() { 280fn main() {
261 let x = vec![1, 2, 3]; 281 let x = vec![1, 2, 3];
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
index 5a27ada6b..be7e724b5 100644
--- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
@@ -1,5 +1,6 @@
1use std::iter::once; 1use std::iter::once;
2 2
3use ide_db::ty_filter::TryEnum;
3use syntax::{ 4use syntax::{
4 ast::{ 5 ast::{
5 self, 6 self,
@@ -10,7 +11,6 @@ use syntax::{
10}; 11};
11 12
12use crate::{AssistContext, AssistId, AssistKind, Assists}; 13use crate::{AssistContext, AssistId, AssistKind, Assists};
13use ide_db::ty_filter::TryEnum;
14 14
15// Assist: replace_let_with_if_let 15// Assist: replace_let_with_if_let
16// 16//
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index f4c7e6fbf..53542d433 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -114,7 +114,6 @@ mod handlers {
114 mod apply_demorgan; 114 mod apply_demorgan;
115 mod auto_import; 115 mod auto_import;
116 mod change_visibility; 116 mod change_visibility;
117 mod convert_for_to_iter_for_each;
118 mod convert_integer_literal; 117 mod convert_integer_literal;
119 mod early_return; 118 mod early_return;
120 mod expand_glob_import; 119 mod expand_glob_import;
@@ -128,11 +127,12 @@ mod handlers {
128 mod flip_trait_bound; 127 mod flip_trait_bound;
129 mod generate_default_from_enum_variant; 128 mod generate_default_from_enum_variant;
130 mod generate_derive; 129 mod generate_derive;
131 mod generate_enum_match_method; 130 mod generate_enum_is_method;
131 mod generate_enum_projection_method;
132 mod generate_from_impl_for_enum; 132 mod generate_from_impl_for_enum;
133 mod generate_function; 133 mod generate_function;
134 mod generate_getter;
135 mod generate_getter_mut; 134 mod generate_getter_mut;
135 mod generate_getter;
136 mod generate_impl; 136 mod generate_impl;
137 mod generate_new; 137 mod generate_new;
138 mod generate_setter; 138 mod generate_setter;
@@ -155,6 +155,7 @@ mod handlers {
155 mod reorder_fields; 155 mod reorder_fields;
156 mod reorder_impl; 156 mod reorder_impl;
157 mod replace_derive_with_manual_impl; 157 mod replace_derive_with_manual_impl;
158 mod replace_for_loop_with_for_each;
158 mod replace_if_let_with_match; 159 mod replace_if_let_with_match;
159 mod replace_impl_trait_with_generic; 160 mod replace_impl_trait_with_generic;
160 mod replace_let_with_if_let; 161 mod replace_let_with_if_let;
@@ -176,11 +177,9 @@ mod handlers {
176 apply_demorgan::apply_demorgan, 177 apply_demorgan::apply_demorgan,
177 auto_import::auto_import, 178 auto_import::auto_import,
178 change_visibility::change_visibility, 179 change_visibility::change_visibility,
179 convert_for_to_iter_for_each::convert_for_to_iter_for_each,
180 convert_integer_literal::convert_integer_literal, 180 convert_integer_literal::convert_integer_literal,
181 early_return::convert_to_guarded_return, 181 early_return::convert_to_guarded_return,
182 expand_glob_import::expand_glob_import, 182 expand_glob_import::expand_glob_import,
183 move_module_to_file::move_module_to_file,
184 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 183 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
185 fill_match_arms::fill_match_arms, 184 fill_match_arms::fill_match_arms,
186 fix_visibility::fix_visibility, 185 fix_visibility::fix_visibility,
@@ -189,11 +188,13 @@ mod handlers {
189 flip_trait_bound::flip_trait_bound, 188 flip_trait_bound::flip_trait_bound,
190 generate_default_from_enum_variant::generate_default_from_enum_variant, 189 generate_default_from_enum_variant::generate_default_from_enum_variant,
191 generate_derive::generate_derive, 190 generate_derive::generate_derive,
192 generate_enum_match_method::generate_enum_match_method, 191 generate_enum_is_method::generate_enum_is_method,
192 generate_enum_projection_method::generate_enum_as_method,
193 generate_enum_projection_method::generate_enum_try_into_method,
193 generate_from_impl_for_enum::generate_from_impl_for_enum, 194 generate_from_impl_for_enum::generate_from_impl_for_enum,
194 generate_function::generate_function, 195 generate_function::generate_function,
195 generate_getter::generate_getter,
196 generate_getter_mut::generate_getter_mut, 196 generate_getter_mut::generate_getter_mut,
197 generate_getter::generate_getter,
197 generate_impl::generate_impl, 198 generate_impl::generate_impl,
198 generate_new::generate_new, 199 generate_new::generate_new,
199 generate_setter::generate_setter, 200 generate_setter::generate_setter,
@@ -207,6 +208,7 @@ mod handlers {
207 move_bounds::move_bounds_to_where_clause, 208 move_bounds::move_bounds_to_where_clause,
208 move_guard::move_arm_cond_to_match_guard, 209 move_guard::move_arm_cond_to_match_guard,
209 move_guard::move_guard_to_arm_body, 210 move_guard::move_guard_to_arm_body,
211 move_module_to_file::move_module_to_file,
210 pull_assignment_up::pull_assignment_up, 212 pull_assignment_up::pull_assignment_up,
211 qualify_path::qualify_path, 213 qualify_path::qualify_path,
212 raw_string::add_hash, 214 raw_string::add_hash,
@@ -218,6 +220,7 @@ mod handlers {
218 reorder_fields::reorder_fields, 220 reorder_fields::reorder_fields,
219 reorder_impl::reorder_impl, 221 reorder_impl::reorder_impl,
220 replace_derive_with_manual_impl::replace_derive_with_manual_impl, 222 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
223 replace_for_loop_with_for_each::replace_for_loop_with_for_each,
221 replace_if_let_with_match::replace_if_let_with_match, 224 replace_if_let_with_match::replace_if_let_with_match,
222 replace_if_let_with_match::replace_match_with_if_let, 225 replace_if_let_with_match::replace_match_with_if_let,
223 replace_impl_trait_with_generic::replace_impl_trait_with_generic, 226 replace_impl_trait_with_generic::replace_impl_trait_with_generic,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 384eb7eee..b7f616760 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -190,8 +190,8 @@ fn assist_order_field_struct() {
190 let mut assists = assists.iter(); 190 let mut assists = assists.iter();
191 191
192 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 192 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
193 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
194 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); 193 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
194 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
195 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); 195 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
196 assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); 196 assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
197} 197}
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index d42875822..4f007aa48 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -193,29 +193,6 @@ pub(crate) fn frobnicate() {}
193} 193}
194 194
195#[test] 195#[test]
196fn doctest_convert_for_to_iter_for_each() {
197 check_doc_test(
198 "convert_for_to_iter_for_each",
199 r#####"
200fn main() {
201 let x = vec![1, 2, 3];
202 for $0v in x {
203 let y = v * 2;
204 }
205}
206"#####,
207 r#####"
208fn main() {
209 let x = vec![1, 2, 3];
210 x.into_iter().for_each(|v| {
211 let y = v * 2;
212 });
213}
214"#####,
215 )
216}
217
218#[test]
219fn doctest_convert_integer_literal() { 196fn doctest_convert_integer_literal() {
220 check_doc_test( 197 check_doc_test(
221 "convert_integer_literal", 198 "convert_integer_literal",
@@ -483,9 +460,38 @@ struct Point {
483} 460}
484 461
485#[test] 462#[test]
486fn doctest_generate_enum_match_method() { 463fn doctest_generate_enum_as_method() {
464 check_doc_test(
465 "generate_enum_as_method",
466 r#####"
467enum Value {
468 Number(i32),
469 Text(String)$0,
470}
471"#####,
472 r#####"
473enum Value {
474 Number(i32),
475 Text(String),
476}
477
478impl Value {
479 fn as_text(&self) -> Option<&String> {
480 if let Self::Text(v) = self {
481 Some(v)
482 } else {
483 None
484 }
485 }
486}
487"#####,
488 )
489}
490
491#[test]
492fn doctest_generate_enum_is_method() {
487 check_doc_test( 493 check_doc_test(
488 "generate_enum_match_method", 494 "generate_enum_is_method",
489 r#####" 495 r#####"
490enum Version { 496enum Version {
491 Undefined, 497 Undefined,
@@ -511,6 +517,35 @@ impl Version {
511} 517}
512 518
513#[test] 519#[test]
520fn doctest_generate_enum_try_into_method() {
521 check_doc_test(
522 "generate_enum_try_into_method",
523 r#####"
524enum Value {
525 Number(i32),
526 Text(String)$0,
527}
528"#####,
529 r#####"
530enum Value {
531 Number(i32),
532 Text(String),
533}
534
535impl Value {
536 fn try_into_text(self) -> Result<String, Self> {
537 if let Self::Text(v) = self {
538 Ok(v)
539 } else {
540 Err(self)
541 }
542 }
543}
544"#####,
545 )
546}
547
548#[test]
514fn doctest_generate_from_impl_for_enum() { 549fn doctest_generate_from_impl_for_enum() {
515 check_doc_test( 550 check_doc_test(
516 "generate_from_impl_for_enum", 551 "generate_from_impl_for_enum",
@@ -1122,6 +1157,29 @@ impl Debug for S {
1122} 1157}
1123 1158
1124#[test] 1159#[test]
1160fn doctest_replace_for_loop_with_for_each() {
1161 check_doc_test(
1162 "replace_for_loop_with_for_each",
1163 r#####"
1164fn main() {
1165 let x = vec![1, 2, 3];
1166 for$0 v in x {
1167 let y = v * 2;
1168 }
1169}
1170"#####,
1171 r#####"
1172fn main() {
1173 let x = vec![1, 2, 3];
1174 x.into_iter().for_each(|v| {
1175 let y = v * 2;
1176 });
1177}
1178"#####,
1179 )
1180}
1181
1182#[test]
1125fn doctest_replace_if_let_with_match() { 1183fn doctest_replace_if_let_with_match() {
1126 check_doc_test( 1184 check_doc_test(
1127 "replace_if_let_with_match", 1185 "replace_if_let_with_match",
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 276792bc1..880ab6fe3 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -21,7 +21,7 @@ use syntax::{
21}; 21};
22 22
23use crate::{ 23use crate::{
24 assist_context::AssistContext, 24 assist_context::{AssistBuilder, AssistContext},
25 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 25 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
26}; 26};
27 27
@@ -464,3 +464,25 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
464 464
465 buf 465 buf
466} 466}
467
468pub(crate) fn add_method_to_adt(
469 builder: &mut AssistBuilder,
470 adt: &ast::Adt,
471 impl_def: Option<ast::Impl>,
472 method: &str,
473) {
474 let mut buf = String::with_capacity(method.len() + 2);
475 if impl_def.is_some() {
476 buf.push('\n');
477 }
478 buf.push_str(method);
479
480 let start_offset = impl_def
481 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
482 .unwrap_or_else(|| {
483 buf = generate_impl_text(&adt, &buf);
484 adt.syntax().text_range().end()
485 });
486
487 builder.insert(start_offset, buf);
488}