1 //! Completion for associated items in a trait implementation.
3 //! This module adds the completion items related to implementing associated
4 //! items within an `impl Trait for Struct` block. The current context node
5 //! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node
6 //! and an direct child of an `IMPL`.
10 //! Considering the following trait `impl`:
17 //! impl SomeTrait for () {
22 //! may result in the completion of the following method:
25 //! # trait SomeTrait {
29 //! impl SomeTrait for () {
34 use hir::{self, HasAttrs};
36 path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node,
37 traits::get_missing_assoc_items, SymbolKind,
40 ast::{self, edit_in_place::AttrsOwnerEdit},
41 AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
43 use text_edit::TextEdit;
46 context::PathCompletionCtx, CompletionContext, CompletionItem, CompletionItemKind,
47 CompletionRelevance, Completions,
50 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
51 enum ImplCompletionKind {
58 pub(crate) fn complete_trait_impl_const(
59 acc: &mut Completions,
60 ctx: &CompletionContext<'_>,
61 name: &Option<ast::Name>,
63 complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Const)
66 pub(crate) fn complete_trait_impl_type_alias(
67 acc: &mut Completions,
68 ctx: &CompletionContext<'_>,
69 name: &Option<ast::Name>,
71 complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::TypeAlias)
74 pub(crate) fn complete_trait_impl_fn(
75 acc: &mut Completions,
76 ctx: &CompletionContext<'_>,
77 name: &Option<ast::Name>,
79 complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Fn)
82 fn complete_trait_impl_name(
83 acc: &mut Completions,
84 ctx: &CompletionContext<'_>,
85 name: &Option<ast::Name>,
86 kind: ImplCompletionKind,
88 let token = ctx.token.clone();
89 let item = match name {
90 Some(name) => name.syntax().parent(),
91 None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
98 replacement_range(ctx, &item),
99 // item -> ASSOC_ITEM_LIST -> IMPL
100 &ast::Impl::cast(item.parent()?.parent()?)?,
105 pub(crate) fn complete_trait_impl_item_by_name(
106 acc: &mut Completions,
107 ctx: &CompletionContext<'_>,
108 path_ctx: &PathCompletionCtx,
109 name_ref: &Option<ast::NameRef>,
110 impl_: &Option<ast::Impl>,
112 if !path_ctx.is_trivial_path() {
115 if let Some(impl_) = impl_ {
119 ImplCompletionKind::All,
121 Some(name) => name.syntax().text_range(),
122 None => ctx.source_range(),
129 fn complete_trait_impl(
130 acc: &mut Completions,
131 ctx: &CompletionContext<'_>,
132 kind: ImplCompletionKind,
133 replacement_range: TextRange,
134 impl_def: &ast::Impl,
136 if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
137 get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
138 use self::ImplCompletionKind::*;
140 (hir::AssocItem::Function(func), All | Fn) => {
141 add_function_impl(acc, ctx, replacement_range, func, hir_impl)
143 (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
144 add_type_alias_impl(acc, ctx, replacement_range, type_alias)
146 (hir::AssocItem::Const(const_), All | Const) => {
147 add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
155 fn add_function_impl(
156 acc: &mut Completions,
157 ctx: &CompletionContext<'_>,
158 replacement_range: TextRange,
162 let fn_name = func.name(ctx.db);
167 if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
170 let completion_kind = if func.has_self_param(ctx.db) {
171 CompletionItemKind::Method
173 CompletionItemKind::SymbolKind(SymbolKind::Function)
176 let mut item = CompletionItem::new(completion_kind, replacement_range, label);
177 item.lookup_by(format!("fn {}", fn_name))
178 .set_documentation(func.docs(ctx.db))
179 .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
181 if let Some(source) = ctx.sema.source(func) {
182 let assoc_item = ast::AssocItem::Fn(source.value);
183 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
184 let transformed_fn = match transformed_item {
185 ast::AssocItem::Fn(func) => func,
189 let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
190 match ctx.config.snippet_cap {
192 let snippet = format!("{} {{\n $0\n}}", function_decl);
193 item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
196 let header = format!("{} {{", function_decl);
197 item.text_edit(TextEdit::replace(replacement_range, header));
205 /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
206 fn get_transformed_assoc_item(
207 ctx: &CompletionContext<'_>,
208 assoc_item: ast::AssocItem,
210 ) -> Option<ast::AssocItem> {
211 let assoc_item = assoc_item.clone_for_update();
212 let trait_ = impl_def.trait_(ctx.db)?;
213 let source_scope = &ctx.sema.scope_for_def(trait_);
214 let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
215 let transform = PathTransform::trait_impl(
219 ctx.sema.source(impl_def)?.value,
222 transform.apply(assoc_item.syntax());
223 if let ast::AssocItem::Fn(func) = &assoc_item {
224 func.remove_attrs_and_docs();
229 fn add_type_alias_impl(
230 acc: &mut Completions,
231 ctx: &CompletionContext<'_>,
232 replacement_range: TextRange,
233 type_alias: hir::TypeAlias,
235 let alias_name = type_alias.name(ctx.db);
236 let (alias_name, escaped_name) =
237 (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str());
239 let label = format!("type {} =", alias_name);
240 let replacement = format!("type {} = ", escaped_name);
242 let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
243 item.lookup_by(format!("type {}", alias_name))
244 .set_documentation(type_alias.docs(ctx.db))
245 .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
246 match ctx.config.snippet_cap {
248 .snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))),
249 None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
255 acc: &mut Completions,
256 ctx: &CompletionContext<'_>,
257 replacement_range: TextRange,
261 let const_name = const_.name(ctx.db).map(|n| n.to_smol_str());
263 if let Some(const_name) = const_name {
264 if let Some(source) = ctx.sema.source(const_) {
265 let assoc_item = ast::AssocItem::Const(source.value);
266 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
267 let transformed_const = match transformed_item {
268 ast::AssocItem::Const(const_) => const_,
272 let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro());
273 let replacement = format!("{} ", label);
275 let mut item = CompletionItem::new(SymbolKind::Const, replacement_range, label);
276 item.lookup_by(format!("const {}", const_name))
277 .set_documentation(const_.docs(ctx.db))
278 .set_relevance(CompletionRelevance {
279 is_item_from_trait: true,
282 match ctx.config.snippet_cap {
283 Some(cap) => item.snippet_edit(
285 TextEdit::replace(replacement_range, format!("{}$0;", replacement)),
287 None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
295 fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
296 const_.remove_attrs_and_docs();
297 let const_ = if needs_whitespace {
298 insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
300 const_.syntax().clone()
303 let start = const_.text_range().start();
304 let const_end = const_.text_range().end();
307 .children_with_tokens()
308 .find(|s| s.kind() == T![;] || s.kind() == T![=])
309 .map_or(const_end, |f| f.text_range().start());
311 let len = end - start;
312 let range = TextRange::new(0.into(), len);
314 let syntax = const_.text().slice(range).to_string();
316 format!("{} =", syntax.trim_end())
319 fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
320 node.remove_attrs_and_docs();
322 let node = if needs_whitespace {
323 insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
325 node.syntax().clone()
328 let start = node.text_range().start();
329 let end = node.text_range().end();
332 .last_child_or_token()
333 .filter(|s| s.kind() == T![;] || s.kind() == SyntaxKind::BLOCK_EXPR)
334 .map_or(end, |f| f.text_range().start());
336 let len = end - start;
337 let range = TextRange::new(0.into(), len);
339 let syntax = node.text().slice(range).to_string();
341 syntax.trim_end().to_owned()
344 fn replacement_range(ctx: &CompletionContext<'_>, item: &SyntaxNode) -> TextRange {
345 let first_child = item
346 .children_with_tokens()
348 !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
350 .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
352 TextRange::new(first_child.text_range().start(), ctx.source_range().end())
357 use expect_test::{expect, Expect};
359 use crate::tests::{check_edit, completion_list_no_kw};
361 fn check(ra_fixture: &str, expect: Expect) {
362 let actual = completion_list_no_kw(ra_fixture);
363 expect.assert_eq(&actual)
367 fn no_completion_inside_fn() {
370 trait Test { fn test(); fn test2(); }
389 trait Test { fn test(); fn test2(); }
403 trait Test { fn test(); fn test2(); }
415 // https://github.com/rust-lang/rust-analyzer/pull/5976#issuecomment-692332191
418 trait Test { fn test(); fn test2(); }
432 trait Test { fn test(_: i32); fn test2(); }
451 trait Test { fn test(_: fn()); fn test2(); }
466 fn no_completion_inside_const() {
469 trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
481 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
498 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
502 const TEST: u32 = f$0
515 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
534 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
548 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
562 fn no_completion_inside_type() {
565 trait Test { type Test; type Test2; fn test(); }
582 trait Test { type Test; type Test2; fn test(); }
594 fn name_ref_single_function() {
623 fn single_function() {
682 fn foo<T>() where T: Into<String>;
692 fn foo<T>() where T: Into<String>;
697 fn foo<T>() where T: Into<String> {
706 fn associated_type() {
724 type SomeType = $0;\n\
745 type SomeType = $0;\n\
752 fn associated_const() {
757 const SOME_CONST: u16;
766 const SOME_CONST: u16;
770 const SOME_CONST: u16 = $0;\n\
779 const SOME_CONST: u16 = 92;
788 const SOME_CONST: u16 = 92;
792 const SOME_CONST: u16 = $0;\n\
799 fn complete_without_name() {
800 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
833 completed, next_sibling
838 // Enumerate some possible next siblings.
839 for next_sibling in &[
841 "fn other_fn() {}", // `const $0 fn` -> `const fn`
842 "type OtherType = i32;",
843 "const OTHER_CONST: i32 = 0;",
844 "async fn other_fn() {}",
845 "unsafe fn other_fn() {}",
846 "default fn other_fn() {}",
847 "default type OtherType = i32;",
848 "default const OTHER_CONST: i32 = 0;",
850 test("fn bar", "fn $0", "fn bar() {\n $0\n}", next_sibling);
851 test("type Foo", "type $0", "type Foo = $0;", next_sibling);
852 test("const CONST", "const $0", "const CONST: u16 = $0;", next_sibling);
857 fn snippet_does_not_overwrite_comment_or_attr() {
858 let test = |completion: &str, hint: &str, completed: &str| {
866 const CONST: i32 = 0;
883 const CONST: i32 = 0;
897 test("fn function", "fn f$0", "fn function() {\n $0\n}");
898 test("type Type", "type T$0", "type Type = $0;");
899 test("const CONST", "const C$0", "const CONST: i32 = $0;");
903 fn generics_are_inlined_in_return_type() {
912 impl Foo<u32> for Bar {
922 impl Foo<u32> for Bar {
923 fn function() -> u32 {
932 fn generics_are_inlined_in_parameter() {
941 impl Foo<u32> for Bar {
951 impl Foo<u32> for Bar {
952 fn function(bar: u32) {
961 fn generics_are_inlined_when_part_of_other_types() {
966 fn function(bar: Vec<T>);
970 impl Foo<u32> for Bar {
976 fn function(bar: Vec<T>);
980 impl Foo<u32> for Bar {
981 fn function(bar: Vec<u32>) {
990 fn generics_are_inlined_complex() {
995 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
999 impl Foo<u32, Vec<usize>, u8> for Bar {
1004 trait Foo<T, U, V> {
1005 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
1009 impl Foo<u32, Vec<usize>, u8> for Bar {
1010 fn function(bar: Vec<u32>, baz: Vec<usize>) -> Arc<Vec<u8>> {
1019 fn generics_are_inlined_in_associated_const() {
1028 impl Foo<u32> for Bar {
1038 impl Foo<u32> for Bar {
1039 const BAR: u32 = $0;
1046 fn generics_are_inlined_in_where_clause() {
1050 trait SomeTrait<T> {}
1054 where Self: SomeTrait<T>;
1058 impl Foo<u32> for Bar {
1063 trait SomeTrait<T> {}
1067 where Self: SomeTrait<T>;
1071 impl Foo<u32> for Bar {
1073 where Self: SomeTrait<u32> {
1082 fn works_directly_in_impl() {
1116 fn fixes_up_macro_generated() {
1121 ($($item: item)*) => {
1128 fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
1140 ($($item: item)*) => {
1147 fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
1154 fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> {