@@ -52,6 +52,9 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
52
52
acc : & mut Assists ,
53
53
ctx : & AssistContext < ' _ > ,
54
54
) -> Option < ( ) > {
55
+ // XXX: We don't currently provide this assist for struct definitions inside macros, but if we
56
+ // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files
57
+ // too.
55
58
let strukt = ctx. find_node_at_offset :: < Either < ast:: Struct , ast:: Variant > > ( ) ?;
56
59
let field_list = strukt. as_ref ( ) . either ( |s| s. field_list ( ) , |v| v. field_list ( ) ) ?;
57
60
let record_fields = match field_list {
@@ -62,12 +65,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
62
65
Either :: Left ( s) => Either :: Left ( ctx. sema . to_def ( s) ?) ,
63
66
Either :: Right ( v) => Either :: Right ( ctx. sema . to_def ( v) ?) ,
64
67
} ;
65
- let target = strukt. as_ref ( ) . either ( |s| s. syntax ( ) , |v| v. syntax ( ) ) . text_range ( ) ;
66
68
67
69
acc. add (
68
70
AssistId ( "convert_named_struct_to_tuple_struct" , AssistKind :: RefactorRewrite ) ,
69
71
"Convert to tuple struct" ,
70
- target ,
72
+ strukt . syntax ( ) . text_range ( ) ,
71
73
|edit| {
72
74
edit_field_references ( ctx, edit, record_fields. fields ( ) ) ;
73
75
edit_struct_references ( ctx, edit, strukt_def) ;
@@ -82,6 +84,8 @@ fn edit_struct_def(
82
84
strukt : & Either < ast:: Struct , ast:: Variant > ,
83
85
record_fields : ast:: RecordFieldList ,
84
86
) {
87
+ // Note that we don't need to consider macro files in this function because this this is
88
+ // currently not triggered for struct definitions inside macro calls.
85
89
let tuple_fields = record_fields
86
90
. fields ( )
87
91
. filter_map ( |f| Some ( ast:: make:: tuple_field ( f. visibility ( ) , f. ty ( ) ?) ) ) ;
@@ -141,8 +145,13 @@ fn edit_struct_references(
141
145
match_ast ! {
142
146
match node {
143
147
ast:: RecordPat ( record_struct_pat) => {
148
+ let Some ( fr) = ctx. sema. original_range_opt( record_struct_pat. syntax( ) ) else {
149
+ // We've found the node to replace, so we should return `Some` even if the
150
+ // replacement failed to stop the ancestor node traversal.
151
+ return Some ( ( ) ) ;
152
+ } ;
144
153
edit. replace(
145
- record_struct_pat . syntax ( ) . text_range ( ) ,
154
+ fr . range ,
146
155
ast:: make:: tuple_struct_pat(
147
156
record_struct_pat. path( ) ?,
148
157
record_struct_pat
@@ -154,14 +163,18 @@ fn edit_struct_references(
154
163
) ;
155
164
} ,
156
165
ast:: RecordExpr ( record_expr) => {
166
+ let Some ( fr) = ctx. sema. original_range_opt( record_expr. syntax( ) ) else {
167
+ // See the comment above.
168
+ return Some ( ( ) ) ;
169
+ } ;
157
170
let path = record_expr. path( ) ?;
158
171
let args = record_expr
159
172
. record_expr_field_list( ) ?
160
173
. fields( )
161
174
. filter_map( |f| f. expr( ) )
162
175
. join( ", " ) ;
163
176
164
- edit. replace( record_expr . syntax ( ) . text_range ( ) , format!( "{path}({args})" ) ) ;
177
+ edit. replace( fr . range , format!( "{path}({args})" ) ) ;
165
178
} ,
166
179
_ => return None ,
167
180
}
@@ -199,7 +212,7 @@ fn edit_field_references(
199
212
if let Some ( name_ref) = r. name . as_name_ref ( ) {
200
213
// Only edit the field reference if it's part of a `.field` access
201
214
if name_ref. syntax ( ) . parent ( ) . and_then ( ast:: FieldExpr :: cast) . is_some ( ) {
202
- edit. replace ( name_ref . syntax ( ) . text_range ( ) , index. to_string ( ) ) ;
215
+ edit. replace ( r . range , index. to_string ( ) ) ;
203
216
}
204
217
}
205
218
}
@@ -813,6 +826,78 @@ use crate::{A::Variant, Inner};
813
826
fn f() {
814
827
let a = Variant(Inner);
815
828
}
829
+ "# ,
830
+ ) ;
831
+ }
832
+
833
+ #[ test]
834
+ fn field_access_inside_macro_call ( ) {
835
+ check_assist (
836
+ convert_named_struct_to_tuple_struct,
837
+ r#"
838
+ struct $0Struct {
839
+ inner: i32,
840
+ }
841
+
842
+ macro_rules! id {
843
+ ($e:expr) => { $e }
844
+ }
845
+
846
+ fn test(c: Struct) {
847
+ id!(c.inner);
848
+ }
849
+ "# ,
850
+ r#"
851
+ struct Struct(i32);
852
+
853
+ macro_rules! id {
854
+ ($e:expr) => { $e }
855
+ }
856
+
857
+ fn test(c: Struct) {
858
+ id!(c.0);
859
+ }
860
+ "# ,
861
+ )
862
+ }
863
+
864
+ #[ test]
865
+ fn struct_usage_inside_macro_call ( ) {
866
+ check_assist (
867
+ convert_named_struct_to_tuple_struct,
868
+ r#"
869
+ macro_rules! id {
870
+ ($($t:tt)*) => { $($t)* }
871
+ }
872
+
873
+ struct $0Struct {
874
+ inner: i32,
875
+ }
876
+
877
+ fn test() {
878
+ id! {
879
+ let s = Struct {
880
+ inner: 42,
881
+ };
882
+ let Struct { inner: value } = s;
883
+ let Struct { inner } = s;
884
+ }
885
+ }
886
+ "# ,
887
+ r#"
888
+ macro_rules! id {
889
+ ($($t:tt)*) => { $($t)* }
890
+ }
891
+
892
+ struct Struct(i32);
893
+
894
+ fn test() {
895
+ id! {
896
+ let s = Struct(42);
897
+ let Struct(value) = s;
898
+ let Struct(inner) = s;
899
+ }
900
+ }
816
901
"# ,
817
902
) ;
818
903
}
0 commit comments