Skip to main content

ide_assists/handlers/
destructure_struct_binding.rs

1use hir::{HasVisibility, Semantics};
2use ide_db::{
3    FxHashMap, FxHashSet, RootDatabase,
4    assists::AssistId,
5    defs::Definition,
6    helpers::mod_path_to_ast_with_factory,
7    search::{FileReference, SearchScope},
8};
9use itertools::Itertools;
10use syntax::syntax_editor::SyntaxEditor;
11use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast};
12use syntax::{
13    SyntaxToken,
14    ast::{HasName, edit::IndentLevel, syntax_factory::SyntaxFactory},
15    syntax_editor::Position,
16};
17
18use crate::{
19    assist_context::{AssistContext, Assists, SourceChangeBuilder},
20    utils::{cover_edit_range, ref_field_expr::determine_ref_and_parens},
21};
22
23// Assist: destructure_struct_binding
24//
25// Destructures a struct binding in place.
26//
27// ```
28// struct Foo {
29//     bar: i32,
30//     baz: i32,
31// }
32// fn main() {
33//     let $0foo = Foo { bar: 1, baz: 2 };
34//     let bar2 = foo.bar;
35//     let baz2 = &foo.baz;
36// }
37// ```
38// ->
39// ```
40// struct Foo {
41//     bar: i32,
42//     baz: i32,
43// }
44// fn main() {
45//     let Foo { bar, baz } = Foo { bar: 1, baz: 2 };
46//     let bar2 = bar;
47//     let baz2 = &baz;
48// }
49// ```
50pub(crate) fn destructure_struct_binding(
51    acc: &mut Assists,
52    ctx: &AssistContext<'_, '_>,
53) -> Option<()> {
54    let target = ctx.find_node_at_offset::<Target>()?;
55    let data = collect_data(target, ctx)?;
56
57    acc.add(
58        AssistId::refactor_rewrite("destructure_struct_binding"),
59        "Destructure struct binding",
60        data.target.syntax().text_range(),
61        |edit| destructure_struct_binding_impl(ctx, edit, &data),
62    );
63
64    Some(())
65}
66
67enum Target {
68    IdentPat(ast::IdentPat),
69    SelfParam { param: ast::SelfParam, insert_after: SyntaxToken },
70}
71
72impl Target {
73    fn ty<'db>(&self, sema: &Semantics<'db, RootDatabase>) -> Option<hir::Type<'db>> {
74        match self {
75            Target::IdentPat(pat) => sema.type_of_binding_in_pat(pat),
76            Target::SelfParam { param, .. } => sema.type_of_self(param),
77        }
78    }
79
80    fn is_ref(&self) -> bool {
81        match self {
82            Target::IdentPat(ident_pat) => ident_pat.ref_token().is_some(),
83            Target::SelfParam { .. } => false,
84        }
85    }
86
87    fn is_mut(&self) -> bool {
88        match self {
89            Target::IdentPat(ident_pat) => ident_pat.mut_token().is_some(),
90            Target::SelfParam { param, .. } => {
91                param.mut_token().is_some() && param.amp_token().is_none()
92            }
93        }
94    }
95}
96
97impl HasName for Target {}
98
99impl AstNode for Target {
100    fn cast(node: SyntaxNode) -> Option<Self> {
101        if ast::IdentPat::can_cast(node.kind()) {
102            ast::IdentPat::cast(node).map(Self::IdentPat)
103        } else {
104            let param = ast::SelfParam::cast(node)?;
105            let param_list = param.syntax().parent().and_then(ast::ParamList::cast)?;
106            let block = param_list.syntax().parent()?.children().find_map(ast::BlockExpr::cast)?;
107            let insert_after = block.stmt_list()?.l_curly_token()?;
108            Some(Self::SelfParam { param, insert_after })
109        }
110    }
111
112    fn can_cast(kind: syntax::SyntaxKind) -> bool {
113        ast::IdentPat::can_cast(kind) || ast::SelfParam::can_cast(kind)
114    }
115
116    fn syntax(&self) -> &SyntaxNode {
117        match self {
118            Target::IdentPat(ident_pat) => ident_pat.syntax(),
119            Target::SelfParam { param, .. } => param.syntax(),
120        }
121    }
122}
123
124fn destructure_struct_binding_impl(
125    ctx: &AssistContext<'_, '_>,
126    builder: &mut SourceChangeBuilder,
127    data: &StructEditData,
128) {
129    let field_names = generate_field_names(ctx, data);
130    let editor = builder.make_editor(data.target.syntax());
131    destructure_pat(ctx, &editor, data, &field_names);
132    update_usages(ctx, &editor, data, &field_names.into_iter().collect());
133    builder.add_file_edits(ctx.vfs_file_id(), editor);
134}
135
136struct StructEditData {
137    target: Target,
138    name: ast::Name,
139    kind: hir::StructKind,
140    struct_def_path: hir::ModPath,
141    visible_fields: Vec<hir::Field>,
142    usages: Vec<FileReference>,
143    names_in_scope: FxHashSet<SmolStr>,
144    has_private_members: bool,
145    need_record_field_name: bool,
146    is_ref: bool,
147    edition: Edition,
148}
149
150impl StructEditData {
151    fn apply_to_destruct(&self, new_pat: ast::Pat, editor: &SyntaxEditor) {
152        let make = editor.make();
153        match &self.target {
154            Target::IdentPat(pat) => {
155                // If the binding is nested inside a record, we need to wrap the new
156                // destructured pattern in a non-shorthand record field
157                if self.need_record_field_name {
158                    let new_pat =
159                        make.record_pat_field(make.name_ref(&self.name.to_string()), new_pat);
160                    editor.replace(pat.syntax(), new_pat.syntax())
161                } else {
162                    editor.replace(pat.syntax(), new_pat.syntax())
163                }
164            }
165            Target::SelfParam { insert_after, .. } => {
166                let indent = IndentLevel::from_token(insert_after) + 1;
167                let newline = make.whitespace(&format!("\n{indent}"));
168                let initializer = make.expr_path(make.ident_path("self"));
169                let let_stmt = make.let_stmt(new_pat, None, Some(initializer));
170                editor.insert_all(
171                    Position::after(insert_after),
172                    vec![newline.into(), let_stmt.syntax().clone().into()],
173                );
174            }
175        }
176    }
177}
178
179fn collect_data(target: Target, ctx: &AssistContext<'_, '_>) -> Option<StructEditData> {
180    let ty = target.ty(&ctx.sema)?;
181    let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None };
182
183    let module = ctx.sema.scope(target.syntax())?.module();
184    let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.db())));
185    let struct_def = hir::ModuleDef::from(struct_type);
186    let kind = struct_type.kind(ctx.db());
187    let struct_def_path = module.find_path(ctx.db(), struct_def, cfg)?;
188
189    let is_non_exhaustive = struct_def.attrs(ctx.db())?.is_non_exhaustive();
190    let is_foreign_crate =
191        struct_def.module(ctx.db()).is_some_and(|m| m.krate(ctx.db()) != module.krate(ctx.db()));
192
193    let fields = struct_type.fields(ctx.db());
194    let n_fields = fields.len();
195
196    let visible_fields =
197        fields.into_iter().filter(|field| field.is_visible_from(ctx.db(), module)).collect_vec();
198
199    if visible_fields.is_empty() {
200        return None;
201    }
202
203    let has_private_members =
204        (is_non_exhaustive && is_foreign_crate) || visible_fields.len() < n_fields;
205
206    // If private members are present, we can only destructure records
207    if !matches!(kind, hir::StructKind::Record) && has_private_members {
208        return None;
209    }
210
211    let is_ref = ty.is_reference();
212    let need_record_field_name = target
213        .syntax()
214        .parent()
215        .and_then(ast::RecordPatField::cast)
216        .is_some_and(|field| field.colon_token().is_none());
217
218    let def = match &target {
219        Target::IdentPat(pat) => ctx.sema.to_def(pat),
220        Target::SelfParam { param, .. } => ctx.sema.to_def(param),
221    };
222    let usages = def
223        .and_then(|def| {
224            Definition::Local(def)
225                .usages(&ctx.sema)
226                .in_scope(&SearchScope::single_file(ctx.file_id()))
227                .all()
228                .iter()
229                .next()
230                .map(|(_, refs)| refs.to_vec())
231        })
232        .unwrap_or_default();
233
234    let names_in_scope = get_names_in_scope(ctx, &target, &usages).unwrap_or_default();
235
236    Some(StructEditData {
237        name: target.name()?,
238        target,
239        kind,
240        struct_def_path,
241        usages,
242        has_private_members,
243        visible_fields,
244        names_in_scope,
245        need_record_field_name,
246        is_ref,
247        edition: module.krate(ctx.db()).edition(ctx.db()),
248    })
249}
250
251fn get_names_in_scope(
252    ctx: &AssistContext<'_, '_>,
253    target: &Target,
254    usages: &[FileReference],
255) -> Option<FxHashSet<SmolStr>> {
256    fn last_usage(usages: &[FileReference]) -> Option<SyntaxNode> {
257        usages.last()?.name.syntax().into_node()
258    }
259
260    // If available, find names visible to the last usage of the binding
261    // else, find names visible to the binding itself
262    let last_usage = last_usage(usages);
263    let node = last_usage.as_ref().unwrap_or(target.syntax());
264    let scope = ctx.sema.scope(node)?;
265
266    let mut names = FxHashSet::default();
267    scope.process_all_names(&mut |name, scope| {
268        if let hir::ScopeDef::Local(_) = scope {
269            names.insert(name.as_str().into());
270        }
271    });
272    Some(names)
273}
274
275fn destructure_pat(
276    _ctx: &AssistContext<'_, '_>,
277    editor: &SyntaxEditor,
278    data: &StructEditData,
279    field_names: &[(SmolStr, SmolStr)],
280) {
281    let make = editor.make();
282    let struct_path = mod_path_to_ast_with_factory(make, &data.struct_def_path, data.edition);
283    let is_ref = data.target.is_ref();
284    let is_mut = data.target.is_mut();
285
286    let new_pat = match data.kind {
287        hir::StructKind::Tuple => {
288            let ident_pats = field_names.iter().map(|(_, new_name)| {
289                let name = make.name(new_name);
290                ast::Pat::from(make.ident_pat(is_ref, is_mut, name))
291            });
292            ast::Pat::TupleStructPat(make.tuple_struct_pat(struct_path, ident_pats))
293        }
294        hir::StructKind::Record => {
295            let fields = field_names.iter().map(|(old_name, new_name)| {
296                // Use shorthand syntax if possible
297                if old_name == new_name {
298                    make.record_pat_field_shorthand(
299                        make.ident_pat(is_ref, is_mut, make.name(old_name)).into(),
300                    )
301                } else {
302                    make.record_pat_field(
303                        make.name_ref(old_name),
304                        ast::Pat::IdentPat(make.ident_pat(is_ref, is_mut, make.name(new_name))),
305                    )
306                }
307            });
308            let field_list = make
309                .record_pat_field_list(fields, data.has_private_members.then_some(make.rest_pat()));
310
311            ast::Pat::RecordPat(make.record_pat_with_fields(struct_path, field_list))
312        }
313        hir::StructKind::Unit => make.path_pat(struct_path),
314    };
315
316    data.apply_to_destruct(new_pat, editor);
317}
318
319fn generate_field_names(
320    ctx: &AssistContext<'_, '_>,
321    data: &StructEditData,
322) -> Vec<(SmolStr, SmolStr)> {
323    match data.kind {
324        hir::StructKind::Tuple => data
325            .visible_fields
326            .iter()
327            .enumerate()
328            .map(|(index, _)| {
329                let new_name = new_field_name((format!("_{index}")).into(), &data.names_in_scope);
330                (index.to_string().into(), new_name)
331            })
332            .collect(),
333        hir::StructKind::Record => data
334            .visible_fields
335            .iter()
336            .map(|field| {
337                let field_name = field.name(ctx.db()).display_no_db(data.edition).to_smolstr();
338                let new_name = new_field_name(field_name.clone(), &data.names_in_scope);
339                (field_name, new_name)
340            })
341            .collect(),
342        hir::StructKind::Unit => Vec::new(),
343    }
344}
345
346fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> SmolStr {
347    let mut name = base_name.clone();
348    let mut i = 1;
349    while names_in_scope.contains(&name) {
350        name = format!("{base_name}_{i}").into();
351        i += 1;
352    }
353    name
354}
355
356fn update_usages(
357    ctx: &AssistContext<'_, '_>,
358    editor: &SyntaxEditor,
359    data: &StructEditData,
360    field_names: &FxHashMap<SmolStr, SmolStr>,
361) {
362    let source = ctx.source_file().syntax();
363    let edits = data
364        .usages
365        .iter()
366        .filter_map(|r| build_usage_edit(ctx, editor.make(), data, r, field_names))
367        .collect_vec();
368    for (old, new) in edits {
369        if let Some(range) = ctx.sema.original_range_opt(&old) {
370            editor.replace_all(cover_edit_range(source, range.range), vec![new.into()]);
371        }
372    }
373}
374
375fn build_usage_edit(
376    ctx: &AssistContext<'_, '_>,
377    make: &SyntaxFactory,
378    data: &StructEditData,
379    usage: &FileReference,
380    field_names: &FxHashMap<SmolStr, SmolStr>,
381) -> Option<(SyntaxNode, SyntaxNode)> {
382    match usage.name.syntax().ancestors().find_map(ast::FieldExpr::cast) {
383        Some(field_expr) => Some({
384            let field_name: SmolStr = field_expr.name_ref()?.to_string().into();
385            let new_field_name = field_names.get(&field_name)?;
386            let new_expr = make.expr_path(make.ident_path(new_field_name));
387
388            // If struct binding is a reference, we might need to deref field usages
389            if data.is_ref {
390                let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &field_expr);
391                (replace_expr.syntax().clone(), ref_data.wrap_expr(new_expr, make).syntax().clone())
392            } else {
393                (field_expr.syntax().clone(), new_expr.syntax().clone())
394            }
395        }),
396        None => Some((
397            usage.name.syntax().as_node().unwrap().clone(),
398            make.expr_macro(
399                make.ident_path("todo"),
400                make.token_tree(syntax::SyntaxKind::L_PAREN, []),
401            )
402            .syntax()
403            .clone(),
404        )),
405    }
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411
412    use crate::tests::{check_assist, check_assist_not_applicable};
413
414    #[test]
415    fn record_struct() {
416        check_assist(
417            destructure_struct_binding,
418            r#"
419            struct Foo { bar: i32, baz: i32 }
420
421            fn main() {
422                let $0foo = Foo { bar: 1, baz: 2 };
423                let bar2 = foo.bar;
424                let baz2 = &foo.baz;
425
426                let foo2 = foo;
427            }
428            "#,
429            r#"
430            struct Foo { bar: i32, baz: i32 }
431
432            fn main() {
433                let Foo { bar, baz } = Foo { bar: 1, baz: 2 };
434                let bar2 = bar;
435                let baz2 = &baz;
436
437                let foo2 = todo!();
438            }
439            "#,
440        )
441    }
442
443    #[test]
444    fn tuple_struct() {
445        check_assist(
446            destructure_struct_binding,
447            r#"
448            struct Foo(i32, i32);
449
450            fn main() {
451                let $0foo = Foo(1, 2);
452                let bar2 = foo.0;
453                let baz2 = foo.1;
454
455                let foo2 = foo;
456            }
457            "#,
458            r#"
459            struct Foo(i32, i32);
460
461            fn main() {
462                let Foo(_0, _1) = Foo(1, 2);
463                let bar2 = _0;
464                let baz2 = _1;
465
466                let foo2 = todo!();
467            }
468            "#,
469        )
470    }
471
472    #[test]
473    fn unit_struct() {
474        check_assist_not_applicable(
475            destructure_struct_binding,
476            r#"
477            struct Foo;
478
479            fn main() {
480                let $0foo = Foo;
481            }
482            "#,
483        )
484    }
485
486    #[test]
487    fn in_foreign_crate() {
488        check_assist(
489            destructure_struct_binding,
490            r#"
491            //- /lib.rs crate:dep
492            pub struct Foo { pub bar: i32 };
493
494            //- /main.rs crate:main deps:dep
495            fn main() {
496                let $0foo = dep::Foo { bar: 1 };
497                let bar2 = foo.bar;
498            }
499            "#,
500            r#"
501            fn main() {
502                let dep::Foo { bar } = dep::Foo { bar: 1 };
503                let bar2 = bar;
504            }
505            "#,
506        )
507    }
508
509    #[test]
510    fn non_exhaustive_record_appends_rest() {
511        check_assist(
512            destructure_struct_binding,
513            r#"
514            //- /lib.rs crate:dep
515            #[non_exhaustive]
516            pub struct Foo { pub bar: i32 };
517
518            //- /main.rs crate:main deps:dep
519            fn main($0foo: dep::Foo) {
520                let bar2 = foo.bar;
521            }
522            "#,
523            r#"
524            fn main(dep::Foo { bar, .. }: dep::Foo) {
525                let bar2 = bar;
526            }
527            "#,
528        )
529    }
530
531    #[test]
532    fn non_exhaustive_tuple_not_applicable() {
533        check_assist_not_applicable(
534            destructure_struct_binding,
535            r#"
536            //- /lib.rs crate:dep
537            #[non_exhaustive]
538            pub struct Foo(pub i32, pub i32);
539
540            //- /main.rs crate:main deps:dep
541            fn main(foo: dep::Foo) {
542                let $0foo2 = foo;
543                let bar = foo2.0;
544                let baz = foo2.1;
545            }
546            "#,
547        )
548    }
549
550    #[test]
551    fn non_exhaustive_unit_not_applicable() {
552        check_assist_not_applicable(
553            destructure_struct_binding,
554            r#"
555            //- /lib.rs crate:dep
556            #[non_exhaustive]
557            pub struct Foo;
558
559            //- /main.rs crate:main deps:dep
560            fn main(foo: dep::Foo) {
561                let $0foo2 = foo;
562            }
563            "#,
564        )
565    }
566
567    #[test]
568    fn record_private_fields_appends_rest() {
569        check_assist(
570            destructure_struct_binding,
571            r#"
572            //- /lib.rs crate:dep
573            pub struct Foo { pub bar: i32, baz: i32 };
574
575            //- /main.rs crate:main deps:dep
576            fn main(foo: dep::Foo) {
577                let $0foo2 = foo;
578                let bar2 = foo2.bar;
579            }
580            "#,
581            r#"
582            fn main(foo: dep::Foo) {
583                let dep::Foo { bar, .. } = foo;
584                let bar2 = bar;
585            }
586            "#,
587        )
588    }
589
590    #[test]
591    fn tuple_private_fields_not_applicable() {
592        check_assist_not_applicable(
593            destructure_struct_binding,
594            r#"
595            //- /lib.rs crate:dep
596            pub struct Foo(pub i32, i32);
597
598            //- /main.rs crate:main deps:dep
599            fn main(foo: dep::Foo) {
600                let $0foo2 = foo;
601                let bar2 = foo2.0;
602            }
603            "#,
604        )
605    }
606
607    #[test]
608    fn nested_inside_record() {
609        check_assist(
610            destructure_struct_binding,
611            r#"
612            struct Foo { fizz: Fizz }
613            struct Fizz { buzz: i32 }
614
615            fn main() {
616                let Foo { $0fizz } = Foo { fizz: Fizz { buzz: 1 } };
617                let buzz2 = fizz.buzz;
618            }
619            "#,
620            r#"
621            struct Foo { fizz: Fizz }
622            struct Fizz { buzz: i32 }
623
624            fn main() {
625                let Foo { fizz: Fizz { buzz } } = Foo { fizz: Fizz { buzz: 1 } };
626                let buzz2 = buzz;
627            }
628            "#,
629        )
630    }
631
632    #[test]
633    fn nested_inside_tuple() {
634        check_assist(
635            destructure_struct_binding,
636            r#"
637            struct Foo(Fizz);
638            struct Fizz { buzz: i32 }
639
640            fn main() {
641                let Foo($0fizz) = Foo(Fizz { buzz: 1 });
642                let buzz2 = fizz.buzz;
643            }
644            "#,
645            r#"
646            struct Foo(Fizz);
647            struct Fizz { buzz: i32 }
648
649            fn main() {
650                let Foo(Fizz { buzz }) = Foo(Fizz { buzz: 1 });
651                let buzz2 = buzz;
652            }
653            "#,
654        )
655    }
656
657    #[test]
658    fn mut_record() {
659        check_assist(
660            destructure_struct_binding,
661            r#"
662            struct Foo { bar: i32, baz: i32 }
663
664            fn main() {
665                let mut $0foo = Foo { bar: 1, baz: 2 };
666                let bar2 = foo.bar;
667                let baz2 = &foo.baz;
668            }
669            "#,
670            r#"
671            struct Foo { bar: i32, baz: i32 }
672
673            fn main() {
674                let Foo { mut bar, mut baz } = Foo { bar: 1, baz: 2 };
675                let bar2 = bar;
676                let baz2 = &baz;
677            }
678            "#,
679        )
680    }
681
682    #[test]
683    fn mut_record_field() {
684        check_assist(
685            destructure_struct_binding,
686            r#"
687            struct Foo { x: () }
688            struct Bar { foo: Foo }
689            fn f(Bar { mut $0foo }: Bar) {}
690            "#,
691            r#"
692            struct Foo { x: () }
693            struct Bar { foo: Foo }
694            fn f(Bar { foo: Foo { mut x } }: Bar) {}
695            "#,
696        )
697    }
698
699    #[test]
700    fn ref_record_field() {
701        check_assist(
702            destructure_struct_binding,
703            r#"
704            struct Foo { x: () }
705            struct Bar { foo: Foo }
706            fn f(Bar { ref $0foo }: Bar) {
707                let _ = foo.x;
708            }
709            "#,
710            r#"
711            struct Foo { x: () }
712            struct Bar { foo: Foo }
713            fn f(Bar { foo: Foo { ref x } }: Bar) {
714                let _ = *x;
715            }
716            "#,
717        )
718    }
719
720    #[test]
721    fn ref_mut_record_field() {
722        check_assist(
723            destructure_struct_binding,
724            r#"
725            struct Foo { x: () }
726            struct Bar { foo: Foo }
727            fn f(Bar { ref mut $0foo }: Bar) {
728                let _ = foo.x;
729            }
730            "#,
731            r#"
732            struct Foo { x: () }
733            struct Bar { foo: Foo }
734            fn f(Bar { foo: Foo { ref mut x } }: Bar) {
735                let _ = *x;
736            }
737            "#,
738        )
739    }
740
741    #[test]
742    fn ref_mut_record_renamed_field() {
743        check_assist(
744            destructure_struct_binding,
745            r#"
746            struct Foo { x: () }
747            struct Bar { foo: Foo }
748            fn f(Bar { foo: ref mut $0foo1 }: Bar) {
749                let _ = foo1.x;
750            }
751            "#,
752            r#"
753            struct Foo { x: () }
754            struct Bar { foo: Foo }
755            fn f(Bar { foo: Foo { ref mut x } }: Bar) {
756                let _ = *x;
757            }
758            "#,
759        )
760    }
761
762    #[test]
763    fn mut_ref() {
764        check_assist(
765            destructure_struct_binding,
766            r#"
767            struct Foo { bar: i32, baz: i32 }
768
769            fn main() {
770                let $0foo = &mut Foo { bar: 1, baz: 2 };
771                foo.bar = 5;
772            }
773            "#,
774            r#"
775            struct Foo { bar: i32, baz: i32 }
776
777            fn main() {
778                let Foo { bar, baz } = &mut Foo { bar: 1, baz: 2 };
779                *bar = 5;
780            }
781            "#,
782        )
783    }
784
785    #[test]
786    fn mut_self_param() {
787        check_assist(
788            destructure_struct_binding,
789            r#"
790            struct Foo { bar: i32, baz: i32 }
791
792            impl Foo {
793                fn foo(mut $0self) {
794                    self.bar = 5;
795                }
796            }
797            "#,
798            r#"
799            struct Foo { bar: i32, baz: i32 }
800
801            impl Foo {
802                fn foo(mut self) {
803                    let Foo { mut bar, mut baz } = self;
804                    bar = 5;
805                }
806            }
807            "#,
808        )
809    }
810
811    #[test]
812    fn ref_mut_self_param() {
813        check_assist(
814            destructure_struct_binding,
815            r#"
816            struct Foo { bar: i32, baz: i32 }
817
818            impl Foo {
819                fn foo(&mut $0self) {
820                    self.bar = 5;
821                }
822            }
823            "#,
824            r#"
825            struct Foo { bar: i32, baz: i32 }
826
827            impl Foo {
828                fn foo(&mut self) {
829                    let Foo { bar, baz } = self;
830                    *bar = 5;
831                }
832            }
833            "#,
834        )
835    }
836
837    #[test]
838    fn ref_self_param() {
839        check_assist(
840            destructure_struct_binding,
841            r#"
842            struct Foo { bar: i32, baz: i32 }
843
844            impl Foo {
845                fn foo(&$0self) -> &i32 {
846                    &self.bar
847                }
848            }
849            "#,
850            r#"
851            struct Foo { bar: i32, baz: i32 }
852
853            impl Foo {
854                fn foo(&self) -> &i32 {
855                    let Foo { bar, baz } = self;
856                    bar
857                }
858            }
859            "#,
860        )
861    }
862
863    #[test]
864    fn ref_not_add_parenthesis_and_deref_record() {
865        check_assist(
866            destructure_struct_binding,
867            r#"
868            struct Foo { bar: i32, baz: i32 }
869
870            fn main() {
871                let $0foo = &Foo { bar: 1, baz: 2 };
872                let _ = &foo.bar;
873            }
874            "#,
875            r#"
876            struct Foo { bar: i32, baz: i32 }
877
878            fn main() {
879                let Foo { bar, baz } = &Foo { bar: 1, baz: 2 };
880                let _ = bar;
881            }
882            "#,
883        )
884    }
885
886    #[test]
887    fn ref_not_add_parenthesis_and_deref_tuple() {
888        check_assist(
889            destructure_struct_binding,
890            r#"
891            struct Foo(i32, i32);
892
893            fn main() {
894                let $0foo = &Foo(1, 2);
895                let _ = &foo.0;
896            }
897            "#,
898            r#"
899            struct Foo(i32, i32);
900
901            fn main() {
902                let Foo(_0, _1) = &Foo(1, 2);
903                let _ = _0;
904            }
905            "#,
906        )
907    }
908
909    #[test]
910    fn record_struct_name_collision() {
911        check_assist(
912            destructure_struct_binding,
913            r#"
914            struct Foo { bar: i32, baz: i32 }
915
916            fn main(baz: i32) {
917                let bar = true;
918                let $0foo = Foo { bar: 1, baz: 2 };
919                let baz_1 = 7;
920                let bar_usage = foo.bar;
921                let baz_usage = foo.baz;
922            }
923            "#,
924            r#"
925            struct Foo { bar: i32, baz: i32 }
926
927            fn main(baz: i32) {
928                let bar = true;
929                let Foo { bar: bar_1, baz: baz_2 } = Foo { bar: 1, baz: 2 };
930                let baz_1 = 7;
931                let bar_usage = bar_1;
932                let baz_usage = baz_2;
933            }
934            "#,
935        )
936    }
937
938    #[test]
939    fn tuple_struct_name_collision() {
940        check_assist(
941            destructure_struct_binding,
942            r#"
943            struct Foo(i32, i32);
944
945            fn main() {
946                let _0 = true;
947                let $0foo = Foo(1, 2);
948                let bar = foo.0;
949                let baz = foo.1;
950            }
951            "#,
952            r#"
953            struct Foo(i32, i32);
954
955            fn main() {
956                let _0 = true;
957                let Foo(_0_1, _1) = Foo(1, 2);
958                let bar = _0_1;
959                let baz = _1;
960            }
961            "#,
962        )
963    }
964
965    #[test]
966    fn record_struct_name_collision_nested_scope() {
967        check_assist(
968            destructure_struct_binding,
969            r#"
970            struct Foo { bar: i32 }
971
972            fn main(foo: Foo) {
973                let bar = 5;
974
975                let new_bar = {
976                    let $0foo2 = foo;
977                    let bar_1 = 5;
978                    foo2.bar
979                };
980            }
981            "#,
982            r#"
983            struct Foo { bar: i32 }
984
985            fn main(foo: Foo) {
986                let bar = 5;
987
988                let new_bar = {
989                    let Foo { bar: bar_2 } = foo;
990                    let bar_1 = 5;
991                    bar_2
992                };
993            }
994            "#,
995        )
996    }
997
998    #[test]
999    fn record_struct_no_public_members() {
1000        check_assist_not_applicable(
1001            destructure_struct_binding,
1002            r#"
1003            //- /lib.rs crate:dep
1004            pub struct Foo { bar: i32, baz: i32 };
1005
1006            //- /main.rs crate:main deps:dep
1007            fn main($0foo: dep::Foo) {}
1008            "#,
1009        )
1010    }
1011
1012    #[test]
1013    fn record_struct_usage_in_macro_call() {
1014        // exact repro from #20716: struct field access inside write! must not panic
1015        check_assist(
1016            destructure_struct_binding,
1017            r#"
1018//- minicore: write, fmt
1019use core::fmt::Write;
1020struct Foo { y: i8 }
1021
1022fn main() {
1023    let mut s = String::new();
1024    let $0x = Foo { y: 8 };
1025    write!(s, "{}", x.y).unwrap();
1026}
1027"#,
1028            r#"
1029use core::fmt::Write;
1030struct Foo { y: i8 }
1031
1032fn main() {
1033    let mut s = String::new();
1034    let Foo { y } = Foo { y: 8 };
1035    write!(s, "{}", y).unwrap();
1036}
1037"#,
1038        )
1039    }
1040}