Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit ab93475

Browse files
committed
Consider macro files when replacing nodes
1 parent 505fd09 commit ab93475

File tree

1 file changed

+90
-5
lines changed

1 file changed

+90
-5
lines changed

crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
5252
acc: &mut Assists,
5353
ctx: &AssistContext<'_>,
5454
) -> 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.
5558
let strukt = ctx.find_node_at_offset::<Either<ast::Struct, ast::Variant>>()?;
5659
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
5760
let record_fields = match field_list {
@@ -62,12 +65,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
6265
Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
6366
Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
6467
};
65-
let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
6668

6769
acc.add(
6870
AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
6971
"Convert to tuple struct",
70-
target,
72+
strukt.syntax().text_range(),
7173
|edit| {
7274
edit_field_references(ctx, edit, record_fields.fields());
7375
edit_struct_references(ctx, edit, strukt_def);
@@ -82,6 +84,8 @@ fn edit_struct_def(
8284
strukt: &Either<ast::Struct, ast::Variant>,
8385
record_fields: ast::RecordFieldList,
8486
) {
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.
8589
let tuple_fields = record_fields
8690
.fields()
8791
.filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
@@ -141,8 +145,13 @@ fn edit_struct_references(
141145
match_ast! {
142146
match node {
143147
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+
};
144153
edit.replace(
145-
record_struct_pat.syntax().text_range(),
154+
fr.range,
146155
ast::make::tuple_struct_pat(
147156
record_struct_pat.path()?,
148157
record_struct_pat
@@ -154,14 +163,18 @@ fn edit_struct_references(
154163
);
155164
},
156165
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+
};
157170
let path = record_expr.path()?;
158171
let args = record_expr
159172
.record_expr_field_list()?
160173
.fields()
161174
.filter_map(|f| f.expr())
162175
.join(", ");
163176

164-
edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
177+
edit.replace(fr.range, format!("{path}({args})"));
165178
},
166179
_ => return None,
167180
}
@@ -199,7 +212,7 @@ fn edit_field_references(
199212
if let Some(name_ref) = r.name.as_name_ref() {
200213
// Only edit the field reference if it's part of a `.field` access
201214
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());
203216
}
204217
}
205218
}
@@ -813,6 +826,78 @@ use crate::{A::Variant, Inner};
813826
fn f() {
814827
let a = Variant(Inner);
815828
}
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+
}
816901
"#,
817902
);
818903
}

0 commit comments

Comments
 (0)