Improve suggestion for calling closure on type mismatch
This commit is contained in:
parent
dc80ca78b6
commit
d15fed79b8
11 changed files with 143 additions and 125 deletions
|
@ -8,15 +8,14 @@ use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind};
|
use rustc_hir::def::{CtorOf, DefKind};
|
||||||
use rustc_hir::lang_items::LangItem;
|
use rustc_hir::lang_items::LangItem;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
Expr, ExprKind, GenericBound, ItemKind, Node, Path, QPath, Stmt, StmtKind, TyKind,
|
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
|
||||||
WherePredicate,
|
|
||||||
};
|
};
|
||||||
use rustc_infer::infer::{self, TyCtxtInferExt};
|
use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||||
use rustc_infer::traits;
|
use rustc_infer::traits;
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::subst::GenericArgKind;
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty};
|
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
|
||||||
use rustc_span::symbol::{kw, sym};
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||||
|
|
||||||
|
@ -78,124 +77,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expected: Ty<'tcx>,
|
expected: Ty<'tcx>,
|
||||||
found: Ty<'tcx>,
|
found: Ty<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let hir = self.tcx.hir();
|
let (def_id, output, inputs) = match *found.kind() {
|
||||||
let (def_id, sig) = match *found.kind() {
|
ty::FnDef(def_id, _) => {
|
||||||
ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
|
let fn_sig = found.fn_sig(self.tcx);
|
||||||
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
|
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
|
||||||
|
}
|
||||||
|
ty::Closure(def_id, substs) => {
|
||||||
|
let fn_sig = substs.as_closure().sig();
|
||||||
|
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
|
||||||
|
}
|
||||||
|
ty::Opaque(def_id, substs) => {
|
||||||
|
let sig = 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((
|
||||||
|
pred.kind().rebind(proj.term.ty().unwrap()),
|
||||||
|
args.len(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if let Some((output, inputs)) = sig {
|
||||||
|
(def_id, output, inputs)
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, sig);
|
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
|
||||||
let sig = self.normalize_associated_types_in(expr.span, sig);
|
let output = self.normalize_associated_types_in(expr.span, output);
|
||||||
if self.can_coerce(sig.output(), expected) {
|
if !output.is_ty_var() && self.can_coerce(output, expected) {
|
||||||
let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
|
let (sugg_call, mut applicability) = match inputs {
|
||||||
(String::new(), Applicability::MachineApplicable)
|
0 => ("".to_string(), Applicability::MachineApplicable),
|
||||||
} else {
|
1..=4 => (
|
||||||
("...".to_string(), Applicability::HasPlaceholders)
|
(0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
),
|
||||||
|
_ => ("...".to_string(), Applicability::HasPlaceholders),
|
||||||
};
|
};
|
||||||
let mut msg = "call this function";
|
|
||||||
match hir.get_if_local(def_id) {
|
let msg = match self.tcx.def_kind(def_id) {
|
||||||
Some(
|
DefKind::Fn => "call this function",
|
||||||
Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
|
DefKind::Closure | DefKind::OpaqueTy => "call this closure",
|
||||||
| Node::ImplItem(hir::ImplItem {
|
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
|
||||||
kind: hir::ImplItemKind::Fn(_, body_id), ..
|
DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
|
||||||
})
|
_ => "call this function",
|
||||||
| Node::TraitItem(hir::TraitItem {
|
};
|
||||||
kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
|
|
||||||
..
|
let sugg = match expr.kind {
|
||||||
}),
|
hir::ExprKind::Call(..)
|
||||||
) => {
|
| hir::ExprKind::Path(..)
|
||||||
let body = hir.body(*body_id);
|
| hir::ExprKind::Index(..)
|
||||||
sugg_call = body
|
| hir::ExprKind::Lit(..) => {
|
||||||
.params
|
vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
|
||||||
.iter()
|
|
||||||
.map(|param| match ¶m.pat.kind {
|
|
||||||
hir::PatKind::Binding(_, _, ident, None)
|
|
||||||
if ident.name != kw::SelfLower =>
|
|
||||||
{
|
|
||||||
ident.to_string()
|
|
||||||
}
|
}
|
||||||
_ => "_".to_string(),
|
hir::ExprKind::Closure { .. } => {
|
||||||
})
|
// Might be `{ expr } || { bool }`
|
||||||
.collect::<Vec<_>>()
|
applicability = Applicability::MaybeIncorrect;
|
||||||
.join(", ");
|
vec![
|
||||||
|
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||||
|
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Some(Node::Expr(hir::Expr {
|
_ => {
|
||||||
kind: ExprKind::Closure { body: body_id, .. },
|
vec![
|
||||||
span: full_closure_span,
|
(expr.span.shrink_to_lo(), "(".to_string()),
|
||||||
..
|
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
|
||||||
})) => {
|
]
|
||||||
if *full_closure_span == expr.span {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
msg = "call this closure";
|
};
|
||||||
let body = hir.body(*body_id);
|
|
||||||
sugg_call = body
|
err.multipart_suggestion_verbose(
|
||||||
.params
|
format!("use parentheses to {msg}"),
|
||||||
.iter()
|
sugg,
|
||||||
.map(|param| match ¶m.pat.kind {
|
|
||||||
hir::PatKind::Binding(_, _, ident, None)
|
|
||||||
if ident.name != kw::SelfLower =>
|
|
||||||
{
|
|
||||||
ident.to_string()
|
|
||||||
}
|
|
||||||
_ => "_".to_string(),
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ");
|
|
||||||
}
|
|
||||||
Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
|
|
||||||
sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
|
|
||||||
match def_id.as_local().map(|def_id| self.tcx.def_kind(def_id)) {
|
|
||||||
Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
|
|
||||||
msg = "instantiate this tuple variant";
|
|
||||||
}
|
|
||||||
Some(DefKind::Ctor(CtorOf::Struct, _)) => {
|
|
||||||
msg = "instantiate this tuple struct";
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(Node::ForeignItem(hir::ForeignItem {
|
|
||||||
kind: hir::ForeignItemKind::Fn(_, idents, _),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
sugg_call = idents
|
|
||||||
.iter()
|
|
||||||
.map(|ident| {
|
|
||||||
if ident.name != kw::SelfLower {
|
|
||||||
ident.to_string()
|
|
||||||
} else {
|
|
||||||
"_".to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
}
|
|
||||||
Some(Node::TraitItem(hir::TraitItem {
|
|
||||||
kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
|
|
||||||
..
|
|
||||||
})) => {
|
|
||||||
sugg_call = idents
|
|
||||||
.iter()
|
|
||||||
.map(|ident| {
|
|
||||||
if ident.name != kw::SelfLower {
|
|
||||||
ident.to_string()
|
|
||||||
} else {
|
|
||||||
"_".to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
expr.span.shrink_to_hi(),
|
|
||||||
&format!("use parentheses to {}", msg),
|
|
||||||
format!("({})", sugg_call),
|
|
||||||
applicability,
|
applicability,
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
12
src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
Normal file
12
src/test/ui/impl-trait/suggest-calling-rpit-closure.rs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
fn whatever() -> i32 {
|
||||||
|
opaque()
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opaque() -> impl Fn() -> i32 {
|
||||||
|
|| 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = whatever();
|
||||||
|
}
|
21
src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
Normal file
21
src/test/ui/impl-trait/suggest-calling-rpit-closure.stderr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/suggest-calling-rpit-closure.rs:2:5
|
||||||
|
|
|
||||||
|
LL | fn whatever() -> i32 {
|
||||||
|
| --- expected `i32` because of return type
|
||||||
|
LL | opaque()
|
||||||
|
| ^^^^^^^^ expected `i32`, found opaque type
|
||||||
|
...
|
||||||
|
LL | fn opaque() -> impl Fn() -> i32 {
|
||||||
|
| ---------------- the found opaque type
|
||||||
|
|
|
||||||
|
= note: expected type `i32`
|
||||||
|
found opaque type `impl Fn() -> i32`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | opaque()()
|
||||||
|
| ++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -201,6 +201,10 @@ LL | { true } || { true }
|
||||||
|
|
|
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found closure `[closure@$DIR/expr-as-stmt.rs:51:14: 51:25]`
|
found closure `[closure@$DIR/expr-as-stmt.rs:51:14: 51:25]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | { true } (|| { true })()
|
||||||
|
| + +++
|
||||||
help: parentheses are required to parse this as an expression
|
help: parentheses are required to parse this as an expression
|
||||||
|
|
|
|
||||||
LL | ({ true }) || { true }
|
LL | ({ true }) || { true }
|
||||||
|
|
|
@ -25,6 +25,12 @@ LL | | }.hi() {
|
||||||
|
|
|
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found closure `[closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 14:11]`
|
found closure `[closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 14:11]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL ~ while (|| Foo {
|
||||||
|
LL | x: 3
|
||||||
|
LL ~ }.hi())() {
|
||||||
|
|
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,6 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr
|
||||||
|
|
|
|
||||||
= note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize`
|
= note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize`
|
||||||
found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
|
found fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
|
||||||
help: use parentheses to call this function
|
|
||||||
|
|
|
||||||
LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute(...);
|
|
||||||
| +++++
|
|
||||||
|
|
||||||
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
|
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
|
||||||
--> $DIR/reify-intrinsic.rs:11:13
|
--> $DIR/reify-intrinsic.rs:11:13
|
||||||
|
|
|
@ -1118,6 +1118,10 @@ LL | if let Range { start: F, end } = F..|| true {}
|
||||||
|
|
|
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found closure `[closure@$DIR/disallowed-positions.rs:136:41: 136:48]`
|
found closure `[closure@$DIR/disallowed-positions.rs:136:41: 136:48]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | if let Range { start: F, end } = F..(|| true)() {}
|
||||||
|
| + +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/disallowed-positions.rs:136:8
|
--> $DIR/disallowed-positions.rs:136:8
|
||||||
|
@ -1314,6 +1318,10 @@ LL | while let Range { start: F, end } = F..|| true {}
|
||||||
|
|
|
|
||||||
= note: expected type `bool`
|
= note: expected type `bool`
|
||||||
found closure `[closure@$DIR/disallowed-positions.rs:200:44: 200:51]`
|
found closure `[closure@$DIR/disallowed-positions.rs:200:44: 200:51]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | while let Range { start: F, end } = F..(|| true)() {}
|
||||||
|
| + +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/disallowed-positions.rs:200:11
|
--> $DIR/disallowed-positions.rs:200:11
|
||||||
|
|
|
@ -8,6 +8,10 @@ LL | let x: () = move || ();
|
||||||
|
|
|
|
||||||
= note: expected unit type `()`
|
= note: expected unit type `()`
|
||||||
found closure `[closure@$DIR/move-closure.rs:5:17: 5:27]`
|
found closure `[closure@$DIR/move-closure.rs:5:17: 5:27]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | let x: () = (move || ())();
|
||||||
|
| + +++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ LL | let _: usize = foo;
|
||||||
found fn item `fn(usize, usize) -> usize {foo}`
|
found fn item `fn(usize, usize) -> usize {foo}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = foo(a, b);
|
LL | let _: usize = foo(_, _);
|
||||||
| ++++++
|
| ++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -105,7 +105,7 @@ LL | let _: usize = T::baz;
|
||||||
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
|
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = T::baz(x, y);
|
LL | let _: usize = T::baz(_, _);
|
||||||
| ++++++
|
| ++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -123,7 +123,7 @@ LL | let _: usize = T::bat;
|
||||||
found fn item `fn(usize) -> usize {<_ as T>::bat}`
|
found fn item `fn(usize) -> usize {<_ as T>::bat}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = T::bat(x);
|
LL | let _: usize = T::bat(_);
|
||||||
| +++
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -159,7 +159,7 @@ LL | let _: usize = X::baz;
|
||||||
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
|
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = X::baz(x, y);
|
LL | let _: usize = X::baz(_, _);
|
||||||
| ++++++
|
| ++++++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -177,7 +177,7 @@ LL | let _: usize = X::bat;
|
||||||
found fn item `fn(usize) -> usize {<X as T>::bat}`
|
found fn item `fn(usize) -> usize {<X as T>::bat}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = X::bat(x);
|
LL | let _: usize = X::bat(_);
|
||||||
| +++
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -195,7 +195,7 @@ LL | let _: usize = X::bax;
|
||||||
found fn item `fn(usize) -> usize {<X as T>::bax}`
|
found fn item `fn(usize) -> usize {<X as T>::bax}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = X::bax(x);
|
LL | let _: usize = X::bax(_);
|
||||||
| +++
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
@ -213,7 +213,7 @@ LL | let _: usize = X::bach;
|
||||||
found fn item `fn(usize) -> usize {<X as T>::bach}`
|
found fn item `fn(usize) -> usize {<X as T>::bach}`
|
||||||
help: use parentheses to call this function
|
help: use parentheses to call this function
|
||||||
|
|
|
|
||||||
LL | let _: usize = X::bach(x);
|
LL | let _: usize = X::bach(_);
|
||||||
| +++
|
| +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
|
|
|
@ -15,6 +15,10 @@ LL | || -> Closure { || () }
|
||||||
|
|
|
|
||||||
= note: expected unit type `()`
|
= note: expected unit type `()`
|
||||||
found closure `[closure@$DIR/issue-63279.rs:8:21: 8:26]`
|
found closure `[closure@$DIR/issue-63279.rs:8:21: 8:26]`
|
||||||
|
help: use parentheses to call this closure
|
||||||
|
|
|
||||||
|
LL | || -> Closure { (|| ())() }
|
||||||
|
| + +++
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/issue-63279.rs:8:5
|
--> $DIR/issue-63279.rs:8:5
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue