Skip to content

Commit a0ec841

Browse files
bors[bot]Veykril
andauthored
Merge #8715
8715: Make CompletionContext expected_type smarter r=Veykril a=Veykril bors r+ Co-authored-by: Lukas Tobias Wirth <lukastw97@gmail.com>
2 parents a234936 + 121bd5c commit a0ec841

File tree

2 files changed

+113
-117
lines changed

2 files changed

+113
-117
lines changed

crates/ide_completion/src/completions/qualified_path.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -736,28 +736,6 @@ fn f() {}
736736
);
737737
}
738738

739-
#[test]
740-
fn completes_function() {
741-
check(
742-
r#"
743-
fn foo(
744-
a: i32,
745-
b: i32
746-
) {
747-
748-
}
749-
750-
fn main() {
751-
fo$0
752-
}
753-
"#,
754-
expect![[r#"
755-
fn main() fn()
756-
fn foo(…) fn(i32, i32)
757-
"#]],
758-
);
759-
}
760-
761739
#[test]
762740
fn completes_self_enum() {
763741
check(

crates/ide_completion/src/context.rs

Lines changed: 113 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -301,103 +301,108 @@ impl<'a> CompletionContext<'a> {
301301
.find_map(ast::Impl::cast);
302302
}
303303

304+
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
305+
let mut node = match self.token.parent() {
306+
Some(it) => it,
307+
None => return (None, None),
308+
};
309+
loop {
310+
break match_ast! {
311+
match node {
312+
ast::LetStmt(it) => {
313+
cov_mark::hit!(expected_type_let_with_leading_char);
314+
cov_mark::hit!(expected_type_let_without_leading_char);
315+
let ty = it.pat()
316+
.and_then(|pat| self.sema.type_of_pat(&pat));
317+
let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
318+
ident.name().map(NameOrNameRef::Name)
319+
} else {
320+
None
321+
};
322+
323+
(ty, name)
324+
},
325+
ast::ArgList(_it) => {
326+
cov_mark::hit!(expected_type_fn_param_with_leading_char);
327+
cov_mark::hit!(expected_type_fn_param_without_leading_char);
328+
ActiveParameter::at_token(
329+
&self.sema,
330+
self.token.clone(),
331+
).map(|ap| {
332+
let name = ap.ident().map(NameOrNameRef::Name);
333+
(Some(ap.ty), name)
334+
})
335+
.unwrap_or((None, None))
336+
},
337+
ast::RecordExprFieldList(_it) => {
338+
cov_mark::hit!(expected_type_struct_field_without_leading_char);
339+
self.token.prev_sibling_or_token()
340+
.and_then(|se| se.into_node())
341+
.and_then(|node| ast::RecordExprField::cast(node))
342+
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
343+
.map(|(f, rf)|(
344+
Some(f.0.ty(self.db)),
345+
rf.field_name().map(NameOrNameRef::NameRef),
346+
))
347+
.unwrap_or((None, None))
348+
},
349+
ast::RecordExprField(it) => {
350+
cov_mark::hit!(expected_type_struct_field_with_leading_char);
351+
self.sema
352+
.resolve_record_field(&it)
353+
.map(|f|(
354+
Some(f.0.ty(self.db)),
355+
it.field_name().map(NameOrNameRef::NameRef),
356+
))
357+
.unwrap_or((None, None))
358+
},
359+
ast::MatchExpr(it) => {
360+
cov_mark::hit!(expected_type_match_arm_without_leading_char);
361+
let ty = it.expr()
362+
.and_then(|e| self.sema.type_of_expr(&e));
363+
(ty, None)
364+
},
365+
ast::IfExpr(it) => {
366+
cov_mark::hit!(expected_type_if_let_without_leading_char);
367+
let ty = it.condition()
368+
.and_then(|cond| cond.expr())
369+
.and_then(|e| self.sema.type_of_expr(&e));
370+
(ty, None)
371+
},
372+
ast::IdentPat(it) => {
373+
cov_mark::hit!(expected_type_if_let_with_leading_char);
374+
cov_mark::hit!(expected_type_match_arm_with_leading_char);
375+
let ty = self.sema.type_of_pat(&ast::Pat::from(it));
376+
(ty, None)
377+
},
378+
ast::Fn(it) => {
379+
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
380+
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
381+
let def = self.sema.to_def(&it);
382+
(def.map(|def| def.ret_type(self.db)), None)
383+
},
384+
ast::Stmt(it) => (None, None),
385+
_ => {
386+
match node.parent() {
387+
Some(n) => {
388+
node = n;
389+
continue;
390+
},
391+
None => (None, None),
392+
}
393+
},
394+
}
395+
};
396+
}
397+
}
398+
304399
fn fill(
305400
&mut self,
306401
original_file: &SyntaxNode,
307402
file_with_fake_ident: SyntaxNode,
308403
offset: TextSize,
309404
) {
310-
let (expected_type, expected_name) = {
311-
let mut node = match self.token.parent() {
312-
Some(it) => it,
313-
None => return,
314-
};
315-
loop {
316-
break match_ast! {
317-
match node {
318-
ast::LetStmt(it) => {
319-
cov_mark::hit!(expected_type_let_with_leading_char);
320-
cov_mark::hit!(expected_type_let_without_leading_char);
321-
let ty = it.pat()
322-
.and_then(|pat| self.sema.type_of_pat(&pat));
323-
let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
324-
ident.name().map(NameOrNameRef::Name)
325-
} else {
326-
None
327-
};
328-
329-
(ty, name)
330-
},
331-
ast::ArgList(_it) => {
332-
cov_mark::hit!(expected_type_fn_param_with_leading_char);
333-
cov_mark::hit!(expected_type_fn_param_without_leading_char);
334-
ActiveParameter::at_token(
335-
&self.sema,
336-
self.token.clone(),
337-
).map(|ap| {
338-
let name = ap.ident().map(NameOrNameRef::Name);
339-
(Some(ap.ty), name)
340-
})
341-
.unwrap_or((None, None))
342-
},
343-
ast::RecordExprFieldList(_it) => {
344-
cov_mark::hit!(expected_type_struct_field_without_leading_char);
345-
self.token.prev_sibling_or_token()
346-
.and_then(|se| se.into_node())
347-
.and_then(|node| ast::RecordExprField::cast(node))
348-
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
349-
.map(|(f, rf)|(
350-
Some(f.0.ty(self.db)),
351-
rf.field_name().map(NameOrNameRef::NameRef),
352-
))
353-
.unwrap_or((None, None))
354-
},
355-
ast::RecordExprField(it) => {
356-
cov_mark::hit!(expected_type_struct_field_with_leading_char);
357-
self.sema
358-
.resolve_record_field(&it)
359-
.map(|f|(
360-
Some(f.0.ty(self.db)),
361-
it.field_name().map(NameOrNameRef::NameRef),
362-
))
363-
.unwrap_or((None, None))
364-
},
365-
ast::MatchExpr(it) => {
366-
cov_mark::hit!(expected_type_match_arm_without_leading_char);
367-
let ty = it.expr()
368-
.and_then(|e| self.sema.type_of_expr(&e));
369-
370-
(ty, None)
371-
},
372-
ast::IdentPat(it) => {
373-
cov_mark::hit!(expected_type_if_let_with_leading_char);
374-
cov_mark::hit!(expected_type_match_arm_with_leading_char);
375-
let ty = self.sema.type_of_pat(&ast::Pat::from(it));
376-
377-
(ty, None)
378-
},
379-
ast::Fn(_it) => {
380-
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
381-
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
382-
let ty = self.token.ancestors()
383-
.find_map(|ancestor| ast::Expr::cast(ancestor))
384-
.and_then(|expr| self.sema.type_of_expr(&expr));
385-
386-
(ty, None)
387-
},
388-
_ => {
389-
match node.parent() {
390-
Some(n) => {
391-
node = n;
392-
continue;
393-
},
394-
None => (None, None),
395-
}
396-
},
397-
}
398-
};
399-
}
400-
};
405+
let (expected_type, expected_name) = self.expected_type_and_name();
401406
self.expected_type = expected_type;
402407
self.expected_name = expected_name;
403408
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
@@ -802,6 +807,7 @@ fn foo() {
802807

803808
#[test]
804809
fn expected_type_if_let_without_leading_char() {
810+
cov_mark::check!(expected_type_if_let_without_leading_char);
805811
check_expected_type_and_name(
806812
r#"
807813
enum Foo { Bar, Baz, Quux }
@@ -811,8 +817,8 @@ fn foo() {
811817
if let $0 = f { }
812818
}
813819
"#,
814-
expect![[r#"ty: (), name: ?"#]],
815-
) // FIXME should be `ty: u32, name: ?`
820+
expect![[r#"ty: Foo, name: ?"#]],
821+
)
816822
}
817823

818824
#[test]
@@ -840,8 +846,8 @@ fn foo() -> u32 {
840846
$0
841847
}
842848
"#,
843-
expect![[r#"ty: (), name: ?"#]],
844-
) // FIXME this should be `ty: u32, name: ?`
849+
expect![[r#"ty: u32, name: ?"#]],
850+
)
845851
}
846852

847853
#[test]
@@ -852,6 +858,18 @@ fn foo() -> u32 {
852858
fn foo() -> u32 {
853859
c$0
854860
}
861+
"#,
862+
expect![[r#"ty: u32, name: ?"#]],
863+
)
864+
}
865+
866+
#[test]
867+
fn expected_type_fn_ret_fn_ref_fully_typed() {
868+
check_expected_type_and_name(
869+
r#"
870+
fn foo() -> u32 {
871+
foo$0
872+
}
855873
"#,
856874
expect![[r#"ty: u32, name: ?"#]],
857875
)

0 commit comments

Comments
 (0)