From 20b12fc38a33d115aead1f28aa703626179eca34 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Sat, 4 Nov 2023 17:10:53 +0100 Subject: [PATCH] feat: needless_move lint An implementation for the lint described in https://github.com/rust-lang/rust-clippy/issues/11721 --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/endian_bytes.rs | 2 +- clippy_lints/src/fallible_impl_from.rs | 2 +- clippy_lints/src/lib.rs | 14 +- clippy_lints/src/loops/manual_memcpy.rs | 2 +- clippy_lints/src/needless_move.rs | 221 +++ clippy_lints/src/panic_in_result_fn.rs | 2 +- clippy_lints/src/unwrap_in_result.rs | 2 +- src/driver.rs | 2 +- tests/ui/needless_move.fixed | 1896 +++++++++++++++++++++++ tests/ui/needless_move.rs | 1896 +++++++++++++++++++++++ tests/ui/needless_move.stderr | 345 +++++ 13 files changed, 4374 insertions(+), 12 deletions(-) create mode 100644 clippy_lints/src/needless_move.rs create mode 100644 tests/ui/needless_move.fixed create mode 100644 tests/ui/needless_move.rs create mode 100644 tests/ui/needless_move.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 1760d19b3851..df6d51cb224c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5335,6 +5335,7 @@ Released 2018-09-13 [`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match +[`needless_move`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_move [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 55dedc0a658a..8327cbf4fee9 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -497,6 +497,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, crate::needless_if::NEEDLESS_IF_INFO, crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_move::NEEDLESS_MOVE_INFO, crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, crate::needless_pass_by_ref_mut::NEEDLESS_PASS_BY_REF_MUT_INFO, crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index 6f5a0cb8801b..a7baee4486c6 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -203,7 +203,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix lint.as_name(prefix), if prefix == Prefix::To { " method" } else { "" }, ), - move |diag| { + |diag| { if let Some(help) = help { diag.help(help); } diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 753f75d83a84..6ee94ae5848e 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -116,7 +116,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl FALLIBLE_IMPL_FROM, impl_span, "consider implementing `TryFrom` instead", - move |diag| { + |diag| { diag.help( "`From` is intended for infallible conversions only. \ Use `TryFrom` if there's a possibility for the conversion to fail", diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 952625c78be1..2549fe9c5f8b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -235,6 +235,7 @@ mod needless_else; mod needless_for_each; mod needless_if; mod needless_late_init; +mod needless_move; mod needless_parens_on_range_literals; mod needless_pass_by_ref_mut; mod needless_pass_by_value; @@ -685,7 +686,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); - store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); + store.register_late_pass(|_| Box::new(needless_question_mark::NeedlessQuestionMark)); store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); @@ -752,7 +753,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); - store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); + store.register_late_pass(|_| Box::new(exhaustive_items::ExhaustiveItems)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); @@ -895,7 +896,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); - store.register_early_pass(move || Box::new(module_style::ModStyle)); + store.register_early_pass(|| Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::::default()); store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); store.register_late_pass(move |_| { @@ -905,9 +906,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(allowed_scripts))); store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); - store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); - store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); - store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); + store.register_late_pass(|_| Box::new(self_named_constructors::SelfNamedConstructors)); + store.register_late_pass(|_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); + store.register_late_pass(|_| Box::new(manual_assert::ManualAssert)); store.register_late_pass(move |_| { Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new( enable_raw_pointer_heuristic_for_send, @@ -1068,6 +1069,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); + store.register_late_pass(|_| Box::new(needless_move::NeedlessMove)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index 40d56240b9de..decbd7669fd7 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -408,7 +408,7 @@ fn get_assignments<'a, 'tcx>( // just increases complexity. (cc #3188 and #4193) stmts .iter() - .filter_map(move |stmt| match stmt.kind { + .filter_map(|stmt| match stmt.kind { StmtKind::Local(..) | StmtKind::Item(..) => None, StmtKind::Expr(e) | StmtKind::Semi(e) => Some(e), }) diff --git a/clippy_lints/src/needless_move.rs b/clippy_lints/src/needless_move.rs new file mode 100644 index 000000000000..c69d864a266a --- /dev/null +++ b/clippy_lints/src/needless_move.rs @@ -0,0 +1,221 @@ +//! This lint works by looking at the min_captures that `rustc` uses, +//! and checks that for the expression `capture_kind_expr_id`, it would +//! actually borrow normally, if it weren't for the move keyword. +//! +//! In such cases, the move keyword changes the semantics of the code (e.g. +//! without it that capture would be a normal by reference capture, but with +//! move it would get captured by value, and therefore we do not remove the `move` +//! keyword from the closure). +//! +//! A small caveat for the approach above: +//! There's both a borrow and a move of the same value into the closure, e.g.: +//! +//! ```no_run +//! let x = String::new(); +//! let closure = move || { +//! let s = x.as_str(); // L1 +//! println!("{s}"); +//! drop(x); // L2 +//! }; +//! ``` +//! +//! In this case, the `x` `String` gets moved into the closure (because of L2), but +//! it is also borrowed prior to that at L1. +//! +//! `rustc`, in the presence of the `move` keyword automatically assumes that if +//! it borrows a value, it's going to move it into the closure (in the example above at L1, +//! so capture_kind_expr_id would point to the use on L1), but here, in the case +//! of this lint, we should behave a little differently, namely we should first look +//! at all the locations where a place is captured, and if any of them actually moves it, +//! the closure would consume the value. +//! +//! The logic for this is handled in `MovedVariablesCtxt::get_required_kind`, where we +//! try to infer the actual min capture kind needed. + +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::sugg::DiagnosticExt; +use rustc_errors::Applicability; +use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, HirId}; +use rustc_hir_typeck::expr_use_visitor as euv; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::mir::FakeReadCause; +use rustc_middle::ty; +use rustc_middle::ty::UpvarCapture; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for closures and `async` blocks where the `move` is not necessary. + /// E.g. all the values are captured by value into the closure / `async` block. + /// + /// ### Why is this bad? + /// Pedantry + /// ### Example + /// ```no_run + /// let a = String::new(); + /// let closure = move || { + /// drop(a); + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// let a = String::new(); + /// let closure = || { + /// drop(a); + /// }; + /// ``` + #[clippy::version = "1.76.0"] + pub NEEDLESS_MOVE, + pedantic, + "checks for needless `move`s on closures / `async` blocks" +} + +declare_lint_pass!(NeedlessMove => [NEEDLESS_MOVE]); + +impl NeedlessMove { + fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, closure: &'tcx Closure<'tcx>) { + let CaptureBy::Value { move_kw } = closure.capture_clause else { + return; + }; + + if move_kw.is_dummy() { + // async fn ...() {} convert the body to an `async move {}` block, + // with a DUMMY_SP for the move_kw + return; + } + + // Collect moved & borrowed variables from the closure, which the closure *actually* needs. + let ctx = { + let mut ctx = MovedVariablesCtxt::default(); + let body = cx.tcx.hir().body(closure.body); + let infcx = cx.tcx.infer_ctxt().build(); + euv::ExprUseVisitor::new(&mut ctx, &infcx, closure.def_id, cx.param_env, cx.typeck_results()) + .consume_body(body); + ctx + }; + + enum LintResult { + /// do not remove the `move` keyword. + NeedMove, + Consumed, + NothingCaptured, + } + + let mut lint_result = LintResult::NothingCaptured; + + for captured_place in cx.typeck_results().closure_min_captures_flattened(closure.def_id) { + let place = &captured_place.place; + if let Some(ck_expr_id) = captured_place.info.capture_kind_expr_id { + let required_ck = ctx.get_required_kind(place, ck_expr_id); + match required_ck { + UpvarCapture::ByRef(_) => { + // no matter what the old `lint_result` is, we keep the move. + lint_result = LintResult::NeedMove; + }, + UpvarCapture::ByValue => { + lint_result = match lint_result { + LintResult::NothingCaptured | LintResult::Consumed => LintResult::Consumed, + LintResult::NeedMove => LintResult::NeedMove, + } + }, + } + } + } + + let lint = |note_msg: &'static str| { + span_lint_and_then( + cx, + NEEDLESS_MOVE, + expr.span, + "you seem to use `move`, but the `move` is unnecessary", + |diag| { + diag.suggest_remove_item(cx, move_kw, "remove the `move`", Applicability::MachineApplicable); + diag.note(note_msg); + }, + ); + }; + + match lint_result { + LintResult::NothingCaptured => { + lint("there were no captured variables, so the `move` is unnecessary"); + }, + LintResult::Consumed => { + lint("there were consumed variables, but no borrowed variables, so the `move` is unnecessary"); + }, + LintResult::NeedMove => { + // there was a value which would be borrowed if it weren't for the move keyword, + // so we should keep it, as removing it would change semantics. + }, + } + } +} + +impl<'tcx> LateLintPass<'tcx> for NeedlessMove { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if expr.span.from_expansion() { + return; + } + + let ExprKind::Closure(closure) = &expr.kind else { + return; + }; + + Self::check_closure(cx, expr, closure); + } +} + +#[derive(Debug, Default)] +struct MovedVariablesCtxt<'tcx> { + // for each base variable, we remember: + /// The places where it was captured (and consumed, e.g. moved into the closure). + moved: Vec<(euv::Place<'tcx>, HirId)>, + /// The places where it was captured by reference (and not consumed). + captured: Vec<(euv::Place<'tcx>, HirId, ty::BorrowKind)>, +} + +impl<'tcx> MovedVariablesCtxt<'tcx> { + fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, hir_id: HirId) { + if let euv::PlaceBase::Upvar(_) = cmt.place.base { + self.moved.push((cmt.place.clone(), hir_id)); + } + } + + fn borrow_common(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, borrow_hir_id: HirId, bk: ty::BorrowKind) { + if let euv::PlaceBase::Upvar(_) = cmt.place.base { + self.captured.push((cmt.place.clone(), borrow_hir_id, bk)); + } + } + + fn get_required_kind(&self, place: &euv::Place<'tcx>, ref_hir_id: HirId) -> UpvarCapture { + match self + .moved + .iter() + .any(|upvar_ref| upvar_ref.0 == *place || upvar_ref.1 == ref_hir_id) + { + true => UpvarCapture::ByValue, + false => self + .captured + .iter() + .find(|upvar_ref| upvar_ref.1 == ref_hir_id) + .map(|it| UpvarCapture::ByRef(it.2)) + .unwrap(), + } + } +} + +impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'tcx> { + fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, hir_id: HirId) { + self.move_common(cmt, hir_id); + } + + fn borrow(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, hir_id: HirId, bk: ty::BorrowKind) { + self.borrow_common(cmt, hir_id, bk); + } + + fn mutate(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, hir_id: HirId) { + self.borrow(cmt, hir_id, ty::BorrowKind::MutBorrow); + } + + fn fake_read(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} +} diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 6a760f9fe64a..0ed1cdc4da08 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -84,7 +84,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir PANIC_IN_RESULT_FN, impl_span, "used `panic!()` or assertion in a function that returns `Result`", - move |diag| { + |diag| { diag.help( "`panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing", ); diff --git a/clippy_lints/src/unwrap_in_result.rs b/clippy_lints/src/unwrap_in_result.rs index df4b42133f8c..fb33d5462ecb 100644 --- a/clippy_lints/src/unwrap_in_result.rs +++ b/clippy_lints/src/unwrap_in_result.rs @@ -106,7 +106,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc UNWRAP_IN_RESULT, impl_span, "used unwrap or expect in a function that returns result or option", - move |diag| { + |diag| { diag.help("unwrap and expect should not be used in a function that returns result or option"); diag.span_note(result, "potential non-recoverable error(s)"); }, diff --git a/src/driver.rs b/src/driver.rs index 1ae8ac81695f..281fa20c61b2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -186,7 +186,7 @@ pub fn main() { handler.note_without_error(format!("Clippy version: {version_info}")); }); - exit(rustc_driver::catch_with_exit_code(move || { + exit(rustc_driver::catch_with_exit_code(|| { let mut orig_args: Vec = env::args().collect(); let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some(); diff --git a/tests/ui/needless_move.fixed b/tests/ui/needless_move.fixed new file mode 100644 index 000000000000..cc0533d3adba --- /dev/null +++ b/tests/ui/needless_move.fixed @@ -0,0 +1,1896 @@ +//! To properly check that the `needless_move` lint is complete, go to the +//! `.fixed` file of this test and check that the code fails to compile if +//! any of the `move`s are removed. + +#![warn(clippy::needless_move)] +#![allow(unused)] +#![allow(ungated_async_fn_track_caller)] +#![allow(clippy::useless_format)] +#![allow(clippy::let_and_return)] +#![allow(clippy::no_effect)] +#![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] +#![allow(clippy::disallowed_names)] +#![allow(clippy::manual_async_fn)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::redundant_closure_call)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::extra_unused_type_parameters)] +#![allow(clippy::unused_unit)] +#![feature(async_closure)] +#![feature(raw_ref_op)] + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +struct Composite { + copy: Copy, + non_copy: NonCopy, +} + +impl Composite { + fn new() -> Self { + Self { + copy: Copy, + non_copy: NonCopy, + } + } +} + +fn with_owned(_: T) {} +fn with_ref(_: &T) {} +fn with_ref_mut(_: &mut T) {} +fn assert_static(v: T) -> T { + v +} + +fn main() { + // doesn't trigger on non-move closures or async blocks + let a = NonCopy; + let b = Copy; + let closure = || { + with_owned(a); + with_owned(b); + }; + + let a = NonCopy; + let b = Copy; + let fut = async { + with_owned(a); + with_owned(b); + }; + + // doesn't trigger on async fns + + // (an async fn is a fn whose body turns into an `async move {}` block, where the `move` kw has + // DUMMY_SP as the Span). It shouldn't trigger the lint. + async fn f() {} + + // triggers on move closures and async blocks which do not capture anything + let closure = assert_static(|| {}); + let fut = assert_static(async {}); + + // owned + NonCopy + let a = NonCopy; + let closure = assert_static(|| { + with_owned(a); + }); + + // owned + Copy + let a = Copy; + let closure = assert_static(move || { + with_owned(a); + }); + + // ref + NonCopy + let a = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref + Copy + let a = Copy; + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref mut + NonCopy + let mut a = NonCopy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // ref mut + Copy + let mut a = Copy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // with async + + // doesn't trigger if not capturing with `move` + let a = NonCopy; + let b = Copy; + let fut = async { + with_owned(a); + with_owned(b); + }; + + // owned + non-copy + let a = NonCopy; + let fut = assert_static(async { + with_owned(a); + }); + + // owned + copy + let a = Copy; + let fut = assert_static(async move { + with_owned(a); + }); + + // ref + non-copy + let a = NonCopy; + let fut = assert_static(async move { + with_ref(&a); + }); + + // ref + copy + let a = Copy; + let fut = assert_static(async move { + with_ref(&a); + }); + + // ref mut + non-copy + let mut a = NonCopy; + let fut = assert_static(async move { + with_ref_mut(&mut a); + }); + + // ref mut + copy + let mut a = Copy; + let fut = assert_static(async move { + with_ref_mut(&mut a); + }); + + // triggers on ref + owned combinations + // ref + owned + non copy + let a = NonCopy; + let closure = assert_static(|| { + with_ref(&a); + with_owned(a); + }); + + // ref + owned + copy + let a = Copy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(a); + }); + + // ref mut + owned + non copy + let mut a = NonCopy; + let closure = assert_static(|| { + with_ref_mut(&mut a); + with_owned(a); + }); + + // ref mut + owned + copy + let mut a = Copy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + with_owned(a); + }); + + // ref + owned + non copy + other owned capture in between + let a = NonCopy; + let b = NonCopy; + let closure = assert_static(|| { + with_ref(&a); + with_owned(b); + with_owned(a); + }); + + // ref + owned + copy + other owned capture in between + let a = Copy; + let b = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(b); + with_owned(a); + }); + + // with composite structures + disjoint captures + + // owned + let a = Composite::new(); + let closure = assert_static(|| { + with_owned(a); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // capturing only the copy part + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a.copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.copy); + }); + + // capturing only the non-copy part + // owned + let a = Composite::new(); + let closure = assert_static(|| { + with_owned(a.non_copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.non_copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.non_copy); + }); + + // capturing both parts + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a.copy); + with_owned(a.non_copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.copy); + with_ref(&a.non_copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.copy); + with_ref_mut(&mut a.non_copy); + }); + + // correctly handles direct mutations of variables + // copy + let mut a = Copy; + let closure = assert_static(move || { + a = Copy; + }); + + // non-copy + let mut a = NonCopy; + let closure = assert_static(move || { + a = NonCopy; + }); + + // composite -> copy + let mut a = Composite::new(); + let closure = assert_static(move || { + a.copy = Copy; + }); + + // composite -> non-copy + let mut a = Composite::new(); + let closure = assert_static(move || { + a.non_copy = NonCopy; + }); + + // copy + owned consume + let mut a = Copy; + let closure = assert_static(move || { + a = Copy; + with_owned(a); + }); + + // non-copy + owned consume + let mut a = NonCopy; + let closure = assert_static(|| { + a = NonCopy; + with_owned(a); + }); + + // composite -> copy + owned consume + let mut a = Composite::new(); + let closure = assert_static(|| { + a.copy = Copy; + with_owned(a); + }); + + // composite -> non-copy + owned consume + let mut a = Composite::new(); + let closure = assert_static(|| { + a.non_copy = NonCopy; + with_owned(a); + }); + + // async blocks + // copy + let mut a = Copy; + let fut = assert_static(async move { + a = Copy; + }); + + // non-copy + let mut a = NonCopy; + let fut = assert_static(async move { + a = NonCopy; + }); + + // composite -> copy + let mut a = Composite::new(); + let fut = assert_static(async move { + a.copy = Copy; + }); + + // composite -> non-copy + let mut a = Composite::new(); + let fut = assert_static(async move { + a.non_copy = NonCopy; + }); + + // copy + owned consume + let mut a = Copy; + let fut = assert_static(async move { + a = Copy; + with_owned(a); + }); + + // non-copy + owned consume + let mut a = NonCopy; + let fut = assert_static(async { + a = NonCopy; + with_owned(a); + }); + + // composite -> copy + owned consume + let mut a = Composite::new(); + let fut = assert_static(async { + a.copy = Copy; + with_owned(a); + }); + + // composite -> non-copy + owned consume + let mut a = Composite::new(); + let fut = assert_static(async { + a.non_copy = NonCopy; + with_owned(a); + }); + + let v = (String::new(), String::new()); + assert_static(move || { + let _w = v.0; + let _h = &v.1; + }); + + // below are a few tests from rustc's testsuite that use move closures, + // which might uncover edge cases + + // rust/$DIR/closures/2229_closure_analysis/migrations/no_migrations.rs + + fn _no_migrations() { + // Set of test cases that don't need migrations + + #![deny(rust_2021_incompatible_closure_captures)] + + // Copy types as copied by the closure instead of being moved into the closure + // Therefore their drop order isn't tied to the closure and won't be requiring any + // migrations. + fn test1_only_copy_types() { + let t = (0i32, 0i32); + + let c = || { + let _t = t.0; + }; + + c(); + } + + // Same as test1 but using a move closure + fn test2_only_copy_types_move_closure() { + let t = (0i32, 0i32); + + let c = move || { + println!("{}", t.0); + }; + + c(); + } + + // Don't need to migrate if captured by ref + fn test3_only_copy_types_move_closure() { + let t = (String::new(), String::new()); + + let c = || { + println!("{}", t.0); + }; + + c(); + } + + // Test migration analysis in case of Insignificant Drop + Non Drop aggregates. + // Note in this test the closure captures a non Drop type and therefore the variable + // is only captured by ref. + fn test4_insignificant_drop_non_drop_aggregate() { + let t = (String::new(), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); + } + + struct Foo(i32); + impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } + } + + // Test migration analysis in case of Significant Drop + Non Drop aggregates. + // Note in this test the closure captures a non Drop type and therefore the variable + // is only captured by ref. + fn test5_significant_drop_non_drop_aggregate() { + let t = (Foo(0), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); + } + + fn main() { + test1_only_copy_types(); + test2_only_copy_types_move_closure(); + test3_only_copy_types_move_closure(); + test4_insignificant_drop_non_drop_aggregate(); + test5_significant_drop_non_drop_aggregate(); + } + } + + // rust/$DIR/closures/2229_closure_analysis/run_pass/issue-88476.rs + + fn _issue_88476() { + use std::rc::Rc; + + // Test that we restrict precision when moving not-`Copy` types, if any of the parent paths + // implement `Drop`. This is to ensure that we don't move out of a type that implements Drop. + pub fn test1() { + struct Foo(Rc); + + impl Drop for Foo { + fn drop(self: &mut Foo) {} + } + + let f = Foo(Rc::new(1)); + let x = move || { + println!("{:?}", f.0); + }; + + x(); + } + + // Test that we don't restrict precision when moving `Copy` types(i.e. when copying), + // even if any of the parent paths implement `Drop`. + pub fn test2() { + struct Character { + hp: u32, + name: String, + } + + impl Drop for Character { + fn drop(&mut self) {} + } + + let character = Character { + hp: 100, + name: format!("A"), + }; + + let c = move || println!("{}", character.hp); + + c(); + + println!("{}", character.name); + } + + fn main() {} + } + + // rust/$DIR/closures/2229_closure_analysis/preserve_field_drop_order2.rs + + fn _preserve_field_drop_order2() { + #[derive(Debug)] + struct Dropable(&'static str); + + impl Drop for Dropable { + fn drop(&mut self) { + println!("Dropping {}", self.0) + } + } + + #[derive(Debug)] + struct A { + x: Dropable, + y: Dropable, + } + + #[derive(Debug)] + struct B { + c: A, + d: A, + } + + #[derive(Debug)] + struct R<'a> { + c: &'a A, + d: &'a A, + } + + fn main() { + let a = A { + x: Dropable("x"), + y: Dropable("y"), + }; + + let c = move || println!("{:?} {:?}", a.y, a.x); + + c(); + + let b = B { + c: A { + x: Dropable("b.c.x"), + y: Dropable("b.c.y"), + }, + d: A { + x: Dropable("b.d.x"), + y: Dropable("b.d.y"), + }, + }; + + let d = move || println!("{:?} {:?} {:?} {:?}", b.d.y, b.d.x, b.c.y, b.c.x); + + d(); + + let r = R { + c: &A { + x: Dropable("r.c.x"), + y: Dropable("r.c.y"), + }, + d: &A { + x: Dropable("r.d.x"), + y: Dropable("r.d.y"), + }, + }; + + let e = move || println!("{:?} {:?} {:?} {:?}", r.d.y, r.d.x, r.c.y, r.c.x); + + e(); + } + } + + // rust/$DIR/closures/issue-72408-nested-closures-exponential.rs + + fn _issue_72408_nested_closures_exponential() { + + /* + // commented out because it takes forever to run with this + + // Closures include captured types twice in a type tree. + // + // Wrapping one closure with another leads to doubling + // the amount of types in the type tree. + // + // This test ensures that rust can handle + // deeply nested type trees with a lot + // of duplicated subtrees. + + fn dup(f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 { + move |a| f(a * 2) + } + + fn main() { + let f = |a| a; + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + // Compiler dies around here if it tries + // to walk the tree exhaustively. + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + println!("Type size was at least {}", f(1)); + } + + */ + } + + // rust/$DIR/closures/issue-97607.rs + + fn _issue_97607() { + #[allow(unused)] + + fn test(f: F) -> Box U + 'static> + where + F: 'static + Fn(T) -> U, + for<'a> U: 'a, // < This is the problematic line, see #97607 + { + Box::new(move |t| f(t)) + } + + fn main() {} + } + + // rust/$DIR/closures/once-move-out-on-heap.rs + + fn _once_move_out_on_heap() { + // Testing guarantees provided by once functions. + + use std::sync::Arc; + + fn foo(blk: F) { + blk(); + } + + pub fn main() { + let x = Arc::new(true); + foo(|| { + assert!(*x); + drop(x); + }); + } + } + + // rust/$DIR/closures/supertrait-hint-references-assoc-ty.rs + + fn _supertrait_hint_references_assoc_ty() { + pub trait Fn0: Fn(i32) -> Self::Out { + type Out; + } + + impl ()> Fn0 for F { + type Out = (); + } + + pub fn closure_typer(_: impl Fn0) {} + + fn main() { + closure_typer(|x| { + let _: i64 = x.into(); + }); + } + } + + // rust/$DIR/unboxed-closures/issue-18652.rs + + fn _issue_18652() { + // Tests multiple free variables being passed by value into an unboxed + // once closure as an optimization by codegen. This used to hit an + // incorrect assert. + + fn main() { + let x = 2u8; + let y = 3u8; + assert_eq!((move || x + y)(), 5); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-all-traits.rs + + fn _unboxed_closures_all_traits() { + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn main() { + let z: isize = 7; + assert_eq!(a(move |x: isize, y| x + y + z), 10); + assert_eq!(b(move |x: isize, y| x + y + z), 14); + assert_eq!(c(move |x: isize, y| x + y + z), 18); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-boxed.rs + + fn _unboxed_closures_boxed() { + use std::ops::FnMut; + + fn make_adder(x: i32) -> Box i32 + 'static> { + Box::new(move |y: i32| -> i32 { x + y }) as Box i32 + 'static> + } + + pub fn main() { + let mut adder = make_adder(3); + let z = adder(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs + + fn _unboxed_closures_call_sugar_object_autoderef() { + // Test that the call operator autoderefs when calling to an object type. + + use std::ops::FnMut; + + fn make_adder(x: isize) -> Box isize + 'static> { + Box::new(move |y| x + y) + } + + pub fn main() { + let mut adder = make_adder(3); + let z = adder(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-call-sugar-object.rs + + fn _unboxed_closures_call_sugar_object() { + use std::ops::FnMut; + + fn make_adder(x: isize) -> Box isize + 'static> { + Box::new(move |y| x + y) + } + + pub fn main() { + let mut adder = make_adder(3); + let z = (*adder)(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-counter-not-moved.rs + + fn _unboxed_closures_counter_not_moved() { + // Test that we mutate a counter on the stack only when we expect to. + + fn call(f: F) + where + F: FnOnce(), + { + f(); + } + + fn main() { + let y = vec![format!("Hello"), format!("World")]; + let mut counter = 22_u32; + + call(|| { + // Move `y`, but do not move `counter`, even though it is read + // by value (note that it is also mutated). + for item in y { + let v = counter; + counter += v; + } + }); + assert_eq!(counter, 88); + + call(move || { + // this mutates a moved copy, and hence doesn't affect original + counter += 1; + }); + assert_eq!(counter, 88); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-drop.rs + + fn _unboxed_closures_drop() { + #![allow(path_statements)] + #![allow(dead_code)] + // A battery of tests to ensure destructors of unboxed closure environments + // run at the right times. + + static mut DROP_COUNT: usize = 0; + + fn drop_count() -> usize { + unsafe { DROP_COUNT } + } + + struct Droppable { + x: isize, + } + + impl Droppable { + fn new() -> Droppable { + Droppable { x: 1 } + } + } + + impl Drop for Droppable { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1 } + } + } + + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn test_fn() { + { + a(|a: isize, b| a + b); + } + assert_eq!(drop_count(), 0); + + { + let z = &Droppable::new(); + a(move |a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 0); + } + assert_eq!(drop_count(), 1); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + a(move |a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 1); + } + assert_eq!(drop_count(), 3); + } + + fn test_fn_mut() { + { + b(|a: isize, b| a + b); + } + assert_eq!(drop_count(), 3); + + { + let z = &Droppable::new(); + b(move |a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 3); + } + assert_eq!(drop_count(), 4); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + b(move |a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 4); + } + assert_eq!(drop_count(), 6); + } + + fn test_fn_once() { + { + c(|a: isize, b| a + b); + } + assert_eq!(drop_count(), 6); + + { + let z = Droppable::new(); + c(|a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 7); + } + assert_eq!(drop_count(), 7); + + { + let z = Droppable::new(); + let zz = Droppable::new(); + c(|a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 9); + } + assert_eq!(drop_count(), 9); + } + + fn main() { + test_fn(); + test_fn_mut(); + test_fn_once(); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-infer-fnmut-move.rs + + fn _unboxed_closures_infer_fnmut_move() { + // Test that we are able to infer a suitable kind for this `move` + // closure that is just called (`FnMut`). + + fn main() { + let mut counter = 0; + + let v = { + let mut tick = move || { + counter += 1; + counter + }; + tick(); + tick() + }; + + assert_eq!(counter, 0); + assert_eq!(v, 2); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-infer-fnonce-move.rs + + fn _unboxed_closures_infer_fnonce_move() { + // Test that we are able to infer a suitable kind for this `move` + // closure that is just called (`FnOnce`). + + use std::mem; + + struct DropMe<'a>(&'a mut i32); + + impl<'a> Drop for DropMe<'a> { + fn drop(&mut self) { + *self.0 += 1; + } + } + + fn main() { + let mut counter = 0; + + { + let drop_me = DropMe(&mut counter); + let tick = || mem::drop(drop_me); + tick(); + } + + assert_eq!(counter, 1); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-monomorphization.rs + + fn _unboxed_closures_monomorphization() { + // Test that unboxed closures in contexts with free type parameters + // monomorphize correctly (issue #16791) + + fn main() { + fn bar<'a, T: Clone + 'a>(t: T) -> Box T + 'a> { + Box::new(move || t.clone()) + } + + let mut f = bar(42_u32); + assert_eq!(f(), 42); + + let mut f = bar("forty-two"); + assert_eq!(f(), "forty-two"); + + let x = 42_u32; + let mut f = bar(&x); + assert_eq!(f(), &x); + + #[derive(Clone, Copy, Debug, PartialEq)] + struct Foo(usize, &'static str); + + let x = Foo(42, "forty-two"); + let mut f = bar(x); + assert_eq!(f(), x); + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-move-mutable.rs + + fn _unboxed_closures_move_mutable() { + // pretty-expanded FIXME #23616 + + #![deny(unused_mut)] + #![allow(unused_must_use)] + + // Test that mutating a mutable upvar in a capture-by-value unboxed + // closure does not ice (issue #18238) and marks the upvar as used + // mutably so we do not get a spurious warning about it not needing to + // be declared mutable (issue #18336 and #18769) + + fn set(x: &mut usize) { + *x = 42; + } + + fn main() { + { + let mut x = 0_usize; + move || x += 1; + } + { + let mut x = 0_usize; + move || x += 1; + } + { + let mut x = 0_usize; + move || set(&mut x); + } + { + let mut x = 0_usize; + move || set(&mut x); + } + } + } + + // rust/$DIR/unboxed-closures/unboxed-closures-single-word-env.rs + + fn _unboxed_closures_single_word_env() { + // Ensures that single-word environments work right in unboxed closures. + // These take a different path in codegen. + + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn main() { + let z = 10; + assert_eq!(a(move |x: isize, y| x + y + z), 13); + assert_eq!(b(move |x: isize, y| x + y + z), 17); + assert_eq!(c(move |x: isize, y| x + y + z), 21); + } + } + + // rust/$DIR/functions-closures/clone-closure.rs + + fn _clone_closure() { + // Check that closures implement `Clone`. + + #[derive(Clone)] + struct S(i32); + + fn main() { + let mut a = S(5); + let mut hello = move || { + a.0 += 1; + println!("Hello {}", a.0); + a.0 + }; + + let mut hello2 = hello.clone(); + assert_eq!(6, hello2()); + assert_eq!(6, hello()); + } + } + + // rust/$DIR/functions-closures/closure-bounds-can-capture-chan.rs + + fn _closure_bounds_can_capture_chan() { + // pretty-expanded FIXME #23616 + + use std::sync::mpsc::channel; + + fn foo(blk: F) { + blk(); + } + + pub fn main() { + let (tx, rx) = channel(); + foo(move || { + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + } + } + + // rust/$DIR/functions-closures/nullable-pointer-opt-closures.rs + + fn _nullable_pointer_opt_closures() { + use std::mem; + + pub fn main() { + // By Ref Capture + let a = 10i32; + let b = Some(|| println!("{}", a)); + // When we capture by reference we can use any of the + // captures as the discriminant since they're all + // behind a pointer. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value Capture + let a = Box::new(12i32); + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since it's a `Box` we can use it + // as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::>()); + + // By Value Capture - Transitive case + let a = "Hello".to_string(); // String -> Vec -> Unique -> NonZero + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since down the chain it contains + // a `NonZero` field, we can use it as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value - No Optimization + let a = 14i32; + let b = Some(move || println!("{}", a)); + // We captured `a` by value but we can't use it as the discriminant + // thus we end up with an extra field for the discriminant + assert_eq!(mem::size_of_val(&b), mem::size_of::<(i32, i32)>()); + } + } + + // rust/$DIR/moves/moves-based-on-type-capture-clause.rs + + fn _moves_based_on_type_capture_clause() { + #![allow(unused_must_use)] + // ignore-emscripten no threads support + + use std::thread; + + pub fn main() { + let x = "Hello world!".to_string(); + thread::spawn(move || { + println!("{}", x); + }) + .join(); + } + } + + // rust/$DIR/borrowck/borrow-raw-address-of-mutability-ok.rs + + fn _borrow_raw_address_of_mutability_ok() { + fn mutable_address_of() { + let mut x = 0; + let y = &raw mut x; + } + + fn mutable_address_of_closure() { + let mut x = 0; + let mut f = || { + let y = &raw mut x; + }; + f(); + } + + fn const_address_of_closure() { + let x = 0; + let f = || { + let y = &raw const x; + }; + f(); + } + + fn make_fn(f: F) -> F { + f + } + + fn const_address_of_fn_closure() { + let x = 0; + let f = make_fn(|| { + let y = &raw const x; + }); + f(); + } + + fn const_address_of_fn_closure_move() { + let x = 0; + let f = make_fn(move || { + let y = &raw const x; + }); + f(); + } + + fn main() {} + } + + // rust/$DIR/borrowck/kindck-implicit-close-over-mut-var.rs + + fn _kindck_implicit_close_over_mut_var() { + #![allow(unused_must_use)] + #![allow(dead_code)] + use std::thread; + + fn user(_i: isize) {} + + fn foo() { + // Here, i is *copied* into the proc (heap closure). + // Requires allocation. The proc's copy is not mutable. + let mut i = 0; + let t = thread::spawn(move || { + user(i); + println!("spawned {}", i) + }); + i += 1; + println!("original {}", i); + t.join(); + } + + fn bar() { + // Here, the original i has not been moved, only copied, so is still + // mutable outside of the proc. + let mut i = 0; + while i < 10 { + let t = thread::spawn(move || { + user(i); + }); + i += 1; + t.join(); + } + } + + fn car() { + // Here, i must be shadowed in the proc to be mutable. + let mut i = 0; + while i < 10 { + let t = thread::spawn(move || { + let mut i = i; + i += 1; + user(i); + }); + i += 1; + t.join(); + } + } + + pub fn main() {} + } + + // rust/$DIR/async-await/track-caller/panic-track-caller.rs + + fn _panic_track_caller() { + // needs-unwind + // gate-test-async_fn_track_caller + #![cfg_attr(afn, feature(async_fn_track_caller))] + #![cfg_attr(cls, feature(closure_track_caller))] + #![allow(unused)] + + use std::future::Future; + use std::panic; + use std::sync::{Arc, Mutex}; + use std::task::{Context, Poll, Wake}; + use std::thread::{self, Thread}; + + /// A waker that wakes up the current thread when called. + struct ThreadWaker(Thread); + + impl Wake for ThreadWaker { + fn wake(self: Arc) { + self.0.unpark(); + } + } + + /// Run a future to completion on the current thread. + fn block_on(fut: impl Future) -> T { + // Pin the future so it can be polled. + let mut fut = Box::pin(fut); + + // Create a new context to be passed to the future. + let t = thread::current(); + let waker = Arc::new(ThreadWaker(t)).into(); + let mut cx = Context::from_waker(&waker); + + // Run the future to completion. + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(res) => return res, + Poll::Pending => thread::park(), + } + } + } + + async fn bar() { + panic!() + } + + async fn foo() { + bar().await + } + + #[track_caller] + async fn bar_track_caller() { + panic!() + } + + async fn foo_track_caller() { + bar_track_caller().await + } + + struct Foo; + + impl Foo { + #[track_caller] + async fn bar_assoc() { + panic!(); + } + } + + async fn foo_assoc() { + Foo::bar_assoc().await + } + + // Since compilation is expected to fail for this fn when using + // `nofeat`, we test that separately in `async-closure-gate.rs` + #[cfg(cls)] + async fn foo_closure() { + let c = #[track_caller] + async || { + panic!(); + }; + c().await + } + + // Since compilation is expected to fail for this fn when using + // `nofeat`, we test that separately in `async-block.rs` + #[cfg(cls)] + async fn foo_block() { + let a = #[track_caller] + async { + panic!(); + }; + a.await + } + + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { + let loc = Arc::new(Mutex::new(None)); + + let hook = panic::take_hook(); + { + let loc = loc.clone(); + panic::set_hook(Box::new(move |info| { + *loc.lock().unwrap() = info.location().map(|loc| loc.line()) + })); + } + panic::catch_unwind(f).unwrap_err(); + panic::set_hook(hook); + let x = loc.lock().unwrap().unwrap(); + x + } + + fn main() { + assert_eq!(panicked_at(|| block_on(foo())), 46); + + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 61); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); + + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); + + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 84); + + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_block())), 96); + } + } + + // rust/$DIR/async-await/deep-futures-are-freeze.rs + + fn _deep_futures_are_freeze() { + // no-prefer-dynamic + + #![recursion_limit = "256"] + + fn main() { + spawn(|| main0()) + } + + fn spawn(future: impl FnOnce() -> F) { + future(); + } + + async fn main0() { + main1().await; + main2().await; + } + async fn main1() { + main2().await; + main3().await; + } + async fn main2() { + main3().await; + main4().await; + } + async fn main3() { + main4().await; + main5().await; + } + async fn main4() { + main5().await; + main6().await; + } + async fn main5() { + main6().await; + main7().await; + } + async fn main6() { + main7().await; + main8().await; + } + async fn main7() { + main8().await; + main9().await; + } + async fn main8() { + main9().await; + main10().await; + } + async fn main9() { + main10().await; + main11().await; + } + async fn main10() { + main11().await; + main12().await; + } + async fn main11() { + main12().await; + main13().await; + } + async fn main12() { + main13().await; + main14().await; + } + async fn main13() { + main14().await; + main15().await; + } + async fn main14() { + main15().await; + main16().await; + } + async fn main15() { + main16().await; + main17().await; + } + async fn main16() { + main17().await; + main18().await; + } + async fn main17() { + main18().await; + main19().await; + } + async fn main18() { + main19().await; + main20().await; + } + async fn main19() { + main20().await; + main21().await; + } + async fn main20() { + main21().await; + main22().await; + } + async fn main21() { + main22().await; + main23().await; + } + async fn main22() { + main23().await; + main24().await; + } + async fn main23() { + main24().await; + main25().await; + } + async fn main24() { + main25().await; + main26().await; + } + async fn main25() { + main26().await; + main27().await; + } + async fn main26() { + main27().await; + main28().await; + } + async fn main27() { + main28().await; + main29().await; + } + async fn main28() { + main29().await; + main30().await; + } + async fn main29() { + main30().await; + main31().await; + } + async fn main30() { + main31().await; + main32().await; + } + async fn main31() { + main32().await; + main33().await; + } + async fn main32() { + main33().await; + main34().await; + } + async fn main33() { + main34().await; + main35().await; + } + async fn main34() { + main35().await; + main36().await; + } + async fn main35() { + main36().await; + main37().await; + } + async fn main36() { + main37().await; + main38().await; + } + async fn main37() { + main38().await; + main39().await; + } + async fn main38() { + main39().await; + main40().await; + } + async fn main39() { + main40().await; + } + async fn main40() { + boom(&mut ()).await; + } + + async fn boom(f: &mut ()) {} + } + + // rust/$DIR/async-await/generics-and-bounds.rs + + fn _generics_and_bounds() { + use std::future::Future; + + pub async fn simple_generic() {} + + pub trait Foo { + fn foo(&self) {} + } + + struct FooType; + impl Foo for FooType {} + + pub async fn call_generic_bound(f: F) { + f.foo() + } + + pub async fn call_where_clause(f: F) + where + F: Foo, + { + f.foo() + } + + pub async fn call_impl_trait(f: impl Foo) { + f.foo() + } + + pub async fn call_with_ref(f: &impl Foo) { + f.foo() + } + + pub fn async_fn_with_same_generic_params_unifies() { + let mut a = call_generic_bound(FooType); + a = call_generic_bound(FooType); + + let mut b = call_where_clause(FooType); + b = call_where_clause(FooType); + + let mut c = call_impl_trait(FooType); + c = call_impl_trait(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref(&f_one); + d = call_with_ref(&f_two); + } + + pub fn simple_generic_block() -> impl Future { + async {} + } + + pub fn call_generic_bound_block(f: F) -> impl Future { + async move { f.foo() } + } + + pub fn call_where_clause_block(f: F) -> impl Future + where + F: Foo, + { + async move { f.foo() } + } + + pub fn call_impl_trait_block(f: impl Foo) -> impl Future { + async move { f.foo() } + } + + pub fn call_with_ref_block<'a>(f: &'a (impl Foo + 'a)) -> impl Future + 'a { + async move { f.foo() } + } + + pub fn async_block_with_same_generic_params_unifies() { + let mut a = call_generic_bound_block(FooType); + a = call_generic_bound_block(FooType); + + let mut b = call_where_clause_block(FooType); + b = call_where_clause_block(FooType); + + let mut c = call_impl_trait_block(FooType); + c = call_impl_trait_block(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref_block(&f_one); + d = call_with_ref_block(&f_two); + } + } + + // rust/$DIR/async-await/issue-105501.rs + + fn _issue_105501() { + // This is a regression test for https://github.com/rust-lang/rust/issues/105501. + // It was minified from the published `msf-ice:0.2.1` crate which failed in a crater run. + // A faulty compiler was triggering a `higher-ranked lifetime error`: + // + // > could not prove `[async block@...]: Send` + + use mini_futures::Stream; + + fn is_send(_: impl Send) {} + + pub fn main() { + let fut = async { + let mut stream = mini_futures::iter([()]) + .then(|_| async {}) + .map(|_| async { None }) + .buffered() + .filter_map(std::future::ready); + + stream.next().await + }; + + is_send(async { + let _: Option<()> = fut.await; + }); + } + + // this is a simplified subset of `futures::StreamExt` and related types + mod mini_futures { + use std::future::Future; + use std::pin::Pin; + use std::task::{Context, Poll}; + + pub fn iter(_: I) -> Iter + where + I: IntoIterator, + { + todo!() + } + + pub trait Stream { + type Item; + + fn then(self, _: F) -> Then + where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized, + { + todo!() + } + + fn map(self, _: F) -> Map + where + F: FnMut(Self::Item) -> T, + Self: Sized, + { + todo!() + } + + fn buffered(self) -> Buffered + where + Self::Item: Future, + Self: Sized, + { + todo!() + } + + fn filter_map(self, _: F) -> FilterMap + where + F: FnMut(Self::Item) -> Fut, + Fut: Future>, + Self: Sized, + { + todo!() + } + + fn next(&mut self) -> Next<'_, Self> { + todo!() + } + } + + pub struct Iter { + __: I, + } + impl Stream for Iter + where + I: Iterator, + { + type Item = I::Item; + } + + pub struct Then { + __: (St, Fut, F), + } + impl Stream for Then + where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, + { + type Item = Fut::Output; + } + + pub struct Map { + __: (St, F), + } + impl Stream for Map + where + St: Stream, + F: FnMut1, + { + type Item = F::Output; + } + + pub trait FnMut1 { + type Output; + } + impl FnMut1 for T + where + T: FnMut(A) -> R, + { + type Output = R; + } + + pub struct Buffered + where + St: Stream, + St::Item: Future, + { + __: (St, St::Item), + } + impl Stream for Buffered + where + St: Stream, + St::Item: Future, + { + type Item = ::Output; + } + + pub struct FilterMap { + __: (St, Fut, F), + } + impl Stream for FilterMap + where + St: Stream, + F: FnMut1, + Fut: Future>, + { + type Item = T; + } + + pub struct Next<'a, St: ?Sized> { + __: &'a mut St, + } + impl Future for Next<'_, St> { + type Output = Option; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + todo!() + } + } + } + } +} diff --git a/tests/ui/needless_move.rs b/tests/ui/needless_move.rs new file mode 100644 index 000000000000..e8a0ea457f9a --- /dev/null +++ b/tests/ui/needless_move.rs @@ -0,0 +1,1896 @@ +//! To properly check that the `needless_move` lint is complete, go to the +//! `.fixed` file of this test and check that the code fails to compile if +//! any of the `move`s are removed. + +#![warn(clippy::needless_move)] +#![allow(unused)] +#![allow(ungated_async_fn_track_caller)] +#![allow(clippy::useless_format)] +#![allow(clippy::let_and_return)] +#![allow(clippy::no_effect)] +#![allow(clippy::box_collection)] +#![allow(clippy::boxed_local)] +#![allow(clippy::disallowed_names)] +#![allow(clippy::manual_async_fn)] +#![allow(clippy::redundant_closure)] +#![allow(clippy::redundant_closure_call)] +#![allow(clippy::clone_on_copy)] +#![allow(clippy::extra_unused_type_parameters)] +#![allow(clippy::unused_unit)] +#![feature(async_closure)] +#![feature(raw_ref_op)] + +#[derive(Copy, Clone)] +struct Copy; + +struct NonCopy; + +struct Composite { + copy: Copy, + non_copy: NonCopy, +} + +impl Composite { + fn new() -> Self { + Self { + copy: Copy, + non_copy: NonCopy, + } + } +} + +fn with_owned(_: T) {} +fn with_ref(_: &T) {} +fn with_ref_mut(_: &mut T) {} +fn assert_static(v: T) -> T { + v +} + +fn main() { + // doesn't trigger on non-move closures or async blocks + let a = NonCopy; + let b = Copy; + let closure = || { + with_owned(a); + with_owned(b); + }; + + let a = NonCopy; + let b = Copy; + let fut = async { + with_owned(a); + with_owned(b); + }; + + // doesn't trigger on async fns + + // (an async fn is a fn whose body turns into an `async move {}` block, where the `move` kw has + // DUMMY_SP as the Span). It shouldn't trigger the lint. + async fn f() {} + + // triggers on move closures and async blocks which do not capture anything + let closure = assert_static(move || {}); + let fut = assert_static(async move {}); + + // owned + NonCopy + let a = NonCopy; + let closure = assert_static(move || { + with_owned(a); + }); + + // owned + Copy + let a = Copy; + let closure = assert_static(move || { + with_owned(a); + }); + + // ref + NonCopy + let a = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref + Copy + let a = Copy; + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref mut + NonCopy + let mut a = NonCopy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // ref mut + Copy + let mut a = Copy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // with async + + // doesn't trigger if not capturing with `move` + let a = NonCopy; + let b = Copy; + let fut = async { + with_owned(a); + with_owned(b); + }; + + // owned + non-copy + let a = NonCopy; + let fut = assert_static(async move { + with_owned(a); + }); + + // owned + copy + let a = Copy; + let fut = assert_static(async move { + with_owned(a); + }); + + // ref + non-copy + let a = NonCopy; + let fut = assert_static(async move { + with_ref(&a); + }); + + // ref + copy + let a = Copy; + let fut = assert_static(async move { + with_ref(&a); + }); + + // ref mut + non-copy + let mut a = NonCopy; + let fut = assert_static(async move { + with_ref_mut(&mut a); + }); + + // ref mut + copy + let mut a = Copy; + let fut = assert_static(async move { + with_ref_mut(&mut a); + }); + + // triggers on ref + owned combinations + // ref + owned + non copy + let a = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(a); + }); + + // ref + owned + copy + let a = Copy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(a); + }); + + // ref mut + owned + non copy + let mut a = NonCopy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + with_owned(a); + }); + + // ref mut + owned + copy + let mut a = Copy; + let closure = assert_static(move || { + with_ref_mut(&mut a); + with_owned(a); + }); + + // ref + owned + non copy + other owned capture in between + let a = NonCopy; + let b = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(b); + with_owned(a); + }); + + // ref + owned + copy + other owned capture in between + let a = Copy; + let b = NonCopy; + let closure = assert_static(move || { + with_ref(&a); + with_owned(b); + with_owned(a); + }); + + // with composite structures + disjoint captures + + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a); + }); + + // capturing only the copy part + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a.copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.copy); + }); + + // capturing only the non-copy part + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a.non_copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.non_copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.non_copy); + }); + + // capturing both parts + // owned + let a = Composite::new(); + let closure = assert_static(move || { + with_owned(a.copy); + with_owned(a.non_copy); + }); + + // ref + let a = Composite::new(); + let closure = assert_static(move || { + with_ref(&a.copy); + with_ref(&a.non_copy); + }); + + // ref mut + let mut a = Composite::new(); + let closure = assert_static(move || { + with_ref_mut(&mut a.copy); + with_ref_mut(&mut a.non_copy); + }); + + // correctly handles direct mutations of variables + // copy + let mut a = Copy; + let closure = assert_static(move || { + a = Copy; + }); + + // non-copy + let mut a = NonCopy; + let closure = assert_static(move || { + a = NonCopy; + }); + + // composite -> copy + let mut a = Composite::new(); + let closure = assert_static(move || { + a.copy = Copy; + }); + + // composite -> non-copy + let mut a = Composite::new(); + let closure = assert_static(move || { + a.non_copy = NonCopy; + }); + + // copy + owned consume + let mut a = Copy; + let closure = assert_static(move || { + a = Copy; + with_owned(a); + }); + + // non-copy + owned consume + let mut a = NonCopy; + let closure = assert_static(move || { + a = NonCopy; + with_owned(a); + }); + + // composite -> copy + owned consume + let mut a = Composite::new(); + let closure = assert_static(move || { + a.copy = Copy; + with_owned(a); + }); + + // composite -> non-copy + owned consume + let mut a = Composite::new(); + let closure = assert_static(move || { + a.non_copy = NonCopy; + with_owned(a); + }); + + // async blocks + // copy + let mut a = Copy; + let fut = assert_static(async move { + a = Copy; + }); + + // non-copy + let mut a = NonCopy; + let fut = assert_static(async move { + a = NonCopy; + }); + + // composite -> copy + let mut a = Composite::new(); + let fut = assert_static(async move { + a.copy = Copy; + }); + + // composite -> non-copy + let mut a = Composite::new(); + let fut = assert_static(async move { + a.non_copy = NonCopy; + }); + + // copy + owned consume + let mut a = Copy; + let fut = assert_static(async move { + a = Copy; + with_owned(a); + }); + + // non-copy + owned consume + let mut a = NonCopy; + let fut = assert_static(async move { + a = NonCopy; + with_owned(a); + }); + + // composite -> copy + owned consume + let mut a = Composite::new(); + let fut = assert_static(async move { + a.copy = Copy; + with_owned(a); + }); + + // composite -> non-copy + owned consume + let mut a = Composite::new(); + let fut = assert_static(async move { + a.non_copy = NonCopy; + with_owned(a); + }); + + let v = (String::new(), String::new()); + assert_static(move || { + let _w = v.0; + let _h = &v.1; + }); + + // below are a few tests from rustc's testsuite that use move closures, + // which might uncover edge cases + + // rust/tests/ui/closures/2229_closure_analysis/migrations/no_migrations.rs + + fn _no_migrations() { + // Set of test cases that don't need migrations + + #![deny(rust_2021_incompatible_closure_captures)] + + // Copy types as copied by the closure instead of being moved into the closure + // Therefore their drop order isn't tied to the closure and won't be requiring any + // migrations. + fn test1_only_copy_types() { + let t = (0i32, 0i32); + + let c = || { + let _t = t.0; + }; + + c(); + } + + // Same as test1 but using a move closure + fn test2_only_copy_types_move_closure() { + let t = (0i32, 0i32); + + let c = move || { + println!("{}", t.0); + }; + + c(); + } + + // Don't need to migrate if captured by ref + fn test3_only_copy_types_move_closure() { + let t = (String::new(), String::new()); + + let c = || { + println!("{}", t.0); + }; + + c(); + } + + // Test migration analysis in case of Insignificant Drop + Non Drop aggregates. + // Note in this test the closure captures a non Drop type and therefore the variable + // is only captured by ref. + fn test4_insignificant_drop_non_drop_aggregate() { + let t = (String::new(), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); + } + + struct Foo(i32); + impl Drop for Foo { + fn drop(&mut self) { + println!("{:?} dropped", self.0); + } + } + + // Test migration analysis in case of Significant Drop + Non Drop aggregates. + // Note in this test the closure captures a non Drop type and therefore the variable + // is only captured by ref. + fn test5_significant_drop_non_drop_aggregate() { + let t = (Foo(0), 0i32); + + let c = || { + let _t = t.1; + }; + + c(); + } + + fn main() { + test1_only_copy_types(); + test2_only_copy_types_move_closure(); + test3_only_copy_types_move_closure(); + test4_insignificant_drop_non_drop_aggregate(); + test5_significant_drop_non_drop_aggregate(); + } + } + + // rust/tests/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs + + fn _issue_88476() { + use std::rc::Rc; + + // Test that we restrict precision when moving not-`Copy` types, if any of the parent paths + // implement `Drop`. This is to ensure that we don't move out of a type that implements Drop. + pub fn test1() { + struct Foo(Rc); + + impl Drop for Foo { + fn drop(self: &mut Foo) {} + } + + let f = Foo(Rc::new(1)); + let x = move || { + println!("{:?}", f.0); + }; + + x(); + } + + // Test that we don't restrict precision when moving `Copy` types(i.e. when copying), + // even if any of the parent paths implement `Drop`. + pub fn test2() { + struct Character { + hp: u32, + name: String, + } + + impl Drop for Character { + fn drop(&mut self) {} + } + + let character = Character { + hp: 100, + name: format!("A"), + }; + + let c = move || println!("{}", character.hp); + + c(); + + println!("{}", character.name); + } + + fn main() {} + } + + // rust/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs + + fn _preserve_field_drop_order2() { + #[derive(Debug)] + struct Dropable(&'static str); + + impl Drop for Dropable { + fn drop(&mut self) { + println!("Dropping {}", self.0) + } + } + + #[derive(Debug)] + struct A { + x: Dropable, + y: Dropable, + } + + #[derive(Debug)] + struct B { + c: A, + d: A, + } + + #[derive(Debug)] + struct R<'a> { + c: &'a A, + d: &'a A, + } + + fn main() { + let a = A { + x: Dropable("x"), + y: Dropable("y"), + }; + + let c = move || println!("{:?} {:?}", a.y, a.x); + + c(); + + let b = B { + c: A { + x: Dropable("b.c.x"), + y: Dropable("b.c.y"), + }, + d: A { + x: Dropable("b.d.x"), + y: Dropable("b.d.y"), + }, + }; + + let d = move || println!("{:?} {:?} {:?} {:?}", b.d.y, b.d.x, b.c.y, b.c.x); + + d(); + + let r = R { + c: &A { + x: Dropable("r.c.x"), + y: Dropable("r.c.y"), + }, + d: &A { + x: Dropable("r.d.x"), + y: Dropable("r.d.y"), + }, + }; + + let e = move || println!("{:?} {:?} {:?} {:?}", r.d.y, r.d.x, r.c.y, r.c.x); + + e(); + } + } + + // rust/tests/ui/closures/issue-72408-nested-closures-exponential.rs + + fn _issue_72408_nested_closures_exponential() { + + /* + // commented out because it takes forever to run with this + + // Closures include captured types twice in a type tree. + // + // Wrapping one closure with another leads to doubling + // the amount of types in the type tree. + // + // This test ensures that rust can handle + // deeply nested type trees with a lot + // of duplicated subtrees. + + fn dup(f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 { + move |a| f(a * 2) + } + + fn main() { + let f = |a| a; + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + // Compiler dies around here if it tries + // to walk the tree exhaustively. + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + let f = dup(f); + + println!("Type size was at least {}", f(1)); + } + + */ + } + + // rust/tests/ui/closures/issue-97607.rs + + fn _issue_97607() { + #[allow(unused)] + + fn test(f: F) -> Box U + 'static> + where + F: 'static + Fn(T) -> U, + for<'a> U: 'a, // < This is the problematic line, see #97607 + { + Box::new(move |t| f(t)) + } + + fn main() {} + } + + // rust/tests/ui/closures/once-move-out-on-heap.rs + + fn _once_move_out_on_heap() { + // Testing guarantees provided by once functions. + + use std::sync::Arc; + + fn foo(blk: F) { + blk(); + } + + pub fn main() { + let x = Arc::new(true); + foo(move || { + assert!(*x); + drop(x); + }); + } + } + + // rust/tests/ui/closures/supertrait-hint-references-assoc-ty.rs + + fn _supertrait_hint_references_assoc_ty() { + pub trait Fn0: Fn(i32) -> Self::Out { + type Out; + } + + impl ()> Fn0 for F { + type Out = (); + } + + pub fn closure_typer(_: impl Fn0) {} + + fn main() { + closure_typer(move |x| { + let _: i64 = x.into(); + }); + } + } + + // rust/tests/ui/unboxed-closures/issue-18652.rs + + fn _issue_18652() { + // Tests multiple free variables being passed by value into an unboxed + // once closure as an optimization by codegen. This used to hit an + // incorrect assert. + + fn main() { + let x = 2u8; + let y = 3u8; + assert_eq!((move || x + y)(), 5); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-all-traits.rs + + fn _unboxed_closures_all_traits() { + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn main() { + let z: isize = 7; + assert_eq!(a(move |x: isize, y| x + y + z), 10); + assert_eq!(b(move |x: isize, y| x + y + z), 14); + assert_eq!(c(move |x: isize, y| x + y + z), 18); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-boxed.rs + + fn _unboxed_closures_boxed() { + use std::ops::FnMut; + + fn make_adder(x: i32) -> Box i32 + 'static> { + Box::new(move |y: i32| -> i32 { x + y }) as Box i32 + 'static> + } + + pub fn main() { + let mut adder = make_adder(3); + let z = adder(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-call-sugar-object-autoderef.rs + + fn _unboxed_closures_call_sugar_object_autoderef() { + // Test that the call operator autoderefs when calling to an object type. + + use std::ops::FnMut; + + fn make_adder(x: isize) -> Box isize + 'static> { + Box::new(move |y| x + y) + } + + pub fn main() { + let mut adder = make_adder(3); + let z = adder(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-call-sugar-object.rs + + fn _unboxed_closures_call_sugar_object() { + use std::ops::FnMut; + + fn make_adder(x: isize) -> Box isize + 'static> { + Box::new(move |y| x + y) + } + + pub fn main() { + let mut adder = make_adder(3); + let z = (*adder)(2); + println!("{}", z); + assert_eq!(z, 5); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-counter-not-moved.rs + + fn _unboxed_closures_counter_not_moved() { + // Test that we mutate a counter on the stack only when we expect to. + + fn call(f: F) + where + F: FnOnce(), + { + f(); + } + + fn main() { + let y = vec![format!("Hello"), format!("World")]; + let mut counter = 22_u32; + + call(|| { + // Move `y`, but do not move `counter`, even though it is read + // by value (note that it is also mutated). + for item in y { + let v = counter; + counter += v; + } + }); + assert_eq!(counter, 88); + + call(move || { + // this mutates a moved copy, and hence doesn't affect original + counter += 1; + }); + assert_eq!(counter, 88); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-drop.rs + + fn _unboxed_closures_drop() { + #![allow(path_statements)] + #![allow(dead_code)] + // A battery of tests to ensure destructors of unboxed closure environments + // run at the right times. + + static mut DROP_COUNT: usize = 0; + + fn drop_count() -> usize { + unsafe { DROP_COUNT } + } + + struct Droppable { + x: isize, + } + + impl Droppable { + fn new() -> Droppable { + Droppable { x: 1 } + } + } + + impl Drop for Droppable { + fn drop(&mut self) { + unsafe { DROP_COUNT += 1 } + } + } + + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn test_fn() { + { + a(move |a: isize, b| a + b); + } + assert_eq!(drop_count(), 0); + + { + let z = &Droppable::new(); + a(move |a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 0); + } + assert_eq!(drop_count(), 1); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + a(move |a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 1); + } + assert_eq!(drop_count(), 3); + } + + fn test_fn_mut() { + { + b(move |a: isize, b| a + b); + } + assert_eq!(drop_count(), 3); + + { + let z = &Droppable::new(); + b(move |a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 3); + } + assert_eq!(drop_count(), 4); + + { + let z = &Droppable::new(); + let zz = &Droppable::new(); + b(move |a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 4); + } + assert_eq!(drop_count(), 6); + } + + fn test_fn_once() { + { + c(move |a: isize, b| a + b); + } + assert_eq!(drop_count(), 6); + + { + let z = Droppable::new(); + c(move |a: isize, b| { + z; + a + b + }); + assert_eq!(drop_count(), 7); + } + assert_eq!(drop_count(), 7); + + { + let z = Droppable::new(); + let zz = Droppable::new(); + c(move |a: isize, b| { + z; + zz; + a + b + }); + assert_eq!(drop_count(), 9); + } + assert_eq!(drop_count(), 9); + } + + fn main() { + test_fn(); + test_fn_mut(); + test_fn_once(); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-infer-fnmut-move.rs + + fn _unboxed_closures_infer_fnmut_move() { + // Test that we are able to infer a suitable kind for this `move` + // closure that is just called (`FnMut`). + + fn main() { + let mut counter = 0; + + let v = { + let mut tick = move || { + counter += 1; + counter + }; + tick(); + tick() + }; + + assert_eq!(counter, 0); + assert_eq!(v, 2); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-infer-fnonce-move.rs + + fn _unboxed_closures_infer_fnonce_move() { + // Test that we are able to infer a suitable kind for this `move` + // closure that is just called (`FnOnce`). + + use std::mem; + + struct DropMe<'a>(&'a mut i32); + + impl<'a> Drop for DropMe<'a> { + fn drop(&mut self) { + *self.0 += 1; + } + } + + fn main() { + let mut counter = 0; + + { + let drop_me = DropMe(&mut counter); + let tick = move || mem::drop(drop_me); + tick(); + } + + assert_eq!(counter, 1); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-monomorphization.rs + + fn _unboxed_closures_monomorphization() { + // Test that unboxed closures in contexts with free type parameters + // monomorphize correctly (issue #16791) + + fn main() { + fn bar<'a, T: Clone + 'a>(t: T) -> Box T + 'a> { + Box::new(move || t.clone()) + } + + let mut f = bar(42_u32); + assert_eq!(f(), 42); + + let mut f = bar("forty-two"); + assert_eq!(f(), "forty-two"); + + let x = 42_u32; + let mut f = bar(&x); + assert_eq!(f(), &x); + + #[derive(Clone, Copy, Debug, PartialEq)] + struct Foo(usize, &'static str); + + let x = Foo(42, "forty-two"); + let mut f = bar(x); + assert_eq!(f(), x); + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-move-mutable.rs + + fn _unboxed_closures_move_mutable() { + // pretty-expanded FIXME #23616 + + #![deny(unused_mut)] + #![allow(unused_must_use)] + + // Test that mutating a mutable upvar in a capture-by-value unboxed + // closure does not ice (issue #18238) and marks the upvar as used + // mutably so we do not get a spurious warning about it not needing to + // be declared mutable (issue #18336 and #18769) + + fn set(x: &mut usize) { + *x = 42; + } + + fn main() { + { + let mut x = 0_usize; + move || x += 1; + } + { + let mut x = 0_usize; + move || x += 1; + } + { + let mut x = 0_usize; + move || set(&mut x); + } + { + let mut x = 0_usize; + move || set(&mut x); + } + } + } + + // rust/tests/ui/unboxed-closures/unboxed-closures-single-word-env.rs + + fn _unboxed_closures_single_word_env() { + // Ensures that single-word environments work right in unboxed closures. + // These take a different path in codegen. + + fn a isize>(f: F) -> isize { + f(1, 2) + } + + fn b isize>(mut f: F) -> isize { + f(3, 4) + } + + fn c isize>(f: F) -> isize { + f(5, 6) + } + + fn main() { + let z = 10; + assert_eq!(a(move |x: isize, y| x + y + z), 13); + assert_eq!(b(move |x: isize, y| x + y + z), 17); + assert_eq!(c(move |x: isize, y| x + y + z), 21); + } + } + + // rust/tests/ui/functions-closures/clone-closure.rs + + fn _clone_closure() { + // Check that closures implement `Clone`. + + #[derive(Clone)] + struct S(i32); + + fn main() { + let mut a = S(5); + let mut hello = move || { + a.0 += 1; + println!("Hello {}", a.0); + a.0 + }; + + let mut hello2 = hello.clone(); + assert_eq!(6, hello2()); + assert_eq!(6, hello()); + } + } + + // rust/tests/ui/functions-closures/closure-bounds-can-capture-chan.rs + + fn _closure_bounds_can_capture_chan() { + // pretty-expanded FIXME #23616 + + use std::sync::mpsc::channel; + + fn foo(blk: F) { + blk(); + } + + pub fn main() { + let (tx, rx) = channel(); + foo(move || { + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); + } + } + + // rust/tests/ui/functions-closures/nullable-pointer-opt-closures.rs + + fn _nullable_pointer_opt_closures() { + use std::mem; + + pub fn main() { + // By Ref Capture + let a = 10i32; + let b = Some(|| println!("{}", a)); + // When we capture by reference we can use any of the + // captures as the discriminant since they're all + // behind a pointer. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value Capture + let a = Box::new(12i32); + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since it's a `Box` we can use it + // as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::>()); + + // By Value Capture - Transitive case + let a = "Hello".to_string(); // String -> Vec -> Unique -> NonZero + let b = Some(move || println!("{}", a)); + // We captured `a` by value and since down the chain it contains + // a `NonZero` field, we can use it as the discriminant. + assert_eq!(mem::size_of_val(&b), mem::size_of::()); + + // By Value - No Optimization + let a = 14i32; + let b = Some(move || println!("{}", a)); + // We captured `a` by value but we can't use it as the discriminant + // thus we end up with an extra field for the discriminant + assert_eq!(mem::size_of_val(&b), mem::size_of::<(i32, i32)>()); + } + } + + // rust/tests/ui/moves/moves-based-on-type-capture-clause.rs + + fn _moves_based_on_type_capture_clause() { + #![allow(unused_must_use)] + // ignore-emscripten no threads support + + use std::thread; + + pub fn main() { + let x = "Hello world!".to_string(); + thread::spawn(move || { + println!("{}", x); + }) + .join(); + } + } + + // rust/tests/ui/borrowck/borrow-raw-address-of-mutability-ok.rs + + fn _borrow_raw_address_of_mutability_ok() { + fn mutable_address_of() { + let mut x = 0; + let y = &raw mut x; + } + + fn mutable_address_of_closure() { + let mut x = 0; + let mut f = || { + let y = &raw mut x; + }; + f(); + } + + fn const_address_of_closure() { + let x = 0; + let f = || { + let y = &raw const x; + }; + f(); + } + + fn make_fn(f: F) -> F { + f + } + + fn const_address_of_fn_closure() { + let x = 0; + let f = make_fn(|| { + let y = &raw const x; + }); + f(); + } + + fn const_address_of_fn_closure_move() { + let x = 0; + let f = make_fn(move || { + let y = &raw const x; + }); + f(); + } + + fn main() {} + } + + // rust/tests/ui/borrowck/kindck-implicit-close-over-mut-var.rs + + fn _kindck_implicit_close_over_mut_var() { + #![allow(unused_must_use)] + #![allow(dead_code)] + use std::thread; + + fn user(_i: isize) {} + + fn foo() { + // Here, i is *copied* into the proc (heap closure). + // Requires allocation. The proc's copy is not mutable. + let mut i = 0; + let t = thread::spawn(move || { + user(i); + println!("spawned {}", i) + }); + i += 1; + println!("original {}", i); + t.join(); + } + + fn bar() { + // Here, the original i has not been moved, only copied, so is still + // mutable outside of the proc. + let mut i = 0; + while i < 10 { + let t = thread::spawn(move || { + user(i); + }); + i += 1; + t.join(); + } + } + + fn car() { + // Here, i must be shadowed in the proc to be mutable. + let mut i = 0; + while i < 10 { + let t = thread::spawn(move || { + let mut i = i; + i += 1; + user(i); + }); + i += 1; + t.join(); + } + } + + pub fn main() {} + } + + // rust/tests/ui/async-await/track-caller/panic-track-caller.rs + + fn _panic_track_caller() { + // needs-unwind + // gate-test-async_fn_track_caller + #![cfg_attr(afn, feature(async_fn_track_caller))] + #![cfg_attr(cls, feature(closure_track_caller))] + #![allow(unused)] + + use std::future::Future; + use std::panic; + use std::sync::{Arc, Mutex}; + use std::task::{Context, Poll, Wake}; + use std::thread::{self, Thread}; + + /// A waker that wakes up the current thread when called. + struct ThreadWaker(Thread); + + impl Wake for ThreadWaker { + fn wake(self: Arc) { + self.0.unpark(); + } + } + + /// Run a future to completion on the current thread. + fn block_on(fut: impl Future) -> T { + // Pin the future so it can be polled. + let mut fut = Box::pin(fut); + + // Create a new context to be passed to the future. + let t = thread::current(); + let waker = Arc::new(ThreadWaker(t)).into(); + let mut cx = Context::from_waker(&waker); + + // Run the future to completion. + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(res) => return res, + Poll::Pending => thread::park(), + } + } + } + + async fn bar() { + panic!() + } + + async fn foo() { + bar().await + } + + #[track_caller] + async fn bar_track_caller() { + panic!() + } + + async fn foo_track_caller() { + bar_track_caller().await + } + + struct Foo; + + impl Foo { + #[track_caller] + async fn bar_assoc() { + panic!(); + } + } + + async fn foo_assoc() { + Foo::bar_assoc().await + } + + // Since compilation is expected to fail for this fn when using + // `nofeat`, we test that separately in `async-closure-gate.rs` + #[cfg(cls)] + async fn foo_closure() { + let c = #[track_caller] + async || { + panic!(); + }; + c().await + } + + // Since compilation is expected to fail for this fn when using + // `nofeat`, we test that separately in `async-block.rs` + #[cfg(cls)] + async fn foo_block() { + let a = #[track_caller] + async { + panic!(); + }; + a.await + } + + fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { + let loc = Arc::new(Mutex::new(None)); + + let hook = panic::take_hook(); + { + let loc = loc.clone(); + panic::set_hook(Box::new(move |info| { + *loc.lock().unwrap() = info.location().map(|loc| loc.line()) + })); + } + panic::catch_unwind(f).unwrap_err(); + panic::set_hook(hook); + let x = loc.lock().unwrap().unwrap(); + x + } + + fn main() { + assert_eq!(panicked_at(|| block_on(foo())), 46); + + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 61); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); + + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); + + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 84); + + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_block())), 96); + } + } + + // rust/tests/ui/async-await/deep-futures-are-freeze.rs + + fn _deep_futures_are_freeze() { + // no-prefer-dynamic + + #![recursion_limit = "256"] + + fn main() { + spawn(move || main0()) + } + + fn spawn(future: impl FnOnce() -> F) { + future(); + } + + async fn main0() { + main1().await; + main2().await; + } + async fn main1() { + main2().await; + main3().await; + } + async fn main2() { + main3().await; + main4().await; + } + async fn main3() { + main4().await; + main5().await; + } + async fn main4() { + main5().await; + main6().await; + } + async fn main5() { + main6().await; + main7().await; + } + async fn main6() { + main7().await; + main8().await; + } + async fn main7() { + main8().await; + main9().await; + } + async fn main8() { + main9().await; + main10().await; + } + async fn main9() { + main10().await; + main11().await; + } + async fn main10() { + main11().await; + main12().await; + } + async fn main11() { + main12().await; + main13().await; + } + async fn main12() { + main13().await; + main14().await; + } + async fn main13() { + main14().await; + main15().await; + } + async fn main14() { + main15().await; + main16().await; + } + async fn main15() { + main16().await; + main17().await; + } + async fn main16() { + main17().await; + main18().await; + } + async fn main17() { + main18().await; + main19().await; + } + async fn main18() { + main19().await; + main20().await; + } + async fn main19() { + main20().await; + main21().await; + } + async fn main20() { + main21().await; + main22().await; + } + async fn main21() { + main22().await; + main23().await; + } + async fn main22() { + main23().await; + main24().await; + } + async fn main23() { + main24().await; + main25().await; + } + async fn main24() { + main25().await; + main26().await; + } + async fn main25() { + main26().await; + main27().await; + } + async fn main26() { + main27().await; + main28().await; + } + async fn main27() { + main28().await; + main29().await; + } + async fn main28() { + main29().await; + main30().await; + } + async fn main29() { + main30().await; + main31().await; + } + async fn main30() { + main31().await; + main32().await; + } + async fn main31() { + main32().await; + main33().await; + } + async fn main32() { + main33().await; + main34().await; + } + async fn main33() { + main34().await; + main35().await; + } + async fn main34() { + main35().await; + main36().await; + } + async fn main35() { + main36().await; + main37().await; + } + async fn main36() { + main37().await; + main38().await; + } + async fn main37() { + main38().await; + main39().await; + } + async fn main38() { + main39().await; + main40().await; + } + async fn main39() { + main40().await; + } + async fn main40() { + boom(&mut ()).await; + } + + async fn boom(f: &mut ()) {} + } + + // rust/tests/ui/async-await/generics-and-bounds.rs + + fn _generics_and_bounds() { + use std::future::Future; + + pub async fn simple_generic() {} + + pub trait Foo { + fn foo(&self) {} + } + + struct FooType; + impl Foo for FooType {} + + pub async fn call_generic_bound(f: F) { + f.foo() + } + + pub async fn call_where_clause(f: F) + where + F: Foo, + { + f.foo() + } + + pub async fn call_impl_trait(f: impl Foo) { + f.foo() + } + + pub async fn call_with_ref(f: &impl Foo) { + f.foo() + } + + pub fn async_fn_with_same_generic_params_unifies() { + let mut a = call_generic_bound(FooType); + a = call_generic_bound(FooType); + + let mut b = call_where_clause(FooType); + b = call_where_clause(FooType); + + let mut c = call_impl_trait(FooType); + c = call_impl_trait(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref(&f_one); + d = call_with_ref(&f_two); + } + + pub fn simple_generic_block() -> impl Future { + async move {} + } + + pub fn call_generic_bound_block(f: F) -> impl Future { + async move { f.foo() } + } + + pub fn call_where_clause_block(f: F) -> impl Future + where + F: Foo, + { + async move { f.foo() } + } + + pub fn call_impl_trait_block(f: impl Foo) -> impl Future { + async move { f.foo() } + } + + pub fn call_with_ref_block<'a>(f: &'a (impl Foo + 'a)) -> impl Future + 'a { + async move { f.foo() } + } + + pub fn async_block_with_same_generic_params_unifies() { + let mut a = call_generic_bound_block(FooType); + a = call_generic_bound_block(FooType); + + let mut b = call_where_clause_block(FooType); + b = call_where_clause_block(FooType); + + let mut c = call_impl_trait_block(FooType); + c = call_impl_trait_block(FooType); + + let f_one = FooType; + let f_two = FooType; + let mut d = call_with_ref_block(&f_one); + d = call_with_ref_block(&f_two); + } + } + + // rust/tests/ui/async-await/issue-105501.rs + + fn _issue_105501() { + // This is a regression test for https://github.com/rust-lang/rust/issues/105501. + // It was minified from the published `msf-ice:0.2.1` crate which failed in a crater run. + // A faulty compiler was triggering a `higher-ranked lifetime error`: + // + // > could not prove `[async block@...]: Send` + + use mini_futures::Stream; + + fn is_send(_: impl Send) {} + + pub fn main() { + let fut = async { + let mut stream = mini_futures::iter([()]) + .then(|_| async {}) + .map(|_| async { None }) + .buffered() + .filter_map(std::future::ready); + + stream.next().await + }; + + is_send(async move { + let _: Option<()> = fut.await; + }); + } + + // this is a simplified subset of `futures::StreamExt` and related types + mod mini_futures { + use std::future::Future; + use std::pin::Pin; + use std::task::{Context, Poll}; + + pub fn iter(_: I) -> Iter + where + I: IntoIterator, + { + todo!() + } + + pub trait Stream { + type Item; + + fn then(self, _: F) -> Then + where + F: FnMut(Self::Item) -> Fut, + Fut: Future, + Self: Sized, + { + todo!() + } + + fn map(self, _: F) -> Map + where + F: FnMut(Self::Item) -> T, + Self: Sized, + { + todo!() + } + + fn buffered(self) -> Buffered + where + Self::Item: Future, + Self: Sized, + { + todo!() + } + + fn filter_map(self, _: F) -> FilterMap + where + F: FnMut(Self::Item) -> Fut, + Fut: Future>, + Self: Sized, + { + todo!() + } + + fn next(&mut self) -> Next<'_, Self> { + todo!() + } + } + + pub struct Iter { + __: I, + } + impl Stream for Iter + where + I: Iterator, + { + type Item = I::Item; + } + + pub struct Then { + __: (St, Fut, F), + } + impl Stream for Then + where + St: Stream, + F: FnMut(St::Item) -> Fut, + Fut: Future, + { + type Item = Fut::Output; + } + + pub struct Map { + __: (St, F), + } + impl Stream for Map + where + St: Stream, + F: FnMut1, + { + type Item = F::Output; + } + + pub trait FnMut1 { + type Output; + } + impl FnMut1 for T + where + T: FnMut(A) -> R, + { + type Output = R; + } + + pub struct Buffered + where + St: Stream, + St::Item: Future, + { + __: (St, St::Item), + } + impl Stream for Buffered + where + St: Stream, + St::Item: Future, + { + type Item = ::Output; + } + + pub struct FilterMap { + __: (St, Fut, F), + } + impl Stream for FilterMap + where + St: Stream, + F: FnMut1, + Fut: Future>, + { + type Item = T; + } + + pub struct Next<'a, St: ?Sized> { + __: &'a mut St, + } + impl Future for Next<'_, St> { + type Output = Option; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + todo!() + } + } + } + } +} diff --git a/tests/ui/needless_move.stderr b/tests/ui/needless_move.stderr new file mode 100644 index 000000000000..2b7944c16371 --- /dev/null +++ b/tests/ui/needless_move.stderr @@ -0,0 +1,345 @@ +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:72:33 + | +LL | let closure = assert_static(move || {}); + | -----^^^^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + = note: `-D clippy::needless-move` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_move)]` + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:73:29 + | +LL | let fut = assert_static(async move {}); + | ^^^^^^-----^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:77:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:123:29 + | +LL | let fut = assert_static(async move { + | ^ ----- help: remove the `move` + | _____________________________| + | | +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:160:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_ref(&a); +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:174:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_ref_mut(&mut a); +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:189:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_ref(&a); +LL | | with_owned(b); +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:208:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:246:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | with_owned(a.non_copy); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:318:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | a = NonCopy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:325:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | a.copy = Copy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:332:33 + | +LL | let closure = assert_static(move || { + | ^---- + | | + | _________________________________help: remove the `move` + | | +LL | | a.non_copy = NonCopy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:371:29 + | +LL | let fut = assert_static(async move { + | ^ ----- help: remove the `move` + | _____________________________| + | | +LL | | a = NonCopy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:378:29 + | +LL | let fut = assert_static(async move { + | ^ ----- help: remove the `move` + | _____________________________| + | | +LL | | a.copy = Copy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:385:29 + | +LL | let fut = assert_static(async move { + | ^ ----- help: remove the `move` + | _____________________________| + | | +LL | | a.non_copy = NonCopy; +LL | | with_owned(a); +LL | | }); + | |_____^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:701:17 + | +LL | foo(move || { + | ^---- + | | + | _________________help: remove the `move` + | | +LL | | assert!(*x); +LL | | drop(x); +LL | | }); + | |_____________^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:722:27 + | +LL | closure_typer(move |x| { + | ^---- + | | + | ___________________________help: remove the `move` + | | +LL | | let _: i64 = x.into(); +LL | | }); + | |_____________^ + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:896:19 + | +LL | a(move |a: isize, b| a + b); + | -----^^^^^^^^^^^^^^^^^^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:925:19 + | +LL | b(move |a: isize, b| a + b); + | -----^^^^^^^^^^^^^^^^^^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:954:19 + | +LL | c(move |a: isize, b| a + b); + | -----^^^^^^^^^^^^^^^^^^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:960:19 + | +LL | c(move |a: isize, b| { + | ^---- + | | + | ___________________help: remove the `move` + | | +LL | | z; +LL | | a + b +LL | | }); + | |_________________^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:971:19 + | +LL | c(move |a: isize, b| { + | ^---- + | | + | ___________________help: remove the `move` + | | +LL | | z; +LL | | zz; +LL | | a + b +LL | | }); + | |_________________^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:1032:28 + | +LL | let tick = move || mem::drop(drop_me); + | -----^^^^^^^^^^^^^^^^^^^^^ + | | + | help: remove the `move` + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:1468:19 + | +LL | spawn(move || main0()) + | -----^^^^^^^^^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:1691:13 + | +LL | async move {} + | ^^^^^^-----^^ + | | + | help: remove the `move` + | + = note: there were no captured variables, so the `move` is unnecessary + +error: you seem to use `move`, but the `move` is unnecessary + --> $DIR/needless_move.rs:1754:21 + | +LL | is_send(async move { + | ^ ----- help: remove the `move` + | _____________________| + | | +LL | | let _: Option<()> = fut.await; +LL | | }); + | |_____________^ + | + = note: there were consumed variables, but no borrowed variables, so the `move` is unnecessary + +error: aborting due to 26 previous errors +