Skip to content

Commit 7128701

Browse files
authored
Merge pull request #19160 from Veykril/push-f3601671f6a468a8cc0774253ddaddff
Improve error recovery when method-calling an assoc function
2 parents fb8bc31 + e6ea353 commit 7128701

File tree

6 files changed

+112
-71
lines changed

6 files changed

+112
-71
lines changed

crates/hir-ty/src/infer.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ pub enum InferenceDiagnostic {
236236
name: Name,
237237
/// Contains the type the field resolves to
238238
field_with_same_name: Option<Ty>,
239-
assoc_func_with_same_name: Option<AssocItemId>,
239+
assoc_func_with_same_name: Option<FunctionId>,
240240
},
241241
UnresolvedAssocItem {
242242
id: ExprOrPatId,

crates/hir-ty/src/infer/expr.rs

+56-35
Original file line numberDiff line numberDiff line change
@@ -1922,21 +1922,32 @@ impl InferenceContext<'_> {
19221922
VisibleFromModule::Filter(self.resolver.module()),
19231923
method_name,
19241924
);
1925-
let (receiver_ty, method_ty, substs) = match resolved {
1925+
match resolved {
19261926
Some((adjust, func, visible)) => {
1927-
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
1928-
let generics = generics(self.db.upcast(), func.into());
1929-
let substs = self.substs_for_method_call(generics, generic_args);
1930-
self.write_expr_adj(receiver, adjustments);
1931-
self.write_method_resolution(tgt_expr, func, substs.clone());
19321927
if !visible {
19331928
self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem {
19341929
id: tgt_expr.into(),
19351930
item: func.into(),
19361931
})
19371932
}
1938-
(ty, self.db.value_ty(func.into()).unwrap(), substs)
1933+
1934+
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
1935+
self.write_expr_adj(receiver, adjustments);
1936+
1937+
let generics = generics(self.db.upcast(), func.into());
1938+
let substs = self.substs_for_method_call(generics, generic_args);
1939+
self.write_method_resolution(tgt_expr, func, substs.clone());
1940+
self.check_method_call(
1941+
tgt_expr,
1942+
args,
1943+
self.db.value_ty(func.into()).expect("we have a function def"),
1944+
substs,
1945+
ty,
1946+
expected,
1947+
)
19391948
}
1949+
// Failed to resolve, report diagnostic and try to resolve as call to field access or
1950+
// assoc function
19401951
None => {
19411952
let field_with_same_name_exists = match self.lookup_field(&receiver_ty, method_name)
19421953
{
@@ -1956,12 +1967,11 @@ impl InferenceContext<'_> {
19561967
VisibleFromModule::Filter(self.resolver.module()),
19571968
Some(method_name),
19581969
method_resolution::LookupMode::Path,
1959-
|_ty, item, visible| {
1960-
if visible {
1961-
Some(item)
1962-
} else {
1963-
None
1970+
|_ty, item, visible| match item {
1971+
hir_def::AssocItemId::FunctionId(function_id) if visible => {
1972+
Some(function_id)
19641973
}
1974+
_ => None,
19651975
},
19661976
);
19671977

@@ -1973,31 +1983,41 @@ impl InferenceContext<'_> {
19731983
assoc_func_with_same_name,
19741984
});
19751985

1976-
return match field_with_same_name_exists {
1977-
Some(field_ty) => match field_ty.callable_sig(self.db) {
1978-
Some(sig) => self.check_call(
1979-
tgt_expr,
1980-
args,
1981-
field_ty,
1982-
sig.params(),
1983-
sig.ret().clone(),
1984-
&[],
1985-
true,
1986-
expected,
1987-
),
1988-
None => {
1989-
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
1990-
field_ty
1991-
}
1992-
},
1986+
let recovered = match assoc_func_with_same_name {
1987+
Some(f) => {
1988+
let generics = generics(self.db.upcast(), f.into());
1989+
let substs = self.substs_for_method_call(generics, generic_args);
1990+
let f = self
1991+
.db
1992+
.value_ty(f.into())
1993+
.expect("we have a function def")
1994+
.substitute(Interner, &substs);
1995+
let sig = f.callable_sig(self.db).expect("we have a function def");
1996+
Some((f, sig, true))
1997+
}
1998+
None => field_with_same_name_exists.and_then(|field_ty| {
1999+
let callable_sig = field_ty.callable_sig(self.db)?;
2000+
Some((field_ty, callable_sig, false))
2001+
}),
2002+
};
2003+
match recovered {
2004+
Some((callee_ty, sig, strip_first)) => self.check_call(
2005+
tgt_expr,
2006+
args,
2007+
callee_ty,
2008+
sig.params().get(strip_first as usize..).unwrap_or(&[]),
2009+
sig.ret().clone(),
2010+
&[],
2011+
true,
2012+
expected,
2013+
),
19932014
None => {
19942015
self.check_call_arguments(tgt_expr, args, &[], &[], &[], true);
19952016
self.err_ty()
19962017
}
1997-
};
2018+
}
19982019
}
1999-
};
2000-
self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected)
2020+
}
20012021
}
20022022

