Skip to content

Commit 50f8ef6

Browse files
authored
Merge branch 'rust-lang:master' into migrate_replace_derive_with_manual_impl
2 parents 2a3d30d + 48ccbe0 commit 50f8ef6

File tree

4 files changed

+355
-6
lines changed

4 files changed

+355
-6
lines changed

crates/ide-assists/src/handlers/generate_impl.rs

Lines changed: 322 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use syntax::{
2-
ast::{self, AstNode, HasName, edit_in_place::Indent, make},
2+
ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make},
33
syntax_editor::{Position, SyntaxEditor},
44
};
55

6-
use crate::{AssistContext, AssistId, Assists, utils};
6+
use crate::{
7+
AssistContext, AssistId, Assists,
8+
utils::{self, DefaultMethods, IgnoreAssocItems},
9+
};
710

8-
fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
11+
fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) {
912
let indent = nominal.indent_level();
13+
14+
impl_.indent(indent);
1015
editor.insert_all(
1116
Position::after(nominal.syntax()),
1217
vec![
@@ -120,6 +125,115 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
120125
)
121126
}
122127

128+
// Assist: generate_impl_trait
129+
//
130+
// Adds this trait impl for a type.
131+
//
132+
// ```
133+
// trait $0Foo {
134+
// fn foo(&self) -> i32;
135+
// }
136+
// ```
137+
// ->
138+
// ```
139+
// trait Foo {
140+
// fn foo(&self) -> i32;
141+
// }
142+
//
143+
// impl Foo for ${1:_} {
144+
// fn foo(&self) -> i32 {
145+
// $0todo!()
146+
// }
147+
// }
148+
// ```
149+
pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
150+
let name = ctx.find_node_at_offset::<ast::Name>()?;
151+
let trait_ = ast::Trait::cast(name.syntax().parent()?)?;
152+
let target_scope = ctx.sema.scope(trait_.syntax())?;
153+
let hir_trait = ctx.sema.to_def(&trait_)?;
154+
155+
let target = trait_.syntax().text_range();
156+
acc.add(
157+
AssistId::generate("generate_impl_trait"),
158+
format!("Generate `{name}` impl for type"),
159+
target,
160+
|edit| {
161+
let mut editor = edit.make_editor(trait_.syntax());
162+
163+
let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder()));
164+
let missing_items = utils::filter_assoc_items(
165+
&ctx.sema,
166+
&hir_trait.items(ctx.db()),
167+
DefaultMethods::No,
168+
IgnoreAssocItems::DocHiddenAttrPresent,
169+
);
170+
let impl_ = make::impl_trait(
171+
trait_.unsafe_token().is_some(),
172+
None,
173+
trait_.generic_param_list().map(|list| {
174+
make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
175+
}),
176+
None,
177+
None,
178+
false,
179+
make::ty(&name.text()),
180+
make::ty_placeholder(),
181+
None,
182+
None,
183+
None,
184+
)
185+
.clone_for_update();
186+
187+
if !missing_items.is_empty() {
188+
utils::add_trait_assoc_items_to_impl(
189+
&ctx.sema,
190+
ctx.config,
191+
&missing_items,
192+
hir_trait,
193+
&impl_,
194+
&target_scope,
195+
);
196+
}
197+
198+
if let Some(cap) = ctx.config.snippet_cap {
199+
if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
200+
for generic in generics.generic_args() {
201+
let placeholder = edit.make_placeholder_snippet(cap);
202+
editor.add_annotation(generic.syntax(), placeholder);
203+
}
204+
}
205+
206+
if let Some(ty) = impl_.self_ty() {
207+
let placeholder = edit.make_placeholder_snippet(cap);
208+
editor.add_annotation(ty.syntax(), placeholder);
209+
}
210+
211+
if let Some(expr) =
212+
impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr))
213+
{
214+
let tabstop = edit.make_tabstop_before(cap);
215+
editor.add_annotation(expr.syntax(), tabstop);
216+
} else if let Some(l_curly) =
217+
impl_.assoc_item_list().and_then(|it| it.l_curly_token())
218+
{
219+
let tabstop = edit.make_tabstop_after(cap);
220+
editor.add_annotation(l_curly, tabstop);
221+
}
222+
}
223+
224+
insert_impl(&mut editor, &impl_, &trait_);
225+
edit.add_file_edits(ctx.vfs_file_id(), editor);
226+
},
227+
)
228+
}
229+
230+
fn extract_expr(item: ast::AssocItem) -> Option<ast::Expr> {
231+
let ast::AssocItem::Fn(f) = item else {
232+
return None;
233+
};
234+
f.body()?.tail_expr()
235+
}
236+
123237
#[cfg(test)]
124238
mod tests {
125239
use crate::tests::{check_assist, check_assist_target};
@@ -492,4 +606,209 @@ mod tests {
492606
"#,
493607
);
494608
}
609+
610+
#[test]
611+
fn test_add_impl_trait() {
612+
check_assist(
613+
generate_impl_trait,
614+
r#"
615+
trait $0Foo {
616+
fn foo(&self) -> i32;
617+
618+
fn bar(&self) -> i32 {
619+
self.foo()
620+
}
621+
}
622+
"#,
623+
r#"
624+
trait Foo {
625+
fn foo(&self) -> i32;
626+
627+
fn bar(&self) -> i32 {
628+
self.foo()
629+
}
630+
}
631+
632+
impl Foo for ${1:_} {
633+
fn foo(&self) -> i32 {
634+
$0todo!()
635+
}
636+
}
637+
"#,
638+
);
639+
}
640+
641+
#[test]
642+
fn test_add_impl_trait_use_generic() {
643+
check_assist(
644+
generate_impl_trait,
645+
r#"
646+
trait $0Foo<T> {
647+
fn foo(&self) -> T;
648+
649+
fn bar(&self) -> T {
650+
self.foo()
651+
}
652+
}
653+
"#,
654+
r#"
655+
trait Foo<T> {
656+
fn foo(&self) -> T;
657+
658+
fn bar(&self) -> T {
659+
self.foo()
660+
}
661+
}
662+
663+
impl Foo<${1:_}> for ${2:_} {
664+
fn foo(&self) -> _ {
665+
$0todo!()
666+
}
667+
}
668+
"#,
669+
);
670+
check_assist(
671+
generate_impl_trait,
672+
r#"
673+
trait $0Foo<T, U> {
674+
fn foo(&self) -> T;
675+
676+
fn bar(&self) -> T {
677+
self.foo()
678+
}
679+
}
680+
"#,
681+
r#"
682+
trait Foo<T, U> {
683+
fn foo(&self) -> T;
684+
685+
fn bar(&self) -> T {
686+
self.foo()
687+
}
688+
}
689+
690+
impl Foo<${1:_}, ${2:_}> for ${3:_} {
691+
fn foo(&self) -> _ {
692+
$0todo!()
693+
}
694+
}
695+
"#,
696+
);
697+
}
698+
699+
#[test]
700+
fn test_add_impl_trait_docs() {
701+
check_assist(
702+
generate_impl_trait,
703+
r#"
704+
/// foo
705+
trait $0Foo {
706+
/// foo method
707+
fn foo(&self) -> i32;
708+
709+
fn bar(&self) -> i32 {
710+
self.foo()
711+
}
712+
}
713+
"#,
714+
r#"
715+
/// foo
716+
trait Foo {
717+
/// foo method
718+
fn foo(&self) -> i32;
719+
720+
fn bar(&self) -> i32 {
721+
self.foo()
722+
}
723+
}
724+
725+
impl Foo for ${1:_} {
726+
fn foo(&self) -> i32 {
727+
$0todo!()
728+
}
729+
}
730+
"#,
731+
);
732+
}
733+
734+
#[test]
735+
fn test_add_impl_trait_assoc_types() {
736+
check_assist(
737+
generate_impl_trait,
738+
r#"
739+
trait $0Foo {
740+
type Output;
741+
742+
fn foo(&self) -> Self::Output;
743+
}
744+
"#,
745+
r#"
746+
trait Foo {
747+
type Output;
748+
749+
fn foo(&self) -> Self::Output;
750+
}
751+
752+
impl Foo for ${1:_} {
753+
type Output;
754+
755+
fn foo(&self) -> Self::Output {
756+
$0todo!()
757+
}
758+
}
759+
"#,
760+
);
761+
}
762+
763+
#[test]
764+
fn test_add_impl_trait_indent() {
765+
check_assist(
766+
generate_impl_trait,
767+
r#"
768+
mod foo {
769+
mod bar {
770+
trait $0Foo {
771+
type Output;
772+
773+
fn foo(&self) -> Self::Output;
774+
}
775+
}
776+
}
777+
"#,
778+
r#"
779+
mod foo {
780+
mod bar {
781+
trait Foo {
782+
type Output;
783+
784+
fn foo(&self) -> Self::Output;
785+
}
786+
787+
impl Foo for ${1:_} {
788+
type Output;
789+
790+
fn foo(&self) -> Self::Output {
791+
$0todo!()
792+
}
793+
}
794+
}
795+
}
796+
"#,
797+
);
798+
}
799+
800+
#[test]
801+
fn test_add_impl_trait_empty() {
802+
check_assist(
803+
generate_impl_trait,
804+
r#"
805+
trait $0Foo {}
806+
"#,
807+
r#"
808+
trait Foo {}
809+
810+
impl Foo for ${1:_} {$0}
811+
"#,
812+
);
813+
}
495814
}

