2 assists
::{AssistId, AssistKind}
,
4 search
::{FileReference, SearchScope, UsageSearchResult}
,
7 ast
::{self, AstNode, FieldExpr, HasName, IdentPat, MethodCallExpr}
,
11 use crate::assist_context
::{AssistBuilder, AssistContext, Assists}
;
13 // Assist: destructure_tuple_binding
15 // Destructures a tuple binding in place.
26 // let ($0_0, _1) = (1,2);
30 pub(crate) fn destructure_tuple_binding(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
31 destructure_tuple_binding_impl(acc
, ctx
, false)
34 // And when `with_sub_pattern` enabled (currently disabled):
35 // Assist: destructure_tuple_binding_in_sub_pattern
37 // Destructures tuple items in sub-pattern (after `@`).
48 // let t @ ($0_0, _1) = (1,2);
52 pub(crate) fn destructure_tuple_binding_impl(
54 ctx
: &AssistContext
<'_
>,
55 with_sub_pattern
: bool
,
57 let ident_pat
= ctx
.find_node_at_offset
::<ast
::IdentPat
>()?
;
58 let data
= collect_data(ident_pat
, ctx
)?
;
62 AssistId("destructure_tuple_binding_in_sub_pattern", AssistKind
::RefactorRewrite
),
63 "Destructure tuple in sub-pattern",
66 edit_tuple_assignment(ctx
, builder
, &data
, true);
67 edit_tuple_usages(&data
, builder
, ctx
, true);
73 AssistId("destructure_tuple_binding", AssistKind
::RefactorRewrite
),
74 if with_sub_pattern { "Destructure tuple in place" }
else { "Destructure tuple" }
,
77 edit_tuple_assignment(ctx
, builder
, &data
, false);
78 edit_tuple_usages(&data
, builder
, ctx
, false);
85 fn collect_data(ident_pat
: IdentPat
, ctx
: &AssistContext
<'_
>) -> Option
<TupleData
> {
86 if ident_pat
.at_token().is_some() {
87 // Cannot destructure pattern with sub-pattern:
88 // Only IdentPat can have sub-pattern,
89 // but not TuplePat (`(a,b)`).
90 cov_mark
::hit
!(destructure_tuple_subpattern
);
94 let ty
= ctx
.sema
.type_of_pat(&ident_pat
.clone().into())?
.adjusted();
95 let ref_type
= if ty
.is_mutable_reference() {
96 Some(RefType
::Mutable
)
97 } else if ty
.is_reference() {
98 Some(RefType
::ReadOnly
)
102 // might be reference
103 let ty
= ty
.strip_references();
105 let field_types
= ty
.tuple_fields(ctx
.db());
106 if field_types
.is_empty() {
107 cov_mark
::hit
!(destructure_tuple_no_tuple
);
111 let name
= ident_pat
.name()?
.to_string();
112 let range
= ident_pat
.syntax().text_range();
114 let usages
= ctx
.sema
.to_def(&ident_pat
).map(|def
| {
115 Definition
::Local(def
)
117 .in_scope(SearchScope
::single_file(ctx
.file_id()))
121 let field_names
= (0..field_types
.len())
122 .map(|i
| generate_name(ctx
, i
, &name
, &ident_pat
, &usages
))
123 .collect
::<Vec
<_
>>();
125 Some(TupleData { ident_pat, range, ref_type, field_names, usages }
)
129 _ctx
: &AssistContext
<'_
>,
132 _ident_pat
: &IdentPat
,
133 _usages
: &Option
<UsageSearchResult
>,
135 // FIXME: detect if name already used
136 format
!("_{}", index
)
147 ref_type
: Option
<RefType
>,
148 field_names
: Vec
<String
>,
149 // field_types: Vec<Type>,
150 usages
: Option
<UsageSearchResult
>,
152 fn edit_tuple_assignment(
153 ctx
: &AssistContext
<'_
>,
154 builder
: &mut AssistBuilder
,
156 in_sub_pattern
: bool
,
159 let original
= &data
.ident_pat
;
160 let is_ref
= original
.ref_token().is_some();
161 let is_mut
= original
.mut_token().is_some();
162 let fields
= data
.field_names
.iter().map(|name
| {
163 ast
::Pat
::from(ast
::make
::ident_pat(is_ref
, is_mut
, ast
::make
::name(name
)))
165 ast
::make
::tuple_pat(fields
)
168 let add_cursor
= |text
: &str| {
169 // place cursor on first tuple item
170 let first_tuple
= &data
.field_names
[0];
171 text
.replacen(first_tuple
, &format
!("$0{}", first_tuple
), 1)
174 // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
176 let text
= format
!(" @ {}", tuple_pat
);
177 match ctx
.config
.snippet_cap
{
179 let snip
= add_cursor(&text
);
180 builder
.insert_snippet(cap
, data
.range
.end(), snip
);
182 None
=> builder
.insert(data
.range
.end(), text
),
185 let text
= tuple_pat
.to_string();
186 match ctx
.config
.snippet_cap
{
188 let snip
= add_cursor(&text
);
189 builder
.replace_snippet(cap
, data
.range
, snip
);
191 None
=> builder
.replace(data
.range
, text
),
196 fn edit_tuple_usages(
198 builder
: &mut AssistBuilder
,
199 ctx
: &AssistContext
<'_
>,
200 in_sub_pattern
: bool
,
202 if let Some(usages
) = data
.usages
.as_ref() {
203 for (file_id
, refs
) in usages
.iter() {
204 builder
.edit_file(*file_id
);
207 edit_tuple_usage(ctx
, builder
, r
, data
, in_sub_pattern
);
213 ctx
: &AssistContext
<'_
>,
214 builder
: &mut AssistBuilder
,
215 usage
: &FileReference
,
217 in_sub_pattern
: bool
,
219 match detect_tuple_index(usage
, data
) {
220 Some(index
) => edit_tuple_field_usage(ctx
, builder
, data
, index
),
223 cov_mark
::hit
!(destructure_tuple_call_with_subpattern
);
227 // no index access -> make invalid -> requires handling by user
228 // -> put usage in block comment
230 // Note: For macro invocations this might result in still valid code:
231 // When a macro accepts the tuple as argument, as well as no arguments at all,
232 // uncommenting the tuple still leaves the macro call working (see `tests::in_macro_call::empty_macro`).
233 // But this is an unlikely case. Usually the resulting macro call will become erroneous.
234 builder
.insert(usage
.range
.start(), "/*");
235 builder
.insert(usage
.range
.end(), "*/");
240 fn edit_tuple_field_usage(
241 ctx
: &AssistContext
<'_
>,
242 builder
: &mut AssistBuilder
,
246 let field_name
= &data
.field_names
[index
.index
];
248 if data
.ref_type
.is_some() {
249 let ref_data
= handle_ref_field_usage(ctx
, &index
.field_expr
);
250 builder
.replace(ref_data
.range
, ref_data
.format(field_name
));
252 builder
.replace(index
.range
, field_name
);
258 field_expr
: FieldExpr
,
260 fn detect_tuple_index(usage
: &FileReference
, data
: &TupleData
) -> Option
<TupleIndex
> {
274 .skip_while(|s
| !ast
::PathExpr
::can_cast(s
.kind()))
275 .skip(1) // PATH_EXPR
276 .find(|s
| !ast
::ParenExpr
::can_cast(s
.kind()))?
; // skip parentheses
278 if let Some(field_expr
) = ast
::FieldExpr
::cast(node
) {
279 let idx
= field_expr
.name_ref()?
.as_tuple_field()?
;
280 if idx
< data
.field_names
.len() {
281 // special case: in macro call -> range of `field_expr` in applied macro, NOT range in actual file!
282 if field_expr
.syntax().ancestors().any(|a
| ast
::MacroStmts
::can_cast(a
.kind())) {
283 cov_mark
::hit
!(destructure_tuple_macro_call
);
285 // issue: cannot differentiate between tuple index passed into macro or tuple index as result of macro:
288 // ($t1:expr, $t2:expr) => { $t1; $t2.0 }
293 // -> 2 tuple index usages detected!
295 // -> only handle `t`
299 Some(TupleIndex { index: idx, range: field_expr.syntax().text_range(), field_expr }
)
301 // tuple index out of range
312 needs_parentheses
: bool
,
315 fn format(&self, field_name
: &str) -> String
{
316 match (self.needs_deref
, self.needs_parentheses
) {
317 (true, true) => format
!("(*{})", field_name
),
318 (true, false) => format
!("*{}", field_name
),
319 (false, true) => format
!("({})", field_name
),
320 (false, false) => field_name
.to_string(),
324 fn handle_ref_field_usage(ctx
: &AssistContext
<'_
>, field_expr
: &FieldExpr
) -> RefData
{
325 let s
= field_expr
.syntax();
327 RefData { range: s.text_range(), needs_deref: true, needs_parentheses: true }
;
329 let parent
= match s
.parent().map(ast
::Expr
::cast
) {
330 Some(Some(parent
)) => parent
,
332 ref_data
.needs_parentheses
= false;
335 None
=> return ref_data
,
339 ast
::Expr
::ParenExpr(it
) => {
340 // already parens in place -> don't replace
341 ref_data
.needs_parentheses
= false;
342 // there might be a ref outside: `&(t.0)` -> can be removed
343 if let Some(it
) = it
.syntax().parent().and_then(ast
::RefExpr
::cast
) {
344 ref_data
.needs_deref
= false;
345 ref_data
.range
= it
.syntax().text_range();
348 ast
::Expr
::RefExpr(it
) => {
349 // `&*` -> cancel each other out
350 ref_data
.needs_deref
= false;
351 ref_data
.needs_parentheses
= false;
352 // might be surrounded by parens -> can be removed too
353 match it
.syntax().parent().and_then(ast
::ParenExpr
::cast
) {
354 Some(parent
) => ref_data
.range
= parent
.syntax().text_range(),
355 None
=> ref_data
.range
= it
.syntax().text_range(),
358 // higher precedence than deref `*`
359 // https://doc.rust-lang.org/reference/expressions.html#expression-precedence
360 // -> requires parentheses
361 ast
::Expr
::PathExpr(_it
) => {}
362 ast
::Expr
::MethodCallExpr(it
) => {
363 // `field_expr` is `self_param` (otherwise it would be in `ArgList`)
365 // test if there's already auto-ref in place (`value` -> `&value`)
366 // -> no method accepting `self`, but `&self` -> no need for deref
368 // other combinations (`&value` -> `value`, `&&value` -> `&value`, `&value` -> `&&value`) might or might not be able to auto-ref/deref,
369 // but there might be trait implementations an added `&` might resolve to
370 // -> ONLY handle auto-ref from `value` to `&value`
371 fn is_auto_ref(ctx
: &AssistContext
<'_
>, call_expr
: &MethodCallExpr
) -> bool
{
372 fn impl_(ctx
: &AssistContext
<'_
>, call_expr
: &MethodCallExpr
) -> Option
<bool
> {
373 let rec
= call_expr
.receiver()?
;
374 let rec_ty
= ctx
.sema
.type_of_expr(&rec
)?
.original();
375 // input must be actual value
376 if rec_ty
.is_reference() {
380 // doesn't resolve trait impl
381 let f
= ctx
.sema
.resolve_method_call(call_expr
)?
;
382 let self_param
= f
.self_param(ctx
.db())?
;
384 match self_param
.access(ctx
.db()) {
385 hir
::Access
::Shared
| hir
::Access
::Exclusive
=> Some(true),
386 hir
::Access
::Owned
=> Some(false),
389 impl_(ctx
, call_expr
).unwrap_or(false)
392 if is_auto_ref(ctx
, &it
) {
393 ref_data
.needs_deref
= false;
394 ref_data
.needs_parentheses
= false;
397 ast
::Expr
::FieldExpr(_it
) => {
399 ref_data
.needs_deref
= false;
400 ref_data
.needs_parentheses
= false;
402 ast
::Expr
::IndexExpr(_it
) => {
404 ref_data
.needs_deref
= false;
405 ref_data
.needs_parentheses
= false;
407 ast
::Expr
::TryExpr(_it
) => {
409 // requires deref and parens: `(*_0)`
411 // lower precedence than deref `*` -> no parens
413 ref_data
.needs_parentheses
= false;
424 use crate::tests
::{check_assist, check_assist_not_applicable}
;
426 // Tests for direct tuple destructure:
427 // `let $0t = (1,2);` -> `let (_0, _1) = (1,2);`
429 fn assist(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
430 destructure_tuple_binding_impl(acc
, ctx
, false)
434 fn dont_trigger_on_unit() {
435 cov_mark
::check
!(destructure_tuple_no_tuple
);
436 check_assist_not_applicable(
446 fn dont_trigger_on_number() {
447 cov_mark
::check
!(destructure_tuple_no_tuple
);
448 check_assist_not_applicable(
459 fn destructure_3_tuple() {
469 let ($0_0, _1, _2) = (1,2,3);
475 fn destructure_2_tuple() {
485 let ($0_0, _1) = (1,2);
491 fn replace_indices() {
504 let ($0_0, _1, _2) = (1,2,3);
514 fn replace_usage_in_parentheses() {
526 let ($0_0, _1, _2) = (1,2,3);
535 fn handle_function_call() {
546 let ($0_0, _1) = (1,2);
547 let v = /*tup*/.into();
554 fn handle_invalid_index() {
565 let ($0_0, _1) = (1,2);
573 fn dont_replace_variable_with_same_name_as_tuple() {
590 let ($0_0, _1, _2) = (1,2,3);
600 fn keep_function_call_in_tuple_item() {
605 let $0t = ("3.14", 0);
606 let pi: f32 = t.0.parse().unwrap_or(0.0);
611 let ($0_0, _1) = ("3.14", 0);
612 let pi: f32 = _0.parse().unwrap_or(0.0);
624 let $0t: (usize, i32) = (1,2);
629 let ($0_0, _1): (usize, i32) = (1,2);
636 fn destructure_reference() {
657 fn destructure_multiple_reference() {
670 let ($0_0, _1) = &&t;
678 fn keep_reference() {
682 fn foo(t: &(usize, usize)) -> usize {
689 fn foo(t: &(usize, usize)) -> usize {
710 let (ref $0_0, ref _1) = (1,2);
730 let (mut $0_0, mut _1) = (1,2);
744 let ref mut $0t = (1,2);
751 let (ref mut $0_0, ref mut _1) = (1,2);
760 fn dont_trigger_for_non_tuple_reference() {
761 check_assist_not_applicable(
773 fn dont_trigger_on_static_tuple() {
774 check_assist_not_applicable(
777 static $0TUP: (usize, usize) = (1,2);
783 fn dont_trigger_on_wildcard() {
784 check_assist_not_applicable(
795 fn dont_trigger_in_struct() {
796 check_assist_not_applicable(
800 $0tup: (usize, usize),
807 fn dont_trigger_in_struct_creation() {
808 check_assist_not_applicable(
824 fn dont_trigger_on_tuple_struct() {
825 check_assist_not_applicable(
828 struct S(usize, usize);
837 fn dont_trigger_when_subpattern_exists() {
838 // sub-pattern is only allowed with IdentPat (name), not other patterns (like TuplePat)
839 cov_mark
::check
!(destructure_tuple_subpattern
);
840 check_assist_not_applicable(
843 fn sum(t: (usize, usize)) -> usize {
845 $0t @ (1..=3,1..=3) => t.0 + t.1,
859 let t1 @ (_, $0t2) = (1, (2,3));
860 let v = t1.0 + t2.0 + t2.1;
865 let t1 @ (_, ($0_0, _1)) = (1, (2,3));
866 let v = t1.0 + _0 + _1;
873 fn in_nested_tuple() {
878 let ($0tup, v) = ((1,2),3);
883 let (($0_0, _1), v) = ((1,2),3);
896 let f = |v| v + tup.1;
901 let ($0_0, _1, _2) = (1,2,3);
909 fn in_closure_args() {
914 let f = |$0t| t.0 + t.1;
920 let f = |($0_0, _1)| _0 + _1;
928 fn in_function_args() {
932 fn f($0t: (usize, usize)) {
937 fn f(($0_0, _1): (usize, usize)) {
949 fn f(t: (usize, usize)) {
956 fn f(t: (usize, usize)) {
957 if let ($0_0, _1) = t {
965 fn in_if_let_option() {
970 fn f(o: Option<(usize, usize)>) {
971 if let Some($0t) = o {
977 fn f(o: Option<(usize, usize)>) {
978 if let Some(($0_0, _1)) = o {
1007 fn in_match_option() {
1011 //- minicore: option
1022 Some(($0_0, _1)) => _1,
1030 fn in_match_reference_option() {
1034 //- minicore: option
1047 Some(($0_0, _1)) => *_1,
1060 //- minicore: iterators
1062 for $0t in core::iter::repeat((1,2)) {
1069 for ($0_0, _1) in core::iter::repeat((1,2)) {
1077 fn in_for_nested() {
1081 //- minicore: iterators
1083 for (a, $0b) in core::iter::repeat((1,(2,3))) {
1090 for (a, ($0_0, _1)) in core::iter::repeat((1,(2,3))) {
1099 fn not_applicable_on_tuple_usage() {
1100 //Improvement: might be reasonable to allow & implement
1101 check_assist_not_applicable(
1120 let s = (t.0 + t.1) / 2;
1121 let f = |v| v + t.0;
1126 (_,2) if t.0 > 2 => 1,
1133 let ($0_0, _1) = (1,2);
1135 let s = (_0 + _1) / 2;
1138 let e = /*t*/ == (9,0);
1141 (_,2) if _0 > 2 => 1,
1150 fn non_trivial_tuple_assignment() {
1192 use crate::tests
::check_assist_by_label
;
1194 fn assist(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
1195 destructure_tuple_binding_impl(acc
, ctx
, true)
1197 fn in_place_assist(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
1198 destructure_tuple_binding_impl(acc
, ctx
, false)
1201 pub(crate) fn check_in_place_assist(ra_fixture_before
: &str, ra_fixture_after
: &str) {
1202 check_assist_by_label(
1206 // "Destructure tuple in place",
1207 "Destructure tuple",
1211 pub(crate) fn check_sub_pattern_assist(ra_fixture_before
: &str, ra_fixture_after
: &str) {
1212 check_assist_by_label(
1216 "Destructure tuple in sub-pattern",
1220 pub(crate) fn check_both_assists(
1221 ra_fixture_before
: &str,
1222 ra_fixture_after_in_place
: &str,
1223 ra_fixture_after_in_sub_pattern
: &str,
1225 check_in_place_assist(ra_fixture_before
, ra_fixture_after_in_place
);
1226 check_sub_pattern_assist(ra_fixture_before
, ra_fixture_after_in_sub_pattern
);
1230 /// Tests for destructure of tuple in sub-pattern:
1231 /// `let $0t = (1,2);` -> `let t @ (_0, _1) = (1,2);`
1233 use super::assist
::*;
1235 use crate::tests
::check_assist_by_label
;
1238 fn destructure_in_sub_pattern() {
1239 check_sub_pattern_assist(
1241 #![feature(bindings_after_at)]
1248 #![feature(bindings_after_at)]
1251 let t @ ($0_0, _1) = (1,2);
1258 fn trigger_both_destructure_tuple_assists() {
1259 fn assist(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
1260 destructure_tuple_binding_impl(acc
, ctx
, true)
1267 check_assist_by_label(
1272 let ($0_0, _1) = (1,2);
1275 "Destructure tuple in place",
1277 check_assist_by_label(
1282 let t @ ($0_0, _1) = (1,2);
1285 "Destructure tuple in sub-pattern",
1290 fn replace_indices() {
1291 check_sub_pattern_assist(
1301 let t @ ($0_0, _1) = (1,2);
1310 fn keep_function_call() {
1311 cov_mark
::check
!(destructure_tuple_call_with_subpattern
);
1312 check_sub_pattern_assist(
1321 let t @ ($0_0, _1) = (1,2);
1330 check_sub_pattern_assist(
1333 let $0t: (usize, i32) = (1,2);
1340 let t @ ($0_0, _1): (usize, i32) = (1,2);
1349 fn in_function_args() {
1350 check_sub_pattern_assist(
1352 fn f($0t: (usize, usize)) {
1358 fn f(t @ ($0_0, _1): (usize, usize)) {
1368 check_sub_pattern_assist(
1371 let ref $0t = (1,2);
1378 let ref t @ (ref $0_0, ref _1) = (1,2);
1387 check_sub_pattern_assist(
1390 let mut $0t = (1,2);
1397 let mut t @ (mut $0_0, mut _1) = (1,2);
1406 check_sub_pattern_assist(
1409 let ref mut $0t = (1,2);
1416 let ref mut t @ (ref mut $0_0, ref mut _1) = (1,2);
1425 /// Tests for tuple usage in macro call:
1426 /// `println!("{}", t.0)`
1428 use super::assist
::*;
1431 fn detect_macro_call() {
1432 cov_mark
::check
!(destructure_tuple_macro_call
);
1433 check_in_place_assist(
1436 ($e:expr) => { "foo"; $e };
1446 ($e:expr) => { "foo"; $e };
1450 let ($0_0, _1) = (1,2);
1460 // leading `"foo"` to ensure `$e` doesn't start at position `0`
1463 ($e:expr) => { "foo"; $e };
1473 ($e:expr) => { "foo"; $e };
1477 let ($0_0, _1) = (1,2);
1483 ($e:expr) => { "foo"; $e };
1487 let t @ ($0_0, _1) = (1,2);
1495 fn tuple_function_usage() {
1499 ($e:expr) => { "foo"; $e };
1509 ($e:expr) => { "foo"; $e };
1513 let ($0_0, _1) = (1,2);
1519 ($e:expr) => { "foo"; $e };
1523 let t @ ($0_0, _1) = (1,2);
1531 fn tuple_index_usage() {
1535 ($e:expr) => { "foo"; $e };
1543 // FIXME: replace `t.0` with `_0` (cannot detect range of tuple index in macro call)
1546 ($e:expr) => { "foo"; $e };
1550 let ($0_0, _1) = (1,2);
1554 // FIXME: replace `t.0` with `_0`
1557 ($e:expr) => { "foo"; $e };
1561 let t @ ($0_0, _1) = (1,2);
1569 fn tuple_in_parentheses_index_usage() {
1573 ($e:expr) => { "foo"; $e };
1581 // FIXME: replace `(t).0` with `_0`
1584 ($e:expr) => { "foo"; $e };
1588 let ($0_0, _1) = (1,2);
1592 // FIXME: replace `(t).0` with `_0`
1595 ($e:expr) => { "foo"; $e };
1599 let t @ ($0_0, _1) = (1,2);
1608 check_in_place_assist(
1612 ($e:expr) => { $e; "foo" };
1620 // FIXME: macro allows no arg -> is valid. But assist should result in invalid code
1624 ($e:expr) => { $e; "foo" };
1628 let ($0_0, _1) = (1,2);
1636 fn tuple_index_in_macro() {
1640 ($t:expr, $i:expr) => { $t.0 + $i };
1648 // FIXME: replace `t.0` in macro call (not IN macro) with `_0`
1651 ($t:expr, $i:expr) => { $t.0 + $i };
1655 let ($0_0, _1) = (1,2);
1659 // FIXME: replace `t.0` in macro call with `_0`
1662 ($t:expr, $i:expr) => { $t.0 + $i };
1666 let t @ ($0_0, _1) = (1,2);
1675 use super::assist
::*;
1679 check_in_place_assist(
1688 let ($0_0, _1) = &(1,2);
1695 fn no_ref_with_parens() {
1696 check_in_place_assist(
1705 let ($0_0, _1) = &(1,2);
1713 check_in_place_assist(
1722 let ($0_0, _1) = &(1,2);
1729 fn with_ref_in_parens_ref() {
1730 check_in_place_assist(
1734 let v: &i32 = &(t.0);
1739 let ($0_0, _1) = &(1,2);
1746 fn with_ref_in_ref_parens() {
1747 check_in_place_assist(
1751 let v: &i32 = (&t.0);
1756 let ($0_0, _1) = &(1,2);
1764 fn deref_and_parentheses() {
1765 // Operator/Expressions with higher precedence than deref (`*`):
1766 // https://doc.rust-lang.org/reference/expressions.html#expression-precedence
1769 // * Field expression
1770 // * Function calls, array indexing
1772 check_in_place_assist(
1774 //- minicore: option
1778 fn do_stuff(self) {}
1781 fn do_stuff(self) {}
1784 fn do_stuff(self) {}
1790 fn foo() -> Option<()> {
1791 let $0t = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
1792 let v: i32 = t.0; // deref, no parens
1793 let v: &i32 = &t.0; // no deref, no parens, remove `&`
1794 f1(t.0); // deref, no parens
1795 f2(&t.0); // `&*` -> cancel out -> no deref, no parens
1796 // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
1797 // let v: i32 = t.1.0; // no deref, no parens
1798 let v: i32 = t.4.value; // no deref, no parens
1799 t.0.do_stuff(); // deref, parens
1800 let v: i32 = t.2?; // deref, parens
1801 let v: i32 = t.3[0]; // no deref, no parens
1802 (t.0).do_stuff(); // deref, no additional parens
1803 let v: i32 = *t.5; // deref (-> 2), no parens
1812 fn do_stuff(self) {}
1815 fn do_stuff(self) {}
1818 fn do_stuff(self) {}
1824 fn foo() -> Option<()> {
1825 let ($0_0, _1, _2, _3, _4, _5) = &(0, (1,"1"), Some(2), [3;3], S4 { value: 4 }, &5);
1826 let v: i32 = *_0; // deref, no parens
1827 let v: &i32 = _0; // no deref, no parens, remove `&`
1828 f1(*_0); // deref, no parens
1829 f2(_0); // `&*` -> cancel out -> no deref, no parens
1830 // https://github.com/rust-lang/rust-analyzer/issues/1109#issuecomment-658868639
1831 // let v: i32 = t.1.0; // no deref, no parens
1832 let v: i32 = _4.value; // no deref, no parens
1833 (*_0).do_stuff(); // deref, parens
1834 let v: i32 = (*_2)?; // deref, parens
1835 let v: i32 = _3[0]; // no deref, no parens
1836 (*_0).do_stuff(); // deref, no additional parens
1837 let v: i32 = **_5; // deref (-> 2), no parens
1849 fn self_auto_ref_doesnt_need_deref() {
1850 check_in_place_assist(
1852 #[derive(Clone, Copy)]
1864 #[derive(Clone, Copy)]
1871 let ($0_0, _1) = &(S,2);
1879 fn self_owned_requires_deref() {
1880 check_in_place_assist(
1882 #[derive(Clone, Copy)]
1894 #[derive(Clone, Copy)]
1901 let ($0_0, _1) = &(S,2);
1909 fn self_auto_ref_in_trait_call_doesnt_require_deref() {
1910 check_in_place_assist(
1915 #[derive(Clone, Copy)]
1926 // FIXME: doesn't need deref * parens. But `ctx.sema.resolve_method_call` doesn't resolve trait implementations
1931 #[derive(Clone, Copy)]
1938 let ($0_0, _1) = &(S,2);
1945 fn no_auto_deref_because_of_owned_and_ref_trait_impl() {
1946 check_in_place_assist(
1951 #[derive(Clone, Copy)]
1969 #[derive(Clone, Copy)]
1979 let ($0_0, _1) = &(S,2);
1987 fn no_outer_parens_when_ref_deref() {
1988 check_in_place_assist(
1990 #[derive(Clone, Copy)]
1993 fn do_stuff(&self) -> i32 { 42 }
1997 let v = (&t.0).do_stuff();
2001 #[derive(Clone, Copy)]
2004 fn do_stuff(&self) -> i32 { 42 }
2007 let ($0_0, _1) = &(S,&S);
2008 let v = _0.do_stuff();
2015 fn auto_ref_deref() {
2016 check_in_place_assist(
2018 #[derive(Clone, Copy)]
2021 fn do_stuff(&self) -> i32 { 42 }
2025 let v = (&t.0).do_stuff(); // no deref, remove parens
2026 // `t.0` gets auto-refed -> no deref needed -> no parens
2027 let v = t.0.do_stuff(); // no deref, no parens
2028 let v = &t.0.do_stuff(); // `&` is for result -> no deref, no parens
2029 // deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
2030 let v = t.1.do_stuff(); // deref, parens
2034 #[derive(Clone, Copy)]
2037 fn do_stuff(&self) -> i32 { 42 }
2040 let ($0_0, _1) = &(S,&S);
2041 let v = _0.do_stuff(); // no deref, remove parens
2042 // `t.0` gets auto-refed -> no deref needed -> no parens
2043 let v = _0.do_stuff(); // no deref, no parens
2044 let v = &_0.do_stuff(); // `&` is for result -> no deref, no parens
2045 // deref: `_1` is `&&S`, but method called is on `&S` -> there might be a method accepting `&&S`
2046 let v = (*_1).do_stuff(); // deref, parens
2054 check_in_place_assist(
2056 fn f_owned(v: i32) {}
2058 fn f_mut(v: &mut i32) { *v = 42; }
2061 let $0t = &mut (1,2);
2070 fn f_owned(v: i32) {}
2072 fn f_mut(v: &mut i32) { *v = 42; }
2075 let ($0_0, _1) = &mut (1,2);
2087 fn with_ref_keyword() {
2088 check_in_place_assist(
2090 fn f_owned(v: i32) {}
2094 let ref $0t = (1,2);
2101 fn f_owned(v: i32) {}
2105 let (ref $0_0, ref _1) = (1,2);
2114 fn with_ref_mut_keywords() {
2115 check_in_place_assist(
2117 fn f_owned(v: i32) {}
2119 fn f_mut(v: &mut i32) { *v = 42; }
2122 let ref mut $0t = (1,2);
2131 fn f_owned(v: i32) {}
2133 fn f_mut(v: &mut i32) { *v = 42; }
2136 let (ref mut $0_0, ref mut _1) = (1,2);