20032023
fn check_method_call(
@@ -2067,9 +2087,10 @@ impl InferenceContext<'_> {
20672087
expected_inputs: &[Ty],
20682088
param_tys: &[Ty],
20692089
skip_indices: &[u32],
2070-
is_varargs: bool,
2090+
ignore_arg_param_mismatch: bool,
20712091
) {
2072-
let arg_count_mismatch = args.len() != param_tys.len() + skip_indices.len() && !is_varargs;
2092+
let arg_count_mismatch =
2093+
!ignore_arg_param_mismatch && args.len() != param_tys.len() + skip_indices.len();
20732094
if arg_count_mismatch {
20742095
self.push_diagnostic(InferenceDiagnostic::MismatchedArgCount {
20752096
call_expr: expr,
@@ -2098,7 +2119,7 @@ impl InferenceContext<'_> {
20982119
continue;
20992120
}
21002121

2101-
while skip_indices.peek().is_some_and(|i| *i < idx as u32) {
2122+
while skip_indices.peek().is_some_and(|&i| i < idx as u32) {
21022123
skip_indices.next();
21032124
}
21042125
if skip_indices.peek().copied() == Some(idx as u32) {

crates/hir-ty/src/tests/diagnostics.rs

+29-4
Original file line numberDiff line numberDiff line change
@@ -159,22 +159,47 @@ fn method_call_on_field() {
159159
check(
160160
r#"
161161
struct S {
162-
field: fn() -> u32,
162+
field: fn(f32) -> u32,
163163
field2: u32
164164
}
165165
166166
fn main() {
167-
let s = S { field: || 0, field2: 0 };
167+
let s = S { field: |_| 0, field2: 0 };
168168
s.field(0);
169-
// ^ type: i32
169+
// ^ expected f32, got i32
170170
// ^^^^^^^^^^ type: u32
171171
s.field2(0);
172172
// ^ type: i32
173-
// ^^^^^^^^^^^ type: u32
173+
// ^^^^^^^^^^^ type: {unknown}
174174
s.not_a_field(0);
175175
// ^ type: i32
176176
// ^^^^^^^^^^^^^^^^ type: {unknown}
177177
}
178178
"#,
179179
);
180180
}
181+
182+
#[test]
183+
fn method_call_on_assoc() {
184+
check(
185+
r#"
186+
struct S;
187+
188+
impl S {
189+
fn not_a_method() -> f32 { 0.0 }
190+
fn not_a_method2(this: Self, param: f32) -> Self { this }
191+
fn not_a_method3(param: f32) -> Self { S }
192+
}
193+
194+
fn main() {
195+
S.not_a_method(0);
196+
// ^^^^^^^^^^^^^^^^^ type: f32
197+
S.not_a_method2(0);
198+
// ^ expected f32, got i32
199+
// ^^^^^^^^^^^^^^^^^^ type: S
200+
S.not_a_method3(0);
201+
// ^^^^^^^^^^^^^^^^^^ type: S
202+
}
203+
"#,
204+
);
205+
}

crates/hir-ty/src/tests/method_resolution.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1210,7 +1210,7 @@ impl<T> Slice<T> {
12101210
fn main() {
12111211
let foo: Slice<u32>;
12121212
foo.into_vec(); // we shouldn't crash on this at least
1213-
} //^^^^^^^^^^^^^^ {unknown}
1213+
} //^^^^^^^^^^^^^^ ()
12141214
"#,
12151215
);
12161216
}

crates/hir/src/diagnostics.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use hir_def::{
1010
hir::ExprOrPatId,
1111
path::{hir_segment_to_ast_segment, ModPath},
1212
type_ref::TypesSourceMap,
13-
AssocItemId, DefWithBodyId, SyntheticSyntax,
13+
DefWithBodyId, SyntheticSyntax,
1414
};
1515
use hir_expand::{name::Name, HirFileId, InFile};
1616
use hir_ty::{
@@ -25,7 +25,7 @@ use syntax::{
2525
};
2626
use triomphe::Arc;
2727

28-
use crate::{AssocItem, Field, Local, Trait, Type};
28+
use crate::{AssocItem, Field, Function, Local, Trait, Type};
2929

3030
pub use hir_def::VariantId;
3131
pub use hir_ty::{
@@ -253,7 +253,7 @@ pub struct UnresolvedMethodCall {
253253
pub receiver: Type,
254254
pub name: Name,
255255
pub field_with_same_name: Option<Type>,
256-
pub assoc_func_with_same_name: Option<AssocItemId>,
256+
pub assoc_func_with_same_name: Option<Function>,
257257
}
258258

259259
#[derive(Debug)]
@@ -623,7 +623,7 @@ impl AnyDiagnostic {
623623
field_with_same_name: field_with_same_name
624624
.clone()
625625
.map(|ty| Type::new(db, def, ty)),
626-
assoc_func_with_same_name: *assoc_func_with_same_name,
626+
assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into),
627627
}
628628
.into()
629629
}

crates/ide-diagnostics/src/handlers/unresolved_method.rs

+21-26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use hir::{db::ExpandDatabase, AssocItem, FileRange, HirDisplay, InFile};
1+
use hir::{db::ExpandDatabase, FileRange, HirDisplay, InFile};
22
use ide_db::text_edit::TextEdit;
33
use ide_db::{
44
assists::{Assist, AssistId, AssistKind},
@@ -112,7 +112,7 @@ fn field_fix(
112112
}
113113

114114
fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -> Option<Assist> {
115-
if let Some(assoc_item_id) = d.assoc_func_with_same_name {
115+
if let Some(f) = d.assoc_func_with_same_name {
116116
let db = ctx.sema.db;
117117

118118
let expr_ptr = &d.expr;
@@ -127,30 +127,25 @@ fn assoc_func_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall) -
127127
let receiver = call.receiver()?;
128128
let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
129129

130-
let need_to_take_receiver_as_first_arg = match hir::AssocItem::from(assoc_item_id) {
131-
AssocItem::Function(f) => {
132-
let assoc_fn_params = f.assoc_fn_params(db);
133-
if assoc_fn_params.is_empty() {
134-
false
135-
} else {
136-
assoc_fn_params
137-
.first()
138-
.map(|first_arg| {
139-
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
140-
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
141-
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
142-
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
143-
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
144-
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
145-
146-
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
147-
first_arg.ty() == receiver_type
148-
|| first_arg.ty().as_adt() == receiver_type.as_adt()
149-
})
150-
.unwrap_or(false)
151-
}
152-
}
153-
_ => false,
130+
let assoc_fn_params = f.assoc_fn_params(db);
131+
let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() {
132+
false
133+
} else {
134+
assoc_fn_params
135+
.first()
136+
.map(|first_arg| {
137+
// For generic type, say `Box`, take `Box::into_raw(b: Self)` as example,
138+
// type of `b` is `Self`, which is `Box<T, A>`, containing unspecified generics.
139+
// However, type of `receiver` is specified, it could be `Box<i32, Global>` or something like that,
140+
// so `first_arg.ty() == receiver_type` evaluate to `false` here.
141+
// Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard,
142+
// apply `.as_adt()` over `Box<T, A>` or `Box<i32, Global>` gets `Box`, so we get `true` here.
143+
144+
// FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver`
145+
first_arg.ty() == receiver_type
146+
|| first_arg.ty().as_adt() == receiver_type.as_adt()
147+
})
148+
.unwrap_or(false)
154149
};
155150

156151
let mut receiver_type_adt_name =

0 commit comments

Comments
 (0)