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, 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 item = match name {
89 Some(name) => name.syntax().parent(),
91 let token = &ctx.token;
93 SyntaxKind::WHITESPACE => token.prev_token()?,
99 let item = ctx.sema.original_syntax_node(&item)?;
100 // item -> ASSOC_ITEM_LIST -> IMPL
101 let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
102 let replacement_range = {
103 // ctx.sema.original_ast_node(item)?;
104 let first_child = item
105 .children_with_tokens()
109 SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
112 .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
114 TextRange::new(first_child.text_range().start(), ctx.source_range().end())
117 complete_trait_impl(acc, ctx, kind, replacement_range, &impl_def);
121 pub(crate) fn complete_trait_impl_item_by_name(
122 acc: &mut Completions,
123 ctx: &CompletionContext<'_>,
124 path_ctx: &PathCompletionCtx,
125 name_ref: &Option<ast::NameRef>,
126 impl_: &Option<ast::Impl>,
128 if !path_ctx.is_trivial_path() {
131 if let Some(impl_) = impl_ {
135 ImplCompletionKind::All,
137 Some(name) => name.syntax().text_range(),
138 None => ctx.source_range(),
145 fn complete_trait_impl(
146 acc: &mut Completions,
147 ctx: &CompletionContext<'_>,
148 kind: ImplCompletionKind,
149 replacement_range: TextRange,
150 impl_def: &ast::Impl,
152 if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
153 get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
154 use self::ImplCompletionKind::*;
156 (hir::AssocItem::Function(func), All | Fn) => {
157 add_function_impl(acc, ctx, replacement_range, func, hir_impl)
159 (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
160 add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
162 (hir::AssocItem::Const(const_), All | Const) => {
163 add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
171 fn add_function_impl(
172 acc: &mut Completions,
173 ctx: &CompletionContext<'_>,
174 replacement_range: TextRange,
178 let fn_name = func.name(ctx.db);
183 if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
186 let completion_kind = if func.has_self_param(ctx.db) {
187 CompletionItemKind::Method
189 CompletionItemKind::SymbolKind(SymbolKind::Function)
192 let mut item = CompletionItem::new(completion_kind, replacement_range, label);
193 item.lookup_by(format!("fn {}", fn_name))
194 .set_documentation(func.docs(ctx.db))
195 .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
197 if let Some(source) = ctx.sema.source(func) {
198 let assoc_item = ast::AssocItem::Fn(source.value);
199 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
200 let transformed_fn = match transformed_item {
201 ast::AssocItem::Fn(func) => func,
205 let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro());
206 match ctx.config.snippet_cap {
208 let snippet = format!("{} {{\n $0\n}}", function_decl);
209 item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
212 let header = format!("{} {{", function_decl);
213 item.text_edit(TextEdit::replace(replacement_range, header));
221 /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
222 fn get_transformed_assoc_item(
223 ctx: &CompletionContext<'_>,
224 assoc_item: ast::AssocItem,
226 ) -> Option<ast::AssocItem> {
227 let assoc_item = assoc_item.clone_for_update();
228 let trait_ = impl_def.trait_(ctx.db)?;
229 let source_scope = &ctx.sema.scope_for_def(trait_);
230 let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
231 let transform = PathTransform::trait_impl(
235 ctx.sema.source(impl_def)?.value,
238 transform.apply(assoc_item.syntax());
239 assoc_item.remove_attrs_and_docs();
243 fn add_type_alias_impl(
244 acc: &mut Completions,
245 ctx: &CompletionContext<'_>,
246 replacement_range: TextRange,
247 type_alias: hir::TypeAlias,
250 let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str();
252 let label = format!("type {} =", alias_name);
254 let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label);
255 item.lookup_by(format!("type {}", alias_name))
256 .set_documentation(type_alias.docs(ctx.db))
257 .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
259 if let Some(source) = ctx.sema.source(type_alias) {
260 let assoc_item = ast::AssocItem::TypeAlias(source.value);
261 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
262 let transformed_ty = match transformed_item {
263 ast::AssocItem::TypeAlias(ty) => ty,
267 let start = transformed_ty.syntax().text_range().start();
268 let Some(end) = transformed_ty
270 .map(|tok| tok.text_range().start())
271 .or(transformed_ty.semicolon_token().map(|tok| tok.text_range().start())) else { return };
273 let len = end - start;
274 let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
275 if !decl.ends_with(' ') {
280 match ctx.config.snippet_cap {
282 let snippet = format!("{}$0;", decl);
283 item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
286 item.text_edit(TextEdit::replace(replacement_range, decl));
295 acc: &mut Completions,
296 ctx: &CompletionContext<'_>,
297 replacement_range: TextRange,
301 let const_name = const_.name(ctx.db).map(|n| n.to_smol_str());
303 if let Some(const_name) = const_name {
304 if let Some(source) = ctx.sema.source(const_) {
305 let assoc_item = ast::AssocItem::Const(source.value);
306 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
307 let transformed_const = match transformed_item {
308 ast::AssocItem::Const(const_) => const_,
312 let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro());
313 let replacement = format!("{} ", label);
315 let mut item = CompletionItem::new(SymbolKind::Const, replacement_range, label);
316 item.lookup_by(format!("const {}", const_name))
317 .set_documentation(const_.docs(ctx.db))
318 .set_relevance(CompletionRelevance {
319 is_item_from_trait: true,
322 match ctx.config.snippet_cap {
323 Some(cap) => item.snippet_edit(
325 TextEdit::replace(replacement_range, format!("{}$0;", replacement)),
327 None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
335 fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String {
336 let const_ = if needs_whitespace {
337 insert_whitespace_into_node::insert_ws_into(const_.syntax().clone())
339 const_.syntax().clone()
342 let start = const_.text_range().start();
343 let const_end = const_.text_range().end();
346 .children_with_tokens()
347 .find(|s| s.kind() == T![;] || s.kind() == T![=])
348 .map_or(const_end, |f| f.text_range().start());
350 let len = end - start;
351 let range = TextRange::new(0.into(), len);
353 let syntax = const_.text().slice(range).to_string();
355 format!("{} =", syntax.trim_end())
358 fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
359 let node = if needs_whitespace {
360 insert_whitespace_into_node::insert_ws_into(node.syntax().clone())
362 node.syntax().clone()
365 let start = node.text_range().start();
366 let end = node.text_range().end();
369 .last_child_or_token()
370 .filter(|s| s.kind() == T![;] || s.kind() == SyntaxKind::BLOCK_EXPR)
371 .map_or(end, |f| f.text_range().start());
373 let len = end - start;
374 let syntax = node.text().slice(..len).to_string();
376 syntax.trim_end().to_owned()
381 use expect_test::{expect, Expect};
383 use crate::tests::{check_edit, completion_list_no_kw};
385 fn check(ra_fixture: &str, expect: Expect) {
386 let actual = completion_list_no_kw(ra_fixture);
387 expect.assert_eq(&actual)
391 fn no_completion_inside_fn() {
394 trait Test { fn test(); fn test2(); }
413 trait Test { fn test(); fn test2(); }
427 trait Test { fn test(); fn test2(); }
439 // https://github.com/rust-lang/rust-analyzer/pull/5976#issuecomment-692332191
442 trait Test { fn test(); fn test2(); }
456 trait Test { fn test(_: i32); fn test2(); }
475 trait Test { fn test(_: fn()); fn test2(); }
490 fn no_completion_inside_const() {
493 trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
505 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
522 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
526 const TEST: u32 = f$0
539 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
558 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
572 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
586 fn no_completion_inside_type() {
589 trait Test { type Test; type Test2; fn test(); }
606 trait Test { type Test; type Test2; fn test(); }
618 fn name_ref_single_function() {
647 fn single_function() {
706 fn foo<T>() where T: Into<String>;
716 fn foo<T>() where T: Into<String>;
721 fn foo<T>() where T: Into<String> {
730 fn associated_type() {
748 type SomeType = $0;\n\
769 type SomeType = $0;\n\
776 fn associated_const() {
781 const SOME_CONST: u16;
790 const SOME_CONST: u16;
794 const SOME_CONST: u16 = $0;\n\
803 const SOME_CONST: u16 = 92;
812 const SOME_CONST: u16 = 92;
816 const SOME_CONST: u16 = $0;\n\
823 fn complete_without_name() {
824 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
857 completed, next_sibling
862 // Enumerate some possible next siblings.
863 for next_sibling in &[
865 "fn other_fn() {}", // `const $0 fn` -> `const fn`
866 "type OtherType = i32;",
867 "const OTHER_CONST: i32 = 0;",
868 "async fn other_fn() {}",
869 "unsafe fn other_fn() {}",
870 "default fn other_fn() {}",
871 "default type OtherType = i32;",
872 "default const OTHER_CONST: i32 = 0;",
874 test("fn bar", "fn $0", "fn bar() {\n $0\n}", next_sibling);
875 test("type Foo", "type $0", "type Foo = $0;", next_sibling);
876 test("const CONST", "const $0", "const CONST: u16 = $0;", next_sibling);
881 fn snippet_does_not_overwrite_comment_or_attr() {
882 let test = |completion: &str, hint: &str, completed: &str| {
890 const CONST: i32 = 0;
907 const CONST: i32 = 0;
921 test("fn function", "fn f$0", "fn function() {\n $0\n}");
922 test("type Type", "type T$0", "type Type = $0;");
923 test("const CONST", "const C$0", "const CONST: i32 = $0;");
927 fn generics_are_inlined_in_return_type() {
936 impl Foo<u32> for Bar {
946 impl Foo<u32> for Bar {
947 fn function() -> u32 {
956 fn generics_are_inlined_in_parameter() {
965 impl Foo<u32> for Bar {
975 impl Foo<u32> for Bar {
976 fn function(bar: u32) {
985 fn generics_are_inlined_when_part_of_other_types() {
990 fn function(bar: Vec<T>);
994 impl Foo<u32> for Bar {
1000 fn function(bar: Vec<T>);
1004 impl Foo<u32> for Bar {
1005 fn function(bar: Vec<u32>) {
1014 fn generics_are_inlined_complex() {
1018 trait Foo<T, U, V> {
1019 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
1023 impl Foo<u32, Vec<usize>, u8> for Bar {
1028 trait Foo<T, U, V> {
1029 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
1033 impl Foo<u32, Vec<usize>, u8> for Bar {
1034 fn function(bar: Vec<u32>, baz: Vec<usize>) -> Arc<Vec<u8>> {
1043 fn generics_are_inlined_in_associated_const() {
1052 impl Foo<u32> for Bar {
1062 impl Foo<u32> for Bar {
1063 const BAR: u32 = $0;
1070 fn generics_are_inlined_in_where_clause() {
1074 trait SomeTrait<T> {}
1078 where Self: SomeTrait<T>;
1082 impl Foo<u32> for Bar {
1087 trait SomeTrait<T> {}
1091 where Self: SomeTrait<T>;
1095 impl Foo<u32> for Bar {
1097 where Self: SomeTrait<u32> {
1106 fn works_directly_in_impl() {
1140 fn fixes_up_macro_generated() {
1145 ($($item: item)*) => {
1152 fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
1164 ($($item: item)*) => {
1171 fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32>;
1178 fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> {
1187 fn includes_gat_generics() {
1192 type Ty<'a: 'b, T: Copy, const C: usize>;
1195 impl<'b> Tr<'b> for () {
1201 type Ty<'a: 'b, T: Copy, const C: usize>;
1204 impl<'b> Tr<'b> for () {
1205 type Ty<'a: 'b, T: Copy, const C: usize> = $0;
1212 fn strips_comments() {
1257 const C: usize = $0;