diff options
author | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
---|---|---|
committer | Dmitry <[email protected]> | 2020-08-14 19:32:05 +0100 |
commit | 178c3e135a2a249692f7784712492e7884ae0c00 (patch) | |
tree | ac6b769dbf7162150caa0c1624786a4dd79ff3be /crates/assists/src/handlers/fill_match_arms.rs | |
parent | 06ff8e6c760ff05f10e868b5d1f9d79e42fbb49c (diff) | |
parent | c2594daf2974dbd4ce3d9b7ec72481764abaceb5 (diff) |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crates/assists/src/handlers/fill_match_arms.rs')
-rw-r--r-- | crates/assists/src/handlers/fill_match_arms.rs | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs new file mode 100644 index 000000000..3d9bdb2bf --- /dev/null +++ b/crates/assists/src/handlers/fill_match_arms.rs | |||
@@ -0,0 +1,747 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | ||
4 | use ide_db::RootDatabase; | ||
5 | use itertools::Itertools; | ||
6 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use crate::{ | ||
10 | utils::{render_snippet, Cursor, FamousDefs}, | ||
11 | AssistContext, AssistId, AssistKind, Assists, | ||
12 | }; | ||
13 | |||
14 | // Assist: fill_match_arms | ||
15 | // | ||
16 | // Adds missing clauses to a `match` expression. | ||
17 | // | ||
18 | // ``` | ||
19 | // enum Action { Move { distance: u32 }, Stop } | ||
20 | // | ||
21 | // fn handle(action: Action) { | ||
22 | // match action { | ||
23 | // <|> | ||
24 | // } | ||
25 | // } | ||
26 | // ``` | ||
27 | // -> | ||
28 | // ``` | ||
29 | // enum Action { Move { distance: u32 }, Stop } | ||
30 | // | ||
31 | // fn handle(action: Action) { | ||
32 | // match action { | ||
33 | // $0Action::Move { distance } => {} | ||
34 | // Action::Stop => {} | ||
35 | // } | ||
36 | // } | ||
37 | // ``` | ||
38 | pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
39 | let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; | ||
40 | let match_arm_list = match_expr.match_arm_list()?; | ||
41 | |||
42 | let expr = match_expr.expr()?; | ||
43 | |||
44 | let mut arms: Vec<MatchArm> = match_arm_list.arms().collect(); | ||
45 | if arms.len() == 1 { | ||
46 | if let Some(Pat::WildcardPat(..)) = arms[0].pat() { | ||
47 | arms.clear(); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | let module = ctx.sema.scope(expr.syntax()).module()?; | ||
52 | |||
53 | let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { | ||
54 | let variants = enum_def.variants(ctx.db()); | ||
55 | |||
56 | let mut variants = variants | ||
57 | .into_iter() | ||
58 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | ||
59 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | ||
60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | ||
61 | .collect::<Vec<_>>(); | ||
62 | if Some(enum_def) == FamousDefs(&ctx.sema, module.krate()).core_option_Option() { | ||
63 | // Match `Some` variant first. | ||
64 | mark::hit!(option_order); | ||
65 | variants.reverse() | ||
66 | } | ||
67 | variants | ||
68 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { | ||
69 | // Partial fill not currently supported for tuple of enums. | ||
70 | if !arms.is_empty() { | ||
71 | return None; | ||
72 | } | ||
73 | |||
74 | // We do not currently support filling match arms for a tuple | ||
75 | // containing a single enum. | ||
76 | if enum_defs.len() < 2 { | ||
77 | return None; | ||
78 | } | ||
79 | |||
80 | // When calculating the match arms for a tuple of enums, we want | ||
81 | // to create a match arm for each possible combination of enum | ||
82 | // values. The `multi_cartesian_product` method transforms | ||
83 | // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)> | ||
84 | // where each tuple represents a proposed match arm. | ||
85 | enum_defs | ||
86 | .into_iter() | ||
87 | .map(|enum_def| enum_def.variants(ctx.db())) | ||
88 | .multi_cartesian_product() | ||
89 | .map(|variants| { | ||
90 | let patterns = | ||
91 | variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); | ||
92 | ast::Pat::from(make::tuple_pat(patterns)) | ||
93 | }) | ||
94 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | ||
95 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | ||
96 | .collect() | ||
97 | } else { | ||
98 | return None; | ||
99 | }; | ||
100 | |||
101 | if missing_arms.is_empty() { | ||
102 | return None; | ||
103 | } | ||
104 | |||
105 | let target = match_expr.syntax().text_range(); | ||
106 | acc.add( | ||
107 | AssistId("fill_match_arms", AssistKind::QuickFix), | ||
108 | "Fill match arms", | ||
109 | target, | ||
110 | |builder| { | ||
111 | let new_arm_list = match_arm_list.remove_placeholder(); | ||
112 | let n_old_arms = new_arm_list.arms().count(); | ||
113 | let new_arm_list = new_arm_list.append_arms(missing_arms); | ||
114 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | ||
115 | let old_range = match_arm_list.syntax().text_range(); | ||
116 | match (first_new_arm, ctx.config.snippet_cap) { | ||
117 | (Some(first_new_arm), Some(cap)) => { | ||
118 | let extend_lifetime; | ||
119 | let cursor = | ||
120 | match first_new_arm.syntax().descendants().find_map(ast::WildcardPat::cast) | ||
121 | { | ||
122 | Some(it) => { | ||
123 | extend_lifetime = it.syntax().clone(); | ||
124 | Cursor::Replace(&extend_lifetime) | ||
125 | } | ||
126 | None => Cursor::Before(first_new_arm.syntax()), | ||
127 | }; | ||
128 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | ||
129 | builder.replace_snippet(cap, old_range, snippet); | ||
130 | } | ||
131 | _ => builder.replace(old_range, new_arm_list.to_string()), | ||
132 | } | ||
133 | }, | ||
134 | ) | ||
135 | } | ||
136 | |||
137 | fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | ||
138 | existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { | ||
139 | // Special casee OrPat as separate top-level pats | ||
140 | let top_level_pats: Vec<Pat> = match pat { | ||
141 | Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(), | ||
142 | _ => vec![pat], | ||
143 | }; | ||
144 | |||
145 | !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) | ||
146 | }) | ||
147 | } | ||
148 | |||
149 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | ||
150 | let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text()); | ||
151 | |||
152 | let pat_head = match pat { | ||
153 | Pat::IdentPat(bind_pat) => { | ||
154 | if let Some(p) = bind_pat.pat() { | ||
155 | first_node_text(&p) | ||
156 | } else { | ||
157 | return false; | ||
158 | } | ||
159 | } | ||
160 | pat => first_node_text(pat), | ||
161 | }; | ||
162 | |||
163 | let var_head = first_node_text(var); | ||
164 | |||
165 | pat_head == var_head | ||
166 | } | ||
167 | |||
168 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | ||
169 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | ||
170 | Some(Adt::Enum(e)) => Some(e), | ||
171 | _ => None, | ||
172 | }) | ||
173 | } | ||
174 | |||
175 | fn resolve_tuple_of_enum_def( | ||
176 | sema: &Semantics<RootDatabase>, | ||
177 | expr: &ast::Expr, | ||
178 | ) -> Option<Vec<hir::Enum>> { | ||
179 | sema.type_of_expr(&expr)? | ||
180 | .tuple_fields(sema.db) | ||
181 | .iter() | ||
182 | .map(|ty| { | ||
183 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | ||
184 | Some(Adt::Enum(e)) => Some(e), | ||
185 | // For now we only handle expansion for a tuple of enums. Here | ||
186 | // we map non-enum items to None and rely on `collect` to | ||
187 | // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. | ||
188 | _ => None, | ||
189 | }) | ||
190 | }) | ||
191 | .collect() | ||
192 | } | ||
193 | |||
194 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { | ||
195 | let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?); | ||
196 | |||
197 | // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though | ||
198 | let pat: ast::Pat = match var.source(db).value.kind() { | ||
199 | ast::StructKind::Tuple(field_list) => { | ||
200 | let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); | ||
201 | make::tuple_struct_pat(path, pats).into() | ||
202 | } | ||
203 | ast::StructKind::Record(field_list) => { | ||
204 | let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); | ||
205 | make::record_pat(path, pats).into() | ||
206 | } | ||
207 | ast::StructKind::Unit => make::path_pat(path), | ||
208 | }; | ||
209 | |||
210 | Some(pat) | ||
211 | } | ||
212 | |||
213 | #[cfg(test)] | ||
214 | mod tests { | ||
215 | use test_utils::mark; | ||
216 | |||
217 | use crate::{ | ||
218 | tests::{check_assist, check_assist_not_applicable, check_assist_target}, | ||
219 | utils::FamousDefs, | ||
220 | }; | ||
221 | |||
222 | use super::fill_match_arms; | ||
223 | |||
224 | #[test] | ||
225 | fn all_match_arms_provided() { | ||
226 | check_assist_not_applicable( | ||
227 | fill_match_arms, | ||
228 | r#" | ||
229 | enum A { | ||
230 | As, | ||
231 | Bs{x:i32, y:Option<i32>}, | ||
232 | Cs(i32, Option<i32>), | ||
233 | } | ||
234 | fn main() { | ||
235 | match A::As<|> { | ||
236 | A::As, | ||
237 | A::Bs{x,y:Some(_)} => {} | ||
238 | A::Cs(_, Some(_)) => {} | ||
239 | } | ||
240 | } | ||
241 | "#, | ||
242 | ); | ||
243 | } | ||
244 | |||
245 | #[test] | ||
246 | fn tuple_of_non_enum() { | ||
247 | // for now this case is not handled, although it potentially could be | ||
248 | // in the future | ||
249 | check_assist_not_applicable( | ||
250 | fill_match_arms, | ||
251 | r#" | ||
252 | fn main() { | ||
253 | match (0, false)<|> { | ||
254 | } | ||
255 | } | ||
256 | "#, | ||
257 | ); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
261 | fn partial_fill_record_tuple() { | ||
262 | check_assist( | ||
263 | fill_match_arms, | ||
264 | r#" | ||
265 | enum A { | ||
266 | As, | ||
267 | Bs { x: i32, y: Option<i32> }, | ||
268 | Cs(i32, Option<i32>), | ||
269 | } | ||
270 | fn main() { | ||
271 | match A::As<|> { | ||
272 | A::Bs { x, y: Some(_) } => {} | ||
273 | A::Cs(_, Some(_)) => {} | ||
274 | } | ||
275 | } | ||
276 | "#, | ||
277 | r#" | ||
278 | enum A { | ||
279 | As, | ||
280 | Bs { x: i32, y: Option<i32> }, | ||
281 | Cs(i32, Option<i32>), | ||
282 | } | ||
283 | fn main() { | ||
284 | match A::As { | ||
285 | A::Bs { x, y: Some(_) } => {} | ||
286 | A::Cs(_, Some(_)) => {} | ||
287 | $0A::As => {} | ||
288 | } | ||
289 | } | ||
290 | "#, | ||
291 | ); | ||
292 | } | ||
293 | |||
294 | #[test] | ||
295 | fn partial_fill_or_pat() { | ||
296 | check_assist( | ||
297 | fill_match_arms, | ||
298 | r#" | ||
299 | enum A { As, Bs, Cs(Option<i32>) } | ||
300 | fn main() { | ||
301 | match A::As<|> { | ||
302 | A::Cs(_) | A::Bs => {} | ||
303 | } | ||
304 | } | ||
305 | "#, | ||
306 | r#" | ||
307 | enum A { As, Bs, Cs(Option<i32>) } | ||
308 | fn main() { | ||
309 | match A::As { | ||
310 | A::Cs(_) | A::Bs => {} | ||
311 | $0A::As => {} | ||
312 | } | ||
313 | } | ||
314 | "#, | ||
315 | ); | ||
316 | } | ||
317 | |||
318 | #[test] | ||
319 | fn partial_fill() { | ||
320 | check_assist( | ||
321 | fill_match_arms, | ||
322 | r#" | ||
323 | enum A { As, Bs, Cs, Ds(String), Es(B) } | ||
324 | enum B { Xs, Ys } | ||
325 | fn main() { | ||
326 | match A::As<|> { | ||
327 | A::Bs if 0 < 1 => {} | ||
328 | A::Ds(_value) => { let x = 1; } | ||
329 | A::Es(B::Xs) => (), | ||
330 | } | ||
331 | } | ||
332 | "#, | ||
333 | r#" | ||
334 | enum A { As, Bs, Cs, Ds(String), Es(B) } | ||
335 | enum B { Xs, Ys } | ||
336 | fn main() { | ||
337 | match A::As { | ||
338 | A::Bs if 0 < 1 => {} | ||
339 | A::Ds(_value) => { let x = 1; } | ||
340 | A::Es(B::Xs) => (), | ||
341 | $0A::As => {} | ||
342 | A::Cs => {} | ||
343 | } | ||
344 | } | ||
345 | "#, | ||
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
350 | fn partial_fill_bind_pat() { | ||
351 | check_assist( | ||
352 | fill_match_arms, | ||
353 | r#" | ||
354 | enum A { As, Bs, Cs(Option<i32>) } | ||
355 | fn main() { | ||
356 | match A::As<|> { | ||
357 | A::As(_) => {} | ||
358 | a @ A::Bs(_) => {} | ||
359 | } | ||
360 | } | ||
361 | "#, | ||
362 | r#" | ||
363 | enum A { As, Bs, Cs(Option<i32>) } | ||
364 | fn main() { | ||
365 | match A::As { | ||
366 | A::As(_) => {} | ||
367 | a @ A::Bs(_) => {} | ||
368 | A::Cs(${0:_}) => {} | ||
369 | } | ||
370 | } | ||
371 | "#, | ||
372 | ); | ||
373 | } | ||
374 | |||
375 | #[test] | ||
376 | fn fill_match_arms_empty_body() { | ||
377 | check_assist( | ||
378 | fill_match_arms, | ||
379 | r#" | ||
380 | enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } } | ||
381 | |||
382 | fn main() { | ||
383 | let a = A::As; | ||
384 | match a<|> {} | ||
385 | } | ||
386 | "#, | ||
387 | r#" | ||
388 | enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } } | ||
389 | |||
390 | fn main() { | ||
391 | let a = A::As; | ||
392 | match a { | ||
393 | $0A::As => {} | ||
394 | A::Bs => {} | ||
395 | A::Cs(_) => {} | ||
396 | A::Ds(_, _) => {} | ||
397 | A::Es { x, y } => {} | ||
398 | } | ||
399 | } | ||
400 | "#, | ||
401 | ); | ||
402 | } | ||
403 | |||
404 | #[test] | ||
405 | fn fill_match_arms_tuple_of_enum() { | ||
406 | check_assist( | ||
407 | fill_match_arms, | ||
408 | r#" | ||
409 | enum A { One, Two } | ||
410 | enum B { One, Two } | ||
411 | |||
412 | fn main() { | ||
413 | let a = A::One; | ||
414 | let b = B::One; | ||
415 | match (a<|>, b) {} | ||
416 | } | ||
417 | "#, | ||
418 | r#" | ||
419 | enum A { One, Two } | ||
420 | enum B { One, Two } | ||
421 | |||
422 | fn main() { | ||
423 | let a = A::One; | ||
424 | let b = B::One; | ||
425 | match (a, b) { | ||
426 | $0(A::One, B::One) => {} | ||
427 | (A::One, B::Two) => {} | ||
428 | (A::Two, B::One) => {} | ||
429 | (A::Two, B::Two) => {} | ||
430 | } | ||
431 | } | ||
432 | "#, | ||
433 | ); | ||
434 | } | ||
435 | |||
436 | #[test] | ||
437 | fn fill_match_arms_tuple_of_enum_ref() { | ||
438 | check_assist( | ||
439 | fill_match_arms, | ||
440 | r#" | ||
441 | enum A { One, Two } | ||
442 | enum B { One, Two } | ||
443 | |||
444 | fn main() { | ||
445 | let a = A::One; | ||
446 | let b = B::One; | ||
447 | match (&a<|>, &b) {} | ||
448 | } | ||
449 | "#, | ||
450 | r#" | ||
451 | enum A { One, Two } | ||
452 | enum B { One, Two } | ||
453 | |||
454 | fn main() { | ||
455 | let a = A::One; | ||
456 | let b = B::One; | ||
457 | match (&a, &b) { | ||
458 | $0(A::One, B::One) => {} | ||
459 | (A::One, B::Two) => {} | ||
460 | (A::Two, B::One) => {} | ||
461 | (A::Two, B::Two) => {} | ||
462 | } | ||
463 | } | ||
464 | "#, | ||
465 | ); | ||
466 | } | ||
467 | |||
468 | #[test] | ||
469 | fn fill_match_arms_tuple_of_enum_partial() { | ||
470 | check_assist_not_applicable( | ||
471 | fill_match_arms, | ||
472 | r#" | ||
473 | enum A { One, Two } | ||
474 | enum B { One, Two } | ||
475 | |||
476 | fn main() { | ||
477 | let a = A::One; | ||
478 | let b = B::One; | ||
479 | match (a<|>, b) { | ||
480 | (A::Two, B::One) => {} | ||
481 | } | ||
482 | } | ||
483 | "#, | ||
484 | ); | ||
485 | } | ||
486 | |||
487 | #[test] | ||
488 | fn fill_match_arms_tuple_of_enum_not_applicable() { | ||
489 | check_assist_not_applicable( | ||
490 | fill_match_arms, | ||
491 | r#" | ||
492 | enum A { One, Two } | ||
493 | enum B { One, Two } | ||
494 | |||
495 | fn main() { | ||
496 | let a = A::One; | ||
497 | let b = B::One; | ||
498 | match (a<|>, b) { | ||
499 | (A::Two, B::One) => {} | ||
500 | (A::One, B::One) => {} | ||
501 | (A::One, B::Two) => {} | ||
502 | (A::Two, B::Two) => {} | ||
503 | } | ||
504 | } | ||
505 | "#, | ||
506 | ); | ||
507 | } | ||
508 | |||
509 | #[test] | ||
510 | fn fill_match_arms_single_element_tuple_of_enum() { | ||
511 | // For now we don't hande the case of a single element tuple, but | ||
512 | // we could handle this in the future if `make::tuple_pat` allowed | ||
513 | // creating a tuple with a single pattern. | ||
514 | check_assist_not_applicable( | ||
515 | fill_match_arms, | ||
516 | r#" | ||
517 | enum A { One, Two } | ||
518 | |||
519 | fn main() { | ||
520 | let a = A::One; | ||
521 | match (a<|>, ) { | ||
522 | } | ||
523 | } | ||
524 | "#, | ||
525 | ); | ||
526 | } | ||
527 | |||
528 | #[test] | ||
529 | fn test_fill_match_arm_refs() { | ||
530 | check_assist( | ||
531 | fill_match_arms, | ||
532 | r#" | ||
533 | enum A { As } | ||
534 | |||
535 | fn foo(a: &A) { | ||
536 | match a<|> { | ||
537 | } | ||
538 | } | ||
539 | "#, | ||
540 | r#" | ||
541 | enum A { As } | ||
542 | |||
543 | fn foo(a: &A) { | ||
544 | match a { | ||
545 | $0A::As => {} | ||
546 | } | ||
547 | } | ||
548 | "#, | ||
549 | ); | ||
550 | |||
551 | check_assist( | ||
552 | fill_match_arms, | ||
553 | r#" | ||
554 | enum A { | ||
555 | Es { x: usize, y: usize } | ||
556 | } | ||
557 | |||
558 | fn foo(a: &mut A) { | ||
559 | match a<|> { | ||
560 | } | ||
561 | } | ||
562 | "#, | ||
563 | r#" | ||
564 | enum A { | ||
565 | Es { x: usize, y: usize } | ||
566 | } | ||
567 | |||
568 | fn foo(a: &mut A) { | ||
569 | match a { | ||
570 | $0A::Es { x, y } => {} | ||
571 | } | ||
572 | } | ||
573 | "#, | ||
574 | ); | ||
575 | } | ||
576 | |||
577 | #[test] | ||
578 | fn fill_match_arms_target() { | ||
579 | check_assist_target( | ||
580 | fill_match_arms, | ||
581 | r#" | ||
582 | enum E { X, Y } | ||
583 | |||
584 | fn main() { | ||
585 | match E::X<|> {} | ||
586 | } | ||
587 | "#, | ||
588 | "match E::X {}", | ||
589 | ); | ||
590 | } | ||
591 | |||
592 | #[test] | ||
593 | fn fill_match_arms_trivial_arm() { | ||
594 | check_assist( | ||
595 | fill_match_arms, | ||
596 | r#" | ||
597 | enum E { X, Y } | ||
598 | |||
599 | fn main() { | ||
600 | match E::X { | ||
601 | <|>_ => {} | ||
602 | } | ||
603 | } | ||
604 | "#, | ||
605 | r#" | ||
606 | enum E { X, Y } | ||
607 | |||
608 | fn main() { | ||
609 | match E::X { | ||
610 | $0E::X => {} | ||
611 | E::Y => {} | ||
612 | } | ||
613 | } | ||
614 | "#, | ||
615 | ); | ||
616 | } | ||
617 | |||
618 | #[test] | ||
619 | fn fill_match_arms_qualifies_path() { | ||
620 | check_assist( | ||
621 | fill_match_arms, | ||
622 | r#" | ||
623 | mod foo { pub enum E { X, Y } } | ||
624 | use foo::E::X; | ||
625 | |||
626 | fn main() { | ||
627 | match X { | ||
628 | <|> | ||
629 | } | ||
630 | } | ||
631 | "#, | ||
632 | r#" | ||
633 | mod foo { pub enum E { X, Y } } | ||
634 | use foo::E::X; | ||
635 | |||
636 | fn main() { | ||
637 | match X { | ||
638 | $0X => {} | ||
639 | foo::E::Y => {} | ||
640 | } | ||
641 | } | ||
642 | "#, | ||
643 | ); | ||
644 | } | ||
645 | |||
646 | #[test] | ||
647 | fn fill_match_arms_preserves_comments() { | ||
648 | check_assist( | ||
649 | fill_match_arms, | ||
650 | r#" | ||
651 | enum A { One, Two } | ||
652 | fn foo(a: A) { | ||
653 | match a { | ||
654 | // foo bar baz<|> | ||
655 | A::One => {} | ||
656 | // This is where the rest should be | ||
657 | } | ||
658 | } | ||
659 | "#, | ||
660 | r#" | ||
661 | enum A { One, Two } | ||
662 | fn foo(a: A) { | ||
663 | match a { | ||
664 | // foo bar baz | ||
665 | A::One => {} | ||
666 | // This is where the rest should be | ||
667 | $0A::Two => {} | ||
668 | } | ||
669 | } | ||
670 | "#, | ||
671 | ); | ||
672 | } | ||
673 | |||
674 | #[test] | ||
675 | fn fill_match_arms_preserves_comments_empty() { | ||
676 | check_assist( | ||
677 | fill_match_arms, | ||
678 | r#" | ||
679 | enum A { One, Two } | ||
680 | fn foo(a: A) { | ||
681 | match a { | ||
682 | // foo bar baz<|> | ||
683 | } | ||
684 | } | ||
685 | "#, | ||
686 | r#" | ||
687 | enum A { One, Two } | ||
688 | fn foo(a: A) { | ||
689 | match a { | ||
690 | // foo bar baz | ||
691 | $0A::One => {} | ||
692 | A::Two => {} | ||
693 | } | ||
694 | } | ||
695 | "#, | ||
696 | ); | ||
697 | } | ||
698 | |||
699 | #[test] | ||
700 | fn fill_match_arms_placeholder() { | ||
701 | check_assist( | ||
702 | fill_match_arms, | ||
703 | r#" | ||
704 | enum A { One, Two, } | ||
705 | fn foo(a: A) { | ||
706 | match a<|> { | ||
707 | _ => (), | ||
708 | } | ||
709 | } | ||
710 | "#, | ||
711 | r#" | ||
712 | enum A { One, Two, } | ||
713 | fn foo(a: A) { | ||
714 | match a { | ||
715 | $0A::One => {} | ||
716 | A::Two => {} | ||
717 | } | ||
718 | } | ||
719 | "#, | ||
720 | ); | ||
721 | } | ||
722 | |||
723 | #[test] | ||
724 | fn option_order() { | ||
725 | mark::check!(option_order); | ||
726 | let before = r#" | ||
727 | fn foo(opt: Option<i32>) { | ||
728 | match opt<|> { | ||
729 | } | ||
730 | } | ||
731 | "#; | ||
732 | let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE); | ||
733 | |||
734 | check_assist( | ||
735 | fill_match_arms, | ||
736 | before, | ||
737 | r#" | ||
738 | fn foo(opt: Option<i32>) { | ||
739 | match opt { | ||
740 | Some(${0:_}) => {} | ||
741 | None => {} | ||
742 | } | ||
743 | } | ||
744 | "#, | ||
745 | ); | ||
746 | } | ||
747 | } | ||