mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
diagnostics: Note capturing closures can't be coerced to fns
Fixes: #72457, #71895
This commit is contained in:
parent
0d9651648d
commit
1841a5f64f
@ -32,6 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
|
||||
return;
|
||||
}
|
||||
self.suggest_no_capture_closure(err, expected, expr_ty);
|
||||
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
|
||||
self.suggest_missing_parentheses(err, expr);
|
||||
self.note_need_for_fn_pointer(err, expected, expr_ty);
|
||||
|
@ -2,7 +2,7 @@ use super::FnCtxt;
|
||||
use crate::astconv::AstConv;
|
||||
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_span::{self, Span};
|
||||
use rustc_span::{self, MultiSpan, Span};
|
||||
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
@ -287,6 +287,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering a closure that captures variables, where a FnPtr is expected,
|
||||
/// suggest a non-capturing closure
|
||||
pub(in super::super) fn suggest_no_capture_closure(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
expected: Ty<'tcx>,
|
||||
found: Ty<'tcx>,
|
||||
) {
|
||||
if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
|
||||
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
|
||||
// Report upto four upvars being captured to reduce the amount error messages
|
||||
// reported back to the user.
|
||||
let spans_and_labels = upvars
|
||||
.iter()
|
||||
.take(4)
|
||||
.map(|(var_hir_id, upvar)| {
|
||||
let var_name = self.tcx.hir().name(*var_hir_id).to_string();
|
||||
let msg = format!("`{}` captured here", var_name);
|
||||
(upvar.span, msg)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut multi_span: MultiSpan =
|
||||
spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
|
||||
for (sp, label) in spans_and_labels {
|
||||
multi_span.push_span_label(sp, label);
|
||||
}
|
||||
err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
|
||||
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
|
||||
&self,
|
||||
|
@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
||||
|
|
||||
= note: expected fn pointer `fn(u8) -> u8`
|
||||
found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-no-fn-1.rs:6:39
|
||||
|
|
||||
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
||||
| ^ `a` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -8,6 +8,11 @@ LL | let bar: fn() -> u8 = || { b };
|
||||
|
|
||||
= note: expected fn pointer `fn() -> u8`
|
||||
found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-no-fn-2.rs:6:32
|
||||
|
|
||||
LL | let bar: fn() -> u8 = || { b };
|
||||
| ^ `b` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
8
src/test/ui/closures/closure-no-fn-4.rs
Normal file
8
src/test/ui/closures/closure-no-fn-4.rs
Normal file
@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let b = 2;
|
||||
let _: fn(usize) -> usize = match true {
|
||||
true => |a| a + 1,
|
||||
false => |a| a - b,
|
||||
//~^ ERROR `match` arms have incompatible types
|
||||
};
|
||||
}
|
24
src/test/ui/closures/closure-no-fn-4.stderr
Normal file
24
src/test/ui/closures/closure-no-fn-4.stderr
Normal file
@ -0,0 +1,24 @@
|
||||
error[E0308]: `match` arms have incompatible types
|
||||
--> $DIR/closure-no-fn-4.rs:5:18
|
||||
|
|
||||
LL | let _: fn(usize) -> usize = match true {
|
||||
| _________________________________-
|
||||
LL | | true => |a| a + 1,
|
||||
| | --------- this is found to be of type `fn(usize) -> usize`
|
||||
LL | | false => |a| a - b,
|
||||
| | ^^^^^^^^^ expected fn pointer, found closure
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____- `match` arms have incompatible types
|
||||
|
|
||||
= note: expected fn pointer `fn(usize) -> usize`
|
||||
found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:27]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-no-fn-4.rs:5:26
|
||||
|
|
||||
LL | false => |a| a - b,
|
||||
| ^ `b` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
12
src/test/ui/closures/closure-no-fn-5.rs
Normal file
12
src/test/ui/closures/closure-no-fn-5.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// When providing diagnostics about not being able to coerce a capturing-closure
|
||||
// to fn type, we want to report only upto 4 captures.
|
||||
|
||||
fn main() {
|
||||
let a = 0u8;
|
||||
let b = 0u8;
|
||||
let c = 0u8;
|
||||
let d = 0u8;
|
||||
let e = 0u8;
|
||||
let bar: fn() -> u8 = || { a; b; c; d; e };
|
||||
//~^ ERROR mismatched types
|
||||
}
|
23
src/test/ui/closures/closure-no-fn-5.stderr
Normal file
23
src/test/ui/closures/closure-no-fn-5.stderr
Normal file
@ -0,0 +1,23 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/closure-no-fn-5.rs:10:27
|
||||
|
|
||||
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
|
||||
| ---------- ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected fn pointer `fn() -> u8`
|
||||
found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:47]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-no-fn-5.rs:10:32
|
||||
|
|
||||
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
|
||||
| ^ ^ ^ ^ `d` captured here
|
||||
| | | |
|
||||
| | | `c` captured here
|
||||
| | `b` captured here
|
||||
| `a` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -8,6 +8,11 @@ LL | call_bare(f)
|
||||
|
|
||||
= note: expected fn pointer `for<'r> fn(&'r str)`
|
||||
found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-reform-bad.rs:10:43
|
||||
|
|
||||
LL | let f = |s: &str| println!("{}{}", s, string);
|
||||
| ^^^^^^ `string` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
||||
|
|
||||
= note: expected fn pointer `fn(u8) -> u8`
|
||||
found closure `[main::{closure#0} closure_substs=(unavailable)]`
|
||||
note: closures can only be coerced to `fn` types if they do not capture any variables
|
||||
--> $DIR/closure-print-verbose.rs:10:39
|
||||
|
|
||||
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
||||
| ^ `a` captured here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user