4 self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
8 use stdx::{format_to, SepBy};
10 use crate::{Assist, AssistCtx, AssistId};
14 // Adds a new inherent impl for a type.
17 // struct Ctx<T: Clone> {
23 // struct Ctx<T: Clone> {
27 // impl<T: Clone> Ctx<T> {
28 // fn new(data: T) -> Self { Self { data } }
32 pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
35 // We want to only apply this to non-union structs with named fields
36 let field_list = match strukt.kind() {
37 StructKind::Record(named) => named,
41 // Return early if we've found an existing new fn
42 let impl_def = find_struct_impl(&ctx, &strukt)?;
44 ctx.add_assist(AssistId("add_new"), "Add default constructor", |edit| {
45 edit.target(strukt.syntax().text_range());
47 let mut buf = String::with_capacity(512);
49 if impl_def.is_some() {
53 let vis = strukt.visibility().map(|v| format!("{} ", v));
54 let vis = vis.as_deref().unwrap_or("");
56 let params = field_list
61 f.name()?.syntax().text(),
62 f.ascribed_type()?.syntax().text()
66 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
68 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
70 let (start_offset, end_offset) = impl_def
71 .and_then(|impl_def| {
75 .descendants_with_tokens()
76 .find(|t| t.kind() == T!['{'])?
80 Some((start, TextSize::of("\n")))
83 buf = generate_impl_text(&strukt, &buf);
84 let start = strukt.syntax().text_range().end();
86 (start, TextSize::of("\n}\n"))
89 edit.set_cursor(start_offset + TextSize::of(&buf) - end_offset);
90 edit.insert(start_offset, buf);
94 // Generates the surrounding `impl Type { <code> }` including type and lifetime
96 fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
97 let type_params = strukt.type_param_list();
98 let mut buf = String::with_capacity(code.len());
99 buf.push_str("\n\nimpl");
100 if let Some(type_params) = &type_params {
101 format_to!(buf, "{}", type_params.syntax());
104 buf.push_str(strukt.name().unwrap().text().as_str());
105 if let Some(type_params) = type_params {
106 let lifetime_params = type_params
108 .filter_map(|it| it.lifetime_token())
109 .map(|it| it.text().clone());
111 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
112 format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", "))
115 format_to!(buf, " {{\n{}\n}}\n", code);
120 // Uses a syntax-driven approach to find any impl blocks for the struct that
121 // exist within the module/file
123 // Returns `None` if we've found an existing `new` fn
125 // FIXME: change the new fn checking to a more semantic approach when that's more
126 // viable (e.g. we process proc macros, etc)
127 fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> {
129 let module = strukt.syntax().ancestors().find(|node| {
130 ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
133 let struct_def = ctx.sema.to_def(strukt)?;
135 let block = module.descendants().filter_map(ast::ImplDef::cast).find_map(|impl_blk| {
136 let blk = ctx.sema.to_def(&impl_blk)?;
138 // FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
139 // (we currently use the wrong type parameter)
140 // also we wouldn't want to use e.g. `impl S<u32>`
141 let same_ty = match blk.target_ty(db).as_adt() {
142 Some(def) => def == Adt::Struct(struct_def),
145 let not_trait_impl = blk.target_trait(db).is_none();
147 if !(same_ty && not_trait_impl) {
154 if let Some(ref impl_blk) = block {
155 if has_new_fn(impl_blk) {
163 fn has_new_fn(imp: &ast::ImplDef) -> bool {
164 if let Some(il) = imp.item_list() {
165 for item in il.assoc_items() {
166 if let ast::AssocItem::FnDef(f) = item {
167 if let Some(name) = f.name() {
168 if name.text().eq_ignore_ascii_case("new") {
181 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
188 // Check output of generation
195 fn new() -> Self { Self { } }<|>
201 "struct Foo<T: Clone> {<|>}",
202 "struct Foo<T: Clone> {}
204 impl<T: Clone> Foo<T> {
205 fn new() -> Self { Self { } }<|>
211 "struct Foo<'a, T: Foo<'a>> {<|>}",
212 "struct Foo<'a, T: Foo<'a>> {}
214 impl<'a, T: Foo<'a>> Foo<'a, T> {
215 fn new() -> Self { Self { } }<|>
221 "struct Foo { baz: String <|>}",
222 "struct Foo { baz: String }
225 fn new(baz: String) -> Self { Self { baz } }<|>
231 "struct Foo { baz: String, qux: Vec<i32> <|>}",
232 "struct Foo { baz: String, qux: Vec<i32> }
235 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
240 // Check that visibility modifiers don't get brought in for fields
243 "struct Foo { pub baz: String, pub qux: Vec<i32> <|>}",
244 "struct Foo { pub baz: String, pub qux: Vec<i32> }
247 fn new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }<|>
252 // Check that it reuses existing impls
262 fn new() -> Self { Self { } }<|>
277 fn new() -> Self { Self { } }<|>
298 fn new() -> Self { Self { } }<|>
308 // Check visibility of new fn based on struct
311 "pub struct Foo {<|>}",
315 pub fn new() -> Self { Self { } }<|>
321 "pub(crate) struct Foo {<|>}",
322 "pub(crate) struct Foo {}
325 pub(crate) fn new() -> Self { Self { } }<|>
332 fn add_new_not_applicable_if_fn_exists() {
333 check_assist_not_applicable(
345 check_assist_not_applicable(
359 fn add_new_target() {
363 struct SomeThingIrrelevant;
364 /// Has a lifetime parameter
365 struct Foo<'a, T: Foo<'a>> {<|>}
366 struct EvenMoreIrrelevant;
368 "/// Has a lifetime parameter
369 struct Foo<'a, T: Foo<'a>> {}",
374 fn test_unrelated_new() {
378 pub struct AstId<N: AstNode> {
380 file_ast_id: FileAstId<N>,
383 impl<N: AstNode> AstId<N> {
384 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
385 AstId { file_id, file_ast_id }
389 pub struct Source<T> {
390 pub file_id: HirFileId,<|>
395 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
396 Source { file_id: self.file_id, ast: f(self.ast) }
401 pub struct AstId<N: AstNode> {
403 file_ast_id: FileAstId<N>,
406 impl<N: AstNode> AstId<N> {
407 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
408 AstId { file_id, file_ast_id }
412 pub struct Source<T> {
413 pub file_id: HirFileId,
418 pub fn new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }<|>
420 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
421 Source { file_id: self.file_id, ast: f(self.ast) }