From e6656c19b4dff3f9f3440461860e4e8525444b84 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 21 Oct 2025 11:16:04 +0800 Subject: [PATCH] Fix invalid RestPat for convert_tuple_struct_to_named_struct ```rust struct X$0(i8, i16, i32, i64); fn foo(X(a, .., d): X) {} ``` **Before this PR**: ```rust struct X { field1: i8, field2: i16, field3: i32, field4: i64 } fn foo(X { field1: a, field2: .., field3: d }: X) {} ``` **After this PR**: ```rust struct X { field1: i8, field2: i16, field3: i32, field4: i64 } fn foo(X { field1: a, field4: d, .. }: X) {} ``` --- .../convert_tuple_struct_to_named_struct.rs | 64 ++++++++++++++++--- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 3d78895477b3..61d844928a8a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -154,14 +154,7 @@ fn edit_struct_references( ast::TupleStructPat(tuple_struct_pat) => { Some(make.record_pat_with_fields( tuple_struct_pat.path()?, - ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( - |(pat, name)| { - ast::make::record_pat_field( - ast::make::name_ref(&name.to_string()), - pat, - ) - }, - ), None), + generate_record_pat_list(&tuple_struct_pat, names), ).syntax().clone()) }, // for tuple struct creations like Foo(42) @@ -284,6 +277,24 @@ fn generate_names(fields: impl Iterator) -> Vec ast::RecordPatFieldList { + let pure_fields = pat.fields().filter(|p| !matches!(p, ast::Pat::RestPat(_))); + let rest_len = names.len().saturating_sub(pure_fields.clone().count()); + let rest_pat = pat.fields().find_map(|p| ast::RestPat::cast(p.syntax().clone())); + let rest_idx = + pat.fields().position(|p| ast::RestPat::can_cast(p.syntax().kind())).unwrap_or(names.len()); + let before_rest = pat.fields().zip(names).take(rest_idx); + let after_rest = pure_fields.zip(names.iter().skip(rest_len)).skip(rest_idx); + + let fields = before_rest + .chain(after_rest) + .map(|(pat, name)| ast::make::record_pat_field(ast::make::name_ref(&name.text()), pat)); + ast::make::record_pat_field_list(fields, rest_pat) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -358,6 +369,43 @@ impl A { ); } + #[test] + fn convert_struct_and_rest_pat() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct Inner; +struct A$0(Inner); +fn foo(A(..): A) {} +"#, + r#" +struct Inner; +struct A { field1: Inner } +fn foo(A { .. }: A) {} +"#, + ); + + check_assist( + convert_tuple_struct_to_named_struct, + r#" +struct A; +struct B; +struct C; +struct D; +struct X$0(A, B, C, D); +fn foo(X(a, .., d): X) {} +"#, + r#" +struct A; +struct B; +struct C; +struct D; +struct X { field1: A, field2: B, field3: C, field4: D } +fn foo(X { field1: a, field4: d, .. }: X) {} +"#, + ); + } + #[test] fn convert_simple_struct_cursor_on_struct_keyword() { check_assist(