diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs index 37055979276..8d4ff84084b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs @@ -2,8 +2,8 @@ use either::Either; use ide_db::{defs::Definition, search::FileReference}; use itertools::Itertools; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasVisibility}, - match_ast, SyntaxKind, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility}, + match_ast, ted, SyntaxKind, }; use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; @@ -87,9 +87,14 @@ fn edit_struct_def( ) { // Note that we don't need to consider macro files in this function because this is // currently not triggered for struct definitions inside macro calls. - let tuple_fields = record_fields - .fields() - .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?))); + let tuple_fields = record_fields.fields().filter_map(|f| { + let field = ast::make::tuple_field(f.visibility(), f.ty()?).clone_for_update(); + ted::insert_all( + ted::Position::first_child_of(field.syntax()), + f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(), + ); + Some(field) + }); let tuple_fields = ast::make::tuple_field_list(tuple_fields); let record_fields_text_range = record_fields.syntax().text_range(); @@ -975,6 +980,22 @@ impl HasAssoc for Struct { let Self::Assoc { value } = a; } } +"#, + ); + } + + #[test] + fn fields_with_attrs() { + check_assist( + convert_named_struct_to_tuple_struct, + r#" +pub struct $0Foo { + #[my_custom_attr] + value: u32, +} +"#, + r#" +pub struct Foo(#[my_custom_attr] u32); "#, ); } 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 c72bd411d64..f01b4ea0fd4 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 @@ -1,8 +1,8 @@ use either::Either; use ide_db::defs::{Definition, NameRefClass}; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasVisibility}, - match_ast, SyntaxKind, SyntaxNode, + ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility}, + match_ast, ted, SyntaxKind, SyntaxNode, }; use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; @@ -83,10 +83,14 @@ fn edit_struct_def( tuple_fields: ast::TupleFieldList, names: Vec, ) { - let record_fields = tuple_fields - .fields() - .zip(names) - .filter_map(|(f, name)| Some(ast::make::record_field(f.visibility(), name, f.ty()?))); + let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| { + let field = ast::make::record_field(f.visibility(), name, f.ty()?).clone_for_update(); + ted::insert_all( + ted::Position::first_child_of(field.syntax()), + f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(), + ); + Some(field) + }); let record_fields = ast::make::record_field_list(record_fields); let tuple_fields_text_range = tuple_fields.syntax().text_range(); @@ -904,6 +908,19 @@ where T: Foo, { pub field1: T } +"#, + ); + } + + #[test] + fn fields_with_attrs() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +pub struct $0Foo(#[my_custom_attr] u32); +"#, + r#" +pub struct Foo { #[my_custom_attr] field1: u32 } "#, ); }