aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-17 09:34:08 +0000
committerGitHub <[email protected]>2020-02-17 09:34:08 +0000
commit8d8d542dfa1d3b83088a1f48a91665e95fd008cc (patch)
tree495e3d223f595404e46d5e58b426cda4b0c633bc /crates
parent953dbe3e02fa87bab2f12452746e6c50a47ac153 (diff)
parent057d0bee5516dc7cba71479b27227c5ad22140ee (diff)
Merge #3108
3108: Magic Completion for `impl Trait for` Associated Items r=matklad a=kdelorey # Summary This PR adds a set of magic completions to auto complete associated trait items (functions/consts/types). ![Associated Trait Impl](https://user-images.githubusercontent.com/2295721/74493144-d8f1af00-4e96-11ea-93a4-82725bf89646.gif) ## Notes Since the assist and completion share the same logic when figuring out the associated items that are missing, a shared utility was created in the `ra_assists::utils` module. Resolves #1046 As this is my first PR to the rust-analyzer project, I'm new to the codebase, feedback welcomed! Co-authored-by: Kevin DeLorey <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs47
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_assists/src/utils.rs75
-rw-r--r--crates/ra_ide/src/completion.rs3
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs436
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs9
6 files changed, 536 insertions, 36 deletions
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 448697d31..ab21388c8 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -1,4 +1,4 @@
1use hir::{db::HirDatabase, HasSource, InFile}; 1use hir::{HasSource, InFile};
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, edit, make, AstNode, NameOwner}, 3 ast::{self, edit, make, AstNode, NameOwner},
4 SmolStr, 4 SmolStr,
@@ -6,6 +6,7 @@ use ra_syntax::{
6 6
7use crate::{ 7use crate::{
8 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 8 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
9 utils::{get_missing_impl_items, resolve_target_trait},
9 Assist, AssistCtx, AssistId, 10 Assist, AssistCtx, AssistId,
10}; 11};
11 12
@@ -103,11 +104,9 @@ fn add_missing_impl_members_inner(
103 let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?; 104 let impl_node = ctx.find_node_at_offset::<ast::ImplBlock>()?;
104 let impl_item_list = impl_node.item_list()?; 105 let impl_item_list = impl_node.item_list()?;
105 106
106 let (trait_, trait_def) = { 107 let analyzer = ctx.source_analyzer(impl_node.syntax(), None);
107 let analyzer = ctx.source_analyzer(impl_node.syntax(), None);
108 108
109 resolve_target_trait_def(ctx.db, &analyzer, &impl_node)? 109 let trait_ = resolve_target_trait(ctx.db, &analyzer, &impl_node)?;
110 };
111 110
112 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> { 111 let def_name = |item: &ast::ImplItem| -> Option<SmolStr> {
113 match item { 112 match item {
@@ -118,11 +117,14 @@ fn add_missing_impl_members_inner(
118 .map(|it| it.text().clone()) 117 .map(|it| it.text().clone())
119 }; 118 };
120 119
121 let trait_items = trait_def.item_list()?.impl_items(); 120 let missing_items = get_missing_impl_items(ctx.db, &analyzer, &impl_node)
122 let impl_items = impl_item_list.impl_items().collect::<Vec<_>>(); 121 .iter()
123 122 .map(|i| match i {
124 let missing_items: Vec<_> = trait_items 123 hir::AssocItem::Function(i) => ast::ImplItem::FnDef(i.source(ctx.db).value),
125 .filter(|t| def_name(t).is_some()) 124 hir::AssocItem::TypeAlias(i) => ast::ImplItem::TypeAliasDef(i.source(ctx.db).value),
125 hir::AssocItem::Const(i) => ast::ImplItem::ConstDef(i.source(ctx.db).value),
126 })
127 .filter(|t| def_name(&t).is_some())
126 .filter(|t| match t { 128 .filter(|t| match t {
127 ast::ImplItem::FnDef(def) => match mode { 129 ast::ImplItem::FnDef(def) => match mode {
128 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(), 130 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
@@ -130,8 +132,8 @@ fn add_missing_impl_members_inner(
130 }, 132 },
131 _ => mode == AddMissingImplMembersMode::NoDefaultMethods, 133 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
132 }) 134 })
133 .filter(|t| impl_items.iter().all(|i| def_name(i) != def_name(t))) 135 .collect::<Vec<_>>();
134 .collect(); 136
135 if missing_items.is_empty() { 137 if missing_items.is_empty() {
136 return None; 138 return None;
137 } 139 }
@@ -177,27 +179,6 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
177 } 179 }
178} 180}
179 181
180/// Given an `ast::ImplBlock`, resolves the target trait (the one being
181/// implemented) to a `ast::TraitDef`.
182fn resolve_target_trait_def(
183 db: &impl HirDatabase,
184 analyzer: &hir::SourceAnalyzer,
185 impl_block: &ast::ImplBlock,
186) -> Option<(hir::Trait, ast::TraitDef)> {
187 let ast_path = impl_block
188 .target_trait()
189 .map(|it| it.syntax().clone())
190 .and_then(ast::PathType::cast)?
191 .path()?;
192
193 match analyzer.resolve_path(db, &ast_path) {
194 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => {
195 Some((def, def.source(db).value))
196 }
197 _ => None,
198 }
199}
200
201#[cfg(test)] 182#[cfg(test)]
202mod tests { 183mod tests {
203 use super::*; 184 use super::*;
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 828a8e9e8..cb124eaf0 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,7 +9,7 @@ mod assist_ctx;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12mod utils; 12pub mod utils;
13pub mod ast_transform; 13pub mod ast_transform;
14 14
15use ra_db::FileRange; 15use ra_db::FileRange;
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 0d5722295..6ff44c95c 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,10 +1,81 @@
1//! Assorted functions shared by several assists. 1//! Assorted functions shared by several assists.
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 ast::{self, make}, 4 ast::{self, make, NameOwner},
5 T, 5 AstNode, T,
6}; 6};
7 7
8use hir::db::HirDatabase;
9use rustc_hash::FxHashSet;
10
11pub fn get_missing_impl_items(
12 db: &impl HirDatabase,
13 analyzer: &hir::SourceAnalyzer,
14 impl_block: &ast::ImplBlock,
15) -> Vec<hir::AssocItem> {
16 // Names must be unique between constants and functions. However, type aliases
17 // may share the same name as a function or constant.
18 let mut impl_fns_consts = FxHashSet::default();
19 let mut impl_type = FxHashSet::default();
20
21 if let Some(item_list) = impl_block.item_list() {
22 for item in item_list.impl_items() {
23 match item {
24 ast::ImplItem::FnDef(f) => {
25 if let Some(n) = f.name() {
26 impl_fns_consts.insert(n.syntax().to_string());
27 }
28 }
29
30 ast::ImplItem::TypeAliasDef(t) => {
31 if let Some(n) = t.name() {
32 impl_type.insert(n.syntax().to_string());
33 }
34 }
35
36 ast::ImplItem::ConstDef(c) => {
37 if let Some(n) = c.name() {
38 impl_fns_consts.insert(n.syntax().to_string());
39 }
40 }
41 }
42 }
43 }
44
45 resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| {
46 target_trait
47 .items(db)
48 .iter()
49 .filter(|i| match i {
50 hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(db).to_string()),
51 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()),
52 hir::AssocItem::Const(c) => c
53 .name(db)
54 .map(|n| !impl_fns_consts.contains(&n.to_string()))
55 .unwrap_or_default(),
56 })
57 .cloned()
58 .collect()
59 })
60}
61
62pub(crate) fn resolve_target_trait(
63 db: &impl HirDatabase,
64 analyzer: &hir::SourceAnalyzer,
65 impl_block: &ast::ImplBlock,
66) -> Option<hir::Trait> {
67 let ast_path = impl_block
68 .target_trait()
69 .map(|it| it.syntax().clone())
70 .and_then(ast::PathType::cast)?
71 .path()?;
72
73 match analyzer.resolve_path(db, &ast_path) {
74 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
75 _ => None,
76 }
77}
78
8pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { 79pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
9 if let Some(expr) = invert_special_case(&expr) { 80 if let Some(expr) = invert_special_case(&expr) {
10 return expr; 81 return expr;
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index fedc02e14..4bdc6ba23 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -15,6 +15,7 @@ mod complete_path;
15mod complete_scope; 15mod complete_scope;
16mod complete_postfix; 16mod complete_postfix;
17mod complete_macro_in_item_position; 17mod complete_macro_in_item_position;
18mod complete_trait_impl;
18 19
19use ra_db::SourceDatabase; 20use ra_db::SourceDatabase;
20use ra_ide_db::RootDatabase; 21use ra_ide_db::RootDatabase;
@@ -74,5 +75,7 @@ pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<C
74 complete_pattern::complete_pattern(&mut acc, &ctx); 75 complete_pattern::complete_pattern(&mut acc, &ctx);
75 complete_postfix::complete_postfix(&mut acc, &ctx); 76 complete_postfix::complete_postfix(&mut acc, &ctx);
76 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); 77 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
78 complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
79
77 Some(acc) 80 Some(acc)
78} 81}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
new file mode 100644
index 000000000..6ff10c017
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -0,0 +1,436 @@
1//! Completion for associated items in a trait implementation.
2//!
3//! This module adds the completion items related to implementing associated
4//! items within a `impl Trait for Struct` block. The current context node
5//! must be within either a `FN_DEF`, `TYPE_ALIAS_DEF`, or `CONST_DEF` node
6//! and an direct child of an `IMPL_BLOCK`.
7//!
8//! # Examples
9//!
10//! Considering the following trait `impl`:
11//!
12//! ```ignore
13//! trait SomeTrait {
14//! fn foo();
15//! }
16//!
17//! impl SomeTrait for () {
18//! fn f<|>
19//! }
20//! ```
21//!
22//! may result in the completion of the following method:
23//!
24//! ```ignore
25//! # trait SomeTrait {
26//! # fn foo();
27//! # }
28//!
29//! impl SomeTrait for () {
30//! fn foo() {}<|>
31//! }
32//! ```
33
34use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items;
36use ra_syntax::{
37 ast::{self, edit},
38 AstNode, SyntaxKind, SyntaxNode, TextRange,
39};
40use ra_text_edit::TextEdit;
41
42use crate::{
43 completion::{
44 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
45 },
46 display::FunctionSignature,
47};
48
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 let trigger = ctx.token.ancestors().find(|p| match p.kind() {
51 SyntaxKind::FN_DEF
52 | SyntaxKind::TYPE_ALIAS_DEF
53 | SyntaxKind::CONST_DEF
54 | SyntaxKind::BLOCK_EXPR => true,
55 _ => false,
56 });
57
58 let impl_block = trigger
59 .as_ref()
60 .and_then(|node| node.parent())
61 .and_then(|node| node.parent())
62 .and_then(|node| ast::ImplBlock::cast(node));
63
64 if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) {
65 match trigger.kind() {
66 SyntaxKind::FN_DEF => {
67 for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
68 .iter()
69 .filter_map(|item| match item {
70 hir::AssocItem::Function(fn_item) => Some(fn_item),
71 _ => None,
72 })
73 {
74 add_function_impl(&trigger, acc, ctx, &missing_fn);
75 }
76 }
77
78 SyntaxKind::TYPE_ALIAS_DEF => {
79 for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
80 .iter()
81 .filter_map(|item| match item {
82 hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83 _ => None,
84 })
85 {
86 add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
87 }
88 }
89
90 SyntaxKind::CONST_DEF => {
91 for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block)
92 .iter()
93 .filter_map(|item| match item {
94 hir::AssocItem::Const(const_item) => Some(const_item),
95 _ => None,
96 })
97 {
98 add_const_impl(&trigger, acc, ctx, &missing_fn);
99 }
100 }
101
102 _ => {}
103 }
104 }
105}
106
107fn add_function_impl(
108 fn_def_node: &SyntaxNode,
109 acc: &mut Completions,
110 ctx: &CompletionContext,
111 func: &hir::Function,
112) {
113 let display = FunctionSignature::from_hir(ctx.db, func.clone());
114
115 let fn_name = func.name(ctx.db).to_string();
116
117 let label = if func.params(ctx.db).len() > 0 {
118 format!("fn {}(..)", fn_name)
119 } else {
120 format!("fn {}()", fn_name)
121 };
122
123 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label.clone())
124 .lookup_by(fn_name)
125 .set_documentation(func.docs(ctx.db));
126
127 let completion_kind = if func.has_self_param(ctx.db) {
128 CompletionItemKind::Method
129 } else {
130 CompletionItemKind::Function
131 };
132
133 let snippet = format!("{} {{}}", display);
134
135 let range = TextRange::from_to(fn_def_node.text_range().start(), ctx.source_range().end());
136
137 builder.text_edit(TextEdit::replace(range, snippet)).kind(completion_kind).add_to(acc);
138}
139
140fn add_type_alias_impl(
141 type_def_node: &SyntaxNode,
142 acc: &mut Completions,
143 ctx: &CompletionContext,
144 type_alias: &hir::TypeAlias,
145) {
146 let alias_name = type_alias.name(ctx.db).to_string();
147
148 let snippet = format!("type {} = ", alias_name);
149
150 let range = TextRange::from_to(type_def_node.text_range().start(), ctx.source_range().end());
151
152 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
153 .text_edit(TextEdit::replace(range, snippet))
154 .lookup_by(alias_name)
155 .kind(CompletionItemKind::TypeAlias)
156 .set_documentation(type_alias.docs(ctx.db))
157 .add_to(acc);
158}
159
160fn add_const_impl(
161 const_def_node: &SyntaxNode,
162 acc: &mut Completions,
163 ctx: &CompletionContext,
164 const_: &hir::Const,
165) {
166 let const_name = const_.name(ctx.db).map(|n| n.to_string());
167
168 if let Some(const_name) = const_name {
169 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
170
171 let range =
172 TextRange::from_to(const_def_node.text_range().start(), ctx.source_range().end());
173
174 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
175 .text_edit(TextEdit::replace(range, snippet))
176 .lookup_by(const_name)
177 .kind(CompletionItemKind::Const)
178 .set_documentation(const_.docs(ctx.db))
179 .add_to(acc);
180 }
181}
182
183fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
184 let const_ = edit::strip_attrs_and_docs(const_);
185
186 let const_start = const_.syntax().text_range().start();
187 let const_end = const_.syntax().text_range().end();
188
189 let start =
190 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
191
192 let end = const_
193 .syntax()
194 .children_with_tokens()
195 .find(|s| s.kind() == SyntaxKind::SEMI || s.kind() == SyntaxKind::EQ)
196 .map_or(const_end, |f| f.text_range().start());
197
198 let len = end - start;
199 let range = TextRange::from_to(0.into(), len);
200
201 let syntax = const_.syntax().text().slice(range).to_string();
202
203 format!("{} = ", syntax.trim_end())
204}
205
206#[cfg(test)]
207mod tests {
208 use crate::completion::{do_completion, CompletionItem, CompletionKind};
209 use insta::assert_debug_snapshot;
210
211 fn complete(code: &str) -> Vec<CompletionItem> {
212 do_completion(code, CompletionKind::Magic)
213 }
214
215 #[test]
216 fn single_function() {
217 let completions = complete(
218 r"
219 trait Test {
220 fn foo();
221 }
222
223 struct T1;
224
225 impl Test for T1 {
226 fn f<|>
227 }
228 ",
229 );
230 assert_debug_snapshot!(completions, @r###"
231 [
232 CompletionItem {
233 label: "fn foo()",
234 source_range: [141; 142),
235 delete: [138; 142),
236 insert: "fn foo() {}",
237 kind: Function,
238 lookup: "foo",
239 },
240 ]
241 "###);
242 }
243
244 #[test]
245 fn hide_implemented_fn() {
246 let completions = complete(
247 r"
248 trait Test {
249 fn foo();
250 fn foo_bar();
251 }
252
253 struct T1;
254
255 impl Test for T1 {
256 fn foo() {}
257
258 fn f<|>
259 }
260 ",
261 );
262 assert_debug_snapshot!(completions, @r###"
263 [
264 CompletionItem {
265 label: "fn foo_bar()",
266 source_range: [200; 201),
267 delete: [197; 201),
268 insert: "fn foo_bar() {}",
269 kind: Function,
270 lookup: "foo_bar",
271 },
272 ]
273 "###);
274 }
275
276 #[test]
277 fn completes_only_on_top_level() {
278 let completions = complete(
279 r"
280 trait Test {
281 fn foo();
282
283 fn foo_bar();
284 }
285
286 struct T1;
287
288 impl Test for T1 {
289 fn foo() {
290 <|>
291 }
292 }
293 ",
294 );
295 assert_debug_snapshot!(completions, @r###"[]"###);
296 }
297
298 #[test]
299 fn generic_fn() {
300 let completions = complete(
301 r"
302 trait Test {
303 fn foo<T>();
304 }
305
306 struct T1;
307
308 impl Test for T1 {
309 fn f<|>
310 }
311 ",
312 );
313 assert_debug_snapshot!(completions, @r###"
314 [
315 CompletionItem {
316 label: "fn foo()",
317 source_range: [144; 145),
318 delete: [141; 145),
319 insert: "fn foo<T>() {}",
320 kind: Function,
321 lookup: "foo",
322 },
323 ]
324 "###);
325 }
326
327 #[test]
328 fn generic_constrait_fn() {
329 let completions = complete(
330 r"
331 trait Test {
332 fn foo<T>() where T: Into<String>;
333 }
334
335 struct T1;
336
337 impl Test for T1 {
338 fn f<|>
339 }
340 ",
341 );
342 assert_debug_snapshot!(completions, @r###"
343 [
344 CompletionItem {
345 label: "fn foo()",
346 source_range: [166; 167),
347 delete: [163; 167),
348 insert: "fn foo<T>()\nwhere T: Into<String> {}",
349 kind: Function,
350 lookup: "foo",
351 },
352 ]
353 "###);
354 }
355
356 #[test]
357 fn associated_type() {
358 let completions = complete(
359 r"
360 trait Test {
361 type SomeType;
362 }
363
364 impl Test for () {
365 type S<|>
366 }
367 ",
368 );
369 assert_debug_snapshot!(completions, @r###"
370 [
371 CompletionItem {
372 label: "type SomeType = ",
373 source_range: [124; 125),
374 delete: [119; 125),
375 insert: "type SomeType = ",
376 kind: TypeAlias,
377 lookup: "SomeType",
378 },
379 ]
380 "###);
381 }
382
383 #[test]
384 fn associated_const() {
385 let completions = complete(
386 r"
387 trait Test {
388 const SOME_CONST: u16;
389 }
390
391 impl Test for () {
392 const S<|>
393 }
394 ",
395 );
396 assert_debug_snapshot!(completions, @r###"
397 [
398 CompletionItem {
399 label: "const SOME_CONST: u16 = ",
400 source_range: [133; 134),
401 delete: [127; 134),
402 insert: "const SOME_CONST: u16 = ",
403 kind: Const,
404 lookup: "SOME_CONST",
405 },
406 ]
407 "###);
408 }
409
410 #[test]
411 fn associated_const_with_default() {
412 let completions = complete(
413 r"
414 trait Test {
415 const SOME_CONST: u16 = 42;
416 }
417
418 impl Test for () {
419 const S<|>
420 }
421 ",
422 );
423 assert_debug_snapshot!(completions, @r###"
424 [
425 CompletionItem {
426 label: "const SOME_CONST: u16 = ",
427 source_range: [138; 139),
428 delete: [132; 139),
429 insert: "const SOME_CONST: u16 = ",
430 kind: Const,
431 lookup: "SOME_CONST",
432 },
433 ]
434 "###);
435 }
436}
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 5a0407fd7..8678a3234 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -25,6 +25,7 @@ pub(crate) struct CompletionContext<'a> {
25 pub(super) use_item_syntax: Option<ast::UseItem>, 25 pub(super) use_item_syntax: Option<ast::UseItem>,
26 pub(super) record_lit_syntax: Option<ast::RecordLit>, 26 pub(super) record_lit_syntax: Option<ast::RecordLit>,
27 pub(super) record_lit_pat: Option<ast::RecordPat>, 27 pub(super) record_lit_pat: Option<ast::RecordPat>,
28 pub(super) impl_block: Option<ast::ImplBlock>,
28 pub(super) is_param: bool, 29 pub(super) is_param: bool,
29 /// If a name-binding or reference to a const in a pattern. 30 /// If a name-binding or reference to a const in a pattern.
30 /// Irrefutable patterns (like let) are excluded. 31 /// Irrefutable patterns (like let) are excluded.
@@ -72,6 +73,7 @@ impl<'a> CompletionContext<'a> {
72 use_item_syntax: None, 73 use_item_syntax: None,
73 record_lit_syntax: None, 74 record_lit_syntax: None,
74 record_lit_pat: None, 75 record_lit_pat: None,
76 impl_block: None,
75 is_param: false, 77 is_param: false,
76 is_pat_binding: false, 78 is_pat_binding: false,
77 is_trivial_path: false, 79 is_trivial_path: false,
@@ -148,6 +150,13 @@ impl<'a> CompletionContext<'a> {
148 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset); 150 self.record_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
149 } 151 }
150 152
153 self.impl_block = self
154 .token
155 .parent()
156 .ancestors()
157 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
158 .find_map(ast::ImplBlock::cast);
159
151 let top_node = name_ref 160 let top_node = name_ref
152 .syntax() 161 .syntax()
153 .ancestors() 162 .ancestors()