crates/ide-assists/src/handlers/generate_trait_from_impl.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ide_db::assists::AssistId;
33
use syntax::{
44
AstNode, SyntaxKind, T,
55
ast::{
6-
self, HasGenericParams, HasName,
6+
self, HasGenericParams, HasName, HasVisibility,
77
edit_in_place::{HasVisibilityEdit, Indent},
88
make,
99
},
@@ -164,6 +164,12 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
164164
/// `E0449` Trait items always share the visibility of their trait
165165
fn remove_items_visibility(item: &ast::AssocItem) {
166166
if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
167+
if let Some(vis) = has_vis.visibility()
168+
&& let Some(token) = vis.syntax().next_sibling_or_token()
169+
&& token.kind() == SyntaxKind::WHITESPACE
170+
{
171+
ted::remove(token);
172+
}
167173
has_vis.set_visibility(None);
168174
}
169175
}
@@ -333,11 +339,11 @@ impl F$0oo {
333339
struct Foo;
334340
335341
trait NewTrait {
336-
fn a_func() -> Option<()>;
342+
fn a_func() -> Option<()>;
337343
}
338344
339345
impl NewTrait for Foo {
340-
fn a_func() -> Option<()> {
346+
fn a_func() -> Option<()> {
341347
Some(())
342348
}
343349
}"#,

crates/ide-assists/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ mod handlers {
302302
generate_function::generate_function,
303303
generate_impl::generate_impl,
304304
generate_impl::generate_trait_impl,
305+
generate_impl::generate_impl_trait,
305306
generate_is_empty_from_len::generate_is_empty_from_len,
306307
generate_mut_trait_impl::generate_mut_trait_impl,
307308
generate_new::generate_new,

0 commit comments

Comments
 (0)