Skip to content

Commit

Permalink
Merge #6043
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
bors[bot] and M-J-Hooper authored Sep 21, 2020
2 parents e70cf70 + 7d90bb1 commit 3b52d31
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 9 deletions.
40 changes: 31 additions & 9 deletions crates/assists/src/handlers/add_missing_impl_members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ fn add_missing_impl_members_inner(
) -> Option<()> {
let _p = profile::span("add_missing_impl_members_inner");
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
let impl_item_list = impl_def.assoc_item_list()?;

let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;

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

let target = impl_def.syntax().text_range();
acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list());

let n_existing_items = impl_item_list.assoc_items().count();
let source_scope = ctx.sema.scope_for_def(trait_);
let target_scope = ctx.sema.scope(impl_item_list.syntax());
let target_scope = ctx.sema.scope(impl_def.syntax());
let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
.or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone()));

let items = missing_items
.into_iter()
.map(|it| ast_transform::apply(&*ast_transform, it))
Expand All @@ -162,12 +163,14 @@ fn add_missing_impl_members_inner(
_ => it,
})
.map(|it| edit::remove_attrs_and_docs(&it));

let new_impl_item_list = impl_item_list.append_items(items);
let first_new_item = new_impl_item_list.assoc_items().nth(n_existing_items).unwrap();
let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
let first_new_item =
new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();

let original_range = impl_item_list.syntax().text_range();
match ctx.config.snippet_cap {
None => builder.replace(original_range, new_impl_item_list.to_string()),
None => builder.replace(target, new_impl_def.to_string()),
Some(cap) => {
let mut cursor = Cursor::Before(first_new_item.syntax());
let placeholder;
Expand All @@ -181,8 +184,8 @@ fn add_missing_impl_members_inner(
}
builder.replace_snippet(
cap,
original_range,
render_snippet(cap, new_impl_item_list.syntax(), cursor),
target,
render_snippet(cap, new_impl_def.syntax(), cursor),
)
}
};
Expand Down Expand Up @@ -310,6 +313,25 @@ impl Foo for S {
);
}

#[test]
fn test_impl_def_without_braces() {
check_assist(
add_missing_impl_members,
r#"
trait Foo { fn foo(&self); }
struct S;
impl Foo for S<|>"#,
r#"
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {
fn foo(&self) {
${0:todo!()}
}
}"#,
);
}

#[test]
fn fill_in_type_params_1() {
check_assist(
Expand Down
16 changes: 16 additions & 0 deletions crates/syntax/src/ast/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ where
}
}

impl ast::Impl {
#[must_use]
pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl {
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
if let Some(old_items) = self.assoc_item_list() {
let to_replace: SyntaxElement = old_items.syntax().clone().into();
to_insert.push(items.syntax().clone().into());
self.replace_children(single_node(to_replace), to_insert)
} else {
to_insert.push(make::tokens::single_space().into());
to_insert.push(items.syntax().clone().into());
self.insert_children(InsertPosition::Last, to_insert)
}
}
}

impl ast::AssocItemList {
#[must_use]
pub fn append_items(
Expand Down
4 changes: 4 additions & 0 deletions crates/syntax/src/ast/make.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ pub fn ty(text: &str) -> ast::Type {
ast_from_text(&format!("impl {} for D {{}};", text))
}

pub fn assoc_item_list() -> ast::AssocItemList {
ast_from_text("impl C for D {};")
}

pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
ast_from_text(&format!("use {};", name_ref))
}
Expand Down

0 comments on commit 3b52d31

Please sign in to comment.