From ab39b0f4c7a69f7d9860e005857e791913c6be6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 28 Jul 2025 02:01:46 +0000 Subject: [PATCH 1/2] Point at the `Fn()` or `FnMut()` bound that coerced a closure, which caused a move error When encountering a move error involving a closure because the captured value isn't `Copy`, and the obligation comes from a bound on a type parameter that requires `Fn` or `FnMut`, we point at it and explain that an `FnOnce` wouldn't cause the move error. ``` error[E0507]: cannot move out of `foo`, a captured variable in an `Fn` closure --> f111.rs:15:25 | 14 | fn do_stuff(foo: Option) { | --- ----------- move occurs because `foo` has type `Option`, which does not implement the `Copy` trait | | | captured outer variable 15 | require_fn_trait(|| async { | -- ^^^^^ `foo` is moved here | | | captured by this `Fn` closure 16 | if foo.map_or(false, |f| f.foo()) { | --- variable moved due to use in coroutine | help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once --> f111.rs:12:53 | 12 | fn require_fn_trait>(_: impl Fn() -> F) {} | ^^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | 16 | if foo.clone().map_or(false, |f| f.foo()) { | ++++++++ ``` --- .../src/diagnostics/move_errors.rs | 40 ++++- compiler/rustc_errors/src/diagnostic.rs | 7 +- tests/ui/borrowck/borrowck-in-static.stderr | 1 + .../borrowck/borrowck-move-by-capture.stderr | 5 + tests/ui/borrowck/issue-103624.stderr | 5 + .../issue-87456-point-to-closure.stderr | 5 + ...ove-upvar-from-non-once-ref-closure.stderr | 5 + tests/ui/issues/issue-4335.stderr | 1 + ...-move-out-of-closure-env-issue-1965.stderr | 5 + ...e-52663-span-decl-captured-variable.stderr | 5 + ...borrowck-call-is-borrow-issue-12224.stderr | 1 + .../dont-suggest-ref/move-into-closure.rs | 21 +++ .../dont-suggest-ref/move-into-closure.stderr | 147 +++++++++++++++--- .../suggestions/option-content-move2.stderr | 11 ++ .../suggestions/option-content-move3.stderr | 12 ++ .../unboxed-closure-illegal-move.stderr | 22 +++ 16 files changed, 268 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index a5661e44af8af..350553a6e868d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -508,12 +508,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); + let mut clause_span = DUMMY_SP; + let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); + if let Some(closure_def_id) = def_id.as_local() + && let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) + && let hir::Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Call(callee, _) = parent.kind + && let Some(ty) = typck_result.node_type_opt(callee.hir_id) + && let ty::FnDef(fn_def_id, args) = ty.kind() + && let predicates = tcx.predicates_of(fn_def_id).instantiate(tcx, args) + && let Some((_, span)) = + predicates.predicates.iter().zip(predicates.spans.iter()).find( + |(pred, _)| match pred.as_trait_clause() { + Some(clause) + if let ty::Closure(clause_closure_def_id, _) = + clause.self_ty().skip_binder().kind() + && clause_closure_def_id == def_id + && (tcx.lang_items().fn_mut_trait() + == Some(clause.def_id()) + || tcx.lang_items().fn_trait() + == Some(clause.def_id())) => + { + // Found `` + true + } + _ => false, + }, + ) + { + // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which + // could be changed to `FnOnce()` to avoid the move error. + clause_span = *span; + } + self.cannot_move_out_of(span, &place_description) .with_span_label(upvar_span, "captured outer variable") .with_span_label( closure_span, format!("captured by this `{closure_kind}` closure"), ) + .with_span_help( + clause_span, + "`Fn` and `FnMut` closures require captured values to be able to be \ + consumed multiple times, but an `FnOnce` consume them only once", + ) } _ => { let source = self.borrowed_content_source(deref_base); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 96c7ba6ed27b9..88e39daba59ec 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -847,17 +847,18 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } + with_fn! { with_span_help, /// Prints the span with some help above it. /// This is like [`Diag::help()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_help>( + pub fn span_help( &mut self, - sp: S, + sp: impl Into, msg: impl Into, ) -> &mut Self { self.sub(Level::Help, msg, sp.into()); self - } + } } /// Disallow attaching suggestions to this diagnostic. /// Any suggestions attached e.g. with the `span_suggestion_*` methods diff --git a/tests/ui/borrowck/borrowck-in-static.stderr b/tests/ui/borrowck/borrowck-in-static.stderr index 9bcf64dd62e2a..d85f6f5fdd5c7 100644 --- a/tests/ui/borrowck/borrowck-in-static.stderr +++ b/tests/ui/borrowck/borrowck-in-static.stderr @@ -10,6 +10,7 @@ LL | Box::new(|| x) | | | captured by this `Fn` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | Box::new(|| x.clone()) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index 732af1593d606..e9e054407662f 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -12,6 +12,11 @@ LL | let _h = to_fn_once(move || -> isize { *bar }); | | | `bar` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/borrowck-move-by-capture.rs:3:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index af65deb16dcf8..ef02280888671 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -13,6 +13,11 @@ LL | LL | self.b; | ^^^^^^ `self.b` is moved here | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-103624.rs:7:36 + | +LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { + | ^^^^^^^^^^^ note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 | diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index a0c7cac2addd0..043e336cd86df 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -10,6 +10,11 @@ LL | LL | let _foo: String = val; | ^^^ move occurs because `val` has type `String`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-87456-point-to-closure.rs:3:24 + | +LL | fn take_mut(_val: impl FnMut()) {} + | ^^^^^^^ help: consider borrowing here | LL | let _foo: String = &val; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index 177e9c8d2487e..d33330413103f 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -10,6 +10,11 @@ LL | y.into_iter(); | | | move occurs because `y` has type `Vec`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 + | +LL | fn call(f: F) where F : Fn() { + | ^^^^ note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/issues/issue-4335.stderr b/tests/ui/issues/issue-4335.stderr index 42ac632256401..b6d8f08616383 100644 --- a/tests/ui/issues/issue-4335.stderr +++ b/tests/ui/issues/issue-4335.stderr @@ -10,6 +10,7 @@ LL | id(Box::new(|| *v)) | | | captured by this `FnMut` closure | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: if `T` implemented `Clone`, you could clone the value --> $DIR/issue-4335.rs:5:10 | diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index 51d0f85c031f5..dfc983bf48744 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -10,6 +10,11 @@ LL | let _f = to_fn(|| test(i)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let _f = to_fn(|| test(i.clone())); diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 5754603700653..7f9a8e50dae66 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -10,6 +10,11 @@ LL | expect_fn(|| drop(x.0)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 + | +LL | fn expect_fn(f: F) where F : Fn() { + | ^^^^ help: consider cloning the value if the performance cost is acceptable | LL | expect_fn(|| drop(x.0.clone())); diff --git a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr index f37dc320fa315..8081f7b3a8b0f 100644 --- a/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr +++ b/tests/ui/span/borrowck-call-is-borrow-issue-12224.stderr @@ -44,6 +44,7 @@ LL | LL | foo(f); | ^ move occurs because `f` has type `{closure@$DIR/borrowck-call-is-borrow-issue-12224.rs:52:17: 52:58}`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider cloning the value if the performance cost is acceptable | LL | foo(f.clone()); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs index 44eac3691a3be..0154810f56439 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.rs @@ -11,8 +11,29 @@ struct X(Y); struct Y; fn consume_fn(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures fn consume_fnmut(_f: F) { } +//~^ HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures +//~| HELP `Fn` and `FnMut` closures pub fn main() { } diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index edda2cbc735a2..d9ed25983d66d 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -1,5 +1,5 @@ error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:28:21 + --> $DIR/move-into-closure.rs:49:21 | LL | let x = X(Y); | - captured outer variable @@ -12,13 +12,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:31:34 + --> $DIR/move-into-closure.rs:52:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -32,13 +37,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:34:37 + --> $DIR/move-into-closure.rs:55:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -52,13 +62,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:37:15 + --> $DIR/move-into-closure.rs:58:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -75,13 +90,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:43:15 + --> $DIR/move-into-closure.rs:64:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -98,13 +118,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:51:25 + --> $DIR/move-into-closure.rs:72:25 | LL | let x = X(Y); | - captured outer variable @@ -118,13 +143,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:54:38 + --> $DIR/move-into-closure.rs:75:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -138,13 +168,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:57:41 + --> $DIR/move-into-closure.rs:78:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -158,13 +193,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:60:15 + --> $DIR/move-into-closure.rs:81:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -181,13 +221,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `Fn` closure - --> $DIR/move-into-closure.rs:66:15 + --> $DIR/move-into-closure.rs:87:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -204,13 +249,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:13:18 + | +LL | fn consume_fn(_f: F) { } + | ^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:85:21 + --> $DIR/move-into-closure.rs:106:21 | LL | let x = X(Y); | - captured outer variable @@ -223,13 +273,18 @@ LL | let X(_t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(_t) = &x; | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:88:34 + --> $DIR/move-into-closure.rs:109:34 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -243,13 +298,18 @@ LL | if let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:91:37 + --> $DIR/move-into-closure.rs:112:37 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -263,13 +323,18 @@ LL | while let Either::One(_t) = e { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(_t) = &e { } | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:94:15 + --> $DIR/move-into-closure.rs:115:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -286,13 +351,18 @@ LL | Either::One(_t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `e.0`, as `e` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:100:15 + --> $DIR/move-into-closure.rs:121:15 | LL | let e = Either::One(X(Y)); | - captured outer variable @@ -309,13 +379,18 @@ LL | Either::One(_t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &e { | + error[E0507]: cannot move out of `x.0`, as `x` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:108:25 + --> $DIR/move-into-closure.rs:129:25 | LL | let x = X(Y); | - captured outer variable @@ -329,13 +404,18 @@ LL | let X(mut _t) = x; | data moved here | move occurs because `_t` has type `Y`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | let X(mut _t) = &x; | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:111:38 + --> $DIR/move-into-closure.rs:132:38 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -349,13 +429,18 @@ LL | if let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:114:41 + --> $DIR/move-into-closure.rs:135:41 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -369,13 +454,18 @@ LL | while let Either::One(mut _t) = em { } | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:117:15 + --> $DIR/move-into-closure.rs:138:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -392,13 +482,18 @@ LL | Either::One(mut _t) | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:123:15 + --> $DIR/move-into-closure.rs:144:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -415,13 +510,18 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { | + error[E0507]: cannot move out of `em.0`, as `em` is a captured variable in an `FnMut` closure - --> $DIR/move-into-closure.rs:130:15 + --> $DIR/move-into-closure.rs:151:15 | LL | let mut em = Either::One(X(Y)); | ------ captured outer variable @@ -438,6 +538,11 @@ LL | Either::One(mut _t) => (), | data moved here | move occurs because `_t` has type `X`, which does not implement the `Copy` trait | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/move-into-closure.rs:25:21 + | +LL | fn consume_fnmut(_f: F) { } + | ^^^^^^^ help: consider borrowing here | LL | match &em { diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c73e874b40368..c8aa6667b583f 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -14,6 +14,11 @@ LL | LL | var = Some(NotCopyable); | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move2.rs:1:1 | @@ -38,6 +43,12 @@ LL | move || { LL | LL | var = Some(NotCopyableButCloneable); | --- variable moved due to use in closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move2.rs:5:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 68c52352a6512..2c9a86c036be4 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -9,6 +9,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -37,6 +38,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -57,6 +63,7 @@ LL | move || { LL | let x = var; | ^^^ move occurs because `var` has type `NotCopyableButCloneable`, which does not implement the `Copy` trait | + = help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once help: consider borrowing here | LL | let x = &var; @@ -77,6 +84,11 @@ LL | move || { LL | let x = var; | --- variable moved due to use in closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/option-content-move3.rs:6:12 + | +LL | fn func H, H: FnMut()>(_: F) {} + | ^^^^^^^^^^^^ help: consider cloning the value before moving it into the closure | LL ~ { diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 8d9a61cb68126..9d87402a15bff 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -10,6 +10,11 @@ LL | let f = to_fn(|| drop(x)); | | | captured by this `Fn` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn(|| drop(x.clone())); @@ -27,6 +32,11 @@ LL | let f = to_fn_mut(|| drop(x)); | | | captured by this `FnMut` closure | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn_mut(|| drop(x.clone())); @@ -43,6 +53,12 @@ LL | let f = to_fn(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `Fn` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:7:33 + | +LL | fn to_fn>(f: F) -> F { f } + | ^^^^^ error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -55,6 +71,12 @@ LL | let f = to_fn_mut(move || drop(x)); | ------- ^ `x` is moved here | | | captured by this `FnMut` closure + | +help: `Fn` and `FnMut` closures require captured values to be able to be consumed multiple times, but an `FnOnce` consume them only once + --> $DIR/unboxed-closure-illegal-move.rs:8:37 + | +LL | fn to_fn_mut>(f: F) -> F { f } + | ^^^^^^^^ error: aborting due to 4 previous errors From 754f7cd525ef02c975e083fc695f1899142483d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 28 Jul 2025 02:15:39 +0000 Subject: [PATCH 2/2] Add label to `help` with suggestion to turn bound to `FnOnce` --- .../src/diagnostics/move_errors.rs | 10 +++-- .../borrowck/borrowck-move-by-capture.stderr | 2 +- tests/ui/borrowck/issue-103624.stderr | 2 +- .../borrowck/issue-87456-point-to-closure.rs | 2 +- .../issue-87456-point-to-closure.stderr | 2 +- ...ove-upvar-from-non-once-ref-closure.stderr | 2 +- ...-move-out-of-closure-env-issue-1965.stderr | 2 +- ...e-52663-span-decl-captured-variable.stderr | 2 +- .../dont-suggest-ref/move-into-closure.stderr | 42 +++++++++---------- .../suggestions/option-content-move2.stderr | 4 +- .../suggestions/option-content-move3.stderr | 4 +- .../unboxed-closure-illegal-move.stderr | 8 ++-- 12 files changed, 43 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 350553a6e868d..666adfca5e40f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -2,7 +2,7 @@ #![allow(rustc::untranslatable_diagnostic)] use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag}; +use rustc_errors::{Applicability, Diag, MultiSpan}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, CaptureBy, ExprKind, HirId, Node}; use rustc_middle::bug; @@ -508,7 +508,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); let closure_span = tcx.def_span(def_id); - let mut clause_span = DUMMY_SP; + let mut clause_span: MultiSpan = DUMMY_SP.into(); let typck_result = self.infcx.tcx.typeck(self.mir_def_id()); if let Some(closure_def_id) = def_id.as_local() && let hir::Node::Expr(expr) = tcx.hir_node_by_def_id(closure_def_id) @@ -538,7 +538,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { { // We point at the `Fn()` or `FnMut()` bound that coerced the closure, which // could be changed to `FnOnce()` to avoid the move error. - clause_span = *span; + clause_span = (*span).into(); + if fn_def_id.is_local() { + clause_span + .push_span_label(*span, "consider changing this bound to be `FnOnce`"); + } } self.cannot_move_out_of(span, &place_description) diff --git a/tests/ui/borrowck/borrowck-move-by-capture.stderr b/tests/ui/borrowck/borrowck-move-by-capture.stderr index e9e054407662f..61e11944aedda 100644 --- a/tests/ui/borrowck/borrowck-move-by-capture.stderr +++ b/tests/ui/borrowck/borrowck-move-by-capture.stderr @@ -16,7 +16,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/borrowck-move-by-capture.rs:3:37 | LL | fn to_fn_mut>(f: F) -> F { f } - | ^^^^^^^^ + | ^^^^^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value before moving it into the closure | LL ~ let value = bar.clone(); diff --git a/tests/ui/borrowck/issue-103624.stderr b/tests/ui/borrowck/issue-103624.stderr index ef02280888671..676acdf31ddd5 100644 --- a/tests/ui/borrowck/issue-103624.stderr +++ b/tests/ui/borrowck/issue-103624.stderr @@ -17,7 +17,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/issue-103624.rs:7:36 | LL | async fn spawn_blocking(f: impl (Fn() -> T) + Send + Sync + 'static) -> T { - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ consider changing this bound to be `FnOnce` note: if `StructB` implemented `Clone`, you could clone the value --> $DIR/issue-103624.rs:23:1 | diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.rs b/tests/ui/borrowck/issue-87456-point-to-closure.rs index 9fc12ba749042..26e3ac80ce44d 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.rs +++ b/tests/ui/borrowck/issue-87456-point-to-closure.rs @@ -1,6 +1,6 @@ // Regression test for #87456. -fn take_mut(_val: impl FnMut()) {} +fn take_mut(_val: impl FnMut()) {} //~ NOTE: consider changing this bound to be `FnOnce` fn main() { let val = String::new(); diff --git a/tests/ui/borrowck/issue-87456-point-to-closure.stderr b/tests/ui/borrowck/issue-87456-point-to-closure.stderr index 043e336cd86df..31007ad763746 100644 --- a/tests/ui/borrowck/issue-87456-point-to-closure.stderr +++ b/tests/ui/borrowck/issue-87456-point-to-closure.stderr @@ -14,7 +14,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/issue-87456-point-to-closure.rs:3:24 | LL | fn take_mut(_val: impl FnMut()) {} - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | let _foo: String = &val; diff --git a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr index d33330413103f..84aa8b0f30c9a 100644 --- a/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr +++ b/tests/ui/borrowck/unboxed-closures-move-upvar-from-non-once-ref-closure.stderr @@ -14,7 +14,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/unboxed-closures-move-upvar-from-non-once-ref-closure.rs:5:28 | LL | fn call(f: F) where F : Fn() { - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` note: `into_iter` takes ownership of the receiver `self`, which moves `y` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL help: you can `clone` the value and consume it, but this might not be your desired behavior diff --git a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr index dfc983bf48744..92c674defe72b 100644 --- a/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr +++ b/tests/ui/moves/moves-based-on-type-move-out-of-closure-env-issue-1965.stderr @@ -14,7 +14,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/moves-based-on-type-move-out-of-closure-env-issue-1965.rs:3:33 | LL | fn to_fn>(f: F) -> F { f } - | ^^^^^ + | ^^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value if the performance cost is acceptable | LL | let _f = to_fn(|| test(i.clone())); diff --git a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr index 7f9a8e50dae66..5ea7980b7399b 100644 --- a/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr +++ b/tests/ui/nll/issue-52663-span-decl-captured-variable.stderr @@ -14,7 +14,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/issue-52663-span-decl-captured-variable.rs:1:33 | LL | fn expect_fn(f: F) where F : Fn() { - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value if the performance cost is acceptable | LL | expect_fn(|| drop(x.0.clone())); diff --git a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr index d9ed25983d66d..ee668de2a4a46 100644 --- a/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr +++ b/tests/ui/suggestions/dont-suggest-ref/move-into-closure.stderr @@ -16,7 +16,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | let X(_t) = &x; @@ -41,7 +41,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | if let Either::One(_t) = &e { } @@ -66,7 +66,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | while let Either::One(_t) = &e { } @@ -94,7 +94,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &e { @@ -122,7 +122,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &e { @@ -147,7 +147,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | let X(mut _t) = &x; @@ -172,7 +172,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } @@ -197,7 +197,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } @@ -225,7 +225,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &em { @@ -253,7 +253,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:13:18 | LL | fn consume_fn(_f: F) { } - | ^^^^ + | ^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &em { @@ -277,7 +277,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | let X(_t) = &x; @@ -302,7 +302,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | if let Either::One(_t) = &e { } @@ -327,7 +327,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | while let Either::One(_t) = &e { } @@ -355,7 +355,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &e { @@ -383,7 +383,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &e { @@ -408,7 +408,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | let X(mut _t) = &x; @@ -433,7 +433,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | if let Either::One(mut _t) = &em { } @@ -458,7 +458,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | while let Either::One(mut _t) = &em { } @@ -486,7 +486,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &em { @@ -514,7 +514,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &em { @@ -542,7 +542,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/move-into-closure.rs:25:21 | LL | fn consume_fnmut(_f: F) { } - | ^^^^^^^ + | ^^^^^^^ consider changing this bound to be `FnOnce` help: consider borrowing here | LL | match &em { diff --git a/tests/ui/suggestions/option-content-move2.stderr b/tests/ui/suggestions/option-content-move2.stderr index c8aa6667b583f..6c01e15a57a67 100644 --- a/tests/ui/suggestions/option-content-move2.stderr +++ b/tests/ui/suggestions/option-content-move2.stderr @@ -18,7 +18,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/option-content-move2.rs:5:12 | LL | fn func H, H: FnMut()>(_: F) {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ consider changing this bound to be `FnOnce` note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move2.rs:1:1 | @@ -48,7 +48,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/option-content-move2.rs:5:12 | LL | fn func H, H: FnMut()>(_: F) {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ consider changing this bound to be `FnOnce` error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/option-content-move3.stderr b/tests/ui/suggestions/option-content-move3.stderr index 2c9a86c036be4..674a8d27a75f7 100644 --- a/tests/ui/suggestions/option-content-move3.stderr +++ b/tests/ui/suggestions/option-content-move3.stderr @@ -42,7 +42,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/option-content-move3.rs:6:12 | LL | fn func H, H: FnMut()>(_: F) {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ consider changing this bound to be `FnOnce` note: if `NotCopyable` implemented `Clone`, you could clone the value --> $DIR/option-content-move3.rs:2:1 | @@ -88,7 +88,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/option-content-move3.rs:6:12 | LL | fn func H, H: FnMut()>(_: F) {} - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value before moving it into the closure | LL ~ { diff --git a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr index 9d87402a15bff..86eb3cf72341c 100644 --- a/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr +++ b/tests/ui/unboxed-closures/unboxed-closure-illegal-move.stderr @@ -14,7 +14,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/unboxed-closure-illegal-move.rs:7:33 | LL | fn to_fn>(f: F) -> F { f } - | ^^^^^ + | ^^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn(|| drop(x.clone())); @@ -36,7 +36,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/unboxed-closure-illegal-move.rs:8:37 | LL | fn to_fn_mut>(f: F) -> F { f } - | ^^^^^^^^ + | ^^^^^^^^ consider changing this bound to be `FnOnce` help: consider cloning the value if the performance cost is acceptable | LL | let f = to_fn_mut(|| drop(x.clone())); @@ -58,7 +58,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/unboxed-closure-illegal-move.rs:7:33 | LL | fn to_fn>(f: F) -> F { f } - | ^^^^^ + | ^^^^^ consider changing this bound to be `FnOnce` error[E0507]: cannot move out of `x`, a captured variable in an `FnMut` closure --> $DIR/unboxed-closure-illegal-move.rs:32:40 @@ -76,7 +76,7 @@ help: `Fn` and `FnMut` closures require captured values to be able to be consume --> $DIR/unboxed-closure-illegal-move.rs:8:37 | LL | fn to_fn_mut>(f: F) -> F { f } - | ^^^^^^^^ + | ^^^^^^^^ consider changing this bound to be `FnOnce` error: aborting due to 4 previous errors