mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Improve error recovery when method-calling a field
This commit is contained in:
parent
8aa4ae5e69
commit
c942fb6061
@ -489,78 +489,7 @@ impl InferenceContext<'_> {
|
|||||||
|
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args, .. } => {
|
Expr::Call { callee, args, .. } => self.infer_call(tgt_expr, *callee, args, expected),
|
||||||
let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
|
|
||||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
|
|
||||||
let (res, derefed_callee) = loop {
|
|
||||||
let Some((callee_deref_ty, _)) = derefs.next() else {
|
|
||||||
break (None, callee_ty.clone());
|
|
||||||
};
|
|
||||||
if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
|
|
||||||
break (Some(res), callee_deref_ty);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// if the function is unresolved, we use is_varargs=true to
|
|
||||||
// suppress the arg count diagnostic here
|
|
||||||
let is_varargs =
|
|
||||||
derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs)
|
|
||||||
|| res.is_none();
|
|
||||||
let (param_tys, ret_ty) = match res {
|
|
||||||
Some((func, params, ret_ty)) => {
|
|
||||||
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
|
||||||
if let TyKind::Closure(c, _) =
|
|
||||||
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
|
||||||
{
|
|
||||||
if let Some(par) = self.current_closure {
|
|
||||||
self.closure_dependencies.entry(par).or_default().push(*c);
|
|
||||||
}
|
|
||||||
self.deferred_closures.entry(*c).or_default().push((
|
|
||||||
derefed_callee.clone(),
|
|
||||||
callee_ty.clone(),
|
|
||||||
params.clone(),
|
|
||||||
tgt_expr,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if let Some(fn_x) = func {
|
|
||||||
self.write_fn_trait_method_resolution(
|
|
||||||
fn_x,
|
|
||||||
&derefed_callee,
|
|
||||||
&mut adjustments,
|
|
||||||
&callee_ty,
|
|
||||||
¶ms,
|
|
||||||
tgt_expr,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
self.write_expr_adj(*callee, adjustments);
|
|
||||||
(params, ret_ty)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
|
|
||||||
call_expr: tgt_expr,
|
|
||||||
found: callee_ty.clone(),
|
|
||||||
});
|
|
||||||
(Vec::new(), self.err_ty())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
|
|
||||||
self.register_obligations_for_call(&callee_ty);
|
|
||||||
|
|
||||||
let expected_inputs = self.expected_inputs_for_expected_output(
|
|
||||||
expected,
|
|
||||||
ret_ty.clone(),
|
|
||||||
param_tys.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.check_call_arguments(
|
|
||||||
tgt_expr,
|
|
||||||
args,
|
|
||||||
&expected_inputs,
|
|
||||||
¶m_tys,
|
|
||||||
&indices_to_skip,
|
|
||||||
is_varargs,
|
|
||||||
);
|
|
||||||
self.normalize_associated_types_in(ret_ty)
|
|
||||||
}
|
|
||||||
Expr::MethodCall { receiver, args, method_name, generic_args } => self
|
Expr::MethodCall { receiver, args, method_name, generic_args } => self
|
||||||
.infer_method_call(
|
.infer_method_call(
|
||||||
tgt_expr,
|
tgt_expr,
|
||||||
@ -1872,6 +1801,107 @@ impl InferenceContext<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_call(
|
||||||
|
&mut self,
|
||||||
|
tgt_expr: ExprId,
|
||||||
|
callee: ExprId,
|
||||||
|
args: &[ExprId],
|
||||||
|
expected: &Expectation,
|
||||||
|
) -> Ty {
|
||||||
|
let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
|
||||||
|
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
|
||||||
|
let (res, derefed_callee) = loop {
|
||||||
|
let Some((callee_deref_ty, _)) = derefs.next() else {
|
||||||
|
break (None, callee_ty.clone());
|
||||||
|
};
|
||||||
|
if let Some(res) = derefs.table.callable_sig(&callee_deref_ty, args.len()) {
|
||||||
|
break (Some(res), callee_deref_ty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// if the function is unresolved, we use is_varargs=true to
|
||||||
|
// suppress the arg count diagnostic here
|
||||||
|
let is_varargs =
|
||||||
|
derefed_callee.callable_sig(self.db).is_some_and(|sig| sig.is_varargs) || res.is_none();
|
||||||
|
let (param_tys, ret_ty) = match res {
|
||||||
|
Some((func, params, ret_ty)) => {
|
||||||
|
let mut adjustments = auto_deref_adjust_steps(&derefs);
|
||||||
|
if let TyKind::Closure(c, _) =
|
||||||
|
self.table.resolve_completely(callee_ty.clone()).kind(Interner)
|
||||||
|
{
|
||||||
|
if let Some(par) = self.current_closure {
|
||||||
|
self.closure_dependencies.entry(par).or_default().push(*c);
|
||||||
|
}
|
||||||
|
self.deferred_closures.entry(*c).or_default().push((
|
||||||
|
derefed_callee.clone(),
|
||||||
|
callee_ty.clone(),
|
||||||
|
params.clone(),
|
||||||
|
tgt_expr,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if let Some(fn_x) = func {
|
||||||
|
self.write_fn_trait_method_resolution(
|
||||||
|
fn_x,
|
||||||
|
&derefed_callee,
|
||||||
|
&mut adjustments,
|
||||||
|
&callee_ty,
|
||||||
|
¶ms,
|
||||||
|
tgt_expr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.write_expr_adj(callee, adjustments);
|
||||||
|
(params, ret_ty)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
|
||||||
|
call_expr: tgt_expr,
|
||||||
|
found: callee_ty.clone(),
|
||||||
|
});
|
||||||
|
(Vec::new(), self.err_ty())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
|
||||||
|
self.check_call(
|
||||||
|
tgt_expr,
|
||||||
|
args,
|
||||||
|
callee_ty,
|
||||||
|
¶m_tys,
|
||||||
|
ret_ty,
|
||||||
|
&indices_to_skip,
|
||||||
|
is_varargs,
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_call(
|
||||||
|
&mut self,
|
||||||
|
tgt_expr: ExprId,
|
||||||
|
args: &[ExprId],
|
||||||
|
callee_ty: Ty,
|
||||||
|
param_tys: &[Ty],
|
||||||
|
ret_ty: Ty,
|
||||||
|
indices_to_skip: &[u32],
|
||||||
|
is_varargs: bool,
|
||||||
|
expected: &Expectation,
|
||||||
|
) -> Ty {
|
||||||
|
self.register_obligations_for_call(&callee_ty);
|
||||||
|
|
||||||
|
let expected_inputs = self.expected_inputs_for_expected_output(
|
||||||
|
expected,
|
||||||
|
ret_ty.clone(),
|
||||||
|
param_tys.to_owned(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.check_call_arguments(
|
||||||
|
tgt_expr,
|
||||||
|
args,
|
||||||
|
&expected_inputs,
|
||||||
|
param_tys,
|
||||||
|
indices_to_skip,
|
||||||
|
is_varargs,
|
||||||
|
);
|
||||||
|
self.normalize_associated_types_in(ret_ty)
|
||||||
|
}
|
||||||
|
|
||||||
fn infer_method_call(
|
fn infer_method_call(
|
||||||
&mut self,
|
&mut self,
|
||||||
tgt_expr: ExprId,
|
tgt_expr: ExprId,
|
||||||
@ -1939,14 +1969,32 @@ impl InferenceContext<'_> {
|
|||||||
expr: tgt_expr,
|
expr: tgt_expr,
|
||||||
receiver: receiver_ty.clone(),
|
receiver: receiver_ty.clone(),
|
||||||
name: method_name.clone(),
|
name: method_name.clone(),
|
||||||
field_with_same_name: field_with_same_name_exists,
|
field_with_same_name: field_with_same_name_exists.clone(),
|
||||||
assoc_func_with_same_name,
|
assoc_func_with_same_name,
|
||||||
});
|
});
|
||||||
(
|
|
||||||
receiver_ty,
|
return match field_with_same_name_exists {
|
||||||
Binders::empty(Interner, self.err_ty()),
|
Some(field_ty) => match field_ty.callable_sig(self.db) {
|
||||||
Substitution::empty(Interner),
|
Some(sig) => self.check_call(
|
||||||
)
|
tgt_expr,
|
||||||
|
args,
|
||||||
|
field_ty,
|
||||||
|
sig.params(),
|
||||||
|
sig.ret().clone(),
|
||||||
|
&[],
|
||||||
|
true,
|
||||||
|
expected,
|
||||||
|
),
|
||||||
|
None => {
|
||||||
|
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
|
||||||
|
field_ty
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
|
||||||
|
self.err_ty()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
|
self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
|
||||||
|
@ -117,7 +117,7 @@ fn check_impl(
|
|||||||
expected.trim_start_matches("adjustments:").trim().to_owned(),
|
expected.trim_start_matches("adjustments:").trim().to_owned(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected annotation: {expected}");
|
panic!("unexpected annotation: {expected} @ {range:?}");
|
||||||
}
|
}
|
||||||
had_annotations = true;
|
had_annotations = true;
|
||||||
}
|
}
|
||||||
|
@ -153,3 +153,28 @@ fn consume() -> Option<()> {
|
|||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn method_call_on_field() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S {
|
||||||
|
field: fn() -> u32,
|
||||||
|
field2: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let s = S { field: || 0, field2: 0 };
|
||||||
|
s.field(0);
|
||||||
|
// ^ type: i32
|
||||||
|
// ^^^^^^^^^^ type: u32
|
||||||
|
s.field2(0);
|
||||||
|
// ^ type: i32
|
||||||
|
// ^^^^^^^^^^^ type: u32
|
||||||
|
s.not_a_field(0);
|
||||||
|
// ^ type: i32
|
||||||
|
// ^^^^^^^^^^^^^^^^ type: {unknown}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user