aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers')
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs222
1 files changed, 199 insertions, 23 deletions
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index a30c4d04e..be927cc1c 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
53 .iter() 53 .iter()
54 .filter_map(ast::MatchArm::pat) 54 .filter_map(ast::MatchArm::pat)
55 .flat_map(|pat| match pat { 55 .flat_map(|pat| match pat {
56 // Special casee OrPat as separate top-level pats 56 // Special case OrPat as separate top-level pats
57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), 57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58 _ => Either::Right(iter::once(pat)), 58 _ => Either::Right(iter::once(pat)),
59 }) 59 })
@@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
74 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
75 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 75 if Some(enum_def)
76 == FamousDefs(&ctx.sema, Some(module.krate()))
77 .core_option_Option()
78 .map(|x| lift_enum(x))
79 {
76 // Match `Some` variant first. 80 // Match `Some` variant first.
77 cov_mark::hit!(option_order); 81 cov_mark::hit!(option_order);
78 variants.reverse() 82 variants.reverse()
@@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
151 } 155 }
152} 156}
153 157
154fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 158#[derive(Eq, PartialEq, Clone)]
159enum ExtendedEnum {
160 Bool,
161 Enum(hir::Enum),
162}
163
164#[derive(Eq, PartialEq, Clone)]
165enum ExtendedVariant {
166 True,
167 False,
168 Variant(hir::Variant),
169}
170
171fn lift_enum(e: hir::Enum) -> ExtendedEnum {
172 ExtendedEnum::Enum(e)
173}
174
175impl ExtendedEnum {
176 fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
177 match self {
178 ExtendedEnum::Enum(e) => {
179 e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>()
180 }
181 ExtendedEnum::Bool => {
182 Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
183 }
184 }
185 }
186}
187
188fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
155 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 189 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
156 Some(Adt::Enum(e)) => Some(e), 190 Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
157 _ => None, 191 _ => {
192 if ty.is_bool() {
193 Some(ExtendedEnum::Bool)
194 } else {
195 None
196 }
197 }
158 }) 198 })
159} 199}
160 200
161fn resolve_tuple_of_enum_def( 201fn resolve_tuple_of_enum_def(
162 sema: &Semantics<RootDatabase>, 202 sema: &Semantics<RootDatabase>,
163 expr: &ast::Expr, 203 expr: &ast::Expr,
164) -> Option<Vec<hir::Enum>> { 204) -> Option<Vec<ExtendedEnum>> {
165 sema.type_of_expr(&expr)? 205 sema.type_of_expr(&expr)?
166 .tuple_fields(sema.db) 206 .tuple_fields(sema.db)
167 .iter() 207 .iter()
168 .map(|ty| { 208 .map(|ty| {
169 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 209 ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
170 Some(Adt::Enum(e)) => Some(e), 210 Some(Adt::Enum(e)) => Some(lift_enum(e)),
171 // For now we only handle expansion for a tuple of enums. Here 211 // For now we only handle expansion for a tuple of enums. Here
172 // we map non-enum items to None and rely on `collect` to 212 // we map non-enum items to None and rely on `collect` to
173 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. 213 // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
174 _ => None, 214 _ => {
215 if ty.is_bool() {
216 Some(ExtendedEnum::Bool)
217 } else {
218 None
219 }
220 }
175 }) 221 })
176 }) 222 })
177 .collect() 223 .collect()
178} 224}
179 225
180fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { 226fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
181 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 227 match var {
228 ExtendedVariant::Variant(var) => {
229 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
230
231 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
232 let pat: ast::Pat = match var.source(db)?.value.kind() {
233 ast::StructKind::Tuple(field_list) => {
234 let pats =
235 iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
236 make::tuple_struct_pat(path, pats).into()
237 }
238 ast::StructKind::Record(field_list) => {
239 let pats =
240 field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
241 make::record_pat(path, pats).into()
242 }
243 ast::StructKind::Unit => make::path_pat(path),
244 };
182 245
183 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 246 Some(pat)
184 let pat: ast::Pat = match var.source(db)?.value.kind() {
185 ast::StructKind::Tuple(field_list) => {
186 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
187 make::tuple_struct_pat(path, pats).into()
188 }
189 ast::StructKind::Record(field_list) => {
190 let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
191 make::record_pat(path, pats).into()
192 } 247 }
193 ast::StructKind::Unit => make::path_pat(path), 248 ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
194 }; 249 ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
195 250 }
196 Some(pat)
197} 251}
198 252
199#[cfg(test)] 253#[cfg(test)]
@@ -226,6 +280,21 @@ mod tests {
226 } 280 }
227 281
228 #[test] 282 #[test]
283 fn all_boolean_match_arms_provided() {
284 check_assist_not_applicable(
285 fill_match_arms,
286 r#"
287 fn foo(a: bool) {
288 match a$0 {
289 true => {}
290 false => {}
291 }
292 }
293 "#,
294 )
295 }
296
297 #[test]
229 fn tuple_of_non_enum() { 298 fn tuple_of_non_enum() {
230 // for now this case is not handled, although it potentially could be 299 // for now this case is not handled, although it potentially could be
231 // in the future 300 // in the future
@@ -241,6 +310,113 @@ mod tests {
241 } 310 }
242 311
243 #[test] 312 #[test]
313 fn fill_match_arms_boolean() {
314 check_assist(
315 fill_match_arms,
316 r#"
317 fn foo(a: bool) {
318 match a$0 {
319 }
320 }
321 "#,
322 r#"
323 fn foo(a: bool) {
324 match a {
325 $0true => {}
326 false => {}
327 }
328 }
329 "#,
330 )
331 }
332
333 #[test]
334 fn partial_fill_boolean() {
335 check_assist(
336 fill_match_arms,
337 r#"
338 fn foo(a: bool) {
339 match a$0 {
340 true => {}
341 }
342 }
343 "#,
344 r#"
345 fn foo(a: bool) {
346 match a {
347 true => {}
348 $0false => {}
349 }
350 }
351 "#,
352 )
353 }
354
355 #[test]
356 fn all_boolean_tuple_arms_provided() {
357 check_assist_not_applicable(
358 fill_match_arms,
359 r#"
360 fn foo(a: bool) {
361 match (a, a)$0 {
362 (true, true) => {}
363 (true, false) => {}
364 (false, true) => {}
365 (false, false) => {}
366 }
367 }
368 "#,
369 )
370 }
371
372 #[test]
373 fn fill_boolean_tuple() {
374 check_assist(
375 fill_match_arms,
376 r#"
377 fn foo(a: bool) {
378 match (a, a)$0 {
379 }
380 }
381 "#,
382 r#"
383 fn foo(a: bool) {
384 match (a, a) {
385 $0(true, true) => {}
386 (true, false) => {}
387 (false, true) => {}
388 (false, false) => {}
389 }
390 }
391 "#,
392 )
393 }
394
395 #[test]
396 fn partial_fill_boolean_tuple() {
397 check_assist(
398 fill_match_arms,
399 r#"
400 fn foo(a: bool) {
401 match (a, a)$0 {
402 (false, true) => {}
403 }
404 }
405 "#,
406 r#"
407 fn foo(a: bool) {
408 match (a, a) {
409 (false, true) => {}
410 $0(true, true) => {}
411 (true, false) => {}
412 (false, false) => {}
413 }
414 }
415 "#,
416 )
417 }
418
419 #[test]
244 fn partial_fill_record_tuple() { 420 fn partial_fill_record_tuple() {
245 check_assist( 421 check_assist(
246 fill_match_arms, 422 fill_match_arms,