Add support for APIT and RPIT callables in label_fn_like
This commit is contained in:
parent
c2f428d2f3
commit
ddb7003b79
5 changed files with 194 additions and 59 deletions
|
@ -25,7 +25,7 @@ use rustc_infer::infer::InferOk;
|
||||||
use rustc_infer::infer::TypeTrace;
|
use rustc_infer::infer::TypeTrace;
|
||||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||||
use rustc_middle::ty::visit::TypeVisitable;
|
use rustc_middle::ty::visit::TypeVisitable;
|
||||||
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
|
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{self, Span};
|
use rustc_span::{self, Span};
|
||||||
|
@ -89,7 +89,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
args_no_rcvr,
|
args_no_rcvr,
|
||||||
false,
|
false,
|
||||||
tuple_arguments,
|
tuple_arguments,
|
||||||
None,
|
method.ok().map(|method| method.def_id),
|
||||||
);
|
);
|
||||||
return self.tcx.ty_error();
|
return self.tcx.ty_error();
|
||||||
}
|
}
|
||||||
|
@ -458,6 +458,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
c_variadic,
|
c_variadic,
|
||||||
err_code,
|
err_code,
|
||||||
fn_def_id,
|
fn_def_id,
|
||||||
|
call_expr,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,6 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
c_variadic: bool,
|
c_variadic: bool,
|
||||||
err_code: &str,
|
err_code: &str,
|
||||||
fn_def_id: Option<DefId>,
|
fn_def_id: Option<DefId>,
|
||||||
|
call_expr: &hir::Expr<'tcx>,
|
||||||
) {
|
) {
|
||||||
// Don't print if it has error types or is just plain `_`
|
// Don't print if it has error types or is just plain `_`
|
||||||
fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
|
fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
|
||||||
|
@ -495,6 +497,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
(self.resolve_vars_if_possible(ty), expr.span)
|
(self.resolve_vars_if_possible(ty), expr.span)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
let callee_expr = match &call_expr.peel_blocks().kind {
|
||||||
|
hir::ExprKind::Call(callee, _) => Some(*callee),
|
||||||
|
hir::ExprKind::MethodCall(_, callee, _) => {
|
||||||
|
if let Some((DefKind::AssocFn, def_id)) =
|
||||||
|
self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
|
||||||
|
&& let Some(assoc) = tcx.opt_associated_item(def_id)
|
||||||
|
&& assoc.fn_has_self_parameter
|
||||||
|
{
|
||||||
|
Some(&callee[0])
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let callee_ty = callee_expr
|
||||||
|
.and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
|
||||||
|
|
||||||
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
|
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
|
||||||
// and treats error types differently
|
// and treats error types differently
|
||||||
|
@ -631,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
label_fn_like(tcx, &mut err, fn_def_id);
|
self.label_fn_like(&mut err, fn_def_id, callee_ty);
|
||||||
err.emit();
|
err.emit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -721,7 +740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
format!("arguments to this {} are incorrect", call_name),
|
format!("arguments to this {} are incorrect", call_name),
|
||||||
);
|
);
|
||||||
// Call out where the function is defined
|
// Call out where the function is defined
|
||||||
label_fn_like(tcx, &mut err, fn_def_id);
|
self.label_fn_like(&mut err, fn_def_id, callee_ty);
|
||||||
err.emit();
|
err.emit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1003,7 +1022,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call out where the function is defined
|
// Call out where the function is defined
|
||||||
label_fn_like(tcx, &mut err, fn_def_id);
|
self.label_fn_like(&mut err, fn_def_id, callee_ty);
|
||||||
|
|
||||||
// And add a suggestion block for all of the parameters
|
// And add a suggestion block for all of the parameters
|
||||||
let suggestion_text = match suggestion_text {
|
let suggestion_text = match suggestion_text {
|
||||||
|
@ -1795,47 +1814,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn label_fn_like<'tcx>(
|
fn label_fn_like(
|
||||||
tcx: TyCtxt<'tcx>,
|
&self,
|
||||||
err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
|
err: &mut rustc_errors::DiagnosticBuilder<'tcx, rustc_errors::ErrorGuaranteed>,
|
||||||
def_id: Option<DefId>,
|
def_id: Option<DefId>,
|
||||||
) {
|
callee_ty: Option<Ty<'tcx>>,
|
||||||
let Some(def_id) = def_id else {
|
) {
|
||||||
return;
|
let Some(mut def_id) = def_id else {
|
||||||
};
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(def_span) = tcx.def_ident_span(def_id) {
|
if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
|
||||||
let mut spans: MultiSpan = def_span.into();
|
&& let trait_def_id = assoc_item.trait_item_def_id.unwrap_or_else(|| self.tcx.parent(def_id))
|
||||||
|
// Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
|
||||||
let params = tcx
|
&& ty::ClosureKind::from_def_id(self.tcx, trait_def_id).is_some()
|
||||||
.hir()
|
&& let Some(callee_ty) = callee_ty
|
||||||
.get_if_local(def_id)
|
{
|
||||||
.and_then(|node| node.body_id())
|
let callee_ty = callee_ty.peel_refs();
|
||||||
.into_iter()
|
match *callee_ty.kind() {
|
||||||
.flat_map(|id| tcx.hir().body(id).params);
|
ty::Param(param) => {
|
||||||
|
let param =
|
||||||
for param in params {
|
self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx);
|
||||||
spans.push_span_label(param.span, "");
|
if param.kind.is_synthetic() {
|
||||||
|
// if it's `impl Fn() -> ..` then just fall down to the def-id based logic
|
||||||
|
def_id = param.def_id;
|
||||||
|
} else {
|
||||||
|
// Otherwise, find the predicate that makes this generic callable,
|
||||||
|
// and point at that.
|
||||||
|
let instantiated = self
|
||||||
|
.tcx
|
||||||
|
.explicit_predicates_of(self.body_id.owner)
|
||||||
|
.instantiate_identity(self.tcx);
|
||||||
|
// FIXME(compiler-errors): This could be problematic if something has two
|
||||||
|
// fn-like predicates with different args, but callable types really never
|
||||||
|
// do that, so it's OK.
|
||||||
|
for (predicate, span) in
|
||||||
|
std::iter::zip(instantiated.predicates, instantiated.spans)
|
||||||
|
{
|
||||||
|
if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
|
||||||
|
&& pred.self_ty() == callee_ty
|
||||||
|
&& ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
|
||||||
|
{
|
||||||
|
err.span_note(span, "callable defined here");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Opaque(new_def_id, _) | ty::Closure(new_def_id, _) | ty::FnDef(new_def_id, _) => {
|
||||||
|
def_id = new_def_id;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let def_kind = tcx.def_kind(def_id);
|
if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
|
||||||
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
|
let mut spans: MultiSpan = def_span.into();
|
||||||
} else {
|
|
||||||
match tcx.hir().get_if_local(def_id) {
|
|
||||||
Some(hir::Node::Expr(hir::Expr {
|
|
||||||
kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
let spans: MultiSpan = (*fn_decl_span).into();
|
|
||||||
|
|
||||||
// Note: We don't point to param spans here because they overlap
|
let params = self
|
||||||
// with the closure span itself
|
.tcx
|
||||||
|
.hir()
|
||||||
|
.get_if_local(def_id)
|
||||||
|
.and_then(|node| node.body_id())
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|id| self.tcx.hir().body(id).params);
|
||||||
|
|
||||||
err.span_note(spans, "closure defined here");
|
for param in params {
|
||||||
|
spans.push_span_label(param.span, "");
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
|
let def_kind = self.tcx.def_kind(def_id);
|
||||||
|
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
|
||||||
|
} else if let def_kind @ (DefKind::Closure | DefKind::OpaqueTy) = self.tcx.def_kind(def_id)
|
||||||
|
{
|
||||||
|
err.span_note(
|
||||||
|
self.tcx.def_span(def_id),
|
||||||
|
&format!("{} defined here", def_kind.descr(def_id)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
src/test/ui/argument-suggestions/exotic-calls.rs
Normal file
26
src/test/ui/argument-suggestions/exotic-calls.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
fn foo<T: Fn()>(t: T) {
|
||||||
|
t(1i32);
|
||||||
|
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(t: impl Fn()) {
|
||||||
|
t(1i32);
|
||||||
|
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz() -> impl Fn() {
|
||||||
|
|| {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz2() {
|
||||||
|
baz()(1i32)
|
||||||
|
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
|
||||||
|
}
|
||||||
|
|
||||||
|
fn qux() {
|
||||||
|
let x = || {};
|
||||||
|
x(1i32);
|
||||||
|
//~^ ERROR this function takes 0 arguments but 1 argument was supplied
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
67
src/test/ui/argument-suggestions/exotic-calls.stderr
Normal file
67
src/test/ui/argument-suggestions/exotic-calls.stderr
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||||
|
--> $DIR/exotic-calls.rs:2:5
|
||||||
|
|
|
||||||
|
LL | t(1i32);
|
||||||
|
| ^ ---- argument of type `i32` unexpected
|
||||||
|
|
|
||||||
|
note: callable defined here
|
||||||
|
--> $DIR/exotic-calls.rs:1:11
|
||||||
|
|
|
||||||
|
LL | fn foo<T: Fn()>(t: T) {
|
||||||
|
| ^^^^
|
||||||
|
help: remove the extra argument
|
||||||
|
|
|
||||||
|
LL | t();
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||||
|
--> $DIR/exotic-calls.rs:7:5
|
||||||
|
|
|
||||||
|
LL | t(1i32);
|
||||||
|
| ^ ---- argument of type `i32` unexpected
|
||||||
|
|
|
||||||
|
note: type parameter defined here
|
||||||
|
--> $DIR/exotic-calls.rs:6:11
|
||||||
|
|
|
||||||
|
LL | fn bar(t: impl Fn()) {
|
||||||
|
| ^^^^^^^^^
|
||||||
|
help: remove the extra argument
|
||||||
|
|
|
||||||
|
LL | t();
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||||
|
--> $DIR/exotic-calls.rs:16:5
|
||||||
|
|
|
||||||
|
LL | baz()(1i32)
|
||||||
|
| ^^^^^ ---- argument of type `i32` unexpected
|
||||||
|
|
|
||||||
|
note: opaque type defined here
|
||||||
|
--> $DIR/exotic-calls.rs:11:13
|
||||||
|
|
|
||||||
|
LL | fn baz() -> impl Fn() {
|
||||||
|
| ^^^^^^^^^
|
||||||
|
help: remove the extra argument
|
||||||
|
|
|
||||||
|
LL | baz()()
|
||||||
|
|
|
||||||
|
|
||||||
|
error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||||
|
--> $DIR/exotic-calls.rs:22:5
|
||||||
|
|
|
||||||
|
LL | x(1i32);
|
||||||
|
| ^ ---- argument of type `i32` unexpected
|
||||||
|
|
|
||||||
|
note: closure defined here
|
||||||
|
--> $DIR/exotic-calls.rs:21:13
|
||||||
|
|
|
||||||
|
LL | let x = || {};
|
||||||
|
| ^^
|
||||||
|
help: remove the extra argument
|
||||||
|
|
|
||||||
|
LL | x();
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0057`.
|
|
@ -4,11 +4,11 @@ error[E0057]: this function takes 0 arguments but 1 argument was supplied
|
||||||
LL | |t| f(t);
|
LL | |t| f(t);
|
||||||
| ^ - argument unexpected
|
| ^ - argument unexpected
|
||||||
|
|
|
|
||||||
note: associated function defined here
|
note: callable defined here
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
--> $DIR/issue-16939.rs:4:12
|
||||||
|
|
|
|
||||||
LL | extern "rust-call" fn call(&self, args: Args) -> Self::Output;
|
LL | fn _foo<F: Fn()> (f: F) {
|
||||||
| ^^^^
|
| ^^^^
|
||||||
help: remove the extra argument
|
help: remove the extra argument
|
||||||
|
|
|
|
||||||
LL | |t| f();
|
LL | |t| f();
|
||||||
|
|
|
@ -5,12 +5,6 @@ LL | let ans = s("what");
|
||||||
| - ^^^^^^ expected `isize`, found `&str`
|
| - ^^^^^^ expected `isize`, found `&str`
|
||||||
| |
|
| |
|
||||||
| arguments to this function are incorrect
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
note: associated function defined here
|
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
|
||||||
| ^^^^^^^^
|
|
||||||
|
|
||||||
error[E0057]: this function takes 1 argument but 0 arguments were supplied
|
error[E0057]: this function takes 1 argument but 0 arguments were supplied
|
||||||
--> $DIR/overloaded-calls-bad.rs:29:15
|
--> $DIR/overloaded-calls-bad.rs:29:15
|
||||||
|
@ -18,11 +12,6 @@ error[E0057]: this function takes 1 argument but 0 arguments were supplied
|
||||||
LL | let ans = s();
|
LL | let ans = s();
|
||||||
| ^-- an argument of type `isize` is missing
|
| ^-- an argument of type `isize` is missing
|
||||||
|
|
|
|
||||||
note: associated function defined here
|
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
|
||||||
| ^^^^^^^^
|
|
||||||
help: provide the argument
|
help: provide the argument
|
||||||
|
|
|
|
||||||
LL | let ans = s(/* isize */);
|
LL | let ans = s(/* isize */);
|
||||||
|
@ -36,11 +25,6 @@ LL | let ans = s("burma", "shave");
|
||||||
| |
|
| |
|
||||||
| expected `isize`, found `&str`
|
| expected `isize`, found `&str`
|
||||||
|
|
|
|
||||||
note: associated function defined here
|
|
||||||
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
|
||||||
|
|
|
||||||
LL | extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
|
||||||
| ^^^^^^^^
|
|
||||||
help: remove the extra argument
|
help: remove the extra argument
|
||||||
|
|
|
|
||||||
LL | let ans = s(/* isize */);
|
LL | let ans = s(/* isize */);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue