]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
a6e3d49e0d1ae59b95fda24da8fa82559bf3aaa1
[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 {enum_name} {{
59     fn default() -> Self {{
60         Self::{variant_name}
61     }}
62 }}"#,
63             );
64             edit.insert(start_offset, buf);
65         },
66     )
67 }
68
69 fn existing_default_impl(
70     sema: &'_ hir::Semantics<'_, RootDatabase>,
71     variant: &ast::Variant,
72 ) -> Option<()> {
73     let variant = sema.to_def(variant)?;
74     let enum_ = variant.parent_enum(sema.db);
75     let krate = enum_.module(sema.db).krate();
76
77     let default_trait = FamousDefs(sema, krate).core_default_Default()?;
78     let enum_type = enum_.ty(sema.db);
79
80     if enum_type.impls_trait(sema.db, default_trait, &[]) {
81         Some(())
82     } else {
83         None
84     }
85 }
86
87 #[cfg(test)]
88 mod tests {
89     use crate::tests::{check_assist, check_assist_not_applicable};
90
91     use super::*;
92
93     #[test]
94     fn test_generate_default_from_variant() {
95         check_assist(
96             generate_default_from_enum_variant,
97             r#"
98 //- minicore: default
99 enum Variant {
100     Undefined,
101     Minor$0,
102     Major,
103 }
104 "#,
105             r#"
106 enum Variant {
107     Undefined,
108     Minor,
109     Major,
110 }
111
112 impl Default for Variant {
113     fn default() -> Self {
114         Self::Minor
115     }
116 }
117 "#,
118         );
119     }
120
121     #[test]
122     fn test_generate_default_already_implemented() {
123         cov_mark::check!(test_gen_default_impl_already_exists);
124         check_assist_not_applicable(
125             generate_default_from_enum_variant,
126             r#"
127 //- minicore: default
128 enum Variant {
129     Undefined,
130     Minor$0,
131     Major,
132 }
133
134 impl Default for Variant {
135     fn default() -> Self {
136         Self::Minor
137     }
138 }
139 "#,
140         );
141     }
142
143     #[test]
144     fn test_add_from_impl_no_element() {
145         cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
146         check_assist_not_applicable(
147             generate_default_from_enum_variant,
148             r#"
149 //- minicore: default
150 enum Variant {
151     Undefined,
152     Minor(u32)$0,
153     Major,
154 }
155 "#,
156         );
157     }
158
159     #[test]
160     fn test_generate_default_from_variant_with_one_variant() {
161         check_assist(
162             generate_default_from_enum_variant,
163             r#"
164 //- minicore: default
165 enum Variant { Undefi$0ned }
166 "#,
167             r#"
168 enum Variant { Undefined }
169
170 impl Default for Variant {
171     fn default() -> Self {
172         Self::Undefined
173     }
174 }
175 "#,
176         );
177     }
178 }