ide_assists/handlers/
generate_default_from_enum_variant.rs1use ide_db::{RootDatabase, famous_defs::FamousDefs};
2use syntax::ast::{self, AstNode, HasName};
3
4use crate::{AssistContext, AssistId, Assists};
5
6pub(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 if !variant.syntax().text_range().contains_range(ctx.selection_trimmed()) {
43 return None;
44 }
45
46 if existing_default_impl(&ctx.sema, &variant).is_some() {
47 cov_mark::hit!(test_gen_default_impl_already_exists);
48 return None;
49 }
50
51 let target = variant.syntax().text_range();
52 acc.add(
53 AssistId::generate("generate_default_from_enum_variant"),
54 "Generate `Default` impl from this enum variant",
55 target,
56 |edit| {
57 let start_offset = variant.parent_enum().syntax().text_range().end();
58 let buf = format!(
59 r#"
60
61impl Default for {enum_name} {{
62 fn default() -> Self {{
63 Self::{variant_name}
64 }}
65}}"#,
66 );
67 edit.insert(start_offset, buf);
68 },
69 )
70}
71
72fn 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(sema.db);
79
80 let default_trait = FamousDefs(sema, krate).core_default_Default()?;
81 let enum_type = enum_.ty(sema.db);
82
83 if enum_type.impls_trait(sema.db, default_trait, &[]) { Some(()) } else { None }
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::tests::{check_assist, check_assist_not_applicable};
89
90 use super::*;
91
92 #[test]
93 fn test_generate_default_from_variant() {
94 check_assist(
95 generate_default_from_enum_variant,
96 r#"
97//- minicore: default
98enum Variant {
99 Undefined,
100 Minor$0,
101 Major,
102}
103"#,
104 r#"
105enum Variant {
106 Undefined,
107 Minor,
108 Major,
109}
110
111impl Default for Variant {
112 fn default() -> Self {
113 Self::Minor
114 }
115}
116"#,
117 );
118 }
119
120 #[test]
121 fn test_generate_default_selected_variant() {
122 check_assist(
123 generate_default_from_enum_variant,
124 r#"
125//- minicore: default
126enum Variant {
127 Undefined,
128 $0Minor$0,
129 Major,
130}
131"#,
132 r#"
133enum Variant {
134 Undefined,
135 Minor,
136 Major,
137}
138
139impl Default for Variant {
140 fn default() -> Self {
141 Self::Minor
142 }
143}
144"#,
145 );
146 }
147
148 #[test]
149 fn test_generate_default_not_applicable_with_multiple_variant_selection() {
150 check_assist_not_applicable(
151 generate_default_from_enum_variant,
152 r#"
153//- minicore: default
154enum Variant {
155 Undefined,
156 $0Minor,
157 M$0ajor,
158}
159"#,
160 );
161 }
162
163 #[test]
164 fn test_generate_default_already_implemented() {
165 cov_mark::check!(test_gen_default_impl_already_exists);
166 check_assist_not_applicable(
167 generate_default_from_enum_variant,
168 r#"
169//- minicore: default
170enum Variant {
171 Undefined,
172 Minor$0,
173 Major,
174}
175
176impl Default for Variant {
177 fn default() -> Self {
178 Self::Minor
179 }
180}
181"#,
182 );
183 }
184
185 #[test]
186 fn test_add_from_impl_no_element() {
187 cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
188 check_assist_not_applicable(
189 generate_default_from_enum_variant,
190 r#"
191//- minicore: default
192enum Variant {
193 Undefined,
194 Minor(u32)$0,
195 Major,
196}
197"#,
198 );
199 }
200
201 #[test]
202 fn test_generate_default_from_variant_with_one_variant() {
203 check_assist(
204 generate_default_from_enum_variant,
205 r#"
206//- minicore: default
207enum Variant { Undefi$0ned }
208"#,
209 r#"
210enum Variant { Undefined }
211
212impl Default for Variant {
213 fn default() -> Self {
214 Self::Undefined
215 }
216}
217"#,
218 );
219 }
220}