Skip to content

Commit 3b52d31

Browse files
Merge #6043
6043: Allow missing trait members assist without needing braces r=matklad a=M-J-Hooper Assist to complete missing items when implementing a trait does not appear without impl def braces (see #5144 ). The reason behind this was that this assist is based on `ast::AssocItemList` which only appears in the AST after the braces are added to the impl def. Instead of relying on and replacing the item list, we now instead replace the entire `ast::Impl` and add the item list if its missing. Co-authored-by: Matt Hooper <[email protected]>
2 parents e70cf70 + 7d90bb1 commit 3b52d31

File tree

3 files changed

+51
-9
lines changed

3 files changed

+51
-9
lines changed

crates/assists/src/handlers/add_missing_impl_members.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,6 @@ fn add_missing_impl_members_inner(
111111
) -> Option<()> {
112112
let _p = profile::span("add_missing_impl_members_inner");
113113
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
114-
let impl_item_list = impl_def.assoc_item_list()?;
115-
116114
let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
117115

118116
let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
@@ -148,11 +146,14 @@ fn add_missing_impl_members_inner(
148146

149147
let target = impl_def.syntax().text_range();
150148
acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
149+
let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list());
150+
151151
let n_existing_items = impl_item_list.assoc_items().count();
152152
let source_scope = ctx.sema.scope_for_def(trait_);
153-
let target_scope = ctx.sema.scope(impl_item_list.syntax());
153+
let target_scope = ctx.sema.scope(impl_def.syntax());
154154
let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
155-
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
155+
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));
156+
156157
let items = missing_items
157158
.into_iter()
158159
.map(|it| ast_transform::apply(&*ast_transform, it))
@@ -162,12 +163,14 @@ fn add_missing_impl_members_inner(
162163
_ => it,
163164
})
164165
.map(|it| edit::remove_attrs_and_docs(&it));
166+
165167
let new_impl_item_list = impl_item_list.append_items(items);
166-
let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
168+
let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
169+
let first_new_item =
170+
new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
167171

168-
let original_range = impl_item_list.syntax().text_range();
169172
match ctx.config.snippet_cap {
170-
None => builder.replace(original_range, new_impl_item_list.to_string()),
173+
None => builder.replace(target, new_impl_def.to_string()),
171174
Some(cap) => {
172175
let mut cursor = Cursor::Before(first_new_item.syntax());
173176
let placeholder;
@@ -181,8 +184,8 @@ fn add_missing_impl_members_inner(
181184
}
182185
builder.replace_snippet(
183186
cap,
184-
original_range,
185-
render_snippet(cap, new_impl_item_list.syntax(), cursor),
187+
target,
188+
render_snippet(cap, new_impl_def.syntax(), cursor),
186189
)
187190
}
188191
};
@@ -310,6 +313,25 @@ impl Foo for S {
310313
);
311314
}
312315

316+
#[test]
317+
fn test_impl_def_without_braces() {
318+
check_assist(
319+
add_missing_impl_members,
320+
r#"
321+
trait Foo { fn foo(&self); }
322+
struct S;
323+
impl Foo for S<|>"#,
324+
r#"
325+
trait Foo { fn foo(&self); }
326+
struct S;
327+
impl Foo for S {
328+
fn foo(&self) {
329+
${0:todo!()}
330+
}
331+
}"#,
332+
);
333+
}
334+
313335
#[test]
314336
fn fill_in_type_params_1() {
315337
check_assist(

crates/syntax/src/ast/edit.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ where
9393
}
9494
}
9595

96+
impl ast::Impl {
97+
#[must_use]
98+
pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl {
99+
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
100+
if let Some(old_items) = self.assoc_item_list() {
101+
let to_replace: SyntaxElement = old_items.syntax().clone().into();
102+
to_insert.push(items.syntax().clone().into());
103+
self.replace_children(single_node(to_replace), to_insert)
104+
} else {
105+
to_insert.push(make::tokens::single_space().into());
106+
to_insert.push(items.syntax().clone().into());
107+
self.insert_children(InsertPosition::Last, to_insert)
108+
}
109+
}
110+
}
111+
96112
impl ast::AssocItemList {
97113
#[must_use]
98114
pub fn append_items(

crates/syntax/src/ast/make.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ pub fn ty(text: &str) -> ast::Type {
2121
ast_from_text(&format!("impl {} for D {{}};", text))
2222
}
2323

24+
pub fn assoc_item_list() -> ast::AssocItemList {
25+
ast_from_text("impl C for D {};")
26+
}
27+
2428
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
2529
ast_from_text(&format!("use {};", name_ref))
2630
}

0 commit comments

Comments
 (0)