Generalize call suggestion for unsatisfied predicate
This commit is contained in:
parent
f5336a969c
commit
7eb2d4e7d0
13 changed files with 174 additions and 74 deletions
|
@ -1,6 +1,6 @@
|
|||
use super::method::probe::{IsSuggestion, Mode, ProbeScope};
|
||||
use super::method::MethodCallee;
|
||||
use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
use crate::type_error_struct;
|
||||
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
|
@ -27,6 +27,7 @@ use rustc_span::Span;
|
|||
use rustc_target::spec::abi;
|
||||
use rustc_trait_selection::autoderef::Autoderef;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
||||
use std::iter;
|
||||
|
|
|
@ -2,7 +2,6 @@ use super::FnCtxt;
|
|||
use crate::astconv::AstConv;
|
||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||
|
||||
use hir::def_id::DefId;
|
||||
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
|
||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
|
@ -19,6 +18,7 @@ use rustc_session::errors::ExprParenthesesNeeded;
|
|||
use rustc_span::symbol::sym;
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
@ -1209,8 +1209,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DefIdOrName {
|
||||
DefId(DefId),
|
||||
Name(&'static str),
|
||||
}
|
||||
|
|
|
@ -2796,3 +2796,8 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum DefIdOrName {
|
||||
DefId(DefId),
|
||||
Name(&'static str),
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use super::{
|
||||
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
|
||||
DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
|
||||
SelectionContext,
|
||||
};
|
||||
|
||||
use crate::autoderef::Autoderef;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::traits::normalize_to;
|
||||
|
||||
use hir::def::CtorOf;
|
||||
use hir::HirId;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
|
@ -812,28 +814,87 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
err: &mut Diagnostic,
|
||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||
) -> bool {
|
||||
// Skipping binder here, remapping below
|
||||
let self_ty = trait_pred.self_ty().skip_binder();
|
||||
if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder()
|
||||
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
|
||||
{
|
||||
// Don't suggest calling to turn an unsized type into a sized type
|
||||
return false;
|
||||
}
|
||||
|
||||
let (def_id, inputs, output, kind) = match *self_ty.kind() {
|
||||
ty::Closure(def_id, substs) => {
|
||||
let sig = substs.as_closure().sig();
|
||||
(def_id, sig.inputs().map_bound(|inputs| &inputs[1..]), sig.output(), "closure")
|
||||
// This is duplicated from `extract_callable_info` in typeck, which
|
||||
// relies on autoderef, so we can't use it here.
|
||||
let found = trait_pred.self_ty().skip_binder().peel_refs();
|
||||
let Some((def_id_or_name, output, inputs)) = (match *found.kind()
|
||||
{
|
||||
ty::FnPtr(fn_sig) => {
|
||||
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs()))
|
||||
}
|
||||
ty::FnDef(def_id, _) => {
|
||||
let sig = self_ty.fn_sig(self.tcx);
|
||||
(
|
||||
def_id,
|
||||
sig.inputs(),
|
||||
sig.output(),
|
||||
match self.tcx.def_kind(def_id) {
|
||||
DefKind::Ctor(..) => "constructor",
|
||||
_ => "function",
|
||||
},
|
||||
)
|
||||
let fn_sig = found.fn_sig(self.tcx);
|
||||
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
ty::Closure(def_id, substs) => {
|
||||
let fn_sig = substs.as_closure().sig();
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
fn_sig.output(),
|
||||
fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
|
||||
))
|
||||
}
|
||||
ty::Opaque(def_id, substs) => {
|
||||
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
|
||||
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
// args tuple will always be substs[1]
|
||||
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::Dynamic(data, _, ty::Dyn) => {
|
||||
data.iter().find_map(|pred| {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
|
||||
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
// for existential projection, substs are shifted over by 1
|
||||
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::Name("trait object"),
|
||||
pred.rebind(proj.term.ty().unwrap()),
|
||||
pred.rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::Param(_) => {
|
||||
obligation.param_env.caller_bounds().iter().find_map(|pred| {
|
||||
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
|
||||
&& proj.projection_ty.self_ty() == found
|
||||
// args tuple will always be substs[1]
|
||||
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::Name("type parameter"),
|
||||
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}) else { return false; };
|
||||
let output = self.replace_bound_vars_with_fresh_vars(
|
||||
obligation.cause.span,
|
||||
LateBoundRegionConversionTime::FnCall,
|
||||
|
@ -859,7 +920,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
// Get the name of the callable and the arguments to be used in the suggestion.
|
||||
let hir = self.tcx.hir();
|
||||
|
||||
let msg = format!("use parentheses to call the {}", kind);
|
||||
let msg = match def_id_or_name {
|
||||
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
|
||||
DefKind::Ctor(CtorOf::Struct, _) => {
|
||||
"use parentheses to instantiate this tuple struct".to_string()
|
||||
}
|
||||
DefKind::Ctor(CtorOf::Variant, _) => {
|
||||
"use parentheses to instantiate this tuple variant".to_string()
|
||||
}
|
||||
kind => format!("use parentheses to call this {}", kind.descr(def_id)),
|
||||
},
|
||||
DefIdOrName::Name(name) => format!("use parentheses to call this {name}"),
|
||||
};
|
||||
|
||||
let args = inputs
|
||||
.map(|ty| {
|
||||
|
@ -872,6 +944,20 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
|
||||
&& obligation.cause.span.can_be_used_for_suggestions()
|
||||
{
|
||||
// When the obligation error has been ensured to have been caused by
|
||||
// an argument, the `obligation.cause.span` points at the expression
|
||||
// of the argument, so we can provide a suggestion. Otherwise, we give
|
||||
// a more general note.
|
||||
err.span_suggestion_verbose(
|
||||
obligation.cause.span.shrink_to_hi(),
|
||||
&msg,
|
||||
format!("({args})"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else if let DefIdOrName::DefId(def_id) = def_id_or_name {
|
||||
let name = match hir.get_if_local(def_id) {
|
||||
Some(hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),
|
||||
|
@ -897,20 +983,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
_ => return false,
|
||||
};
|
||||
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
|
||||
&& obligation.cause.span.can_be_used_for_suggestions()
|
||||
{
|
||||
// When the obligation error has been ensured to have been caused by
|
||||
// an argument, the `obligation.cause.span` points at the expression
|
||||
// of the argument, so we can provide a suggestion. Otherwise, we give
|
||||
// a more general note.
|
||||
err.span_suggestion_verbose(
|
||||
obligation.cause.span.shrink_to_hi(),
|
||||
&msg,
|
||||
format!("({args})"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else {
|
||||
err.help(&format!("{msg}: `{name}({args})`"));
|
||||
}
|
||||
true
|
||||
|
|
|
@ -19,7 +19,7 @@ LL | assert_eq!(foo, y);
|
|||
| ^^^^^^^^^^^^^^^^^^ `for<'a> fn(&'a i32) -> &'a i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}`
|
||||
= help: use parentheses to call the function: `foo(/* &i32 */)`
|
||||
= help: use parentheses to call this function: `foo(/* &i32 */)`
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
|
@ -11,6 +11,10 @@ note: required by a bound in `take_const_owned`
|
|||
|
|
||||
LL | fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send {
|
||||
| ^^^^ required by this bound in `take_const_owned`
|
||||
help: use parentheses to call this type parameter
|
||||
|
|
||||
LL | take_const_owned(f());
|
||||
| ++
|
||||
help: consider further restricting this bound
|
||||
|
|
||||
LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + std::marker::Sync {
|
||||
|
|
|
@ -29,7 +29,7 @@ LL | assert_eq!(a, 0);
|
|||
| ^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
||||
|
|
||||
= help: the trait `Debug` is not implemented for fn item `fn() -> i32 {a}`
|
||||
= help: use parentheses to call the function: `a()`
|
||||
= help: use parentheses to call this function: `a()`
|
||||
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
error[E0277]: `fn() -> impl Future<Output = ()> {foo}` is not a future
|
||||
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:9
|
||||
|
|
||||
LL | async fn foo() {}
|
||||
| --- consider calling this function
|
||||
...
|
||||
LL | bar(foo);
|
||||
| --- ^^^ `fn() -> impl Future<Output = ()> {foo}` is not a future
|
||||
| |
|
||||
|
@ -16,7 +13,7 @@ note: required by a bound in `bar`
|
|||
|
|
||||
LL | fn bar(f: impl Future<Output=()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
|
||||
help: use parentheses to call the function
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | bar(foo());
|
||||
| ++
|
||||
|
@ -24,8 +21,6 @@ LL | bar(foo());
|
|||
error[E0277]: `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future
|
||||
--> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:9
|
||||
|
|
||||
LL | let async_closure = async || ();
|
||||
| -------- consider calling this closure
|
||||
LL | bar(async_closure);
|
||||
| --- ^^^^^^^^^^^^^ `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future
|
||||
| |
|
||||
|
@ -38,7 +33,7 @@ note: required by a bound in `bar`
|
|||
|
|
||||
LL | fn bar(f: impl Future<Output=()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
|
||||
help: use parentheses to call the closure
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | bar(async_closure());
|
||||
| ++
|
||||
|
|
|
@ -2,7 +2,7 @@ fn main() {
|
|||
insert_resource(Marker);
|
||||
insert_resource(Time);
|
||||
//~^ ERROR the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied
|
||||
//~| HELP use parentheses to call the constructor
|
||||
//~| HELP use parentheses to instantiate this tuple struct
|
||||
}
|
||||
|
||||
trait Resource {}
|
||||
|
|
|
@ -5,16 +5,13 @@ LL | insert_resource(Time);
|
|||
| --------------- ^^^^ the trait `Resource` is not implemented for fn item `fn(u32) -> Time {Time}`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
...
|
||||
LL | struct Time(u32);
|
||||
| ----------- consider calling the constructor for `Time`
|
||||
|
|
||||
note: required by a bound in `insert_resource`
|
||||
--> $DIR/call-on-unimplemented-ctor.rs:10:23
|
||||
|
|
||||
LL | fn insert_resource<R: Resource>(resource: R) {}
|
||||
| ^^^^^^^^ required by this bound in `insert_resource`
|
||||
help: use parentheses to call the constructor
|
||||
help: use parentheses to instantiate this tuple struct
|
||||
|
|
||||
LL | insert_resource(Time(/* u32 */));
|
||||
| +++++++++++
|
||||
|
|
15
src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs
Normal file
15
src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
struct Foo;
|
||||
|
||||
trait Bar {}
|
||||
|
||||
impl Bar for Foo {}
|
||||
|
||||
fn needs_bar<T: Bar>(_: T) {}
|
||||
|
||||
fn blah(f: fn() -> Foo) {
|
||||
needs_bar(f);
|
||||
//~^ ERROR the trait bound `fn() -> Foo: Bar` is not satisfied
|
||||
//~| HELP use parentheses to call this function pointer
|
||||
}
|
||||
|
||||
fn main() {}
|
21
src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr
Normal file
21
src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr
Normal file
|
@ -0,0 +1,21 @@
|
|||
error[E0277]: the trait bound `fn() -> Foo: Bar` is not satisfied
|
||||
--> $DIR/call-on-unimplemented-fn-ptr.rs:10:15
|
||||
|
|
||||
LL | needs_bar(f);
|
||||
| --------- ^ the trait `Bar` is not implemented for `fn() -> Foo`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
note: required by a bound in `needs_bar`
|
||||
--> $DIR/call-on-unimplemented-fn-ptr.rs:7:17
|
||||
|
|
||||
LL | fn needs_bar<T: Bar>(_: T) {}
|
||||
| ^^^ required by this bound in `needs_bar`
|
||||
help: use parentheses to call this function pointer
|
||||
|
|
||||
LL | needs_bar(f());
|
||||
| ++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,9 +1,6 @@
|
|||
error[E0277]: the trait bound `fn() -> impl T<O = ()> {foo}: T` is not satisfied
|
||||
--> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:17:9
|
||||
|
|
||||
LL | fn foo() -> impl T<O=()> { S }
|
||||
| --- consider calling this function
|
||||
...
|
||||
LL | bar(foo);
|
||||
| --- ^^^ the trait `T` is not implemented for fn item `fn() -> impl T<O = ()> {foo}`
|
||||
| |
|
||||
|
@ -14,7 +11,7 @@ note: required by a bound in `bar`
|
|||
|
|
||||
LL | fn bar(f: impl T<O=()>) {}
|
||||
| ^^^^^^^ required by this bound in `bar`
|
||||
help: use parentheses to call the function
|
||||
help: use parentheses to call this function
|
||||
|
|
||||
LL | bar(foo());
|
||||
| ++
|
||||
|
@ -22,8 +19,6 @@ LL | bar(foo());
|
|||
error[E0277]: the trait bound `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:21]: T` is not satisfied
|
||||
--> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:19:9
|
||||
|
|
||||
LL | let closure = || S;
|
||||
| -- consider calling this closure
|
||||
LL | bar(closure);
|
||||
| --- ^^^^^^^ the trait `T` is not implemented for closure `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:21]`
|
||||
| |
|
||||
|
@ -34,7 +29,7 @@ note: required by a bound in `bar`
|
|||
|
|
||||
LL | fn bar(f: impl T<O=()>) {}
|
||||
| ^^^^^^^ required by this bound in `bar`
|
||||
help: use parentheses to call the closure
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | bar(closure());
|
||||
| ++
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue