Skip to content

Commit 5480bed

Browse files
Merge #7800
7800: [WIP] 7708: Initial implementation of generate Default assist. r=Veykril a=chetankhilosiya The Generate Default impl from new function. Co-authored-by: Chetan Khilosiya <[email protected]>
2 parents 856c285 + d40a4fc commit 5480bed

File tree

3 files changed

+407
-0
lines changed

3 files changed

+407
-0
lines changed
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
use crate::{
2+
assist_context::{AssistContext, Assists},
3+
AssistId,
4+
};
5+
use ide_db::helpers::FamousDefs;
6+
use syntax::{
7+
ast::{self, Impl, NameOwner},
8+
AstNode,
9+
};
10+
use test_utils::mark;
11+
12+
// Assist: generate_default_from_new
13+
//
14+
// Generates default implementation from new method.
15+
//
16+
// ```
17+
// struct Example { _inner: () }
18+
//
19+
// impl Example {
20+
// pub fn n$0ew() -> Self {
21+
// Self { _inner: () }
22+
// }
23+
// }
24+
// ```
25+
// ->
26+
// ```
27+
// struct Example { _inner: () }
28+
//
29+
// impl Example {
30+
// pub fn new() -> Self {
31+
// Self { _inner: () }
32+
// }
33+
// }
34+
//
35+
// impl Default for Example {
36+
// fn default() -> Self {
37+
// Self::new()
38+
// }
39+
// }
40+
// ```
41+
pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42+
let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
43+
let fn_name = fn_node.name()?;
44+
45+
if fn_name.text() != "new" {
46+
mark::hit!(other_function_than_new);
47+
return None;
48+
}
49+
50+
if fn_node.param_list()?.params().next().is_some() {
51+
mark::hit!(new_function_with_parameters);
52+
return None;
53+
}
54+
55+
let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?;
56+
if is_default_implemented(ctx, &impl_) {
57+
mark::hit!(default_block_is_already_present);
58+
mark::hit!(struct_in_module_with_default);
59+
return None;
60+
}
61+
62+
let insert_location = impl_.syntax().text_range();
63+
64+
acc.add(
65+
AssistId("generate_default_from_new", crate::AssistKind::Generate),
66+
"Generate a Default impl from a new fn",
67+
insert_location,
68+
move |builder| {
69+
let code = default_fn_node_for_new(impl_);
70+
builder.insert(insert_location.end(), code);
71+
},
72+
)
73+
}
74+
75+
fn default_fn_node_for_new(impl_: Impl) -> String {
76+
format!(
77+
"
78+
79+
impl Default for {} {{
80+
fn default() -> Self {{
81+
Self::new()
82+
}}
83+
}}",
84+
impl_.self_ty().unwrap().syntax().text()
85+
)
86+
}
87+
88+
fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool {
89+
let db = ctx.sema.db;
90+
let impl_ = ctx.sema.to_def(impl_);
91+
let impl_def = match impl_ {
92+
Some(value) => value,
93+
None => return false,
94+
};
95+
96+
let ty = impl_def.target_ty(db);
97+
let krate = impl_def.module(db).krate();
98+
let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default();
99+
let default_trait = match default {
100+
Some(value) => value,
101+
None => return false,
102+
};
103+
104+
ty.impls_trait(db, default_trait, &[])
105+
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
use ide_db::helpers::FamousDefs;
110+
111+
use crate::tests::{check_assist, check_assist_not_applicable};
112+
113+
use super::*;
114+
115+
#[test]
116+
fn generate_default() {
117+
check_pass(
118+
r#"
119+
struct Example { _inner: () }
120+
121+
impl Example {
122+
pub fn ne$0w() -> Self {
123+
Self { _inner: () }
124+
}
125+
}
126+
127+
fn main() {}
128+
"#,
129+
r#"
130+
struct Example { _inner: () }
131+
132+
impl Example {
133+
pub fn new() -> Self {
134+
Self { _inner: () }
135+
}
136+
}
137+
138+
impl Default for Example {
139+
fn default() -> Self {
140+
Self::new()
141+
}
142+
}
143+
144+
fn main() {}
145+
"#,
146+
);
147+
}
148+
149+
#[test]
150+
fn generate_default2() {
151+
check_pass(
152+
r#"
153+
struct Test { value: u32 }
154+
155+
impl Test {
156+
pub fn ne$0w() -> Self {
157+
Self { value: 0 }
158+
}
159+
}
160+
"#,
161+
r#"
162+
struct Test { value: u32 }
163+
164+
impl Test {
165+
pub fn new() -> Self {
166+
Self { value: 0 }
167+
}
168+
}
169+
170+
impl Default for Test {
171+
fn default() -> Self {
172+
Self::new()
173+
}
174+
}
175+
"#,
176+
);
177+
}
178+
179+
#[test]
180+
fn new_function_with_parameters() {
181+
mark::check!(new_function_with_parameters);
182+
check_not_applicable(
183+
r#"
184+
struct Example { _inner: () }
185+
186+
impl Example {
187+
pub fn $0new(value: ()) -> Self {
188+
Self { _inner: value }
189+
}
190+
}
191+
"#,
192+
);
193+
}
194+
195+
#[test]
196+
fn other_function_than_new() {
197+
mark::check!(other_function_than_new);
198+
check_not_applicable(
199+
r#"
200+
struct Example { _inner: () }
201+
202+
impl Example {
203+
pub fn a$0dd() -> Self {
204+
Self { _inner: () }
205+
}
206+
}
207+
208+
"#,
209+
);
210+
}
211+
212+
#[test]
213+
fn default_block_is_already_present() {
214+
mark::check!(default_block_is_already_present);
215+
check_not_applicable(
216+
r#"
217+
struct Example { _inner: () }
218+
219+
impl Example {
220+
pub fn n$0ew() -> Self {
221+
Self { _inner: () }
222+
}
223+
}
224+
225+
impl Default for Example {
226+
fn default() -> Self {
227+
Self::new()
228+
}
229+
}
230+
"#,
231+
);
232+
}
233+
234+
#[test]
235+
fn standalone_new_function() {
236+
check_not_applicable(
237+
r#"
238+
fn n$0ew() -> u32 {
239+
0
240+
}
241+
"#,
242+
);
243+
}
244+
245+
#[test]
246+
fn multiple_struct_blocks() {
247+
check_pass(
248+
r#"
249+
struct Example { _inner: () }
250+
struct Test { value: u32 }
251+
252+
impl Example {
253+
pub fn new$0() -> Self {
254+
Self { _inner: () }
255+
}
256+
}
257+
"#,
258+
r#"
259+
struct Example { _inner: () }
260+
struct Test { value: u32 }
261+
262+
impl Example {
263+
pub fn new() -> Self {
264+
Self { _inner: () }
265+
}
266+
}
267+
268+
impl Default for Example {
269+
fn default() -> Self {
270+
Self::new()
271+
}
272+
}
273+
"#,
274+
);
275+
}
276+
277+
#[test]
278+
fn when_struct_is_after_impl() {
279+
check_pass(
280+
r#"
281+
impl Example {
282+
pub fn $0new() -> Self {
283+
Self { _inner: () }
284+
}
285+
}
286+
287+
struct Example { _inner: () }
288+
"#,
289+
r#"
290+
impl Example {
291+
pub fn new() -> Self {
292+
Self { _inner: () }
293+
}
294+
}
295+
296+
impl Default for Example {
297+
fn default() -> Self {
298+
Self::new()
299+
}
300+
}
301+
302+
struct Example { _inner: () }
303+
"#,
304+
);
305+
}
306+
307+
#[test]
308+
fn struct_in_module() {
309+
check_pass(
310+
r#"
311+
mod test {
312+
struct Example { _inner: () }
313+
314+
impl Example {
315+
pub fn n$0ew() -> Self {
316+
Self { _inner: () }
317+
}
318+
}
319+
}
320+
"#,
321+
r#"
322+
mod test {
323+
struct Example { _inner: () }
324+
325+
impl Example {
326+
pub fn new() -> Self {
327+
Self { _inner: () }
328+
}
329+
}
330+
331+
impl Default for Example {
332+
fn default() -> Self {
333+
Self::new()
334+
}
335+
}
336+
}
337+
"#,
338+
);
339+
}
340+
341+
#[test]
342+
fn struct_in_module_with_default() {
343+
mark::check!(struct_in_module_with_default);
344+
check_not_applicable(
345+
r#"
346+
mod test {
347+
struct Example { _inner: () }
348+
349+
impl Example {
350+
pub fn n$0ew() -> Self {
351+
Self { _inner: () }
352+
}
353+
}
354+
355+
impl Default for Example {
356+
fn default() -> Self {
357+
Self::new()
358+
}
359+
}
360+
}
361+
"#,
362+
);
363+
}
364+
365+
fn check_pass(before: &str, after: &str) {
366+
let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
367+
check_assist(generate_default_from_new, before, after);
368+
}
369+
370+
fn check_not_applicable(before: &str) {
371+
let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE);
372+
check_assist_not_applicable(generate_default_from_new, before);
373+
}
374+
}

crates/ide_assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ mod handlers {
127127
mod flip_comma;
128128
mod flip_trait_bound;
129129
mod generate_default_from_enum_variant;
130+
mod generate_default_from_new;
130131
mod generate_derive;
131132
mod generate_enum_is_method;
132133
mod generate_enum_projection_method;
@@ -189,6 +190,7 @@ mod handlers {
189190
flip_comma::flip_comma,
190191
flip_trait_bound::flip_trait_bound,
191192
generate_default_from_enum_variant::generate_default_from_enum_variant,
193+
generate_default_from_new::generate_default_from_new,
192194
generate_derive::generate_derive,
193195
generate_enum_is_method::generate_enum_is_method,
194196
generate_enum_projection_method::generate_enum_as_method,

0 commit comments

Comments
 (0)