Make CompletionContext expected_type smarter
This commit is contained in:
parent
544a93ee08
commit
121bd5c533
@ -736,28 +736,6 @@ fn f() {}
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn completes_function() {
|
|
||||||
check(
|
|
||||||
r#"
|
|
||||||
fn foo(
|
|
||||||
a: i32,
|
|
||||||
b: i32
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
fo$0
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
expect![[r#"
|
|
||||||
fn main() fn()
|
|
||||||
fn foo(…) fn(i32, i32)
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_self_enum() {
|
fn completes_self_enum() {
|
||||||
check(
|
check(
|
||||||
|
@ -301,103 +301,108 @@ fn fill_impl_def(&mut self) {
|
|||||||
.find_map(ast::Impl::cast);
|
.find_map(ast::Impl::cast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expected_type_and_name(&self) -> (Option<Type>, Option<NameOrNameRef>) {
|
||||||
|
let mut node = match self.token.parent() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return (None, None),
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
break match_ast! {
|
||||||
|
match node {
|
||||||
|
ast::LetStmt(it) => {
|
||||||
|
cov_mark::hit!(expected_type_let_with_leading_char);
|
||||||
|
cov_mark::hit!(expected_type_let_without_leading_char);
|
||||||
|
let ty = it.pat()
|
||||||
|
.and_then(|pat| self.sema.type_of_pat(&pat));
|
||||||
|
let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
|
||||||
|
ident.name().map(NameOrNameRef::Name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(ty, name)
|
||||||
|
},
|
||||||
|
ast::ArgList(_it) => {
|
||||||
|
cov_mark::hit!(expected_type_fn_param_with_leading_char);
|
||||||
|
cov_mark::hit!(expected_type_fn_param_without_leading_char);
|
||||||
|
ActiveParameter::at_token(
|
||||||
|
&self.sema,
|
||||||
|
self.token.clone(),
|
||||||
|
).map(|ap| {
|
||||||
|
let name = ap.ident().map(NameOrNameRef::Name);
|
||||||
|
(Some(ap.ty), name)
|
||||||
|
})
|
||||||
|
.unwrap_or((None, None))
|
||||||
|
},
|
||||||
|
ast::RecordExprFieldList(_it) => {
|
||||||
|
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
||||||
|
self.token.prev_sibling_or_token()
|
||||||
|
.and_then(|se| se.into_node())
|
||||||
|
.and_then(|node| ast::RecordExprField::cast(node))
|
||||||
|
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
|
||||||
|
.map(|(f, rf)|(
|
||||||
|
Some(f.0.ty(self.db)),
|
||||||
|
rf.field_name().map(NameOrNameRef::NameRef),
|
||||||
|
))
|
||||||
|
.unwrap_or((None, None))
|
||||||
|
},
|
||||||
|
ast::RecordExprField(it) => {
|
||||||
|
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
||||||
|
self.sema
|
||||||
|
.resolve_record_field(&it)
|
||||||
|
.map(|f|(
|
||||||
|
Some(f.0.ty(self.db)),
|
||||||
|
it.field_name().map(NameOrNameRef::NameRef),
|
||||||
|
))
|
||||||
|
.unwrap_or((None, None))
|
||||||
|
},
|
||||||
|
ast::MatchExpr(it) => {
|
||||||
|
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
||||||
|
let ty = it.expr()
|
||||||
|
.and_then(|e| self.sema.type_of_expr(&e));
|
||||||
|
(ty, None)
|
||||||
|
},
|
||||||
|
ast::IfExpr(it) => {
|
||||||
|
cov_mark::hit!(expected_type_if_let_without_leading_char);
|
||||||
|
let ty = it.condition()
|
||||||
|
.and_then(|cond| cond.expr())
|
||||||
|
.and_then(|e| self.sema.type_of_expr(&e));
|
||||||
|
(ty, None)
|
||||||
|
},
|
||||||
|
ast::IdentPat(it) => {
|
||||||
|
cov_mark::hit!(expected_type_if_let_with_leading_char);
|
||||||
|
cov_mark::hit!(expected_type_match_arm_with_leading_char);
|
||||||
|
let ty = self.sema.type_of_pat(&ast::Pat::from(it));
|
||||||
|
(ty, None)
|
||||||
|
},
|
||||||
|
ast::Fn(it) => {
|
||||||
|
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
|
||||||
|
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
|
||||||
|
let def = self.sema.to_def(&it);
|
||||||
|
(def.map(|def| def.ret_type(self.db)), None)
|
||||||
|
},
|
||||||
|
ast::Stmt(it) => (None, None),
|
||||||
|
_ => {
|
||||||
|
match node.parent() {
|
||||||
|
Some(n) => {
|
||||||
|
node = n;
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
None => (None, None),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fill(
|
fn fill(
|
||||||
&mut self,
|
&mut self,
|
||||||
original_file: &SyntaxNode,
|
original_file: &SyntaxNode,
|
||||||
file_with_fake_ident: SyntaxNode,
|
file_with_fake_ident: SyntaxNode,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) {
|
) {
|
||||||
let (expected_type, expected_name) = {
|
let (expected_type, expected_name) = self.expected_type_and_name();
|
||||||
let mut node = match self.token.parent() {
|
|
||||||
Some(it) => it,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
loop {
|
|
||||||
break match_ast! {
|
|
||||||
match node {
|
|
||||||
ast::LetStmt(it) => {
|
|
||||||
cov_mark::hit!(expected_type_let_with_leading_char);
|
|
||||||
cov_mark::hit!(expected_type_let_without_leading_char);
|
|
||||||
let ty = it.pat()
|
|
||||||
.and_then(|pat| self.sema.type_of_pat(&pat));
|
|
||||||
let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
|
|
||||||
ident.name().map(NameOrNameRef::Name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
(ty, name)
|
|
||||||
},
|
|
||||||
ast::ArgList(_it) => {
|
|
||||||
cov_mark::hit!(expected_type_fn_param_with_leading_char);
|
|
||||||
cov_mark::hit!(expected_type_fn_param_without_leading_char);
|
|
||||||
ActiveParameter::at_token(
|
|
||||||
&self.sema,
|
|
||||||
self.token.clone(),
|
|
||||||
).map(|ap| {
|
|
||||||
let name = ap.ident().map(NameOrNameRef::Name);
|
|
||||||
(Some(ap.ty), name)
|
|
||||||
})
|
|
||||||
.unwrap_or((None, None))
|
|
||||||
},
|
|
||||||
ast::RecordExprFieldList(_it) => {
|
|
||||||
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
|
||||||
self.token.prev_sibling_or_token()
|
|
||||||
.and_then(|se| se.into_node())
|
|
||||||
.and_then(|node| ast::RecordExprField::cast(node))
|
|
||||||
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
|
|
||||||
.map(|(f, rf)|(
|
|
||||||
Some(f.0.ty(self.db)),
|
|
||||||
rf.field_name().map(NameOrNameRef::NameRef),
|
|
||||||
))
|
|
||||||
.unwrap_or((None, None))
|
|
||||||
},
|
|
||||||
ast::RecordExprField(it) => {
|
|
||||||
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
|
||||||
self.sema
|
|
||||||
.resolve_record_field(&it)
|
|
||||||
.map(|f|(
|
|
||||||
Some(f.0.ty(self.db)),
|
|
||||||
it.field_name().map(NameOrNameRef::NameRef),
|
|
||||||
))
|
|
||||||
.unwrap_or((None, None))
|
|
||||||
},
|
|
||||||
ast::MatchExpr(it) => {
|
|
||||||
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
|
||||||
let ty = it.expr()
|
|
||||||
.and_then(|e| self.sema.type_of_expr(&e));
|
|
||||||
|
|
||||||
(ty, None)
|
|
||||||
},
|
|
||||||
ast::IdentPat(it) => {
|
|
||||||
cov_mark::hit!(expected_type_if_let_with_leading_char);
|
|
||||||
cov_mark::hit!(expected_type_match_arm_with_leading_char);
|
|
||||||
let ty = self.sema.type_of_pat(&ast::Pat::from(it));
|
|
||||||
|
|
||||||
(ty, None)
|
|
||||||
},
|
|
||||||
ast::Fn(_it) => {
|
|
||||||
cov_mark::hit!(expected_type_fn_ret_with_leading_char);
|
|
||||||
cov_mark::hit!(expected_type_fn_ret_without_leading_char);
|
|
||||||
let ty = self.token.ancestors()
|
|
||||||
.find_map(|ancestor| ast::Expr::cast(ancestor))
|
|
||||||
.and_then(|expr| self.sema.type_of_expr(&expr));
|
|
||||||
|
|
||||||
(ty, None)
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
match node.parent() {
|
|
||||||
Some(n) => {
|
|
||||||
node = n;
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
None => (None, None),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.expected_type = expected_type;
|
self.expected_type = expected_type;
|
||||||
self.expected_name = expected_name;
|
self.expected_name = expected_name;
|
||||||
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
|
self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
|
||||||
@ -802,6 +807,7 @@ fn foo() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expected_type_if_let_without_leading_char() {
|
fn expected_type_if_let_without_leading_char() {
|
||||||
|
cov_mark::check!(expected_type_if_let_without_leading_char);
|
||||||
check_expected_type_and_name(
|
check_expected_type_and_name(
|
||||||
r#"
|
r#"
|
||||||
enum Foo { Bar, Baz, Quux }
|
enum Foo { Bar, Baz, Quux }
|
||||||
@ -811,8 +817,8 @@ fn foo() {
|
|||||||
if let $0 = f { }
|
if let $0 = f { }
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"ty: (), name: ?"#]],
|
expect![[r#"ty: Foo, name: ?"#]],
|
||||||
) // FIXME should be `ty: u32, name: ?`
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -840,8 +846,8 @@ fn foo() -> u32 {
|
|||||||
$0
|
$0
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"ty: (), name: ?"#]],
|
expect![[r#"ty: u32, name: ?"#]],
|
||||||
) // FIXME this should be `ty: u32, name: ?`
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -852,6 +858,18 @@ fn expected_type_fn_ret_with_leading_char() {
|
|||||||
fn foo() -> u32 {
|
fn foo() -> u32 {
|
||||||
c$0
|
c$0
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"ty: u32, name: ?"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_type_fn_ret_fn_ref_fully_typed() {
|
||||||
|
check_expected_type_and_name(
|
||||||
|
r#"
|
||||||
|
fn foo() -> u32 {
|
||||||
|
foo$0
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"ty: u32, name: ?"#]],
|
expect![[r#"ty: u32, name: ?"#]],
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user