mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-03 07:04:49 +00:00
Merge #8945
8945: fix: Make expected type work in more situations r=flodiebold a=flodiebold Also makes call info show the correct types for generic methods.   Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
495c9586ec
14 changed files with 175 additions and 69 deletions
|
@ -513,9 +513,8 @@ impl Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the type as in the signature of the struct (i.e., with
|
/// Returns the type as in the signature of the struct (i.e., with
|
||||||
/// placeholder types for type parameters). This is good for showing
|
/// placeholder types for type parameters). Only use this in the context of
|
||||||
/// signature help, but not so good to actually get the type of the field
|
/// the field definition.
|
||||||
/// when you actually have a variable of the struct.
|
|
||||||
pub fn ty(&self, db: &dyn HirDatabase) -> Type {
|
pub fn ty(&self, db: &dyn HirDatabase) -> Type {
|
||||||
let var_id = self.parent.into();
|
let var_id = self.parent.into();
|
||||||
let generic_def_id: GenericDefId = match self.parent {
|
let generic_def_id: GenericDefId = match self.parent {
|
||||||
|
|
|
@ -11,7 +11,7 @@ use hir_def::{
|
||||||
AsMacroCall, FunctionId, TraitId, VariantId,
|
AsMacroCall, FunctionId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::AsName, ExpansionInfo};
|
use hir_expand::{name::AsName, ExpansionInfo};
|
||||||
use hir_ty::associated_type_shorthand_candidates;
|
use hir_ty::{associated_type_shorthand_candidates, Interner};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
|
@ -227,7 +227,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
pub fn resolve_record_field(
|
pub fn resolve_record_field(
|
||||||
&self,
|
&self,
|
||||||
field: &ast::RecordExprField,
|
field: &ast::RecordExprField,
|
||||||
) -> Option<(Field, Option<Local>)> {
|
) -> Option<(Field, Option<Local>, Type)> {
|
||||||
self.imp.resolve_record_field(field)
|
self.imp.resolve_record_field(field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,14 +501,12 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
|
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
|
||||||
self.analyze(call.syntax()).resolve_method_call(self.db, call)
|
self.analyze(call.syntax()).resolve_method_call(self.db, call).map(|(id, _)| id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||||
// FIXME: this erases Substs, we should instead record the correct
|
let (func, subst) = self.analyze(call.syntax()).resolve_method_call(self.db, call)?;
|
||||||
// substitution during inference and use that
|
let ty = self.db.value_ty(func.into()).substitute(&Interner, &subst);
|
||||||
let func = self.resolve_method_call(call)?;
|
|
||||||
let ty = hir_ty::TyBuilder::value_ty(self.db, func.into()).fill_with_unknown().build();
|
|
||||||
let resolver = self.analyze(call.syntax()).resolver;
|
let resolver = self.analyze(call.syntax()).resolver;
|
||||||
let ty = Type::new_with_resolver(self.db, &resolver, ty)?;
|
let ty = Type::new_with_resolver(self.db, &resolver, ty)?;
|
||||||
let mut res = ty.as_callable(self.db)?;
|
let mut res = ty.as_callable(self.db)?;
|
||||||
|
@ -520,7 +518,10 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
self.analyze(field.syntax()).resolve_field(self.db, field)
|
self.analyze(field.syntax()).resolve_field(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_record_field(&self, field: &ast::RecordExprField) -> Option<(Field, Option<Local>)> {
|
fn resolve_record_field(
|
||||||
|
&self,
|
||||||
|
field: &ast::RecordExprField,
|
||||||
|
) -> Option<(Field, Option<Local>, Type)> {
|
||||||
self.analyze(field.syntax()).resolve_record_field(self.db, field)
|
self.analyze(field.syntax()).resolve_record_field(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ impl SourceAnalyzer {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
call: &ast::MethodCallExpr,
|
call: &ast::MethodCallExpr,
|
||||||
) -> Option<FunctionId> {
|
) -> Option<(FunctionId, Substitution)> {
|
||||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||||
self.infer.as_ref()?.method_resolution(expr_id)
|
self.infer.as_ref()?.method_resolution(expr_id)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ impl SourceAnalyzer {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
field: &ast::RecordExprField,
|
field: &ast::RecordExprField,
|
||||||
) -> Option<(Field, Option<Local>)> {
|
) -> Option<(Field, Option<Local>, Type)> {
|
||||||
let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
|
let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
|
||||||
let expr = ast::Expr::from(record_expr);
|
let expr = ast::Expr::from(record_expr);
|
||||||
let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?;
|
let expr_id = self.body_source_map.as_ref()?.node_expr(InFile::new(self.file_id, &expr))?;
|
||||||
|
@ -178,10 +178,13 @@ impl SourceAnalyzer {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let (_, subst) = self.infer.as_ref()?.type_of_expr.get(expr_id)?.as_adt()?;
|
||||||
let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
|
let variant = self.infer.as_ref()?.variant_resolution_for_expr(expr_id)?;
|
||||||
let variant_data = variant.variant_data(db.upcast());
|
let variant_data = variant.variant_data(db.upcast());
|
||||||
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
|
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
|
||||||
Some((field.into(), local))
|
let field_ty =
|
||||||
|
db.field_types(variant).get(field.local_id)?.clone().substitute(&Interner, subst);
|
||||||
|
Some((field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_record_pat_field(
|
pub(crate) fn resolve_record_pat_field(
|
||||||
|
|
|
@ -485,6 +485,14 @@ impl VariantId {
|
||||||
VariantId::UnionId(it) => it.lookup(db).id.file_id(),
|
VariantId::UnionId(it) => it.lookup(db).id.file_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn adt_id(self) -> AdtId {
|
||||||
|
match self {
|
||||||
|
VariantId::EnumVariantId(it) => it.parent.into(),
|
||||||
|
VariantId::StructId(it) => it.into(),
|
||||||
|
VariantId::UnionId(it) => it.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trait Intern {
|
trait Intern {
|
||||||
|
|
|
@ -181,7 +181,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
for (id, expr) in body.exprs.iter() {
|
for (id, expr) in body.exprs.iter() {
|
||||||
if let Expr::MethodCall { receiver, .. } = expr {
|
if let Expr::MethodCall { receiver, .. } = expr {
|
||||||
let function_id = match self.infer.method_resolution(id) {
|
let function_id = match self.infer.method_resolution(id) {
|
||||||
Some(id) => id,
|
Some((id, _)) => id,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -239,15 +239,11 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: note that we erase information about substs here. This
|
let (callee, subst) = match self.infer.method_resolution(call_id) {
|
||||||
// is not right, but, luckily, doesn't matter as we care only
|
Some(it) => it,
|
||||||
// about the number of params
|
|
||||||
let callee = match self.infer.method_resolution(call_id) {
|
|
||||||
Some(callee) => callee,
|
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let sig =
|
let sig = db.callable_item_signature(callee.into()).substitute(&Interner, &subst);
|
||||||
db.callable_item_signature(callee.into()).into_value_and_skipped_binders().0;
|
|
||||||
|
|
||||||
(sig, args)
|
(sig, args)
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ fn walk_unsafe(
|
||||||
Expr::MethodCall { .. } => {
|
Expr::MethodCall { .. } => {
|
||||||
if infer
|
if infer
|
||||||
.method_resolution(current)
|
.method_resolution(current)
|
||||||
.map(|func| db.function_data(func).is_unsafe())
|
.map(|(func, _)| db.function_data(func).is_unsafe())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
|
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||||
|
|
|
@ -37,8 +37,8 @@ use syntax::SmolStr;
|
||||||
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
use super::{DomainGoal, InEnvironment, ProjectionTy, TraitEnvironment, TraitRef, Ty};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
|
db::HirDatabase, fold_tys, infer::diagnostics::InferenceDiagnostic,
|
||||||
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, TyBuilder,
|
lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Goal, Interner, Substitution,
|
||||||
TyExt, TyKind,
|
TyBuilder, TyExt, TyKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This lint has a false positive here. See the link below for details.
|
// This lint has a false positive here. See the link below for details.
|
||||||
|
@ -132,7 +132,7 @@ impl Default for InternedStandardTypes {
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
#[derive(Clone, PartialEq, Eq, Debug, Default)]
|
||||||
pub struct InferenceResult {
|
pub struct InferenceResult {
|
||||||
/// For each method call expr, records the function it resolves to.
|
/// For each method call expr, records the function it resolves to.
|
||||||
method_resolutions: FxHashMap<ExprId, FunctionId>,
|
method_resolutions: FxHashMap<ExprId, (FunctionId, Substitution)>,
|
||||||
/// For each field access expr, records the field it resolves to.
|
/// For each field access expr, records the field it resolves to.
|
||||||
field_resolutions: FxHashMap<ExprId, FieldId>,
|
field_resolutions: FxHashMap<ExprId, FieldId>,
|
||||||
/// For each struct literal or pattern, records the variant it resolves to.
|
/// For each struct literal or pattern, records the variant it resolves to.
|
||||||
|
@ -152,8 +152,8 @@ pub struct InferenceResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InferenceResult {
|
impl InferenceResult {
|
||||||
pub fn method_resolution(&self, expr: ExprId) -> Option<FunctionId> {
|
pub fn method_resolution(&self, expr: ExprId) -> Option<(FunctionId, Substitution)> {
|
||||||
self.method_resolutions.get(&expr).copied()
|
self.method_resolutions.get(&expr).cloned()
|
||||||
}
|
}
|
||||||
pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
|
pub fn field_resolution(&self, expr: ExprId) -> Option<FieldId> {
|
||||||
self.field_resolutions.get(&expr).copied()
|
self.field_resolutions.get(&expr).copied()
|
||||||
|
@ -284,14 +284,17 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.table.propagate_diverging_flag();
|
self.table.propagate_diverging_flag();
|
||||||
let mut result = std::mem::take(&mut self.result);
|
let mut result = std::mem::take(&mut self.result);
|
||||||
for ty in result.type_of_expr.values_mut() {
|
for ty in result.type_of_expr.values_mut() {
|
||||||
*ty = self.table.resolve_ty_completely(ty.clone());
|
*ty = self.table.resolve_completely(ty.clone());
|
||||||
}
|
}
|
||||||
for ty in result.type_of_pat.values_mut() {
|
for ty in result.type_of_pat.values_mut() {
|
||||||
*ty = self.table.resolve_ty_completely(ty.clone());
|
*ty = self.table.resolve_completely(ty.clone());
|
||||||
}
|
}
|
||||||
for mismatch in result.type_mismatches.values_mut() {
|
for mismatch in result.type_mismatches.values_mut() {
|
||||||
mismatch.expected = self.table.resolve_ty_completely(mismatch.expected.clone());
|
mismatch.expected = self.table.resolve_completely(mismatch.expected.clone());
|
||||||
mismatch.actual = self.table.resolve_ty_completely(mismatch.actual.clone());
|
mismatch.actual = self.table.resolve_completely(mismatch.actual.clone());
|
||||||
|
}
|
||||||
|
for (_, subst) in result.method_resolutions.values_mut() {
|
||||||
|
*subst = self.table.resolve_completely(subst.clone());
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -300,8 +303,8 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.result.type_of_expr.insert(expr, ty);
|
self.result.type_of_expr.insert(expr, ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId) {
|
fn write_method_resolution(&mut self, expr: ExprId, func: FunctionId, subst: Substitution) {
|
||||||
self.result.method_resolutions.insert(expr, func);
|
self.result.method_resolutions.insert(expr, (func, subst));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) {
|
fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) {
|
||||||
|
|
|
@ -891,17 +891,21 @@ impl<'a> InferenceContext<'a> {
|
||||||
method_name,
|
method_name,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let (derefed_receiver_ty, method_ty, def_generics) = match resolved {
|
let (derefed_receiver_ty, method_ty, substs) = match resolved {
|
||||||
Some((ty, func)) => {
|
Some((ty, func)) => {
|
||||||
let ty = canonicalized_receiver.decanonicalize_ty(ty);
|
let ty = canonicalized_receiver.decanonicalize_ty(ty);
|
||||||
self.write_method_resolution(tgt_expr, func);
|
let generics = generics(self.db.upcast(), func.into());
|
||||||
(ty, self.db.value_ty(func.into()), Some(generics(self.db.upcast(), func.into())))
|
let substs = self.substs_for_method_call(generics, generic_args, &ty);
|
||||||
|
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||||
|
(ty, self.db.value_ty(func.into()), substs)
|
||||||
}
|
}
|
||||||
None => (receiver_ty, Binders::empty(&Interner, self.err_ty()), None),
|
None => (
|
||||||
|
receiver_ty,
|
||||||
|
Binders::empty(&Interner, self.err_ty()),
|
||||||
|
Substitution::empty(&Interner),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
let substs = self.substs_for_method_call(def_generics, generic_args, &derefed_receiver_ty);
|
|
||||||
let method_ty = method_ty.substitute(&Interner, &substs);
|
let method_ty = method_ty.substitute(&Interner, &substs);
|
||||||
let method_ty = self.insert_type_vars(method_ty);
|
|
||||||
self.register_obligations_for_call(&method_ty);
|
self.register_obligations_for_call(&method_ty);
|
||||||
let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) {
|
let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) {
|
||||||
Some(sig) => {
|
Some(sig) => {
|
||||||
|
@ -950,23 +954,21 @@ impl<'a> InferenceContext<'a> {
|
||||||
|
|
||||||
fn substs_for_method_call(
|
fn substs_for_method_call(
|
||||||
&mut self,
|
&mut self,
|
||||||
def_generics: Option<Generics>,
|
def_generics: Generics,
|
||||||
generic_args: Option<&GenericArgs>,
|
generic_args: Option<&GenericArgs>,
|
||||||
receiver_ty: &Ty,
|
receiver_ty: &Ty,
|
||||||
) -> Substitution {
|
) -> Substitution {
|
||||||
let (parent_params, self_params, type_params, impl_trait_params) =
|
let (parent_params, self_params, type_params, impl_trait_params) =
|
||||||
def_generics.as_ref().map_or((0, 0, 0, 0), |g| g.provenance_split());
|
def_generics.provenance_split();
|
||||||
assert_eq!(self_params, 0); // method shouldn't have another Self param
|
assert_eq!(self_params, 0); // method shouldn't have another Self param
|
||||||
let total_len = parent_params + type_params + impl_trait_params;
|
let total_len = parent_params + type_params + impl_trait_params;
|
||||||
let mut substs = Vec::with_capacity(total_len);
|
let mut substs = Vec::with_capacity(total_len);
|
||||||
// Parent arguments are unknown, except for the receiver type
|
// Parent arguments are unknown, except for the receiver type
|
||||||
if let Some(parent_generics) = def_generics.as_ref().map(|p| p.iter_parent()) {
|
for (_id, param) in def_generics.iter_parent() {
|
||||||
for (_id, param) in parent_generics {
|
|
||||||
if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf {
|
if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf {
|
||||||
substs.push(receiver_ty.clone());
|
substs.push(receiver_ty.clone());
|
||||||
} else {
|
} else {
|
||||||
substs.push(self.err_ty());
|
substs.push(self.table.new_type_var());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// handle provided type arguments
|
// handle provided type arguments
|
||||||
|
@ -989,7 +991,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
};
|
};
|
||||||
let supplied_params = substs.len();
|
let supplied_params = substs.len();
|
||||||
for _ in supplied_params..total_len {
|
for _ in supplied_params..total_len {
|
||||||
substs.push(self.err_ty());
|
substs.push(self.table.new_type_var());
|
||||||
}
|
}
|
||||||
assert_eq!(substs.len(), total_len);
|
assert_eq!(substs.len(), total_len);
|
||||||
Substitution::from_iter(&Interner, substs)
|
Substitution::from_iter(&Interner, substs)
|
||||||
|
|
|
@ -295,8 +295,11 @@ impl<'a> InferenceTable<'a> {
|
||||||
.expect("fold failed unexpectedly")
|
.expect("fold failed unexpectedly")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
|
pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T::Result
|
||||||
self.resolve_with_fallback(ty, |_, _, d, _| d)
|
where
|
||||||
|
T: HasInterner<Interner = Interner> + Fold<Interner>,
|
||||||
|
{
|
||||||
|
self.resolve_with_fallback(t, |_, _, d, _| d)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unify two types and register new trait goals that arise from that.
|
/// Unify two types and register new trait goals that arise from that.
|
||||||
|
|
|
@ -85,7 +85,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
|
||||||
|
|
||||||
fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
||||||
let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
|
let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
|
||||||
let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
|
let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?;
|
||||||
|
|
||||||
let current_module = ctx.sema.scope(record_field.syntax()).module()?;
|
let current_module = ctx.sema.scope(record_field.syntax()).module()?;
|
||||||
let visibility = record_field_def.visibility(ctx.db());
|
let visibility = record_field_def.visibility(ctx.db());
|
||||||
|
|
|
@ -337,25 +337,24 @@ impl<'a> CompletionContext<'a> {
|
||||||
},
|
},
|
||||||
ast::RecordExprFieldList(_it) => {
|
ast::RecordExprFieldList(_it) => {
|
||||||
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
cov_mark::hit!(expected_type_struct_field_without_leading_char);
|
||||||
self.token.prev_sibling_or_token()
|
// wouldn't try {} be nice...
|
||||||
.and_then(|se| se.into_node())
|
(|| {
|
||||||
.and_then(|node| ast::RecordExprField::cast(node))
|
let expr_field = self.token.prev_sibling_or_token()?
|
||||||
.and_then(|rf| self.sema.resolve_record_field(&rf).zip(Some(rf)))
|
.into_node()
|
||||||
.map(|(f, rf)|(
|
.and_then(|node| ast::RecordExprField::cast(node))?;
|
||||||
Some(f.0.ty(self.db)),
|
let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
|
||||||
rf.field_name().map(NameOrNameRef::NameRef),
|
Some((
|
||||||
|
Some(ty),
|
||||||
|
expr_field.field_name().map(NameOrNameRef::NameRef),
|
||||||
))
|
))
|
||||||
.unwrap_or((None, None))
|
})().unwrap_or((None, None))
|
||||||
},
|
},
|
||||||
ast::RecordExprField(it) => {
|
ast::RecordExprField(it) => {
|
||||||
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
cov_mark::hit!(expected_type_struct_field_with_leading_char);
|
||||||
self.sema
|
(
|
||||||
.resolve_record_field(&it)
|
it.expr().as_ref().and_then(|e| self.sema.type_of_expr(e)),
|
||||||
.map(|f|(
|
|
||||||
Some(f.0.ty(self.db)),
|
|
||||||
it.field_name().map(NameOrNameRef::NameRef),
|
it.field_name().map(NameOrNameRef::NameRef),
|
||||||
))
|
)
|
||||||
.unwrap_or((None, None))
|
|
||||||
},
|
},
|
||||||
ast::MatchExpr(it) => {
|
ast::MatchExpr(it) => {
|
||||||
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
cov_mark::hit!(expected_type_match_arm_without_leading_char);
|
||||||
|
@ -382,6 +381,12 @@ impl<'a> CompletionContext<'a> {
|
||||||
let def = self.sema.to_def(&it);
|
let def = self.sema.to_def(&it);
|
||||||
(def.map(|def| def.ret_type(self.db)), None)
|
(def.map(|def| def.ret_type(self.db)), None)
|
||||||
},
|
},
|
||||||
|
ast::ClosureExpr(it) => {
|
||||||
|
let ty = self.sema.type_of_expr(&it.into());
|
||||||
|
ty.and_then(|ty| ty.as_callable(self.db))
|
||||||
|
.map(|c| (Some(c.return_type()), None))
|
||||||
|
.unwrap_or((None, None))
|
||||||
|
},
|
||||||
ast::Stmt(_it) => (None, None),
|
ast::Stmt(_it) => (None, None),
|
||||||
_ => {
|
_ => {
|
||||||
match node.parent() {
|
match node.parent() {
|
||||||
|
@ -784,6 +789,19 @@ fn foo() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_type_generic_struct_field() {
|
||||||
|
check_expected_type_and_name(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { a: T }
|
||||||
|
fn foo() -> Foo<u32> {
|
||||||
|
Foo { a: $0 }
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"ty: u32, name: a"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn expected_type_struct_field_with_leading_char() {
|
fn expected_type_struct_field_with_leading_char() {
|
||||||
cov_mark::check!(expected_type_struct_field_with_leading_char);
|
cov_mark::check!(expected_type_struct_field_with_leading_char);
|
||||||
|
@ -895,4 +913,52 @@ fn foo() -> u32 {
|
||||||
expect![[r#"ty: u32, name: ?"#]],
|
expect![[r#"ty: u32, name: ?"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_type_closure_param_return() {
|
||||||
|
// FIXME: make this work with `|| $0`
|
||||||
|
check_expected_type_and_name(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
bar(|| a$0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(f: impl FnOnce() -> u32) {}
|
||||||
|
#[lang = "fn_once"]
|
||||||
|
trait FnOnce { type Output; }
|
||||||
|
"#,
|
||||||
|
expect![[r#"ty: u32, name: ?"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_type_generic_function() {
|
||||||
|
check_expected_type_and_name(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
bar::<u32>($0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar<T>(t: T) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"ty: u32, name: t"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_type_generic_method() {
|
||||||
|
check_expected_type_and_name(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
S(1u32).bar($0);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T> S<T> {
|
||||||
|
fn bar(self, t: T) {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"ty: u32, name: t"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -667,6 +667,13 @@ fn foo() { A { the$0 } }
|
||||||
),
|
),
|
||||||
detail: "u32",
|
detail: "u32",
|
||||||
deprecated: true,
|
deprecated: true,
|
||||||
|
relevance: CompletionRelevance {
|
||||||
|
exact_name_match: false,
|
||||||
|
type_match: Some(
|
||||||
|
CouldUnify,
|
||||||
|
),
|
||||||
|
is_local: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
"#]],
|
"#]],
|
||||||
|
|
|
@ -188,6 +188,24 @@ fn main() { S.foo($0); }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fn_signature_for_generic_method() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S<T>(T);
|
||||||
|
impl<T> S<T> {
|
||||||
|
fn foo(&self, x: T) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { S(1u32).foo($0); }
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo(&self, x: u32)
|
||||||
|
(<x: u32>)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
|
fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
|
||||||
check(
|
check(
|
||||||
|
|
|
@ -311,7 +311,7 @@ impl NameRefClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
|
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
|
||||||
if let Some((field, local)) = sema.resolve_record_field(&record_field) {
|
if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
|
||||||
let field = Definition::Field(field);
|
let field = Definition::Field(field);
|
||||||
let res = match local {
|
let res = match local {
|
||||||
None => NameRefClass::Definition(field),
|
None => NameRefClass::Definition(field),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue