Skip to content

Commit e4638de

Browse files
committed
Support record fields in generate_enum_variant
1 parent 15d8049 commit e4638de

File tree

1 file changed

+156
-9
lines changed

1 file changed

+156
-9
lines changed

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

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ use crate::assist_context::{AssistContext, Assists};
3232
// }
3333
// ```
3434
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
35-
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
36-
let path = path_expr.path()?;
35+
let path: ast::Path = ctx.find_node_at_offset()?;
3736

3837
if ctx.sema.resolve_path(&path).is_some() {
3938
// No need to generate anything if the path resolves
@@ -95,30 +94,69 @@ fn make_field_list(ctx: &AssistContext<'_>, path: &ast::Path) -> Option<ast::Fie
9594
path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast)
9695
{
9796
make_tuple_field_list(call_expr, ctx, &scope)
97+
} else if let Some(record_expr) = path.syntax().parent().and_then(ast::RecordExpr::cast) {
98+
make_record_field_list(record_expr, ctx, &scope)
9899
} else {
99100
None
100101
}
101102
}
102103

104+
fn make_record_field_list(
105+
record: ast::RecordExpr,
106+
ctx: &AssistContext<'_>,
107+
scope: &hir::SemanticsScope<'_>,
108+
) -> Option<ast::FieldList> {
109+
let fields = record.record_expr_field_list()?.fields();
110+
let record_fields = fields.map(|field| {
111+
let name = name_from_field(&field);
112+
113+
let ty = field
114+
.expr()
115+
.and_then(|it| expr_ty(ctx, it, scope))
116+
.unwrap_or_else(make::ty_placeholder);
117+
118+
make::record_field(None, name, ty)
119+
});
120+
Some(make::record_field_list(record_fields).into())
121+
}
122+
123+
fn name_from_field(field: &ast::RecordExprField) -> ast::Name {
124+
let text = match field.name_ref() {
125+
Some(it) => it.to_string(),
126+
None => name_from_field_shorthand(field).unwrap_or("unknown".to_string()),
127+
};
128+
make::name(&text)
129+
}
130+
131+
fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
132+
let path = match field.expr()? {
133+
ast::Expr::PathExpr(path_expr) => path_expr.path(),
134+
_ => None,
135+
}?;
136+
Some(path.as_single_name_ref()?.to_string())
137+
}
138+
103139
fn make_tuple_field_list(
104140
call_expr: ast::CallExpr,
105141
ctx: &AssistContext<'_>,
106142
scope: &hir::SemanticsScope<'_>,
107143
) -> Option<ast::FieldList> {
108144
let args = call_expr.arg_list()?.args();
109145
let tuple_fields = args.map(|arg| {
110-
let ty = expr_ty(ctx, arg, &scope);
146+
let ty = expr_ty(ctx, arg, &scope).unwrap_or_else(make::ty_placeholder);
111147
make::tuple_field(None, ty)
112148
});
113149
Some(make::tuple_field_list(tuple_fields).into())
114150
}
115151

116-
fn expr_ty(ctx: &AssistContext<'_>, arg: ast::Expr, scope: &hir::SemanticsScope<'_>) -> ast::Type {
117-
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted());
118-
let text = ty
119-
.and_then(|it| it.display_source_code(ctx.db(), scope.module().into()).ok())
120-
.unwrap_or_else(|| "_".to_string());
121-
make::ty(&text)
152+
fn expr_ty(
153+
ctx: &AssistContext<'_>,
154+
arg: ast::Expr,
155+
scope: &hir::SemanticsScope<'_>,
156+
) -> Option<ast::Type> {
157+
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
158+
let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
159+
Some(make::ty(&text))
122160
}
123161

124162
#[cfg(test)]
@@ -318,6 +356,115 @@ enum Foo {
318356
fn main() {
319357
Foo::Bar(true, x, Struct {})
320358
}
359+
",
360+
)
361+
}
362+
363+
#[test]
364+
fn associated_record() {
365+
check_assist(
366+
generate_enum_variant,
367+
r"
368+
enum Foo {}
369+
fn main() {
370+
Foo::$0Bar { x: true }
371+
}
372+
",
373+
r"
374+
enum Foo {
375+
Bar { x: bool },
376+
}
377+
fn main() {
378+
Foo::Bar { x: true }
379+
}
380+
",
381+
)
382+
}
383+
384+
#[test]
385+
fn associated_record_unknown_type() {
386+
check_assist(
387+
generate_enum_variant,
388+
r"
389+
enum Foo {}
390+
fn main() {
391+
Foo::$0Bar { x: y }
392+
}
393+
",
394+
r"
395+
enum Foo {
396+
Bar { x: _ },
397+
}
398+
fn main() {
399+
Foo::Bar { x: y }
400+
}
401+
",
402+
)
403+
}
404+
405+
#[test]
406+
fn associated_record_field_shorthand() {
407+
check_assist(
408+
generate_enum_variant,
409+
r"
410+
enum Foo {}
411+
fn main() {
412+
let x = true;
413+
Foo::$0Bar { x }
414+
}
415+
",
416+
r"
417+
enum Foo {
418+
Bar { x: bool },
419+
}
420+
fn main() {
421+
let x = true;
422+
Foo::Bar { x }
423+
}
424+
",
425+
)
426+
}
427+
428+
#[test]
429+
fn associated_record_field_shorthand_unknown_type() {
430+
check_assist(
431+
generate_enum_variant,
432+
r"
433+
enum Foo {}
434+
fn main() {
435+
Foo::$0Bar { x }
436+
}
437+
",
438+
r"
439+
enum Foo {
440+
Bar { x: _ },
441+
}
442+
fn main() {
443+
Foo::Bar { x }
444+
}
445+
",
446+
)
447+
}
448+
449+
#[test]
450+
fn associated_record_field_multiple_fields() {
451+
check_assist(
452+
generate_enum_variant,
453+
r"
454+
struct Struct {}
455+
enum Foo {}
456+
fn main() {
457+
Foo::$0Bar { x, y: x, s: Struct {} }
458+
}
459+
",
460+
r"
461+
struct Struct {}
462+
enum Foo {
463+
Bar { x: _, y: _, s: Struct },
464+
}
465+
fn main() {
466+
Foo::Bar { x, y: x, s: Struct {} }
467+
}
321468
",
322469
)
323470
}

0 commit comments

Comments
 (0)