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