aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/context.rs')
-rw-r--r--crates/ide_completion/src/context.rs495
1 files changed, 280 insertions, 215 deletions
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 787eb2fd3..7c46c815d 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -1,29 +1,34 @@
1//! See `CompletionContext` structure. 1//! See `CompletionContext` structure.
2 2
3use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type}; 3use hir::{Local, ScopeDef, Semantics, SemanticsScope, Type};
4use ide_db::base_db::{FilePosition, SourceDatabase}; 4use ide_db::{
5use ide_db::{call_info::ActiveParameter, RootDatabase}; 5 base_db::{FilePosition, SourceDatabase},
6 call_info::ActiveParameter,
7 RootDatabase,
8};
6use syntax::{ 9use 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
14use text_edit::Indel; 16use text_edit::Indel;
15 17
16use crate::{ 18use crate::{
17 patterns::{ 19 patterns::{
18 fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, 20 determine_location, determine_prev_sibling, for_is_prev2, inside_impl_trait_block,
19 has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, 21 is_in_loop_body, previous_token, ImmediateLocation, ImmediatePrevSibling,
20 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
21 has_trait_parent, if_is_prev, inside_impl_trait_block, is_in_loop_body, is_match_arm,
22 unsafe_is_prev,
23 }, 22 },
24 CompletionConfig, 23 CompletionConfig,
25}; 24};
26 25
26#[derive(Copy, Clone, Debug, PartialEq, Eq)]
27pub(crate) enum PatternRefutability {
28 Refutable,
29 Irrefutable,
30}
31
27/// `CompletionContext` is created early during completion to figure out, where 32/// `CompletionContext` is created early during completion to figure out, where
28/// exactly is the cursor, syntax-wise. 33/// exactly is the cursor, syntax-wise.
29#[derive(Debug)] 34#[derive(Debug)]
@@ -41,28 +46,34 @@ pub(crate) struct CompletionContext<'a> {
41 pub(super) expected_name: Option<NameOrNameRef>, 46 pub(super) expected_name: Option<NameOrNameRef>,
42 pub(super) expected_type: Option<Type>, 47 pub(super) expected_type: Option<Type>,
43 pub(super) name_ref_syntax: Option<ast::NameRef>, 48 pub(super) name_ref_syntax: Option<ast::NameRef>,
44 pub(super) lifetime_syntax: Option<ast::Lifetime>, 49
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>, 50 pub(super) use_item_syntax: Option<ast::Use>,
48 pub(super) record_lit_syntax: Option<ast::RecordExpr>, 51
49 pub(super) record_pat_syntax: Option<ast::RecordPat>, 52 /// The parent function of the cursor position if it exists.
50 pub(super) record_field_syntax: Option<ast::RecordExprField>, 53 pub(super) function_def: Option<ast::Fn>,
54 /// The parent impl of the cursor position if it exists.
51 pub(super) impl_def: Option<ast::Impl>, 55 pub(super) impl_def: Option<ast::Impl>,
56
57 // potentially set if we are completing a lifetime
58 pub(super) lifetime_syntax: Option<ast::Lifetime>,
59 pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
52 pub(super) lifetime_allowed: bool, 60 pub(super) lifetime_allowed: bool,
61 pub(super) is_label_ref: bool,
62
63 // potentially set if we are completing a name
64 pub(super) is_pat_or_const: Option<PatternRefutability>,
65 pub(super) is_param: bool,
66
67 pub(super) completion_location: Option<ImmediateLocation>,
68 pub(super) prev_sibling: Option<ImmediatePrevSibling>,
69 pub(super) attribute_under_caret: Option<ast::Attr>,
70
53 /// FIXME: `ActiveParameter` is string-based, which is very very wrong 71 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
54 pub(super) active_parameter: Option<ActiveParameter>, 72 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. 73 /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
62 pub(super) is_trivial_path: bool, 74 pub(super) is_trivial_path: bool,
63 /// If not a trivial path, the prefix (qualifier). 75 /// If not a trivial path, the prefix (qualifier).
64 pub(super) path_qual: Option<ast::Path>, 76 pub(super) path_qual: Option<ast::Path>,
65 pub(super) after_if: bool,
66 /// `true` if we are a statement or a last expr in the block. 77 /// `true` if we are a statement or a last expr in the block.
67 pub(super) can_be_stmt: bool, 78 pub(super) can_be_stmt: bool,
68 /// `true` if we expect an expression at the cursor position. 79 /// `true` if we expect an expression at the cursor position.
@@ -80,26 +91,13 @@ pub(crate) struct CompletionContext<'a> {
80 pub(super) is_macro_call: bool, 91 pub(super) is_macro_call: bool,
81 pub(super) is_path_type: bool, 92 pub(super) is_path_type: bool,
82 pub(super) has_type_args: bool, 93 pub(super) has_type_args: bool,
83 pub(super) attribute_under_caret: Option<ast::Attr>, 94 pub(super) locals: Vec<(String, Local)>,
84 pub(super) mod_declaration_under_caret: Option<ast::Module>, 95
85 pub(super) unsafe_is_prev: bool, 96 pub(super) previous_token: Option<SyntaxToken>,
86 pub(super) if_is_prev: bool,
87 pub(super) block_expr_parent: bool,
88 pub(super) bind_pat_parent: bool,
89 pub(super) ref_pat_parent: bool,
90 pub(super) in_loop_body: bool, 97 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,
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, 98 pub(super) incomplete_let: bool,
102 pub(super) locals: Vec<(String, Local)>, 99
100 no_completion_required: bool,
103} 101}
104 102
105impl<'a> CompletionContext<'a> { 103impl<'a> CompletionContext<'a> {
@@ -149,20 +147,15 @@ impl<'a> CompletionContext<'a> {
149 name_ref_syntax: None, 147 name_ref_syntax: None,
150 lifetime_syntax: None, 148 lifetime_syntax: None,
151 lifetime_param_syntax: None, 149 lifetime_param_syntax: None,
152 function_syntax: None, 150 function_def: None,
153 use_item_syntax: None, 151 use_item_syntax: None,
154 record_lit_syntax: None,
155 record_pat_syntax: None,
156 record_field_syntax: None,
157 impl_def: None, 152 impl_def: None,
158 active_parameter: ActiveParameter::at(db, position), 153 active_parameter: ActiveParameter::at(db, position),
159 is_label_ref: false, 154 is_label_ref: false,
160 is_param: false, 155 is_param: false,
161 is_pat_binding_or_const: false, 156 is_pat_or_const: None,
162 is_irrefutable_pat_binding: false,
163 is_trivial_path: false, 157 is_trivial_path: false,
164 path_qual: None, 158 path_qual: None,
165 after_if: false,
166 can_be_stmt: false, 159 can_be_stmt: false,
167 is_expr: false, 160 is_expr: false,
168 is_new_item: false, 161 is_new_item: false,
@@ -173,69 +166,56 @@ impl<'a> CompletionContext<'a> {
173 is_macro_call: false, 166 is_macro_call: false,
174 is_path_type: false, 167 is_path_type: false,
175 has_type_args: false, 168 has_type_args: false,
176 attribute_under_caret: None, 169 previous_token: None,
177 mod_declaration_under_caret: None,
178 unsafe_is_prev: false,
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, 170 in_loop_body: false,
184 has_trait_parent: false, 171 completion_location: None,
185 has_impl_parent: false, 172 prev_sibling: None,
186 inside_impl_trait_block: false, 173 no_completion_required: false,
187 has_field_list_parent: false,
188 trait_as_prev_sibling: false,
189 impl_as_prev_sibling: false,
190 is_match_arm: false,
191 has_item_list_or_source_file_parent: false,
192 for_is_prev2: false,
193 fn_is_prev: false,
194 incomplete_let: false, 174 incomplete_let: false,
175 attribute_under_caret: None,
195 locals, 176 locals,
196 }; 177 };
197 178
198 let mut original_file = original_file.syntax().clone(); 179 let mut original_file = original_file.syntax().clone();
199 let mut hypothetical_file = file_with_fake_ident.syntax().clone(); 180 let mut speculative_file = file_with_fake_ident.syntax().clone();
200 let mut offset = position.offset; 181 let mut offset = position.offset;
201 let mut fake_ident_token = fake_ident_token; 182 let mut fake_ident_token = fake_ident_token;
202 183
203 // Are we inside a macro call? 184 // Are we inside a macro call?
204 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = ( 185 while let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
205 find_node_at_offset::<ast::MacroCall>(&original_file, offset), 186 find_node_at_offset::<ast::MacroCall>(&original_file, offset),
206 find_node_at_offset::<ast::MacroCall>(&hypothetical_file, offset), 187 find_node_at_offset::<ast::MacroCall>(&speculative_file, offset),
207 ) { 188 ) {
208 if actual_macro_call.path().as_ref().map(|s| s.syntax().text()) 189 if actual_macro_call.path().as_ref().map(|s| s.syntax().text())
209 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text()) 190 != macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text())
210 { 191 {
211 break; 192 break;
212 } 193 }
213 let hypothetical_args = match macro_call_with_fake_ident.token_tree() { 194 let speculative_args = match macro_call_with_fake_ident.token_tree() {
214 Some(tt) => tt, 195 Some(tt) => tt,
215 None => break, 196 None => break,
216 }; 197 };
217 if let (Some(actual_expansion), Some(hypothetical_expansion)) = ( 198 if let (Some(actual_expansion), Some(speculative_expansion)) = (
218 ctx.sema.expand(&actual_macro_call), 199 ctx.sema.expand(&actual_macro_call),
219 ctx.sema.speculative_expand( 200 ctx.sema.speculative_expand(
220 &actual_macro_call, 201 &actual_macro_call,
221 &hypothetical_args, 202 &speculative_args,
222 fake_ident_token, 203 fake_ident_token,
223 ), 204 ),
224 ) { 205 ) {
225 let new_offset = hypothetical_expansion.1.text_range().start(); 206 let new_offset = speculative_expansion.1.text_range().start();
226 if new_offset > actual_expansion.text_range().end() { 207 if new_offset > actual_expansion.text_range().end() {
227 break; 208 break;
228 } 209 }
229 original_file = actual_expansion; 210 original_file = actual_expansion;
230 hypothetical_file = hypothetical_expansion.0; 211 speculative_file = speculative_expansion.0;
231 fake_ident_token = hypothetical_expansion.1; 212 fake_ident_token = speculative_expansion.1;
232 offset = new_offset; 213 offset = new_offset;
233 } else { 214 } else {
234 break; 215 break;
235 } 216 }
236 } 217 }
237 ctx.fill_keyword_patterns(&hypothetical_file, offset); 218 ctx.fill(&original_file, speculative_file, offset);
238 ctx.fill(&original_file, hypothetical_file, offset);
239 Some(ctx) 219 Some(ctx)
240 } 220 }
241 221
@@ -245,7 +225,7 @@ impl<'a> CompletionContext<'a> {
245 /// Exception for this case is `impl Trait for Foo`, where we would like to hint trait method names. 225 /// 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. 226 /// - `for _ i$0` -- obviously, it'll be "in" keyword.
247 pub(crate) fn no_completion_required(&self) -> bool { 227 pub(crate) fn no_completion_required(&self) -> bool {
248 (self.fn_is_prev && !self.inside_impl_trait_block) || self.for_is_prev2 228 self.no_completion_required
249 } 229 }
250 230
251 /// The range of the identifier that is being completed. 231 /// The range of the identifier that is being completed.
@@ -264,33 +244,67 @@ impl<'a> CompletionContext<'a> {
264 } 244 }
265 } 245 }
266 246
267 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { 247 pub(crate) fn previous_token_is(&self, kind: SyntaxKind) -> bool {
268 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); 248 self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
269 let syntax_element = NodeOrToken::Token(fake_ident_token); 249 }
270 self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); 250
271 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); 251 pub(crate) fn expects_assoc_item(&self) -> bool {
272 self.if_is_prev = if_is_prev(syntax_element.clone()); 252 matches!(
273 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); 253 self.completion_location,
274 self.ref_pat_parent = has_ref_parent(syntax_element.clone()); 254 Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
275 self.in_loop_body = is_in_loop_body(syntax_element.clone()); 255 )
276 self.has_trait_parent = has_trait_parent(syntax_element.clone()); 256 }
277 self.has_impl_parent = has_impl_parent(syntax_element.clone()); 257
278 self.inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone()); 258 pub(crate) fn expects_use_tree(&self) -> bool {
279 self.has_field_list_parent = has_field_list_parent(syntax_element.clone()); 259 matches!(self.completion_location, Some(ImmediateLocation::Use))
280 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); 260 }
281 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); 261
282 self.is_match_arm = is_match_arm(syntax_element.clone()); 262 pub(crate) fn expects_non_trait_assoc_item(&self) -> bool {
283 self.has_item_list_or_source_file_parent = 263 matches!(self.completion_location, Some(ImmediateLocation::Impl))
284 has_item_list_or_source_file_parent(syntax_element.clone()); 264 }
285 self.mod_declaration_under_caret = 265
286 find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) 266 pub(crate) fn expects_item(&self) -> bool {
287 .filter(|module| module.item_list().is_none()); 267 matches!(self.completion_location, Some(ImmediateLocation::ItemList))
288 self.for_is_prev2 = for_is_prev2(syntax_element.clone()); 268 }
289 self.fn_is_prev = fn_is_prev(syntax_element.clone()); 269
290 self.incomplete_let = 270 pub(crate) fn expects_expression(&self) -> bool {
291 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| { 271 self.is_expr
292 it.syntax().text_range().end() == syntax_element.text_range().end() 272 }
293 }); 273
274 pub(crate) fn has_block_expr_parent(&self) -> bool {
275 matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
276 }
277
278 pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
279 matches!(
280 self.completion_location,
281 Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
282 )
283 }
284
285 pub(crate) fn expect_record_field(&self) -> bool {
286 matches!(self.completion_location, Some(ImmediateLocation::RecordField))
287 }
288
289 pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
290 matches!(
291 self.prev_sibling,
292 Some(ImmediatePrevSibling::ImplDefType) | Some(ImmediatePrevSibling::TraitDefName)
293 )
294 }
295
296 pub(crate) fn after_if(&self) -> bool {
297 matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
298 }
299
300 pub(crate) fn is_path_disallowed(&self) -> bool {
301 matches!(
302 self.completion_location,
303 Some(ImmediateLocation::Attribute(_))
304 | Some(ImmediateLocation::ModDeclaration(_))
305 | Some(ImmediateLocation::RecordPat(_))
306 | Some(ImmediateLocation::RecordExpr(_))
307 ) || self.attribute_under_caret.is_some()
294 } 308 }
295 309
296 fn fill_impl_def(&mut self) { 310 fn fill_impl_def(&mut self) {
@@ -337,25 +351,24 @@ impl<'a> CompletionContext<'a> {
337 }, 351 },
338 ast::RecordExprFieldList(_it) => { 352 ast::RecordExprFieldList(_it) => {
339 cov_mark::hit!(expected_type_struct_field_without_leading_char); 353 cov_mark::hit!(expected_type_struct_field_without_leading_char);
340 self.token.prev_sibling_or_token() 354 // wouldn't try {} be nice...
341 .and_then(|se| se.into_node()) 355 (|| {
342 .and_then(|node| ast::RecordExprField::cast(node)) 356 let expr_field = self.token.prev_sibling_or_token()?
343 .and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf))) 357 .into_node()
344 .map(|(f, rf)|( 358 .and_then(|node| ast::RecordExprField::cast(node))?;
345 Some(f.0.ty(self.db)), 359 let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
346 rf.field_name().map(NameOrNameRef::NameRef), 360 Some((
361 Some(ty),
362 expr_field.field_name().map(NameOrNameRef::NameRef),
347 )) 363 ))
348 .unwrap_or((None, None)) 364 })().unwrap_or((None, None))
349 }, 365 },
350 ast::RecordExprField(it) => { 366 ast::RecordExprField(it) => {
351 cov_mark::hit!(expected_type_struct_field_with_leading_char); 367 cov_mark::hit!(expected_type_struct_field_with_leading_char);
352 self.sema 368 (
353 .resolve_record_field(&it) 369 it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)),
354 .map(|f|( 370 it.field_name().map(NameOrNameRef::NameRef),
355 Some(f.0.ty(self.db)), 371 )
356 it.field_name().map(NameOrNameRef::NameRef),
357 ))
358 .unwrap_or((None, None))
359 }, 372 },
360 ast::MatchExpr(it) => { 373 ast::MatchExpr(it) => {
361 cov_mark::hit!(expected_type_match_arm_without_leading_char); 374 cov_mark::hit!(expected_type_match_arm_without_leading_char);
@@ -382,6 +395,12 @@ impl<'a> CompletionContext<'a> {
382 let def = self.sema.to_def(&it); 395 let def = self.sema.to_def(&it);
383 (def.map(|def| def.ret_type(self.db)), None) 396 (def.map(|def| def.ret_type(self.db)), None)
384 }, 397 },
398 ast::ClosureExpr(it) => {
399 let ty = self.sema.type_of_expr(&it.into());
400 ty.and_then(|ty| ty.as_callable(self.db))
401 .map(|c| (Some(c.return_type()), None))
402 .unwrap_or((None, None))
403 },
385 ast::Stmt(_it) => (None, None), 404 ast::Stmt(_it) => (None, None),
386 _ => { 405 _ => {
387 match node.parent() { 406 match node.parent() {
@@ -403,71 +422,43 @@ impl<'a> CompletionContext<'a> {
403 file_with_fake_ident: SyntaxNode, 422 file_with_fake_ident: SyntaxNode,
404 offset: TextSize, 423 offset: TextSize,
405 ) { 424 ) {
425 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
426 let syntax_element = NodeOrToken::Token(fake_ident_token);
427 self.previous_token = previous_token(syntax_element.clone());
428 self.attribute_under_caret = syntax_element.ancestors().find_map(ast::Attr::cast);
429 self.no_completion_required = {
430 let inside_impl_trait_block = inside_impl_trait_block(syntax_element.clone());
431 let fn_is_prev = self.previous_token_is(T![fn]);
432 let for_is_prev2 = for_is_prev2(syntax_element.clone());
433 (fn_is_prev && !inside_impl_trait_block) || for_is_prev2
434 };
435 self.in_loop_body = is_in_loop_body(syntax_element.clone());
436
437 self.incomplete_let =
438 syntax_element.ancestors().take(6).find_map(ast::LetStmt::cast).map_or(false, |it| {
439 it.syntax().text_range().end() == syntax_element.text_range().end()
440 });
441
406 let (expected_type, expected_name) = self.expected_type_and_name(); 442 let (expected_type, expected_name) = self.expected_type_and_name();
407 self.expected_type = expected_type; 443 self.expected_type = expected_type;
408 self.expected_name = expected_name; 444 self.expected_name = expected_name;
409 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
410
411 if let Some(lifetime) = find_node_at_offset::<ast::Lifetime>(&file_with_fake_ident, offset)
412 {
413 self.classify_lifetime(original_file, lifetime, offset);
414 }
415
416 // First, let's try to complete a reference to some declaration.
417 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
418 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
419 // See RFC#1685.
420 if is_node::<ast::Param>(name_ref.syntax()) {
421 self.is_param = true;
422 return;
423 }
424 // FIXME: remove this (V) duplication and make the check more precise
425 if name_ref.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() {
426 self.record_pat_syntax =
427 self.sema.find_node_at_offset_with_macros(&original_file, offset);
428 }
429 self.classify_name_ref(original_file, name_ref, offset);
430 }
431
432 // Otherwise, see if this is a declaration. We can use heuristics to
433 // suggest declaration names, see `CompletionKind::Magic`.
434 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
435 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::IdentPat::cast) {
436 self.is_pat_binding_or_const = true;
437 if bind_pat.at_token().is_some()
438 || bind_pat.ref_token().is_some()
439 || bind_pat.mut_token().is_some()
440 {
441 self.is_pat_binding_or_const = false;
442 }
443 if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
444 self.is_pat_binding_or_const = false;
445 }
446 if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
447 match_ast! {
448 match node {
449 ast::LetStmt(it) => Some(it.pat()),
450 ast::Param(it) => Some(it.pat()),
451 _ => None,
452 }
453 }
454 }) {
455 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
456 self.is_pat_binding_or_const = false;
457 self.is_irrefutable_pat_binding = true;
458 }
459 }
460 445
461 self.fill_impl_def(); 446 let name_like = match find_node_at_offset(&&file_with_fake_ident, offset) {
447 Some(it) => it,
448 None => return,
449 };
450 self.completion_location =
451 determine_location(&self.sema, original_file, offset, &name_like);
452 self.prev_sibling = determine_prev_sibling(&name_like);
453 match name_like {
454 ast::NameLike::Lifetime(lifetime) => {
455 self.classify_lifetime(original_file, lifetime, offset);
462 } 456 }
463 if is_node::<ast::Param>(name.syntax()) { 457 ast::NameLike::NameRef(name_ref) => {
464 self.is_param = true; 458 self.classify_name_ref(original_file, name_ref);
465 return;
466 } 459 }
467 // FIXME: remove this (^) duplication and make the check more precise 460 ast::NameLike::Name(name) => {
468 if name.syntax().ancestors().find_map(ast::RecordPatFieldList::cast).is_some() { 461 self.classify_name(name);
469 self.record_pat_syntax =
470 self.sema.find_node_at_offset_with_macros(&original_file, offset);
471 } 462 }
472 } 463 }
473 } 464 }
@@ -501,22 +492,55 @@ impl<'a> CompletionContext<'a> {
501 } 492 }
502 } 493 }
503 494
504 fn classify_name_ref( 495 fn classify_name(&mut self, name: ast::Name) {
505 &mut self, 496 if let Some(bind_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
506 original_file: &SyntaxNode, 497 self.is_pat_or_const = Some(PatternRefutability::Refutable);
507 name_ref: ast::NameRef, 498 // if any of these is here our bind pat can't be a const pat anymore
508 offset: TextSize, 499 let complex_ident_pat = bind_pat.at_token().is_some()
509 ) { 500 || bind_pat.ref_token().is_some()
510 self.name_ref_syntax = 501 || bind_pat.mut_token().is_some();
511 find_node_at_offset(original_file, name_ref.syntax().text_range().start()); 502 if complex_ident_pat {
512 let name_range = name_ref.syntax().text_range(); 503 self.is_pat_or_const = None;
513 if ast::RecordExprField::for_field_name(&name_ref).is_some() { 504 } else {
514 self.record_lit_syntax = 505 let irrefutable_pat = bind_pat.syntax().ancestors().find_map(|node| {
515 self.sema.find_node_at_offset_with_macros(original_file, offset); 506 match_ast! {
507 match node {
508 ast::LetStmt(it) => Some(it.pat()),
509 ast::Param(it) => Some(it.pat()),
510 _ => None,
511 }
512 }
513 });
514 if let Some(Some(pat)) = irrefutable_pat {
515 // This check is here since we could be inside a pattern in the initializer expression of the let statement.
516 if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
517 self.is_pat_or_const = Some(PatternRefutability::Irrefutable);
518 }
519 }
520
521 let is_name_in_field_pat = bind_pat
522 .syntax()
523 .parent()
524 .and_then(ast::RecordPatField::cast)
525 .map_or(false, |pat_field| pat_field.name_ref().is_none());
526 if is_name_in_field_pat {
527 self.is_pat_or_const = None;
528 }
529 }
530
531 self.fill_impl_def();
516 } 532 }
517 533
534 self.is_param |= is_node::<ast::Param>(name.syntax());
535 }
536
537 fn classify_name_ref(&mut self, original_file: &SyntaxNode, name_ref: ast::NameRef) {
518 self.fill_impl_def(); 538 self.fill_impl_def();
519 539
540 self.name_ref_syntax =
541 find_node_at_offset(original_file, name_ref.syntax().text_range().start());
542
543 let name_range = name_ref.syntax().text_range();
520 let top_node = name_ref 544 let top_node = name_ref
521 .syntax() 545 .syntax()
522 .ancestors() 546 .ancestors()
@@ -524,31 +548,20 @@ impl<'a> CompletionContext<'a> {
524 .last() 548 .last()
525 .unwrap(); 549 .unwrap();
526 550
527 match top_node.parent().map(|it| it.kind()) { 551 if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
528 Some(SOURCE_FILE) | Some(ITEM_LIST) => { 552 self.is_new_item = true;
529 self.is_new_item = true; 553 return;
530 return;
531 }
532 _ => (),
533 } 554 }
534 555
535 self.use_item_syntax = 556 self.use_item_syntax =
536 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast); 557 self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
537 558
538 self.function_syntax = self 559 self.function_def = self
539 .sema 560 .sema
540 .token_ancestors_with_macros(self.token.clone()) 561 .token_ancestors_with_macros(self.token.clone())
541 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 562 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
542 .find_map(ast::Fn::cast); 563 .find_map(ast::Fn::cast);
543 564
544 self.record_field_syntax = self
545 .sema
546 .token_ancestors_with_macros(self.token.clone())
547 .take_while(|it| {
548 it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
549 })
550 .find_map(ast::RecordExprField::cast);
551
552 let parent = match name_ref.syntax().parent() { 565 let parent = match name_ref.syntax().parent() {
553 Some(it) => it, 566 Some(it) => it,
554 None => return, 567 None => return,
@@ -609,18 +622,8 @@ impl<'a> CompletionContext<'a> {
609 }) 622 })
610 .unwrap_or(false); 623 .unwrap_or(false);
611 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some(); 624 self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
612
613 if let Some(off) = name_ref.syntax().text_range().start().checked_sub(2.into()) {
614 if let Some(if_expr) =
615 self.sema.find_node_at_offset_with_macros::<ast::IfExpr>(original_file, off)
616 {
617 if if_expr.syntax().text_range().end() < name_ref.syntax().text_range().start()
618 {
619 self.after_if = true;
620 }
621 }
622 }
623 } 625 }
626
624 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) { 627 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
625 // The receiver comes before the point of insertion of the fake 628 // The receiver comes before the point of insertion of the fake
626 // ident, so it should have the same range in the non-modified file 629 // ident, so it should have the same range in the non-modified file
@@ -638,6 +641,7 @@ impl<'a> CompletionContext<'a> {
638 false 641 false
639 }; 642 };
640 } 643 }
644
641 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { 645 if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
642 // As above 646 // As above
643 self.dot_receiver = method_call_expr 647 self.dot_receiver = method_call_expr
@@ -785,6 +789,19 @@ fn foo() {
785 } 789 }
786 790
787 #[test] 791 #[test]
792 fn expected_type_generic_struct_field() {
793 check_expected_type_and_name(
794 r#"
795struct Foo<T> { a: T }
796fn foo() -> Foo<u32> {
797 Foo { a: $0 }
798}
799"#,
800 expect![[r#"ty: u32, name: a"#]],
801 )
802 }
803
804 #[test]
788 fn expected_type_struct_field_with_leading_char() { 805 fn expected_type_struct_field_with_leading_char() {
789 cov_mark::check!(expected_type_struct_field_with_leading_char); 806 cov_mark::check!(expected_type_struct_field_with_leading_char);
790 check_expected_type_and_name( 807 check_expected_type_and_name(
@@ -895,4 +912,52 @@ fn foo() -> u32 {
895 expect![[r#"ty: u32, name: ?"#]], 912 expect![[r#"ty: u32, name: ?"#]],
896 ) 913 )
897 } 914 }
915
916 #[test]
917 fn expected_type_closure_param_return() {
918 // FIXME: make this work with `|| $0`
919 check_expected_type_and_name(
920 r#"
921fn foo() {
922 bar(|| a$0);
923}
924
925fn bar(f: impl FnOnce() -> u32) {}
926#[lang = "fn_once"]
927trait FnOnce { type Output; }
928"#,
929 expect![[r#"ty: u32, name: ?"#]],
930 );
931 }
932
933 #[test]
934 fn expected_type_generic_function() {
935 check_expected_type_and_name(
936 r#"
937fn foo() {
938 bar::<u32>($0);
939}
940
941fn bar<T>(t: T) {}
942"#,
943 expect![[r#"ty: u32, name: t"#]],
944 );
945 }
946
947 #[test]
948 fn expected_type_generic_method() {
949 check_expected_type_and_name(
950 r#"
951fn foo() {
952 S(1u32).bar($0);
953}
954
955struct S<T>(T);
956impl<T> S<T> {
957 fn bar(self, t: T) {}
958}
959"#,
960 expect![[r#"ty: u32, name: t"#]],
961 );
962 }
898} 963}