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