Skip to content

Commit c1c36ac

Browse files
bors[bot]bnjjj
andauthored
Merge #6861
6861: generate default implementation for an enum from an enum variant #6860 r=matklad a=bnjjj close #6860 Co-authored-by: Benjamin Coenen <[email protected]>
2 parents ece626f + 2c82a1a commit c1c36ac

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
use ide_db::helpers::FamousDefs;
2+
use ide_db::RootDatabase;
3+
use syntax::ast::{self, AstNode, NameOwner};
4+
use test_utils::mark;
5+
6+
use crate::{AssistContext, AssistId, AssistKind, Assists};
7+
8+
// Assist: generate_default_from_enum_variant
9+
//
10+
// Adds a Default impl for an enum using a variant.
11+
//
12+
// ```
13+
// enum Version {
14+
// Undefined,
15+
// Minor<|>,
16+
// Major,
17+
// }
18+
// ```
19+
// ->
20+
// ```
21+
// enum Version {
22+
// Undefined,
23+
// Minor,
24+
// Major,
25+
// }
26+
//
27+
// impl Default for Version {
28+
// fn default() -> Self {
29+
// Self::Minor
30+
// }
31+
// }
32+
// ```
33+
pub(crate) fn generate_default_from_enum_variant(
34+
acc: &mut Assists,
35+
ctx: &AssistContext,
36+
) -> Option<()> {
37+
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
38+
let variant_name = variant.name()?;
39+
let enum_name = variant.parent_enum().name()?;
40+
if !matches!(variant.kind(), ast::StructKind::Unit) {
41+
mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42+
return None;
43+
}
44+
45+
if existing_default_impl(&ctx.sema, &variant).is_some() {
46+
mark::hit!(test_gen_default_impl_already_exists);
47+
return None;
48+
}
49+
50+
let target = variant.syntax().text_range();
51+
acc.add(
52+
AssistId("generate_default_from_enum_variant", AssistKind::Generate),
53+
"Generate `Default` impl from this enum variant",
54+
target,
55+
|edit| {
56+
let start_offset = variant.parent_enum().syntax().text_range().end();
57+
let buf = format!(
58+
r#"
59+
60+
impl Default for {0} {{
61+
fn default() -> Self {{
62+
Self::{1}
63+
}}
64+
}}"#,
65+
enum_name, variant_name
66+
);
67+
edit.insert(start_offset, buf);
68+
},
69+
)
70+
}
71+
72+
fn existing_default_impl(
73+
sema: &'_ hir::Semantics<'_, RootDatabase>,
74+
variant: &ast::Variant,
75+
) -> Option<()> {
76+
let variant = sema.to_def(variant)?;
77+
let enum_ = variant.parent_enum(sema.db);
78+
let krate = enum_.module(sema.db).krate();
79+
80+
let default_trait = FamousDefs(sema, Some(krate)).core_default_Default()?;
81+
let enum_type = enum_.ty(sema.db);
82+
83+
if enum_type.impls_trait(sema.db, default_trait, &[]) {
84+
Some(())
85+
} else {
86+
None
87+
}
88+
}
89+
90+
#[cfg(test)]
91+
mod tests {
92+
use test_utils::mark;
93+
94+
use crate::tests::{check_assist, check_assist_not_applicable};
95+
96+
use super::*;
97+
98+
fn check_not_applicable(ra_fixture: &str) {
99+
let fixture =
100+
format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
101+
check_assist_not_applicable(generate_default_from_enum_variant, &fixture)
102+
}
103+
104+
#[test]
105+
fn test_generate_default_from_variant() {
106+
check_assist(
107+
generate_default_from_enum_variant,
108+
r#"
109+
enum Variant {
110+
Undefined,
111+
Minor<|>,
112+
Major,
113+
}"#,
114+
r#"enum Variant {
115+
Undefined,
116+
Minor,
117+
Major,
118+
}
119+
120+
impl Default for Variant {
121+
fn default() -> Self {
122+
Self::Minor
123+
}
124+
}"#,
125+
);
126+
}
127+
128+
#[test]
129+
fn test_generate_default_already_implemented() {
130+
mark::check!(test_gen_default_impl_already_exists);
131+
check_not_applicable(
132+
r#"
133+
enum Variant {
134+
Undefined,
135+
Minor<|>,
136+
Major,
137+
}
138+
139+
impl Default for Variant {
140+
fn default() -> Self {
141+
Self::Minor
142+
}
143+
}"#,
144+
);
145+
}
146+
147+
#[test]
148+
fn test_add_from_impl_no_element() {
149+
mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
150+
check_not_applicable(
151+
r#"
152+
enum Variant {
153+
Undefined,
154+
Minor(u32)<|>,
155+
Major,
156+
}"#,
157+
);
158+
}
159+
160+
#[test]
161+
fn test_generate_default_from_variant_with_one_variant() {
162+
check_assist(
163+
generate_default_from_enum_variant,
164+
r#"enum Variant { Undefi<|>ned }"#,
165+
r#"
166+
enum Variant { Undefined }
167+
168+
impl Default for Variant {
169+
fn default() -> Self {
170+
Self::Undefined
171+
}
172+
}"#,
173+
);
174+
}
175+
}

crates/assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mod handlers {
137137
mod flip_comma;
138138
mod flip_trait_bound;
139139
mod generate_derive;
140+
mod generate_default_from_enum_variant;
140141
mod generate_from_impl_for_enum;
141142
mod generate_function;
142143
mod generate_impl;
@@ -186,6 +187,7 @@ mod handlers {
186187
flip_comma::flip_comma,
187188
flip_trait_bound::flip_trait_bound,
188189
generate_derive::generate_derive,
190+
generate_default_from_enum_variant::generate_default_from_enum_variant,
189191
generate_from_impl_for_enum::generate_from_impl_for_enum,
190192
generate_function::generate_function,
191193
generate_impl::generate_impl,

crates/assists/src/tests/generated.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,33 @@ fn foo<T: Copy + Clone>() { }
364364
)
365365
}
366366

367+
#[test]
368+
fn doctest_generate_default_from_enum_variant() {
369+
check_doc_test(
370+
"generate_default_from_enum_variant",
371+
r#####"
372+
enum Version {
373+
Undefined,
374+
Minor<|>,
375+
Major,
376+
}
377+
"#####,
378+
r#####"
379+
enum Version {
380+
Undefined,
381+
Minor,
382+
Major,
383+
}
384+
385+
impl Default for Version {
386+
fn default() -> Self {
387+
Self::Minor
388+
}
389+
}
390+
"#####,
391+
)
392+
}
393+
367394
#[test]
368395
fn doctest_generate_derive() {
369396
check_doc_test(

0 commit comments

Comments
 (0)