1use ide_db::{
2 assists::AssistId,
3 defs::Definition,
4 search::{FileReference, SearchScope},
5 syntax_helpers::suggest_name,
6 text_edit::TextRange,
7};
8use itertools::Itertools;
9use syntax::{
10 T,
11 ast::{self, AstNode, FieldExpr, HasName, IdentPat, make},
12 ted,
13};
14
15use crate::{
16 assist_context::{AssistContext, Assists, SourceChangeBuilder},
17 utils::ref_field_expr::determine_ref_and_parens,
18};
19
20pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
38 destructure_tuple_binding_impl(acc, ctx, false)
39}
40
41pub(crate) fn destructure_tuple_binding_impl(
60 acc: &mut Assists,
61 ctx: &AssistContext<'_>,
62 with_sub_pattern: bool,
63) -> Option<()> {
64 let ident_pat = ctx.find_node_at_offset::<ast::IdentPat>()?;
65 let data = collect_data(ident_pat, ctx)?;
66
67 if with_sub_pattern {
68 acc.add(
69 AssistId::refactor_rewrite("destructure_tuple_binding_in_sub_pattern"),
70 "Destructure tuple in sub-pattern",
71 data.ident_pat.syntax().text_range(),
72 |edit| destructure_tuple_edit_impl(ctx, edit, &data, true),
73 );
74 }
75
76 acc.add(
77 AssistId::refactor_rewrite("destructure_tuple_binding"),
78 if with_sub_pattern { "Destructure tuple in place" } else { "Destructure tuple" },
79 data.ident_pat.syntax().text_range(),
80 |edit| destructure_tuple_edit_impl(ctx, edit, &data, false),
81 );
82
83 Some(())
84}
85
86fn destructure_tuple_edit_impl(
87 ctx: &AssistContext<'_>,
88 edit: &mut SourceChangeBuilder,
89 data: &TupleData,
90 in_sub_pattern: bool,
91) {
92 let assignment_edit = edit_tuple_assignment(ctx, edit, data, in_sub_pattern);
93 let current_file_usages_edit = edit_tuple_usages(data, edit, ctx, in_sub_pattern);
94
95 assignment_edit.apply();
96 if let Some(usages_edit) = current_file_usages_edit {
97 usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit))
98 }
99}
100
101fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> {
102 if ident_pat.at_token().is_some() {
103 cov_mark::hit!(destructure_tuple_subpattern);
107 return None;
108 }
109
110 let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?;
111 let ref_type = if ty.is_mutable_reference() {
112 Some(RefType::Mutable)
113 } else if ty.is_reference() {
114 Some(RefType::ReadOnly)
115 } else {
116 None
117 };
118 let ty = ty.strip_references();
120 let field_types = ty.tuple_fields(ctx.db());
122 if field_types.is_empty() {
123 cov_mark::hit!(destructure_tuple_no_tuple);
124 return None;
125 }
126
127 let usages = ctx.sema.to_def(&ident_pat).and_then(|def| {
128 Definition::Local(def)
129 .usages(&ctx.sema)
130 .in_scope(&SearchScope::single_file(ctx.file_id()))
131 .all()
132 .iter()
133 .next()
134 .map(|(_, refs)| refs.to_vec())
135 });
136
137 let mut name_generator =
138 suggest_name::NameGenerator::new_from_scope_locals(ctx.sema.scope(ident_pat.syntax()));
139
140 let field_names = field_types
141 .into_iter()
142 .enumerate()
143 .map(|(id, ty)| {
144 match name_generator.for_type(&ty, ctx.db(), ctx.edition()) {
145 Some(name) => name,
146 None => name_generator.suggest_name(&format!("_{id}")),
147 }
148 .to_string()
149 })
150 .collect::<Vec<_>>();
151
152 Some(TupleData { ident_pat, ref_type, field_names, usages })
153}
154
155enum RefType {
156 ReadOnly,
157 Mutable,
158}
159struct TupleData {
160 ident_pat: IdentPat,
161 ref_type: Option<RefType>,
162 field_names: Vec<String>,
163 usages: Option<Vec<FileReference>>,
164}
165fn edit_tuple_assignment(
166 ctx: &AssistContext<'_>,
167 edit: &mut SourceChangeBuilder,
168 data: &TupleData,
169 in_sub_pattern: bool,
170) -> AssignmentEdit {
171 let ident_pat = edit.make_mut(data.ident_pat.clone());
172
173 let tuple_pat = {
174 let original = &data.ident_pat;
175 let is_ref = original.ref_token().is_some();
176 let is_mut = original.mut_token().is_some();
177 let fields = data
178 .field_names
179 .iter()
180 .map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name))));
181 make::tuple_pat(fields).clone_for_update()
182 };
183 let is_shorthand_field = ident_pat
184 .name()
185 .as_ref()
186 .and_then(ast::RecordPatField::for_field_name)
187 .is_some_and(|field| field.colon_token().is_none());
188
189 if let Some(cap) = ctx.config.snippet_cap {
190 if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() {
192 edit.add_tabstop_before(
193 cap,
194 first_pat.name().expect("first ident pattern should have a name"),
195 )
196 }
197 }
198
199 AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern, is_shorthand_field }
200}
201struct AssignmentEdit {
202 ident_pat: ast::IdentPat,
203 tuple_pat: ast::TuplePat,
204 in_sub_pattern: bool,
205 is_shorthand_field: bool,
206}
207
208impl AssignmentEdit {
209 fn apply(self) {
210 if self.in_sub_pattern {
212 self.ident_pat.set_pat(Some(self.tuple_pat.into()))
213 } else if self.is_shorthand_field {
214 ted::insert(ted::Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax());
215 ted::insert_raw(ted::Position::after(self.ident_pat.syntax()), make::token(T![:]));
216 } else {
217 ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax())
218 }
219 }
220}
221
222fn edit_tuple_usages(
223 data: &TupleData,
224 edit: &mut SourceChangeBuilder,
225 ctx: &AssistContext<'_>,
226 in_sub_pattern: bool,
227) -> Option<Vec<EditTupleUsage>> {
228 let edits = data
237 .usages
238 .as_ref()?
239 .as_slice()
240 .iter()
241 .filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
242 .collect_vec();
243
244 Some(edits)
245}
246fn edit_tuple_usage(
247 ctx: &AssistContext<'_>,
248 builder: &mut SourceChangeBuilder,
249 usage: &FileReference,
250 data: &TupleData,
251 in_sub_pattern: bool,
252) -> Option<EditTupleUsage> {
253 match detect_tuple_index(usage, data) {
254 Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)),
255 None if in_sub_pattern => {
256 cov_mark::hit!(destructure_tuple_call_with_subpattern);
257 None
258 }
259 None => Some(EditTupleUsage::NoIndex(usage.range)),
260 }
261}
262
263fn edit_tuple_field_usage(
264 ctx: &AssistContext<'_>,
265 builder: &mut SourceChangeBuilder,
266 data: &TupleData,
267 index: TupleIndex,
268) -> EditTupleUsage {
269 let field_name = &data.field_names[index.index];
270 let field_name = make::expr_path(make::ext::ident_path(field_name));
271
272 if data.ref_type.is_some() {
273 let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &index.field_expr);
274 let replace_expr = builder.make_mut(replace_expr);
275 EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name))
276 } else {
277 let field_expr = builder.make_mut(index.field_expr);
278 EditTupleUsage::ReplaceExpr(field_expr.into(), field_name)
279 }
280}
281enum EditTupleUsage {
282 NoIndex(TextRange),
290 ReplaceExpr(ast::Expr, ast::Expr),
291}
292
293impl EditTupleUsage {
294 fn apply(self, edit: &mut SourceChangeBuilder) {
295 match self {
296 EditTupleUsage::NoIndex(range) => {
297 edit.insert(range.start(), "/*");
298 edit.insert(range.end(), "*/");
299 }
300 EditTupleUsage::ReplaceExpr(target_expr, replace_with) => {
301 ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax())
302 }
303 }
304 }
305}
306
307struct TupleIndex {
308 index: usize,
309 field_expr: FieldExpr,
310}
311fn detect_tuple_index(usage: &FileReference, data: &TupleData) -> Option<TupleIndex> {
312 let node = usage
322 .name
323 .syntax()
324 .ancestors()
325 .skip_while(|s| !ast::PathExpr::can_cast(s.kind()))
326 .skip(1) .find(|s| !ast::ParenExpr::can_cast(s.kind()))?; if let Some(field_expr) = ast::FieldExpr::cast(node) {
330 let idx = field_expr.name_ref()?.as_tuple_field()?;
331 if idx < data.field_names.len() {
332 if field_expr.syntax().ancestors().any(|a| ast::MacroStmts::can_cast(a.kind())) {
334 cov_mark::hit!(destructure_tuple_macro_call);
335
336 return None;
348 }
349
350 Some(TupleIndex { index: idx, field_expr })
351 } else {
352 None
354 }
355 } else {
356 None
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 use crate::tests::{check_assist, check_assist_not_applicable};
365
366 fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
370 destructure_tuple_binding_impl(acc, ctx, false)
371 }
372
373 #[test]
374 fn dont_trigger_on_unit() {
375 cov_mark::check!(destructure_tuple_no_tuple);
376 check_assist_not_applicable(
377 assist,
378 r#"
379fn main() {
380let $0v = ();
381}
382 "#,
383 )
384 }
385 #[test]
386 fn dont_trigger_on_number() {
387 cov_mark::check!(destructure_tuple_no_tuple);
388 check_assist_not_applicable(
389 assist,
390 r#"
391fn main() {
392let $0v = 32;
393}
394 "#,
395 )
396 }
397
398 #[test]
399 fn destructure_3_tuple() {
400 check_assist(
401 assist,
402 r#"
403fn main() {
404 let $0tup = (1,2,3);
405}
406 "#,
407 r#"
408fn main() {
409 let ($0_0, _1, _2) = (1,2,3);
410}
411 "#,
412 )
413 }
414 #[test]
415 fn destructure_2_tuple() {
416 check_assist(
417 assist,
418 r#"
419fn main() {
420 let $0tup = (1,2);
421}
422 "#,
423 r#"
424fn main() {
425 let ($0_0, _1) = (1,2);
426}
427 "#,
428 )
429 }
430 #[test]
431 fn replace_indices() {
432 check_assist(
433 assist,
434 r#"
435fn main() {
436 let $0tup = (1,2,3);
437 let v1 = tup.0;
438 let v2 = tup.1;
439 let v3 = tup.2;
440}
441 "#,
442 r#"
443fn main() {
444 let ($0_0, _1, _2) = (1,2,3);
445 let v1 = _0;
446 let v2 = _1;
447 let v3 = _2;
448}
449 "#,
450 )
451 }
452
453 #[test]
454 fn replace_usage_in_parentheses() {
455 check_assist(
456 assist,
457 r#"
458fn main() {
459 let $0tup = (1,2,3);
460 let a = (tup).1;
461 let b = ((tup)).1;
462}
463 "#,
464 r#"
465fn main() {
466 let ($0_0, _1, _2) = (1,2,3);
467 let a = _1;
468 let b = _1;
469}
470 "#,
471 )
472 }
473
474 #[test]
475 fn handle_function_call() {
476 check_assist(
477 assist,
478 r#"
479fn main() {
480 let $0tup = (1,2);
481 let v = tup.into();
482}
483 "#,
484 r#"
485fn main() {
486 let ($0_0, _1) = (1,2);
487 let v = /*tup*/.into();
488}
489 "#,
490 )
491 }
492
493 #[test]
494 fn handle_invalid_index() {
495 check_assist(
496 assist,
497 r#"
498fn main() {
499 let $0tup = (1,2);
500 let v = tup.3;
501}
502 "#,
503 r#"
504fn main() {
505 let ($0_0, _1) = (1,2);
506 let v = /*tup*/.3;
507}
508 "#,
509 )
510 }
511
512 #[test]
513 fn dont_replace_variable_with_same_name_as_tuple() {
514 check_assist(
515 assist,
516 r#"
517fn main() {
518 let tup = (1,2);
519 let v = tup.1;
520 let $0tup = (1,2,3);
521 let v = tup.1;
522 let tup = (1,2,3);
523 let v = tup.1;
524}
525 "#,
526 r#"
527fn main() {
528 let tup = (1,2);
529 let v = tup.1;
530 let ($0_0, _1, _2) = (1,2,3);
531 let v = _1;
532 let tup = (1,2,3);
533 let v = tup.1;
534}
535 "#,
536 )
537 }
538
539 #[test]
540 fn keep_function_call_in_tuple_item() {
541 check_assist(
542 assist,
543 r#"
544fn main() {
545 let $0t = ("3.14", 0);
546 let pi: f32 = t.0.parse().unwrap_or(0.0);
547}
548 "#,
549 r#"
550fn main() {
551 let ($0_0, _1) = ("3.14", 0);
552 let pi: f32 = _0.parse().unwrap_or(0.0);
553}
554 "#,
555 )
556 }
557
558 #[test]
559 fn keep_type() {
560 check_assist(
561 assist,
562 r#"
563fn main() {
564 let $0t: (usize, i32) = (1,2);
565}
566 "#,
567 r#"
568fn main() {
569 let ($0_0, _1): (usize, i32) = (1,2);
570}
571 "#,
572 )
573 }
574
575 #[test]
576 fn destructure_reference() {
577 check_assist(
578 assist,
579 r#"
580fn main() {
581 let t = (1,2);
582 let $0t = &t;
583 let v = t.0;
584}
585 "#,
586 r#"
587fn main() {
588 let t = (1,2);
589 let ($0_0, _1) = &t;
590 let v = *_0;
591}
592 "#,
593 )
594 }
595
596 #[test]
597 fn destructure_multiple_reference() {
598 check_assist(
599 assist,
600 r#"
601fn main() {
602 let t = (1,2);
603 let $0t = &&t;
604 let v = t.0;
605}
606 "#,
607 r#"
608fn main() {
609 let t = (1,2);
610 let ($0_0, _1) = &&t;
611 let v = *_0;
612}
613 "#,
614 )
615 }
616
617 #[test]
618 fn keep_reference() {
619 check_assist(
620 assist,
621 r#"
622fn foo(t: &(usize, usize)) -> usize {
623 match t {
624 &$0t => t.0
625 }
626}
627 "#,
628 r#"
629fn foo(t: &(usize, usize)) -> usize {
630 match t {
631 &($0_0, _1) => _0
632 }
633}
634 "#,
635 )
636 }
637
638 #[test]
639 fn with_ref() {
640 check_assist(
641 assist,
642 r#"
643fn main() {
644 let ref $0t = (1,2);
645 let v = t.0;
646}
647 "#,
648 r#"
649fn main() {
650 let (ref $0_0, ref _1) = (1,2);
651 let v = *_0;
652}
653 "#,
654 )
655 }
656
657 #[test]
658 fn with_mut() {
659 check_assist(
660 assist,
661 r#"
662fn main() {
663 let mut $0t = (1,2);
664 t.0 = 42;
665 let v = t.0;
666}
667 "#,
668 r#"
669fn main() {
670 let (mut $0_0, mut _1) = (1,2);
671 _0 = 42;
672 let v = _0;
673}
674 "#,
675 )
676 }
677
678 #[test]
679 fn with_ref_mut() {
680 check_assist(
681 assist,
682 r#"
683fn main() {
684 let ref mut $0t = (1,2);
685 t.0 = 42;
686 let v = t.0;
687}
688 "#,
689 r#"
690fn main() {
691 let (ref mut $0_0, ref mut _1) = (1,2);
692 *_0 = 42;
693 let v = *_0;
694}
695 "#,
696 )
697 }
698
699 #[test]
700 fn dont_trigger_for_non_tuple_reference() {
701 check_assist_not_applicable(
702 assist,
703 r#"
704fn main() {
705 let v = 42;
706 let $0v = &42;
707}
708 "#,
709 )
710 }
711
712 #[test]
713 fn dont_trigger_on_static_tuple() {
714 check_assist_not_applicable(
715 assist,
716 r#"
717static $0TUP: (usize, usize) = (1,2);
718 "#,
719 )
720 }
721
722 #[test]
723 fn dont_trigger_on_wildcard() {
724 check_assist_not_applicable(
725 assist,
726 r#"
727fn main() {
728 let $0_ = (1,2);
729}
730 "#,
731 )
732 }
733
734 #[test]
735 fn dont_trigger_in_struct() {
736 check_assist_not_applicable(
737 assist,
738 r#"
739struct S {
740 $0tup: (usize, usize),
741}
742 "#,
743 )
744 }
745
746 #[test]
747 fn dont_trigger_in_struct_creation() {
748 check_assist_not_applicable(
749 assist,
750 r#"
751struct S {
752 tup: (usize, usize),
753}
754fn main() {
755 let s = S {
756 $0tup: (1,2),
757 };
758}
759 "#,
760 )
761 }
762
763 #[test]
764 fn dont_trigger_on_tuple_struct() {
765 check_assist_not_applicable(
766 assist,
767 r#"
768struct S(usize, usize);
769fn main() {
770 let $0s = S(1,2);
771}
772 "#,
773 )
774 }
775
776 #[test]
777 fn dont_trigger_when_subpattern_exists() {
778 cov_mark::check!(destructure_tuple_subpattern);
780 check_assist_not_applicable(
781 assist,
782 r#"
783fn sum(t: (usize, usize)) -> usize {
784 match t {
785 $0t @ (1..=3,1..=3) => t.0 + t.1,
786 _ => 0,
787 }
788}
789 "#,
790 )
791 }
792
793 #[test]
794 fn in_subpattern() {
795 check_assist(
796 assist,
797 r#"
798fn main() {
799 let t1 @ (_, $0t2) = (1, (2,3));
800 let v = t1.0 + t2.0 + t2.1;
801}
802 "#,
803 r#"
804fn main() {
805 let t1 @ (_, ($0_0, _1)) = (1, (2,3));
806 let v = t1.0 + _0 + _1;
807}
808 "#,
809 )
810 }
811
812 #[test]
813 fn in_record_shorthand_field() {
814 check_assist(
815 assist,
816 r#"
817struct S { field: (i32, i32) }
818fn main() {
819 let S { $0field } = S { field: (2, 3) };
820 let v = field.0 + field.1;
821}
822 "#,
823 r#"
824struct S { field: (i32, i32) }
825fn main() {
826 let S { field: ($0_0, _1) } = S { field: (2, 3) };
827 let v = _0 + _1;
828}
829 "#,
830 )
831 }
832
833 #[test]
834 fn in_record_field() {
835 check_assist(
836 assist,
837 r#"
838struct S { field: (i32, i32) }
839fn main() {
840 let S { field: $0t } = S { field: (2, 3) };
841 let v = t.0 + t.1;
842}
843 "#,
844 r#"
845struct S { field: (i32, i32) }
846fn main() {
847 let S { field: ($0_0, _1) } = S { field: (2, 3) };
848 let v = _0 + _1;
849}
850 "#,
851 )
852 }
853
854 #[test]
855 fn in_nested_tuple() {
856 check_assist(
857 assist,
858 r#"
859fn main() {
860 let ($0tup, v) = ((1,2),3);
861}
862 "#,
863 r#"
864fn main() {
865 let (($0_0, _1), v) = ((1,2),3);
866}
867 "#,
868 )
869 }
870
871 #[test]
872 fn in_closure() {
873 check_assist(
874 assist,
875 r#"
876fn main() {
877 let $0tup = (1,2,3);
878 let f = |v| v + tup.1;
879}
880 "#,
881 r#"
882fn main() {
883 let ($0_0, _1, _2) = (1,2,3);
884 let f = |v| v + _1;
885}
886 "#,
887 )
888 }
889
890 #[test]
891 fn in_closure_args() {
892 check_assist(
893 assist,
894 r#"
895fn main() {
896 let f = |$0t| t.0 + t.1;
897 let v = f((1,2));
898}
899 "#,
900 r#"
901fn main() {
902 let f = |($0_0, _1)| _0 + _1;
903 let v = f((1,2));
904}
905 "#,
906 )
907 }
908
909 #[test]
910 fn in_function_args() {
911 check_assist(
912 assist,
913 r#"
914fn f($0t: (usize, usize)) {
915 let v = t.0;
916}
917 "#,
918 r#"
919fn f(($0_0, _1): (usize, usize)) {
920 let v = _0;
921}
922 "#,
923 )
924 }
925
926 #[test]
927 fn in_if_let() {
928 check_assist(
929 assist,
930 r#"
931fn f(t: (usize, usize)) {
932 if let $0t = t {
933 let v = t.0;
934 }
935}
936 "#,
937 r#"
938fn f(t: (usize, usize)) {
939 if let ($0_0, _1) = t {
940 let v = _0;
941 }
942}
943 "#,
944 )
945 }
946 #[test]
947 fn in_if_let_option() {
948 check_assist(
949 assist,
950 r#"
951//- minicore: option
952fn f(o: Option<(usize, usize)>) {
953 if let Some($0t) = o {
954 let v = t.0;
955 }
956}
957 "#,
958 r#"
959fn f(o: Option<(usize, usize)>) {
960 if let Some(($0_0, _1)) = o {
961 let v = _0;
962 }
963}
964 "#,
965 )
966 }
967
968 #[test]
969 fn in_match() {
970 check_assist(
971 assist,
972 r#"
973fn main() {
974 match (1,2) {
975 $0t => t.1,
976 };
977}
978 "#,
979 r#"
980fn main() {
981 match (1,2) {
982 ($0_0, _1) => _1,
983 };
984}
985 "#,
986 )
987 }
988 #[test]
989 fn in_match_option() {
990 check_assist(
991 assist,
992 r#"
993//- minicore: option
994fn main() {
995 match Some((1,2)) {
996 Some($0t) => t.1,
997 _ => 0,
998 };
999}
1000 "#,
1001 r#"
1002fn main() {
1003 match Some((1,2)) {
1004 Some(($0_0, _1)) => _1,
1005 _ => 0,
1006 };
1007}
1008 "#,
1009 )
1010 }
1011 #[test]
1012 fn in_match_reference_option() {
1013 check_assist(
1014 assist,
1015 r#"
1016//- minicore: option
1017fn main() {
1018 let t = (1,2);
1019 match Some(&t) {
1020 Some($0t) => t.1,
1021 _ => 0,
1022 };
1023}
1024 "#,
1025 r#"
1026fn main() {
1027 let t = (1,2);
1028 match Some(&t) {
1029 Some(($0_0, _1)) => *_1,
1030 _ => 0,
1031 };
1032}
1033 "#,
1034 )
1035 }
1036
1037 #[test]
1038 fn in_for() {
1039 check_assist(
1040 assist,
1041 r#"
1042//- minicore: iterators
1043fn main() {
1044 for $0t in core::iter::repeat((1,2)) {
1045 let v = t.1;
1046 }
1047}
1048 "#,
1049 r#"
1050fn main() {
1051 for ($0_0, _1) in core::iter::repeat((1,2)) {
1052 let v = _1;
1053 }
1054}
1055 "#,
1056 )
1057 }
1058 #[test]
1059 fn in_for_nested() {
1060 check_assist(
1061 assist,
1062 r#"
1063//- minicore: iterators
1064fn main() {
1065 for (a, $0b) in core::iter::repeat((1,(2,3))) {
1066 let v = b.1;
1067 }
1068}
1069 "#,
1070 r#"
1071fn main() {
1072 for (a, ($0_0, _1)) in core::iter::repeat((1,(2,3))) {
1073 let v = _1;
1074 }
1075}
1076 "#,
1077 )
1078 }
1079
1080 #[test]
1081 fn not_applicable_on_tuple_usage() {
1082 check_assist_not_applicable(
1084 assist,
1085 r#"
1086fn main() {
1087 let t = (1,2);
1088 let v = $0t.0;
1089}
1090 "#,
1091 )
1092 }
1093
1094 #[test]
1095 fn replace_all() {
1096 check_assist(
1097 assist,
1098 r#"
1099fn main() {
1100 let $0t = (1,2);
1101 let v = t.1;
1102 let s = (t.0 + t.1) / 2;
1103 let f = |v| v + t.0;
1104 let r = f(t.1);
1105 let e = t == (9,0);
1106 let m =
1107 match t {
1108 (_,2) if t.0 > 2 => 1,
1109 _ => 0,
1110 };
1111}
1112 "#,
1113 r#"
1114fn main() {
1115 let ($0_0, _1) = (1,2);
1116 let v = _1;
1117 let s = (_0 + _1) / 2;
1118 let f = |v| v + _0;
1119 let r = f(_1);
1120 let e = /*t*/ == (9,0);
1121 let m =
1122 match /*t*/ {
1123 (_,2) if _0 > 2 => 1,
1124 _ => 0,
1125 };
1126}
1127 "#,
1128 )
1129 }
1130
1131 #[test]
1132 fn non_trivial_tuple_assignment() {
1133 check_assist(
1134 assist,
1135 r#"
1136fn main {
1137 let $0t =
1138 if 1 > 2 {
1139 (1,2)
1140 } else {
1141 (5,6)
1142 };
1143 let v1 = t.0;
1144 let v2 =
1145 if t.0 > t.1 {
1146 t.0 - t.1
1147 } else {
1148 t.1 - t.0
1149 };
1150}
1151 "#,
1152 r#"
1153fn main {
1154 let ($0_0, _1) =
1155 if 1 > 2 {
1156 (1,2)
1157 } else {
1158 (5,6)
1159 };
1160 let v1 = _0;
1161 let v2 =
1162 if _0 > _1 {
1163 _0 - _1
1164 } else {
1165 _1 - _0
1166 };
1167}
1168 "#,
1169 )
1170 }
1171
1172 mod assist {
1173 use super::*;
1174 use crate::tests::check_assist_by_label;
1175
1176 fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
1177 destructure_tuple_binding_impl(acc, ctx, true)
1178 }
1179 fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
1180 destructure_tuple_binding_impl(acc, ctx, false)
1181 }
1182
1183 pub(crate) fn check_in_place_assist(
1184 #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
1185 #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
1186 ) {
1187 check_assist_by_label(
1188 in_place_assist,
1189 ra_fixture_before,
1190 ra_fixture_after,
1191 "Destructure tuple",
1193 );
1194 }
1195
1196 pub(crate) fn check_sub_pattern_assist(
1197 #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
1198 #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
1199 ) {
1200 check_assist_by_label(
1201 assist,
1202 ra_fixture_before,
1203 ra_fixture_after,
1204 "Destructure tuple in sub-pattern",
1205 );
1206 }
1207
1208 pub(crate) fn check_both_assists(
1209 ra_fixture_before: &str,
1210 ra_fixture_after_in_place: &str,
1211 ra_fixture_after_in_sub_pattern: &str,
1212 ) {
1213 check_in_place_assist(ra_fixture_before, ra_fixture_after_in_place);
1214 check_sub_pattern_assist(ra_fixture_before, ra_fixture_after_in_sub_pattern);
1215 }
1216 }
1217
1218 mod sub_pattern {
1221 use super::assist::*;
1222 use super::*;
1223 use crate::tests::check_assist_by_label;
1224
1225 #[test]
1226 fn destructure_in_sub_pattern() {
1227 check_sub_pattern_assist(
1228 r#"
1229#![feature(bindings_after_at)]
1230
1231fn main() {
1232 let $0t = (1,2);
1233}
1234 "#,
1235 r#"
1236#![feature(bindings_after_at)]
1237
1238fn main() {
1239 let t @ ($0_0, _1) = (1,2);
1240}
1241 "#,
1242 )
1243 }
1244
1245 #[test]
1246 fn trigger_both_destructure_tuple_assists() {
1247 fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
1248 destructure_tuple_binding_impl(acc, ctx, true)
1249 }
1250 let text = r#"
1251fn main() {
1252 let $0t = (1,2);
1253}
1254 "#;
1255 check_assist_by_label(
1256 assist,
1257 text,
1258 r#"
1259fn main() {
1260 let ($0_0, _1) = (1,2);
1261}
1262 "#,
1263 "Destructure tuple in place",
1264 );
1265 check_assist_by_label(
1266 assist,
1267 text,
1268 r#"
1269fn main() {
1270 let t @ ($0_0, _1) = (1,2);
1271}
1272 "#,
1273 "Destructure tuple in sub-pattern",
1274 );
1275 }
1276
1277 #[test]
1278 fn replace_indices() {
1279 check_sub_pattern_assist(
1280 r#"
1281fn main() {
1282 let $0t = (1,2);
1283 let v1 = t.0;
1284 let v2 = t.1;
1285}
1286 "#,
1287 r#"
1288fn main() {
1289 let t @ ($0_0, _1) = (1,2);
1290 let v1 = _0;
1291 let v2 = _1;
1292}
1293 "#,
1294 )
1295 }
1296
1297 #[test]
1298 fn keep_function_call() {
1299 cov_mark::check!(destructure_tuple_call_with_subpattern);
1300 check_sub_pattern_assist(
1301 r#"
1302fn main() {
1303 let $0t = (1,2);
1304 let v = t.into();
1305}
1306 "#,
1307 r#"
1308fn main() {
1309 let t @ ($0_0, _1) = (1,2);
1310 let v = t.into();
1311}
1312 "#,
1313 )
1314 }
1315
1316 #[test]
1317 fn keep_type() {
1318 check_sub_pattern_assist(
1319 r#"
1320fn main() {
1321 let $0t: (usize, i32) = (1,2);
1322 let v = t.1;
1323 let f = t.into();
1324}
1325 "#,
1326 r#"
1327fn main() {
1328 let t @ ($0_0, _1): (usize, i32) = (1,2);
1329 let v = _1;
1330 let f = t.into();
1331}
1332 "#,
1333 )
1334 }
1335
1336 #[test]
1337 fn in_function_args() {
1338 check_sub_pattern_assist(
1339 r#"
1340fn f($0t: (usize, usize)) {
1341 let v = t.0;
1342 let f = t.into();
1343}
1344 "#,
1345 r#"
1346fn f(t @ ($0_0, _1): (usize, usize)) {
1347 let v = _0;
1348 let f = t.into();
1349}
1350 "#,
1351 )
1352 }
1353
1354 #[test]
1355 fn with_ref() {
1356 check_sub_pattern_assist(
1357 r#"
1358fn main() {
1359 let ref $0t = (1,2);
1360 let v = t.1;
1361 let f = t.into();
1362}
1363 "#,
1364 r#"
1365fn main() {
1366 let ref t @ (ref $0_0, ref _1) = (1,2);
1367 let v = *_1;
1368 let f = t.into();
1369}
1370 "#,
1371 )
1372 }
1373 #[test]
1374 fn with_mut() {
1375 check_sub_pattern_assist(
1376 r#"
1377fn main() {
1378 let mut $0t = (1,2);
1379 let v = t.1;
1380 let f = t.into();
1381}
1382 "#,
1383 r#"
1384fn main() {
1385 let mut t @ (mut $0_0, mut _1) = (1,2);
1386 let v = _1;
1387 let f = t.into();
1388}
1389 "#,
1390 )
1391 }
1392 #[test]
1393 fn with_ref_mut() {
1394 check_sub_pattern_assist(
1395 r#"
1396fn main() {
1397 let ref mut $0t = (1,2);
1398 let v = t.1;
1399 let f = t.into();
1400}
1401 "#,
1402 r#"
1403fn main() {
1404 let ref mut t @ (ref mut $0_0, ref mut _1) = (1,2);
1405 let v = *_1;
1406 let f = t.into();
1407}
1408 "#,
1409 )
1410 }
1411 }
1412
1413 mod in_macro_call {
1416 use super::assist::*;
1417
1418 #[test]
1419 fn detect_macro_call() {
1420 cov_mark::check!(destructure_tuple_macro_call);
1421 check_in_place_assist(
1422 r#"
1423macro_rules! m {
1424 ($e:expr) => { "foo"; $e };
1425}
1426
1427fn main() {
1428 let $0t = (1,2);
1429 m!(t.0);
1430}
1431 "#,
1432 r#"
1433macro_rules! m {
1434 ($e:expr) => { "foo"; $e };
1435}
1436
1437fn main() {
1438 let ($0_0, _1) = (1,2);
1439 m!(/*t*/.0);
1440}
1441 "#,
1442 )
1443 }
1444
1445 #[test]
1446 fn tuple_usage() {
1447 check_both_assists(
1448 r#"
1450macro_rules! m {
1451 ($e:expr) => { "foo"; $e };
1452}
1453
1454fn main() {
1455 let $0t = (1,2);
1456 m!(t);
1457}
1458 "#,
1459 r#"
1460macro_rules! m {
1461 ($e:expr) => { "foo"; $e };
1462}
1463
1464fn main() {
1465 let ($0_0, _1) = (1,2);
1466 m!(/*t*/);
1467}
1468 "#,
1469 r#"
1470macro_rules! m {
1471 ($e:expr) => { "foo"; $e };
1472}
1473
1474fn main() {
1475 let t @ ($0_0, _1) = (1,2);
1476 m!(t);
1477}
1478 "#,
1479 )
1480 }
1481
1482 #[test]
1483 fn tuple_function_usage() {
1484 check_both_assists(
1485 r#"
1486macro_rules! m {
1487 ($e:expr) => { "foo"; $e };
1488}
1489
1490fn main() {
1491 let $0t = (1,2);
1492 m!(t.into());
1493}
1494 "#,
1495 r#"
1496macro_rules! m {
1497 ($e:expr) => { "foo"; $e };
1498}
1499
1500fn main() {
1501 let ($0_0, _1) = (1,2);
1502 m!(/*t*/.into());
1503}
1504 "#,
1505 r#"
1506macro_rules! m {
1507 ($e:expr) => { "foo"; $e };
1508}
1509
1510fn main() {
1511 let t @ ($0_0, _1) = (1,2);
1512 m!(t.into());
1513}
1514 "#,
1515 )
1516 }
1517
1518 #[test]
1519 fn tuple_index_usage() {
1520 check_both_assists(
1521 r#"
1522macro_rules! m {
1523 ($e:expr) => { "foo"; $e };
1524}
1525
1526fn main() {
1527 let $0t = (1,2);
1528 m!(t.0);
1529}
1530 "#,
1531 r#"
1533macro_rules! m {
1534 ($e:expr) => { "foo"; $e };
1535}
1536
1537fn main() {
1538 let ($0_0, _1) = (1,2);
1539 m!(/*t*/.0);
1540}
1541 "#,
1542 r#"
1544macro_rules! m {
1545 ($e:expr) => { "foo"; $e };
1546}
1547
1548fn main() {
1549 let t @ ($0_0, _1) = (1,2);
1550 m!(t.0);
1551}
1552 "#,
1553 )
1554 }
1555
1556 #[test]
1557 fn tuple_in_parentheses_index_usage() {
1558 check_both_assists(
1559 r#"
1560macro_rules! m {
1561 ($e:expr) => { "foo"; $e };
1562}
1563
1564fn main() {
1565 let $0t = (1,2);
1566 m!((t).0);
1567}
1568 "#,
1569 r#"
1571macro_rules! m {
1572 ($e:expr) => { "foo"; $e };
1573}
1574
1575fn main() {
1576 let ($0_0, _1) = (1,2);
1577 m!((/*t*/).0);
1578}
1579 "#,
1580 r#"
1582macro_rules! m {
1583 ($e:expr) => { "foo"; $e };
1584}
1585
1586fn main() {
1587 let t @ ($0_0, _1) = (1,2);
1588 m!((t).0);
1589}
1590 "#,
1591 )
1592 }
1593
1594 #[test]
1595 fn empty_macro() {
1596 check_in_place_assist(
1597 r#"
1598macro_rules! m {
1599 () => { "foo" };
1600 ($e:expr) => { $e; "foo" };
1601}
1602
1603fn main() {
1604 let $0t = (1,2);
1605 m!(t);
1606}
1607 "#,
1608 r#"
1610macro_rules! m {
1611 () => { "foo" };
1612 ($e:expr) => { $e; "foo" };
1613}
1614
1615fn main() {
1616 let ($0_0, _1) = (1,2);
1617 m!(/*t*/);
1618}
1619 "#,
1620 )
1621 }
1622
1623 #[test]
1624 fn tuple_index_in_macro() {
1625 check_both_assists(
1626 r#"
1627macro_rules! m {
1628 ($t:expr, $i:expr) => { $t.0 + $i };
1629}
1630
1631fn main() {
1632 let $0t = (1,2);
1633 m!(t, t.0);
1634}
1635 "#,
1636 r#"
1638macro_rules! m {
1639 ($t:expr, $i:expr) => { $t.0 + $i };
1640}
1641
1642fn main() {
1643 let ($0_0, _1) = (1,2);
1644 m!(/*t*/, /*t*/.0);
1645}
1646 "#,
1647 r#"
1649macro_rules! m {
1650 ($t:expr, $i:expr) => { $t.0 + $i };
1651}
1652
1653fn main() {
1654 let t @ ($0_0, _1) = (1,2);
1655 m!(t, t.0);
1656}
1657 "#,
1658 )
1659 }
1660 }
1661
1662 mod refs {
1663 use super::assist::*;
1664
1665 #[test]
1666 fn no_ref() {
1667 check_in_place_assist(
1668 r#"
1669fn main() {
1670 let $0t = &(1,2);
1671 let v: i32 = t.0;
1672}
1673 "#,
1674 r#"
1675fn main() {
1676 let ($0_0, _1) = &(1,2);
1677 let v: i32 = *_0;
1678}
1679 "#,
1680 )
1681 }
1682 #[test]
1683 fn no_ref_with_parens() {
1684 check_in_place_assist(
1685 r#"
1686fn main() {
1687 let $0t = &(1,2);
1688 let v: i32 = (t.0);
1689}
1690 "#,
1691 r#"
1692fn main() {
1693 let ($0_0, _1) = &(1,2);
1694 let v: i32 = (*_0);
1695}
1696 "#,
1697 )
1698 }
1699 #[test]
1700 fn with_ref() {
1701 check_in_place_assist(
1702 r#"
1703fn main() {
1704 let $0t = &(1,2);
1705 let v: &i32 = &t.0;
1706}
1707 "#,
1708 r#"
1709fn main() {
1710 let ($0_0, _1) = &(1,2);
1711 let v: &i32 = _0;
1712}
1713 "#,
1714 )
1715 }
1716 #[test]
1717 fn with_ref_in_parens_ref() {
1718 check_in_place_assist(
1719 r#"
1720fn main() {
1721 let $0t = &(1,2);
1722 let v: &i32 = &(t.0);
1723}
1724 "#,
1725 r#"
1726fn main() {
1727 let ($0_0, _1) = &(1,2);
1728 let v: &i32 = _0;
1729}
1730 "#,
1731 )
1732 }
1733 #[test]
1734 fn with_ref_in_ref_parens() {
1735 check_in_place_assist(
1736 r#"
1737fn main() {
1738 let $0t = &(1,2);
1739 let v: &i32 = (&t.0);
1740}
1741 "#,
1742 r#"
1743fn main() {
1744 let ($0_0, _1) = &(1,2);
1745 let v: &i32 = _0;
1746}
1747 "#,
1748 )
1749 }
1750
1751 #[test]
1752 fn deref_and_parentheses() {
1753 check_in_place_assist(
1761 r#"
1762//- minicore: option
1763fn f1(v: i32) {}
1764fn f2(v: &i32) {}
1765trait T {
1766 fn do_stuff(self) {}
1767}
1768impl T for i32 {
1769 fn do_stuff(self) {}
1770}
1771impl T for &i32 {
1772 fn do_stuff(self) {}
1773}
1774struct S4 {
1775 value: i32,
1776}
1777
1778fn foo() -> Option<()> {
1779 let $0t = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
1780 let v: i32 = t.0; // deref, no parens
1781 let v: &i32 = &t.0; // no deref, no parens, remove `&`
1782 f1(t.0); // deref, no parens
1783 f2(&t.0); // `&*` -> cancel out -> no deref, no parens
1784 // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
1785 // let v: i32 = t.1.0; // no deref, no parens
1786 let v: i32 = t.4.value; // no deref, no parens
1787 t.0.do_stuff(); // deref, parens
1788 let v: i32 = t.2?; // deref, parens
1789 let v: i32 = t.3[0]; // no deref, no parens
1790 (t.0).do_stuff(); // deref, no additional parens
1791 let v: i32 = *t.5; // deref (-> 2), no parens
1792
1793 None
1794}
1795 "#,
1796 r#"
1797fn f1(v: i32) {}
1798fn f2(v: &i32) {}
1799trait T {
1800 fn do_stuff(self) {}
1801}
1802impl T for i32 {
1803 fn do_stuff(self) {}
1804}
1805impl T for &i32 {
1806 fn do_stuff(self) {}
1807}
1808struct S4 {
1809 value: i32,
1810}
1811
1812fn foo() -> Option<()> {
1813 let ($0_0, _1, _2, _3, s4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
1814 let v: i32 = *_0; // deref, no parens
1815 let v: &i32 = _0; // no deref, no parens, remove `&`
1816 f1(*_0); // deref, no parens
1817 f2(_0); // `&*` -> cancel out -> no deref, no parens
1818 // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
1819 // let v: i32 = t.1.0; // no deref, no parens
1820 let v: i32 = s4.value; // no deref, no parens
1821 (*_0).do_stuff(); // deref, parens
1822 let v: i32 = (*_2)?; // deref, parens
1823 let v: i32 = _3[0]; // no deref, no parens
1824 (*_0).do_stuff(); // deref, no additional parens
1825 let v: i32 = **_5; // deref (-> 2), no parens
1826
1827 None
1828}
1829 "#,
1830 )
1831 }
1832
1833 #[test]
1837 fn self_auto_ref_doesnt_need_deref() {
1838 check_in_place_assist(
1839 r#"
1840#[derive(Clone, Copy)]
1841struct S;
1842impl S {
1843 fn f(&self) {}
1844}
1845
1846fn main() {
1847 let $0t = &(S,2);
1848 let s = t.0.f();
1849}
1850 "#,
1851 r#"
1852#[derive(Clone, Copy)]
1853struct S;
1854impl S {
1855 fn f(&self) {}
1856}
1857
1858fn main() {
1859 let ($0s, _1) = &(S,2);
1860 let s = s.f();
1861}
1862 "#,
1863 )
1864 }
1865
1866 #[test]
1867 fn self_owned_requires_deref() {
1868 check_in_place_assist(
1869 r#"
1870#[derive(Clone, Copy)]
1871struct S;
1872impl S {
1873 fn f(self) {}
1874}
1875
1876fn main() {
1877 let $0t = &(S,2);
1878 let s = t.0.f();
1879}
1880 "#,
1881 r#"
1882#[derive(Clone, Copy)]
1883struct S;
1884impl S {
1885 fn f(self) {}
1886}
1887
1888fn main() {
1889 let ($0s, _1) = &(S,2);
1890 let s = (*s).f();
1891}
1892 "#,
1893 )
1894 }
1895
1896 #[test]
1897 fn self_auto_ref_in_trait_call_doesnt_require_deref() {
1898 check_in_place_assist(
1899 r#"
1900trait T {
1901 fn f(self);
1902}
1903#[derive(Clone, Copy)]
1904struct S;
1905impl T for &S {
1906 fn f(self) {}
1907}
1908
1909fn main() {
1910 let $0t = &(S,2);
1911 let s = t.0.f();
1912}
1913 "#,
1914 r#"
1916trait T {
1917 fn f(self);
1918}
1919#[derive(Clone, Copy)]
1920struct S;
1921impl T for &S {
1922 fn f(self) {}
1923}
1924
1925fn main() {
1926 let ($0s, _1) = &(S,2);
1927 let s = (*s).f();
1928}
1929 "#,
1930 )
1931 }
1932 #[test]
1933 fn no_auto_deref_because_of_owned_and_ref_trait_impl() {
1934 check_in_place_assist(
1935 r#"
1936trait T {
1937 fn f(self);
1938}
1939#[derive(Clone, Copy)]
1940struct S;
1941impl T for S {
1942 fn f(self) {}
1943}
1944impl T for &S {
1945 fn f(self) {}
1946}
1947
1948fn main() {
1949 let $0t = &(S,2);
1950 let s = t.0.f();
1951}
1952 "#,
1953 r#"
1954trait T {
1955 fn f(self);
1956}
1957#[derive(Clone, Copy)]
1958struct S;
1959impl T for S {
1960 fn f(self) {}
1961}
1962impl T for &S {
1963 fn f(self) {}
1964}
1965
1966fn main() {
1967 let ($0s, _1) = &(S,2);
1968 let s = (*s).f();
1969}
1970 "#,
1971 )
1972 }
1973
1974 #[test]
1975 fn no_outer_parens_when_ref_deref() {
1976 check_in_place_assist(
1977 r#"
1978#[derive(Clone, Copy)]
1979struct S;
1980impl S {
1981 fn do_stuff(&self) -> i32 { 42 }
1982}
1983fn main() {
1984 let $0t = &(S,&S);
1985 let v = (&t.0).do_stuff();
1986}
1987 "#,
1988 r#"
1989#[derive(Clone, Copy)]
1990struct S;
1991impl S {
1992 fn do_stuff(&self) -> i32 { 42 }
1993}
1994fn main() {
1995 let ($0s, s1) = &(S,&S);
1996 let v = s.do_stuff();
1997}
1998 "#,
1999 )
2000 }
2001
2002 #[test]
2003 fn auto_ref_deref() {
2004 check_in_place_assist(
2005 r#"
2006#[derive(Clone, Copy)]
2007struct S;
2008impl S {
2009 fn do_stuff(&self) -> i32 { 42 }
2010}
2011fn main() {
2012 let $0t = &(S,&S);
2013 let v = (&t.0).do_stuff(); // no deref, remove parens
2014 // `t.0` gets auto-refed -> no deref needed -> no parens
2015 let v = t.0.do_stuff(); // no deref, no parens
2016 let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens
2017 // deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
2018 let v = t.1.do_stuff(); // deref, parens
2019}
2020 "#,
2021 r#"
2022#[derive(Clone, Copy)]
2023struct S;
2024impl S {
2025 fn do_stuff(&self) -> i32 { 42 }
2026}
2027fn main() {
2028 let ($0s, s1) = &(S,&S);
2029 let v = s.do_stuff(); // no deref, remove parens
2030 // `t.0` gets auto-refed -> no deref needed -> no parens
2031 let v = s.do_stuff(); // no deref, no parens
2032 let v = &s.do_stuff(); // `&` is for result -> no deref, no parens
2033 // deref: `s1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
2034 let v = (*s1).do_stuff(); // deref, parens
2035}
2036 "#,
2037 )
2038 }
2039
2040 #[test]
2041 fn mutable() {
2042 check_in_place_assist(
2043 r#"
2044fn f_owned(v: i32) {}
2045fn f(v: &i32) {}
2046fn f_mut(v: &mut i32) { *v = 42; }
2047
2048fn main() {
2049 let $0t = &mut (1,2);
2050 let v = t.0;
2051 t.0 = 42;
2052 f_owned(t.0);
2053 f(&t.0);
2054 f_mut(&mut t.0);
2055}
2056 "#,
2057 r#"
2058fn f_owned(v: i32) {}
2059fn f(v: &i32) {}
2060fn f_mut(v: &mut i32) { *v = 42; }
2061
2062fn main() {
2063 let ($0_0, _1) = &mut (1,2);
2064 let v = *_0;
2065 *_0 = 42;
2066 f_owned(*_0);
2067 f(_0);
2068 f_mut(_0);
2069}
2070 "#,
2071 )
2072 }
2073
2074 #[test]
2075 fn with_ref_keyword() {
2076 check_in_place_assist(
2077 r#"
2078fn f_owned(v: i32) {}
2079fn f(v: &i32) {}
2080
2081fn main() {
2082 let ref $0t = (1,2);
2083 let v = t.0;
2084 f_owned(t.0);
2085 f(&t.0);
2086}
2087 "#,
2088 r#"
2089fn f_owned(v: i32) {}
2090fn f(v: &i32) {}
2091
2092fn main() {
2093 let (ref $0_0, ref _1) = (1,2);
2094 let v = *_0;
2095 f_owned(*_0);
2096 f(_0);
2097}
2098 "#,
2099 )
2100 }
2101 #[test]
2102 fn with_ref_mut_keywords() {
2103 check_in_place_assist(
2104 r#"
2105fn f_owned(v: i32) {}
2106fn f(v: &i32) {}
2107fn f_mut(v: &mut i32) { *v = 42; }
2108
2109fn main() {
2110 let ref mut $0t = (1,2);
2111 let v = t.0;
2112 t.0 = 42;
2113 f_owned(t.0);
2114 f(&t.0);
2115 f_mut(&mut t.0);
2116}
2117 "#,
2118 r#"
2119fn f_owned(v: i32) {}
2120fn f(v: &i32) {}
2121fn f_mut(v: &mut i32) { *v = 42; }
2122
2123fn main() {
2124 let (ref mut $0_0, ref mut _1) = (1,2);
2125 let v = *_0;
2126 *_0 = 42;
2127 f_owned(*_0);
2128 f(_0);
2129 f_mut(_0);
2130}
2131 "#,
2132 )
2133 }
2134 }
2135}