diff options
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r-- | crates/ide_completion/src/context.rs | 362 |
1 files changed, 213 insertions, 149 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 2f3fb1710..66577df94 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -1,29 +1,53 @@ | |||
1 | //! See `CompletionContext` structure. | 1 | //! See `CompletionContext` structure. |
2 | 2 | ||
3 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; | 3 | use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; |
4 | use ide_db::base_db::{FilePosition, SourceDatabase}; | 4 | use ide_db::{ |
5 | use ide_db::{call_info::ActiveParameter, RootDatabase}; | 5 | base_db::{FilePosition, SourceDatabase}, |
6 | call_info::ActiveParameter, | ||
7 | RootDatabase, | ||
8 | }; | ||
6 | use syntax::{ | 9 | use syntax::{ |
7 | algo::find_node_at_offset, | 10 | algo::find_node_at_offset, |
8 | ast::{self, NameOrNameRef, NameOwner}, | 11 | ast::{self, NameOrNameRef, NameOwner}, |
9 | match_ast, AstNode, NodeOrToken, | 12 | match_ast, AstNode, NodeOrToken, |
10 | SyntaxKind::*, | 13 | SyntaxKind::{self, *}, |
11 | SyntaxNode, SyntaxToken, TextRange, TextSize, | 14 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
12 | }; | 15 | }; |
13 | |||
14 | use text_edit::Indel; | 16 | use text_edit::Indel; |
15 | 17 | ||
16 | use crate::{ | 18 | use crate::{ |
17 | patterns::{ | 19 | patterns::{ |
18 | fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, | 20 | for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, |
19 | has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, | 21 | has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent, |
20 | has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, | 22 | has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token, |
21 | has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm, | ||
22 | unsafe_is_prev, | ||
23 | }, | 23 | }, |
24 | CompletionConfig, | 24 | CompletionConfig, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
28 | pub(crate) enum PatternRefutability { | ||
29 | Refutable, | ||
30 | Irrefutable, | ||
31 | } | ||
32 | |||
33 | /// Direct parent container of the cursor position | ||
34 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
35 | pub(crate) enum ImmediateLocation { | ||
36 | Impl, | ||
37 | Trait, | ||
38 | RecordFieldList, | ||
39 | RefPatOrExpr, | ||
40 | IdentPat, | ||
41 | BlockExpr, | ||
42 | ItemList, | ||
43 | } | ||
44 | |||
45 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
46 | pub(crate) enum PrevSibling { | ||
47 | Trait, | ||
48 | Impl, | ||
49 | } | ||
50 | |||
27 | /// `CompletionContext` is created early during completion to figure out, where | 51 | /// `CompletionContext` is created early during completion to figure out, where |
28 | /// exactly is the cursor, syntax-wise. | 52 | /// exactly is the cursor, syntax-wise. |
29 | #[derive(Debug)] | 53 | #[derive(Debug)] |
@@ -41,23 +65,33 @@ pub(crate) struct CompletionContext<'a> { | |||
41 | pub(super) expected_name: Option<NameOrNameRef>, | 65 | pub(super) expected_name: Option<NameOrNameRef>, |
42 | pub(super) expected_type: Option<Type>, | 66 | pub(super) expected_type: Option<Type>, |
43 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 67 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
44 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | 68 | |
45 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | ||
46 | pub(super) function_syntax: Option<ast::Fn>, | ||
47 | pub(super) use_item_syntax: Option<ast::Use>, | 69 | pub(super) use_item_syntax: Option<ast::Use>, |
70 | |||
71 | /// The parent function of the cursor position if it exists. | ||
72 | pub(super) function_def: Option<ast::Fn>, | ||
73 | /// The parent impl of the cursor position if it exists. | ||
74 | pub(super) impl_def: Option<ast::Impl>, | ||
75 | |||
76 | /// RecordExpr the token is a field of | ||
48 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, | 77 | pub(super) record_lit_syntax: Option<ast::RecordExpr>, |
78 | /// RecordPat the token is a field of | ||
49 | pub(super) record_pat_syntax: Option<ast::RecordPat>, | 79 | pub(super) record_pat_syntax: Option<ast::RecordPat>, |
50 | pub(super) record_field_syntax: Option<ast::RecordExprField>, | 80 | |
51 | pub(super) impl_def: Option<ast::Impl>, | 81 | // potentially set if we are completing a lifetime |
82 | pub(super) lifetime_syntax: Option<ast::Lifetime>, | ||
83 | pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>, | ||
52 | pub(super) lifetime_allowed: bool, | 84 | pub(super) lifetime_allowed: bool, |
85 | pub(super) is_label_ref: bool, | ||
86 | |||
87 | // potentially set if we are completing a name | ||
88 | pub(super) is_pat_or_const: Option<PatternRefutability>, | ||
89 | pub(super) is_param: bool, | ||
90 | |||
91 | pub(super) completion_location: Option<ImmediateLocation>, | ||
92 | |||
53 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong | 93 | /// FIXME: `ActiveParameter` is string-based, which is very very wrong |
54 | pub(super) active_parameter: Option<ActiveParameter>, | 94 | pub(super) active_parameter: Option<ActiveParameter>, |
55 | pub(super) is_param: bool, | ||
56 | pub(super) is_label_ref: bool, | ||
57 | /// If a name-binding or reference to a const in a pattern. | ||
58 | /// Irrefutable patterns (like let) are excluded. | ||
59 | pub(super) is_pat_binding_or_const: bool, | ||
60 | pub(super) is_irrefutable_pat_binding: bool, | ||
61 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. | 95 | /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. |
62 | pub(super) is_trivial_path: bool, | 96 | pub(super) is_trivial_path: bool, |
63 | /// If not a trivial path, the prefix (qualifier). | 97 | /// If not a trivial path, the prefix (qualifier). |
@@ -82,26 +116,17 @@ pub(crate) struct CompletionContext<'a> { | |||
82 | pub(super) has_type_args: bool, | 116 | pub(super) has_type_args: bool, |
83 | pub(super) attribute_under_caret: Option<ast::Attr>, | 117 | pub(super) attribute_under_caret: Option<ast::Attr>, |
84 | pub(super) mod_declaration_under_caret: Option<ast::Module>, | 118 | pub(super) mod_declaration_under_caret: Option<ast::Module>, |
85 | pub(super) unsafe_is_prev: bool, | 119 | pub(super) locals: Vec<(String, Local)>, |
86 | pub(super) if_is_prev: bool, | 120 | |
87 | pub(super) block_expr_parent: bool, | 121 | // keyword patterns |
88 | pub(super) bind_pat_parent: bool, | 122 | pub(super) previous_token: Option<SyntaxToken>, |
89 | pub(super) ref_pat_parent: bool, | 123 | pub(super) prev_sibling: Option<PrevSibling>, |
90 | pub(super) in_loop_body: bool, | 124 | pub(super) in_loop_body: bool, |
91 | pub(super) has_trait_parent: bool, | ||
92 | pub(super) has_impl_parent: bool, | ||
93 | pub(super) inside_impl_trait_block: bool, | ||
94 | pub(super) has_field_list_parent: bool, | ||
95 | pub(super) trait_as_prev_sibling: bool, | ||
96 | pub(super) impl_as_prev_sibling: bool, | ||
97 | pub(super) is_match_arm: bool, | 125 | pub(super) is_match_arm: bool, |
98 | pub(super) has_item_list_or_source_file_parent: bool, | ||
99 | pub(super) for_is_prev2: bool, | ||
100 | pub(super) fn_is_prev: bool, | ||
101 | pub(super) incomplete_let: bool, | 126 | pub(super) incomplete_let: bool, |
102 | pub(super) locals: Vec<(String, Local)>, | ||
103 | } | ||
104 | 127 | ||
128 | no_completion_required: bool, | ||
129 | } | ||
105 | impl<'a> CompletionContext<'a> { | 130 | impl<'a> CompletionContext<'a> { |
106 | pub(super) fn new( | 131 | pub(super) fn new( |
107 | db: &'a RootDatabase, | 132 | db: &'a RootDatabase, |
@@ -149,17 +174,15 @@ impl<'a> CompletionContext<'a> { | |||
149 | name_ref_syntax: None, | 174 | name_ref_syntax: None, |
150 | lifetime_syntax: None, | 175 | lifetime_syntax: None, |
151 | lifetime_param_syntax: None, | 176 | lifetime_param_syntax: None, |
152 | function_syntax: None, | 177 | function_def: None, |
153 | use_item_syntax: None, | 178 | use_item_syntax: None, |
154 | record_lit_syntax: None, | 179 | record_lit_syntax: None, |
155 | record_pat_syntax: None, | 180 | record_pat_syntax: None, |
156 | record_field_syntax: None, | ||
157 | impl_def: None, | 181 | impl_def: None, |
158 | active_parameter: ActiveParameter::at(db, position), | 182 | active_parameter: ActiveParameter::at(db, position), |
159 | is_label_ref: false, | 183 | is_label_ref: false, |
160 | is_param: false, | 184 | is_param: false, |
161 | is_pat_binding_or_const: false, | 185 | is_pat_or_const: None, |
162 | is_irrefutable_pat_binding: false, | ||
163 | is_trivial_path: false, | 186 | is_trivial_path: false, |
164 | path_qual: None, | 187 | path_qual: None, |
165 | after_if: false, | 188 | after_if: false, |
@@ -175,22 +198,12 @@ impl<'a> CompletionContext<'a> { | |||
175 | has_type_args: false, | 198 | has_type_args: false, |
176 | attribute_under_caret: None, | 199 | attribute_under_caret: None, |
177 | mod_declaration_under_caret: None, | 200 | mod_declaration_under_caret: None, |
178 | unsafe_is_prev: false, | 201 | previous_token: None, |
179 | if_is_prev: false, | ||
180 | block_expr_parent: false, | ||
181 | bind_pat_parent: false, | ||
182 | ref_pat_parent: false, | ||
183 | in_loop_body: false, | 202 | in_loop_body: false, |
184 | has_trait_parent: false, | 203 | completion_location: None, |
185 | has_impl_parent: false, | 204 | prev_sibling: None, |
186 | inside_impl_trait_block: false, | ||
187 | has_field_list_parent: false, | ||
188 | trait_as_prev_sibling: false, | ||
189 | impl_as_prev_sibling: false, | ||
190 | is_match_arm: false, | 205 | is_match_arm: false, |
191 | has_item_list_or_source_file_parent: false, | 206 | no_completion_required: false, |
192 | for_is_prev2: false, | ||
193 | fn_is_prev: false, | ||
194 | incomplete_let: false, | 207 | incomplete_let: false, |
195 | locals, | 208 | locals, |
196 | }; | 209 | }; |
@@ -245,7 +258,7 @@ impl<'a> CompletionContext<'a> { | |||
245 | /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. | 258 | /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. |
246 | /// - `for _ i$0` -- obviously, it'll be "in" keyword. | 259 | /// - `for _ i$0` -- obviously, it'll be "in" keyword. |
247 | pub(crate) fn no_completion_required(&self) -> bool { | 260 | pub(crate) fn no_completion_required(&self) -> bool { |
248 | (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 | 261 | self.no_completion_required |
249 | } | 262 | } |
250 | 263 | ||
251 | /// The range of the identifier that is being completed. | 264 | /// The range of the identifier that is being completed. |
@@ -264,33 +277,92 @@ impl<'a> CompletionContext<'a> { | |||
264 | } | 277 | } |
265 | } | 278 | } |
266 | 279 | ||
280 | pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool { | ||
281 | self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind) | ||
282 | } | ||
283 | |||
284 | pub(crate) fn has_impl_or_trait_parent(&self) -> bool { | ||
285 | matches!( | ||
286 | self.completion_location, | ||
287 | Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl) | ||
288 | ) | ||
289 | } | ||
290 | |||
291 | pub(crate) fn has_block_expr_parent(&self) -> bool { | ||
292 | matches!(self.completion_location, Some(ImmediateLocation::BlockExpr)) | ||
293 | } | ||
294 | |||
295 | pub(crate) fn has_item_list_parent(&self) -> bool { | ||
296 | matches!(self.completion_location, Some(ImmediateLocation::ItemList)) | ||
297 | } | ||
298 | |||
299 | pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool { | ||
300 | matches!( | ||
301 | self.completion_location, | ||
302 | Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr) | ||
303 | ) | ||
304 | } | ||
305 | |||
306 | pub(crate) fn has_impl_parent(&self) -> bool { | ||
307 | matches!(self.completion_location, Some(ImmediateLocation::Impl)) | ||
308 | } | ||
309 | |||
310 | pub(crate) fn has_field_list_parent(&self) -> bool { | ||
311 | matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList)) | ||
312 | } | ||
313 | |||
314 | pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool { | ||
315 | self.prev_sibling.is_some() | ||
316 | } | ||
317 | |||
318 | pub(crate) fn is_path_disallowed(&self) -> bool { | ||
319 | self.record_lit_syntax.is_some() | ||
320 | || self.record_pat_syntax.is_some() | ||
321 | || self.attribute_under_caret.is_some() | ||
322 | || self.mod_declaration_under_caret.is_some() | ||
323 | || self.has_impl_or_trait_parent() | ||
324 | } | ||
325 | |||
267 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { | 326 | fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { |
268 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); | 327 | let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); |
269 | let syntax_element = NodeOrToken::Token(fake_ident_token); | 328 | let syntax_element = NodeOrToken::Token(fake_ident_token); |
270 | self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); | 329 | self.previous_token = previous_token(syntax_element.clone()); |
271 | self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); | ||
272 | self.if_is_prev = if_is_prev(syntax_element.clone()); | ||
273 | self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); | ||
274 | self.ref_pat_parent = has_ref_parent(syntax_element.clone()); | ||
275 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); | 330 | self.in_loop_body = is_in_loop_body(syntax_element.clone()); |
276 | self.has_trait_parent = has_trait_parent(syntax_element.clone()); | ||
277 | self.has_impl_parent = has_impl_parent(syntax_element.clone()); | ||
278 | self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
279 | self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); | ||
280 | self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); | ||
281 | self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); | ||
282 | self.is_match_arm = is_match_arm(syntax_element.clone()); | 331 | self.is_match_arm = is_match_arm(syntax_element.clone()); |
283 | self.has_item_list_or_source_file_parent = | 332 | if has_prev_sibling(syntax_element.clone(), IMPL) { |
284 | has_item_list_or_source_file_parent(syntax_element.clone()); | 333 | self.prev_sibling = Some(PrevSibling::Impl) |
334 | } else if has_prev_sibling(syntax_element.clone(), TRAIT) { | ||
335 | self.prev_sibling = Some(PrevSibling::Trait) | ||
336 | } | ||
337 | |||
338 | if has_block_expr_parent(syntax_element.clone()) { | ||
339 | self.completion_location = Some(ImmediateLocation::BlockExpr); | ||
340 | } else if has_bind_pat_parent(syntax_element.clone()) { | ||
341 | self.completion_location = Some(ImmediateLocation::IdentPat); | ||
342 | } else if has_ref_parent(syntax_element.clone()) { | ||
343 | self.completion_location = Some(ImmediateLocation::RefPatOrExpr); | ||
344 | } else if has_impl_parent(syntax_element.clone()) { | ||
345 | self.completion_location = Some(ImmediateLocation::Impl); | ||
346 | } else if has_field_list_parent(syntax_element.clone()) { | ||
347 | self.completion_location = Some(ImmediateLocation::RecordFieldList); | ||
348 | } else if has_trait_parent(syntax_element.clone()) { | ||
349 | self.completion_location = Some(ImmediateLocation::Trait); | ||
350 | } else if has_item_list_or_source_file_parent(syntax_element.clone()) { | ||
351 | self.completion_location = Some(ImmediateLocation::ItemList); | ||
352 | } | ||
353 | |||
285 | self.mod_declaration_under_caret = | 354 | self.mod_declaration_under_caret = |
286 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 355 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
287 | .filter(|module| module.item_list().is_none()); | 356 | .filter(|module| module.item_list().is_none()); |
288 | self.for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
289 | self.fn_is_prev = fn_is_prev(syntax_element.clone()); | ||
290 | self.incomplete_let = | 357 | self.incomplete_let = |
291 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { | 358 | syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { |
292 | it.syntax().text_range().end() == syntax_element.text_range().end() | 359 | it.syntax().text_range().end() == syntax_element.text_range().end() |
293 | }); | 360 | }); |
361 | |||
362 | let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); | ||
363 | let fn_is_prev = self.previous_token_is(T![fn]); | ||
364 | let for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
365 | self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2; | ||
294 | } | 366 | } |
295 | 367 | ||
296 | fn fill_impl_def(&mut self) { | 368 | fn fill_impl_def(&mut self) { |
@@ -412,67 +484,19 @@ impl<'a> CompletionContext<'a> { | |||
412 | self.expected_type = expected_type; | 484 | self.expected_type = expected_type; |
413 | self.expected_name = expected_name; | 485 | self.expected_name = expected_name; |
414 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 486 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
415 | 487 | let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) { | |
416 | if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset) | 488 | Some(it) => it, |
417 | { | 489 | None => return, |
418 | self.classify_lifetime(original_file, lifetime, offset); | 490 | }; |
419 | } | 491 | match name_like { |
420 | 492 | ast::NameLike::Lifetime(lifetime) => { | |
421 | // First, let's try to complete a reference to some declaration. | 493 | self.classify_lifetime(original_file, lifetime, offset); |
422 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { | ||
423 | // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. | ||
424 | // See RFC#1685. | ||
425 | if is_node::<ast::Param>(name_ref.syntax()) { | ||
426 | self.is_param = true; | ||
427 | return; | ||
428 | } | ||
429 | // FIXME: remove this (V) duplication and make the check more precise | ||
430 | if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | ||
431 | self.record_pat_syntax = | ||
432 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
433 | } | ||
434 | self.classify_name_ref(original_file, name_ref, offset); | ||
435 | } | ||
436 | |||
437 | // Otherwise, see if this is a declaration. We can use heuristics to | ||
438 | // suggest declaration names, see `CompletionKind::Magic`. | ||
439 | if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { | ||
440 | if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) { | ||
441 | self.is_pat_binding_or_const = true; | ||
442 | if bind_pat.at_token().is_some() | ||
443 | || bind_pat.ref_token().is_some() | ||
444 | || bind_pat.mut_token().is_some() | ||
445 | { | ||
446 | self.is_pat_binding_or_const = false; | ||
447 | } | ||
448 | if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() { | ||
449 | self.is_pat_binding_or_const = false; | ||
450 | } | ||
451 | if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| { | ||
452 | match_ast! { | ||
453 | match node { | ||
454 | ast::LetStmt(it) => Some(it.pat()), | ||
455 | ast::Param(it) => Some(it.pat()), | ||
456 | _ => None, | ||
457 | } | ||
458 | } | ||
459 | }) { | ||
460 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
461 | self.is_pat_binding_or_const = false; | ||
462 | self.is_irrefutable_pat_binding = true; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | self.fill_impl_def(); | ||
467 | } | 494 | } |
468 | if is_node::<ast::Param>(name.syntax()) { | 495 | ast::NameLike::NameRef(name_ref) => { |
469 | self.is_param = true; | 496 | self.classify_name_ref(original_file, name_ref, offset); |
470 | return; | ||
471 | } | 497 | } |
472 | // FIXME: remove this (^) duplication and make the check more precise | 498 | ast::NameLike::Name(name) => { |
473 | if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { | 499 | self.classify_name(original_file, name, offset); |
474 | self.record_pat_syntax = | ||
475 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
476 | } | 500 | } |
477 | } | 501 | } |
478 | } | 502 | } |
@@ -506,22 +530,71 @@ impl<'a> CompletionContext<'a> { | |||
506 | } | 530 | } |
507 | } | 531 | } |
508 | 532 | ||
533 | fn classify_name(&mut self, original_file: &SyntaxNode, name: ast::Name, offset: TextSize) { | ||
534 | if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) { | ||
535 | self.is_pat_or_const = Some(PatternRefutability::Refutable); | ||
536 | // if any of these is here our bind pat can't be a const pat anymore | ||
537 | let complex_ident_pat = bind_pat.at_token().is_some() | ||
538 | || bind_pat.ref_token().is_some() | ||
539 | || bind_pat.mut_token().is_some(); | ||
540 | if complex_ident_pat { | ||
541 | self.is_pat_or_const = None; | ||
542 | } else { | ||
543 | let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| { | ||
544 | match_ast! { | ||
545 | match node { | ||
546 | ast::LetStmt(it) => Some(it.pat()), | ||
547 | ast::Param(it) => Some(it.pat()), | ||
548 | _ => None, | ||
549 | } | ||
550 | } | ||
551 | }); | ||
552 | if let Some(Some(pat)) = irrefutable_pat { | ||
553 | // This check is here since we could be inside a pattern in the initializer expression of the let statement. | ||
554 | if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) { | ||
555 | self.is_pat_or_const = Some(PatternRefutability::Irrefutable); | ||
556 | } | ||
557 | } | ||
558 | |||
559 | let is_name_in_field_pat = bind_pat | ||
560 | .syntax() | ||
561 | .parent() | ||
562 | .and_then(ast::RecordPatField::cast) | ||
563 | .map_or(false, |pat_field| pat_field.name_ref().is_none()); | ||
564 | if is_name_in_field_pat { | ||
565 | self.is_pat_or_const = None; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | self.fill_impl_def(); | ||
570 | } | ||
571 | self.is_param |= is_node::<ast::Param>(name.syntax()); | ||
572 | if ast::RecordPatField::for_field_name(&name).is_some() { | ||
573 | self.record_pat_syntax = | ||
574 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
575 | } | ||
576 | } | ||
577 | |||
509 | fn classify_name_ref( | 578 | fn classify_name_ref( |
510 | &mut self, | 579 | &mut self, |
511 | original_file: &SyntaxNode, | 580 | original_file: &SyntaxNode, |
512 | name_ref: ast::NameRef, | 581 | name_ref: ast::NameRef, |
513 | offset: TextSize, | 582 | offset: TextSize, |
514 | ) { | 583 | ) { |
515 | self.name_ref_syntax = | 584 | self.fill_impl_def(); |
516 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | ||
517 | let name_range = name_ref.syntax().text_range(); | ||
518 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { | 585 | if ast::RecordExprField::for_field_name(&name_ref).is_some() { |
519 | self.record_lit_syntax = | 586 | self.record_lit_syntax = |
520 | self.sema.find_node_at_offset_with_macros(original_file, offset); | 587 | self.sema.find_node_at_offset_with_macros(original_file, offset); |
521 | } | 588 | } |
589 | if ast::RecordPatField::for_field_name_ref(&name_ref).is_some() { | ||
590 | self.record_pat_syntax = | ||
591 | self.sema.find_node_at_offset_with_macros(&original_file, offset); | ||
592 | } | ||
522 | 593 | ||
523 | self.fill_impl_def(); | 594 | self.name_ref_syntax = |
595 | find_node_at_offset(original_file, name_ref.syntax().text_range().start()); | ||
524 | 596 | ||
597 | let name_range = name_ref.syntax().text_range(); | ||
525 | let top_node = name_ref | 598 | let top_node = name_ref |
526 | .syntax() | 599 | .syntax() |
527 | .ancestors() | 600 | .ancestors() |
@@ -529,31 +602,20 @@ impl<'a> CompletionContext<'a> { | |||
529 | .last() | 602 | .last() |
530 | .unwrap(); | 603 | .unwrap(); |
531 | 604 | ||
532 | match top_node.parent().map(|it| it.kind()) { | 605 | if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) { |
533 | Some(SOURCE_FILE) | Some(ITEM_LIST) => { | 606 | self.is_new_item = true; |
534 | self.is_new_item = true; | 607 | return; |
535 | return; | ||
536 | } | ||
537 | _ => (), | ||
538 | } | 608 | } |
539 | 609 | ||
540 | self.use_item_syntax = | 610 | self.use_item_syntax = |
541 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); | 611 | self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); |
542 | 612 | ||
543 | self.function_syntax = self | 613 | self.function_def = self |
544 | .sema | 614 | .sema |
545 | .token_ancestors_with_macros(self.token.clone()) | 615 | .token_ancestors_with_macros(self.token.clone()) |
546 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | 616 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) |
547 | .find_map(ast::Fn::cast); | 617 | .find_map(ast::Fn::cast); |
548 | 618 | ||
549 | self.record_field_syntax = self | ||
550 | .sema | ||
551 | .token_ancestors_with_macros(self.token.clone()) | ||
552 | .take_while(|it| { | ||
553 | it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR | ||
554 | }) | ||
555 | .find_map(ast::RecordExprField::cast); | ||
556 | |||
557 | let parent = match name_ref.syntax().parent() { | 619 | let parent = match name_ref.syntax().parent() { |
558 | Some(it) => it, | 620 | Some(it) => it, |
559 | None => return, | 621 | None => return, |
@@ -626,6 +688,7 @@ impl<'a> CompletionContext<'a> { | |||
626 | } | 688 | } |
627 | } | 689 | } |
628 | } | 690 | } |
691 | |||
629 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { | 692 | if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { |
630 | // The receiver comes before the point of insertion of the fake | 693 | // The receiver comes before the point of insertion of the fake |
631 | // ident, so it should have the same range in the non-modified file | 694 | // ident, so it should have the same range in the non-modified file |
@@ -643,6 +706,7 @@ impl<'a> CompletionContext<'a> { | |||
643 | false | 706 | false |
644 | }; | 707 | }; |
645 | } | 708 | } |
709 | |||
646 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 710 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
647 | // As above | 711 | // As above |
648 | self.dot_receiver = method_call_expr | 712 | self.dot_receiver = method_call_expr |