mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-31 20:09:01 +00:00
Improve error recovery when method-calling an assoc function
This commit is contained in:
parent
fb8bc313ff
commit
e6ea353d94
6 changed files with 111 additions and 70 deletions
|
|
@ -236,7 +236,7 @@ pub enum InferenceDiagnostic {
|
||||||
name: Name,
|
name: Name,
|
||||||
/// Contains the type the field resolves to
|
/// Contains the type the field resolves to
|
||||||
field_with_same_name: Option<Ty>,
|
field_with_same_name: Option<Ty>,
|
||||||
assoc_func_with_same_name: Option<AssocItemId>,
|
assoc_func_with_same_name: Option<FunctionId>,
|
||||||
},
|
},
|
||||||
UnresolvedAssocItem {
|
UnresolvedAssocItem {
|
||||||
id: ExprOrPatId,
|
id: ExprOrPatId,
|
||||||
|
|
|
||||||
|
|
@ -1922,21 +1922,32 @@ impl InferenceContext<'_> {
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
method_name,
|
method_name,
|
||||||
);
|
);
|
||||||
let (receiver_ty, method_ty, substs) = match resolved {
|
match resolved {
|
||||||
Some((adjust, func, visible)) => {
|
Some((adjust, func, visible)) => {
|
||||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
|
||||||
let generics = generics(self.db.upcast(), func.into());
|
|
||||||
let substs = self.substs_for_method_call(generics, generic_args);
|
|
||||||
self.write_expr_adj(receiver, adjustments);
|
|
||||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
|
||||||
if !visible {
|
if !visible {
|
||||||
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
|
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
|
||||||
id: tgt_expr.into(),
|
id: tgt_expr.into(),
|
||||||
item: func.into(),
|
item: func.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
(ty, self.db.value_ty(func.into()).unwrap(), substs)
|
|
||||||
|
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||||
|
self.write_expr_adj(receiver, adjustments);
|
||||||
|
|
||||||
|
let generics = generics(self.db.upcast(), func.into());
|
||||||
|
let substs = self.substs_for_method_call(generics, generic_args);
|
||||||
|
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||||
|
self.check_method_call(
|
||||||
|
tgt_expr,
|
||||||
|
args,
|
||||||
|
self.db.value_ty(func.into()).expect("we have a function def"),
|
||||||
|
substs,
|
||||||
|
ty,
|
||||||
|
expected,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
// Failed to resolve, report diagnostic and try to resolve as call to field access or
|
||||||
|
// assoc function
|
||||||
None => {
|
None => {
|
||||||
let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
|
let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
|
||||||
{
|
{
|
||||||
|
|
@ -1956,12 +1967,11 @@ impl InferenceContext<'_> {
|
||||||
VisibleFromModule::Filter(self.resolver.module()),
|
VisibleFromModule::Filter(self.resolver.module()),
|
||||||
Some(method_name),
|
Some(method_name),
|
||||||
method_resolution::LookupMode::Path,
|
method_resolution::LookupMode::Path,
|
||||||
|_ty, item, visible| {
|
|_ty, item, visible| match item {
|
||||||
if visible {
|
hir_def::AssocItemId::FunctionId(function_id) if visible => {
|
||||||
Some(item)
|
Some(function_id)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -1973,31 +1983,41 @@ impl InferenceContext<'_> {
|
||||||
assoc_func_with_same_name,
|
assoc_func_with_same_name,
|
||||||
});
|
});
|
||||||
|
|
||||||
return match field_with_same_name_exists {
|
let recovered = match assoc_func_with_same_name {
|
||||||
Some(field_ty) => match field_ty.callable_sig(self.db) {
|
Some(f) => {
|
||||||
Some(sig) => self.check_call(
|
let generics = generics(self.db.upcast(), f.into());
|
||||||
tgt_expr,
|
let substs = self.substs_for_method_call(generics, generic_args);
|
||||||
args,
|
let f = self
|
||||||
field_ty,
|
.db
|
||||||
sig.params(),
|
.value_ty(f.into())
|
||||||
sig.ret().clone(),
|
.expect("we have a function def")
|
||||||
&[],
|
.substitute(Interner, &substs);
|
||||||
true,
|
let sig = f.callable_sig(self.db).expect("we have a function def");
|
||||||
expected,
|
Some((f, sig, true))
|
||||||
),
|
}
|
||||||
None => {
|
None => field_with_same_name_exists.and_then(|field_ty| {
|
||||||
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
|
let callable_sig = field_ty.callable_sig(self.db)?;
|
||||||
field_ty
|
Some((field_ty, callable_sig, false))
|
||||||
}
|
}),
|
||||||
},
|
};
|
||||||
|
match recovered {
|
||||||
|
Some((callee_ty, sig, strip_first)) => self.check_call(
|
||||||
|
tgt_expr,
|
||||||
|
args,
|
||||||
|
callee_ty,
|
||||||
|
sig.params().get(strip_first as usize..).unwrap_or(&[]),
|
||||||
|
sig.ret().clone(),
|
||||||
|
&[],
|
||||||
|
true,
|
||||||
|
expected,
|
||||||
|
),
|
||||||
None => {
|
None => {
|
||||||
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
|
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
|
||||||
self.err_ty()
|
self.err_ty()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_method_call(
|
fn check_method_call(
|
||||||
|
|
@ -2067,9 +2087,10 @@ impl InferenceContext<'_> {
|
||||||
expected_inputs: &[Ty],
|
expected_inputs: &[Ty],
|
||||||
param_tys: &[Ty],
|
param_tys: &[Ty],
|
||||||
skip_indices: &[u32],
|
skip_indices: &[u32],
|
||||||
is_varargs: bool,
|
ignore_arg_param_mismatch: bool,
|
||||||
) {
|
) {
|
||||||
let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs;
|
let arg_count_mismatch =
|
||||||
|
!ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len();
|
||||||
if arg_count_mismatch {
|
if arg_count_mismatch {
|
||||||
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
|
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
|
||||||
call_expr: expr,
|
call_expr: expr,
|
||||||
|
|
@ -2098,7 +2119,7 @@ impl InferenceContext<'_> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
while skip_indices.peek().is_some_and(|i| *i < idx as u32) {
|
while skip_indices.peek().is_some_and(|&i| i < idx as u32) {
|
||||||
skip_indices.next();
|
skip_indices.next();
|
||||||
}
|
}
|
||||||
if skip_indices.peek().copied() == Some(idx as u32) {
|
if skip_indices.peek().copied() == Some(idx as u32) {
|
||||||
|
|
|
||||||
|
|
@ -159,18 +159,18 @@ fn method_call_on_field() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
struct S {
|
struct S {
|
||||||
field: fn() -> u32,
|
field: fn(f32) -> u32,
|
||||||
field2: u32
|
field2: u32
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let s = S { field: || 0, field2: 0 };
|
let s = S { field: |_| 0, field2: 0 };
|
||||||
s.field(0);
|
s.field(0);
|
||||||
// ^ type: i32
|
// ^ expected f32, got i32
|
||||||
// ^^^^^^^^^^ type: u32
|
// ^^^^^^^^^^ type: u32
|
||||||
s.field2(0);
|
s.field2(0);
|
||||||
// ^ type: i32
|
// ^ type: i32
|
||||||
// ^^^^^^^^^^^ type: u32
|
// ^^^^^^^^^^^ type: {unknown}
|
||||||
s.not_a_field(0);
|
s.not_a_field(0);
|
||||||
// ^ type: i32
|
// ^ type: i32
|
||||||
// ^^^^^^^^^^^^^^^^ type: {unknown}
|
// ^^^^^^^^^^^^^^^^ type: {unknown}
|
||||||
|
|
@ -178,3 +178,28 @@ fn main() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn method_call_on_assoc() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn not_a_method() -> f32 { 0.0 }
|
||||||
|
fn not_a_method2(this: Self, param: f32) -> Self { this }
|
||||||
|
fn not_a_method3(param: f32) -> Self { S }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
S.not_a_method(0);
|
||||||
|
// ^^^^^^^^^^^^^^^^^ type: f32
|
||||||
|
S.not_a_method2(0);
|
||||||
|
// ^ expected f32, got i32
|
||||||
|
// ^^^^^^^^^^^^^^^^^^ type: S
|
||||||
|
S.not_a_method3(0);
|
||||||
|
// ^^^^^^^^^^^^^^^^^^ type: S
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1210,7 +1210,7 @@ impl<T> Slice<T> {
|
||||||
fn main() {
|
fn main() {
|
||||||
let foo: Slice<u32>;
|
let foo: Slice<u32>;
|
||||||
foo.into_vec(); // we shouldn't crash on this at least
|
foo.into_vec(); // we shouldn't crash on this at least
|
||||||
} //^^^^^^^^^^^^^^ {unknown}
|
} //^^^^^^^^^^^^^^ ()
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use hir_def::{
|
||||||
hir::ExprOrPatId,
|
hir::ExprOrPatId,
|
||||||
path::{hir_segment_to_ast_segment, ModPath},
|
path::{hir_segment_to_ast_segment, ModPath},
|
||||||
type_ref::TypesSourceMap,
|
type_ref::TypesSourceMap,
|
||||||
AssocItemId, DefWithBodyId, SyntheticSyntax,
|
DefWithBodyId, SyntheticSyntax,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::Name, HirFileId, InFile};
|
use hir_expand::{name::Name, HirFileId, InFile};
|
||||||
use hir_ty::{
|
use hir_ty::{
|
||||||
|
|
@ -25,7 +25,7 @@ use syntax::{
|
||||||
};
|
};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{AssocItem, Field, Local, Trait, Type};
|
use crate::{AssocItem, Field, Function, Local, Trait, Type};
|
||||||
|
|
||||||
pub use hir_def::VariantId;
|
pub use hir_def::VariantId;
|
||||||
pub use hir_ty::{
|
pub use hir_ty::{
|
||||||
|
|
@ -253,7 +253,7 @@ pub struct UnresolvedMethodCall {
|
||||||
pub receiver: Type,
|
pub receiver: Type,
|
||||||
pub name: Name,
|
pub name: Name,
|
||||||
pub field_with_same_name: Option<Type>,
|
pub field_with_same_name: Option<Type>,
|
||||||
pub assoc_func_with_same_name: Option<AssocItemId>,
|
pub assoc_func_with_same_name: Option<Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -623,7 +623,7 @@ impl AnyDiagnostic {
|
||||||
field_with_same_name: field_with_same_name
|
field_with_same_name: field_with_same_name
|
||||||
.clone()
|
.clone()
|
||||||
.map(|ty| Type::new(db, def, ty)),
|
.map(|ty| Type::new(db, def, ty)),
|
||||||
assoc_func_with_same_name: *assoc_func_with_same_name,
|
assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile};
|
use hir::{db::ExpandDatabase, FileRange, HirDisplay, InFile};
|
||||||
use ide_db::text_edit::TextEdit;
|
use ide_db::text_edit::TextEdit;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
assists::{Assist, AssistId, AssistKind},
|
assists::{Assist, AssistId, AssistKind},
|
||||||
|
|
@ -112,7 +112,7 @@ fn field_fix(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
|
fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
|
||||||
if let Some(assoc_item_id) = d.assoc_func_with_same_name {
|
if let Some(f) = d.assoc_func_with_same_name {
|
||||||
let db = ctx.sema.db;
|
let db = ctx.sema.db;
|
||||||
|
|
||||||
let expr_ptr = &d.expr;
|
let expr_ptr = &d.expr;
|
||||||
|
|
@ -127,30 +127,25 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
|
||||||
let receiver = call.receiver()?;
|
let receiver = call.receiver()?;
|
||||||
let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
|
let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
|
||||||
|
|
||||||
let need_to_take_receiver_as_first_arg = match hir::AssocItem::from(assoc_item_id) {
|
let assoc_fn_params = f.assoc_fn_params(db);
|
||||||
AssocItem::Function(f) => {
|
let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() {
|
||||||
let assoc_fn_params = f.assoc_fn_params(db);
|
false
|
||||||
if assoc_fn_params.is_empty() {
|
} else {
|
||||||
false
|
assoc_fn_params
|
||||||
} else {
|
.first()
|
||||||
assoc_fn_params
|
.map(|first_arg| {
|
||||||
.first()
|
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
|
||||||
.map(|first_arg| {
|
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
|
||||||
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
|
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
|
||||||
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
|
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
|
||||||
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
|
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
|
||||||
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
|
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
|
||||||
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
|
|
||||||
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
|
|
||||||
|
|
||||||
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
|
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
|
||||||
first_arg.ty() == receiver_type
|
first_arg.ty() == receiver_type
|
||||||
|| first_arg.ty().as_adt() == receiver_type.as_adt()
|
|| first_arg.ty().as_adt() == receiver_type.as_adt()
|
||||||
})
|
})
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut receiver_type_adt_name =
|
let mut receiver_type_adt_name =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue