about summary refs log tree commit diff
path: root/tests/ui/closures
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ui/closures')
-rw-r--r--tests/ui/closures/2229_closure_analysis/arrays-completely-captured.rs22
-rw-r--r--tests/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr53
-rw-r--r--tests/ui/closures/2229_closure_analysis/by_value.rs37
-rw-r--r--tests/ui/closures/2229_closure_analysis/by_value.stderr58
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-1.stderr68
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs28
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-2.stderr56
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-3.stderr56
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs31
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-analysis-4.stderr53
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs29
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs24
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-enum-field.rs27
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-enums.rs62
-rw-r--r--tests/ui/closures/2229_closure_analysis/capture-enums.stderr113
-rw-r--r--tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs50
-rw-r--r--tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr61
-rw-r--r--tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs24
-rw-r--r--tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr61
-rw-r--r--tests/ui/closures/2229_closure_analysis/destructure_patterns.rs77
-rw-r--r--tests/ui/closures/2229_closure_analysis/destructure_patterns.stderr178
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs85
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr104
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs19
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr19
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs19
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr19
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs17
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr22
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs20
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr22
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs26
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/box.rs64
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs19
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr12
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs34
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs13
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr22
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs27
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs18
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs22
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs13
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs13
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr22
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs92
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr65
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs43
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr33
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs28
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr17
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs37
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr26
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs32
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr29
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs25
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/union.rs25
-rw-r--r--tests/ui/closures/2229_closure_analysis/diagnostics/union.stderr18
-rw-r--r--tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs18
-rw-r--r--tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/filter-on-struct-member.rs41
-rw-r--r--tests/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr26
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-87378.rs26
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-87378.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-87987.rs27
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-87987.stderr14
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-88118-2.rs24
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-88118-2.stderr12
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-88476.rs62
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-88476.stderr97
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-89606.rs40
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-90465.fixed35
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-90465.rs34
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-90465.stderr26
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs14
-rw-r--r--tests/ui/closures/2229_closure_analysis/issue_88118.rs15
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs10
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-87097.rs35
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-87097.stderr36
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-87426.rs14
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-87988.rs19
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-88331.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/issue-88331.stderr39
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs44
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs37
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr17
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs54
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr70
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs79
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr88
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs137
-rw-r--r--tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr203
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed88
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/auto_traits.rs87
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr67
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed33
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs32
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr33
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed38
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs38
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed76
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs74
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr45
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs45
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs10
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr12
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/issue-86753.rs34
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs37
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/macro.fixed25
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/macro.rs25
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/macro.stderr22
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed47
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs46
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr41
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed50
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs49
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr26
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed157
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs153
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr118
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/no_migrations.rs81
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/old_name.rs9
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/old_name.stderr10
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/precise.fixed69
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/precise.rs67
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/precise.stderr55
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs104
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed229
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/significant_drop.rs220
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr214
-rw-r--r--tests/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs13
-rw-r--r--tests/ui/closures/2229_closure_analysis/move_closure.rs222
-rw-r--r--tests/ui/closures/2229_closure_analysis/move_closure.stderr462
-rw-r--r--tests/ui/closures/2229_closure_analysis/multilevel-path-1.rs39
-rw-r--r--tests/ui/closures/2229_closure_analysis/multilevel-path-1.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/multilevel-path-2.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/multilevel-path-2.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/nested-closure.rs53
-rw-r--r--tests/ui/closures/2229_closure_analysis/nested-closure.stderr106
-rw-r--r--tests/ui/closures/2229_closure_analysis/optimization/edge_case.rs34
-rw-r--r--tests/ui/closures/2229_closure_analysis/optimization/edge_case.stderr36
-rw-r--r--tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs27
-rw-r--r--tests/ui/closures/2229_closure_analysis/path-with-array-access.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/path-with-array-access.stderr48
-rw-r--r--tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs101
-rw-r--r--tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr228
-rw-r--r--tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs58
-rw-r--r--tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout13
-rw-r--r--tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout13
-rw-r--r--tests/ui/closures/2229_closure_analysis/repr_packed.rs101
-rw-r--r--tests/ui/closures/2229_closure_analysis/repr_packed.stderr161
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/box.rs92
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/by_value.rs26
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs24
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs20
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs21
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs27
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs21
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr21
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs119
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr39
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs23
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs11
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/edition.rs23
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs37
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs42
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs16
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs19
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs59
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs47
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs30
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/move_closure.rs93
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs33
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs31
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs28
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs54
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs43
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs36
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs49
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs43
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs47
-rw-r--r--tests/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs11
-rw-r--r--tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs39
-rw-r--r--tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr56
-rw-r--r--tests/ui/closures/2229_closure_analysis/unsafe_ptr.rs62
-rw-r--r--tests/ui/closures/2229_closure_analysis/unsafe_ptr.stderr93
-rw-r--r--tests/ui/closures/2229_closure_analysis/wild_patterns.rs73
-rw-r--r--tests/ui/closures/2229_closure_analysis/wild_patterns.stderr138
-rw-r--r--tests/ui/closures/add_semicolon_non_block_closure.rs11
-rw-r--r--tests/ui/closures/add_semicolon_non_block_closure.stderr16
-rw-r--r--tests/ui/closures/binder/async-closure-with-binder.rs8
-rw-r--r--tests/ui/closures/binder/async-closure-with-binder.stderr16
-rw-r--r--tests/ui/closures/binder/disallow-const.rs6
-rw-r--r--tests/ui/closures/binder/disallow-const.stderr8
-rw-r--r--tests/ui/closures/binder/disallow-ty.rs6
-rw-r--r--tests/ui/closures/binder/disallow-ty.stderr8
-rw-r--r--tests/ui/closures/binder/implicit-return.rs6
-rw-r--r--tests/ui/closures/binder/implicit-return.stderr10
-rw-r--r--tests/ui/closures/binder/implicit-stuff.rs27
-rw-r--r--tests/ui/closures/binder/implicit-stuff.stderr107
-rw-r--r--tests/ui/closures/binder/late-bound-in-body.rs9
-rw-r--r--tests/ui/closures/binder/nested-closures-regions.rs9
-rw-r--r--tests/ui/closures/binder/nested-closures-regions.stderr38
-rw-r--r--tests/ui/closures/binder/nested-closures.rs7
-rw-r--r--tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs7
-rw-r--r--tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr33
-rw-r--r--tests/ui/closures/closure-array-break-length.rs7
-rw-r--r--tests/ui/closures/closure-array-break-length.stderr21
-rw-r--r--tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs11
-rw-r--r--tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr19
-rw-r--r--tests/ui/closures/closure-bounds-static-cant-capture-borrowed.rs13
-rw-r--r--tests/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr44
-rw-r--r--tests/ui/closures/closure-bounds-subtype.rs16
-rw-r--r--tests/ui/closures/closure-bounds-subtype.stderr25
-rw-r--r--tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr37
-rw-r--r--tests/ui/closures/closure-expected-type/expect-region-supply-region-2.rs24
-rw-r--r--tests/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr22
-rw-r--r--tests/ui/closures/closure-expected-type/expect-region-supply-region.rs57
-rw-r--r--tests/ui/closures/closure-expected-type/expect-region-supply-region.stderr23
-rw-r--r--tests/ui/closures/closure-expected.rs5
-rw-r--r--tests/ui/closures/closure-expected.stderr16
-rw-r--r--tests/ui/closures/closure-immutable-outer-variable.fixed13
-rw-r--r--tests/ui/closures/closure-immutable-outer-variable.rs13
-rw-r--r--tests/ui/closures/closure-immutable-outer-variable.rs.fixed10
-rw-r--r--tests/ui/closures/closure-immutable-outer-variable.stderr11
-rw-r--r--tests/ui/closures/closure-move-sync.rs22
-rw-r--r--tests/ui/closures/closure-move-sync.stderr43
-rw-r--r--tests/ui/closures/closure-no-fn-1.rs8
-rw-r--r--tests/ui/closures/closure-no-fn-1.stderr19
-rw-r--r--tests/ui/closures/closure-no-fn-2.rs8
-rw-r--r--tests/ui/closures/closure-no-fn-2.stderr19
-rw-r--r--tests/ui/closures/closure-no-fn-3.rs8
-rw-r--r--tests/ui/closures/closure-no-fn-3.stderr9
-rw-r--r--tests/ui/closures/closure-no-fn-4.rs8
-rw-r--r--tests/ui/closures/closure-no-fn-4.stderr24
-rw-r--r--tests/ui/closures/closure-no-fn-5.rs12
-rw-r--r--tests/ui/closures/closure-no-fn-5.stderr23
-rw-r--r--tests/ui/closures/closure-referencing-itself-issue-25954.rs18
-rw-r--r--tests/ui/closures/closure-referencing-itself-issue-25954.stderr9
-rw-r--r--tests/ui/closures/closure-reform-bad.rs12
-rw-r--r--tests/ui/closures/closure-reform-bad.stderr26
-rw-r--r--tests/ui/closures/closure-return-type-mismatch.rs17
-rw-r--r--tests/ui/closures/closure-return-type-mismatch.stderr21
-rw-r--r--tests/ui/closures/closure-return-type-must-be-sized.rs74
-rw-r--r--tests/ui/closures/closure-return-type-must-be-sized.stderr99
-rw-r--r--tests/ui/closures/closure-wrong-kind.rs12
-rw-r--r--tests/ui/closures/closure-wrong-kind.stderr21
-rw-r--r--tests/ui/closures/closure_cap_coerce_many_fail.rs39
-rw-r--r--tests/ui/closures/closure_cap_coerce_many_fail.stderr82
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_check_pass.rs166
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_run_pass.rs59
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr19
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs25
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr19
-rw-r--r--tests/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs23
-rw-r--r--tests/ui/closures/closure_promotion.rs5
-rw-r--r--tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr11
-rw-r--r--tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs8
-rw-r--r--tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr11
-rw-r--r--tests/ui/closures/coerce-unsafe-to-closure.rs4
-rw-r--r--tests/ui/closures/coerce-unsafe-to-closure.stderr16
-rw-r--r--tests/ui/closures/deeply-nested_closures.rs23
-rw-r--r--tests/ui/closures/diverging-closure.rs10
-rw-r--r--tests/ui/closures/issue-101696.rs36
-rw-r--r--tests/ui/closures/issue-102089-multiple-opaque-cast.rs17
-rw-r--r--tests/ui/closures/issue-10398.rs9
-rw-r--r--tests/ui/closures/issue-10398.stderr13
-rw-r--r--tests/ui/closures/issue-23012-supertrait-signature-inference.rs29
-rw-r--r--tests/ui/closures/issue-41366.rs13
-rw-r--r--tests/ui/closures/issue-42463.rs32
-rw-r--r--tests/ui/closures/issue-46742.rs9
-rw-r--r--tests/ui/closures/issue-48109.rs14
-rw-r--r--tests/ui/closures/issue-52437.rs5
-rw-r--r--tests/ui/closures/issue-52437.stderr20
-rw-r--r--tests/ui/closures/issue-67123.rs5
-rw-r--r--tests/ui/closures/issue-67123.stderr17
-rw-r--r--tests/ui/closures/issue-6801.rs21
-rw-r--r--tests/ui/closures/issue-6801.stderr16
-rw-r--r--tests/ui/closures/issue-68025.rs12
-rw-r--r--tests/ui/closures/issue-72408-nested-closures-exponential.rs59
-rw-r--r--tests/ui/closures/issue-78720.rs19
-rw-r--r--tests/ui/closures/issue-78720.stderr53
-rw-r--r--tests/ui/closures/issue-80313-mutable-borrow-in-closure.rs7
-rw-r--r--tests/ui/closures/issue-80313-mutable-borrow-in-closure.stderr17
-rw-r--r--tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs7
-rw-r--r--tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr17
-rw-r--r--tests/ui/closures/issue-80313-mutation-in-closure.rs7
-rw-r--r--tests/ui/closures/issue-80313-mutation-in-closure.stderr17
-rw-r--r--tests/ui/closures/issue-80313-mutation-in-move-closure.rs7
-rw-r--r--tests/ui/closures/issue-80313-mutation-in-move-closure.stderr17
-rw-r--r--tests/ui/closures/issue-81700-mut-borrow.rs5
-rw-r--r--tests/ui/closures/issue-81700-mut-borrow.stderr16
-rw-r--r--tests/ui/closures/issue-82438-mut-without-upvar.rs28
-rw-r--r--tests/ui/closures/issue-82438-mut-without-upvar.stderr14
-rw-r--r--tests/ui/closures/issue-84044-drop-non-mut.rs6
-rw-r--r--tests/ui/closures/issue-84044-drop-non-mut.stderr14
-rw-r--r--tests/ui/closures/issue-84128.rs16
-rw-r--r--tests/ui/closures/issue-84128.stderr17
-rw-r--r--tests/ui/closures/issue-87461.rs29
-rw-r--r--tests/ui/closures/issue-87461.stderr36
-rw-r--r--tests/ui/closures/issue-87814-1.rs8
-rw-r--r--tests/ui/closures/issue-87814-2.rs10
-rw-r--r--tests/ui/closures/issue-90871.rs7
-rw-r--r--tests/ui/closures/issue-90871.stderr26
-rw-r--r--tests/ui/closures/issue-97607.rs12
-rw-r--r--tests/ui/closures/issue-99565.rs7
-rw-r--r--tests/ui/closures/issue-99565.stderr14
-rw-r--r--tests/ui/closures/local-type-mix.rs17
-rw-r--r--tests/ui/closures/local-type-mix.stderr51
-rw-r--r--tests/ui/closures/multiple-fn-bounds.rs15
-rw-r--r--tests/ui/closures/multiple-fn-bounds.stderr26
-rw-r--r--tests/ui/closures/old-closure-arg-call-as.rs12
-rw-r--r--tests/ui/closures/old-closure-arg.rs11
-rw-r--r--tests/ui/closures/old-closure-explicit-types.rs6
-rw-r--r--tests/ui/closures/old-closure-expr-precedence.rs62
-rw-r--r--tests/ui/closures/old-closure-expr-precedence.stderr10
-rw-r--r--tests/ui/closures/old-closure-expression-remove-semicolon.fixed12
-rw-r--r--tests/ui/closures/old-closure-expression-remove-semicolon.rs12
-rw-r--r--tests/ui/closures/old-closure-expression-remove-semicolon.stderr14
-rw-r--r--tests/ui/closures/old-closure-fn-coerce.rs11
-rw-r--r--tests/ui/closures/old-closure-iter-1.rs15
-rw-r--r--tests/ui/closures/old-closure-iter-2.rs15
-rw-r--r--tests/ui/closures/once-move-out-on-heap.rs18
-rw-r--r--tests/ui/closures/print/closure-print-generic-1.rs23
-rw-r--r--tests/ui/closures/print/closure-print-generic-1.stderr20
-rw-r--r--tests/ui/closures/print/closure-print-generic-2.rs13
-rw-r--r--tests/ui/closures/print/closure-print-generic-2.stderr20
-rw-r--r--tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs16
-rw-r--r--tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr20
-rw-r--r--tests/ui/closures/print/closure-print-generic-verbose-1.rs24
-rw-r--r--tests/ui/closures/print/closure-print-generic-verbose-1.stderr20
-rw-r--r--tests/ui/closures/print/closure-print-generic-verbose-2.rs16
-rw-r--r--tests/ui/closures/print/closure-print-generic-verbose-2.stderr20
-rw-r--r--tests/ui/closures/print/closure-print-verbose.rs12
-rw-r--r--tests/ui/closures/print/closure-print-verbose.stderr19
-rw-r--r--tests/ui/closures/semistatement-in-lambda.rs12
-rw-r--r--tests/ui/closures/supertrait-hint-cycle-2.rs18
-rw-r--r--tests/ui/closures/supertrait-hint-cycle-3.rs16
-rw-r--r--tests/ui/closures/supertrait-hint-cycle.rs65
-rw-r--r--tests/ui/closures/supertrait-hint-references-assoc-ty.rs17
-rw-r--r--tests/ui/closures/thir-unsafeck-issue-85871.rs20
352 files changed, 13446 insertions, 0 deletions
diff --git a/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.rs b/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.rs
new file mode 100644
index 00000000000..2bcbd792e3a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.rs
@@ -0,0 +1,22 @@
+// edition:2021
+#![feature(rustc_attrs)]
+
+// Ensure that capture analysis results in arrays being completely captured.
+fn main() {
+    let mut m = [1, 2, 3, 4, 5];
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        m[0] += 10;
+        //~^ NOTE: Capturing m[] -> MutBorrow
+        //~| NOTE: Min Capture m[] -> MutBorrow
+        m[1] += 40;
+        //~^ NOTE: Capturing m[] -> MutBorrow
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr b/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr
new file mode 100644
index 00000000000..129b26456ce
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/arrays-completely-captured.stderr
@@ -0,0 +1,53 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/arrays-completely-captured.rs:8:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/arrays-completely-captured.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         m[0] += 10;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing m[] -> MutBorrow
+  --> $DIR/arrays-completely-captured.rs:14:9
+   |
+LL |         m[0] += 10;
+   |         ^
+note: Capturing m[] -> MutBorrow
+  --> $DIR/arrays-completely-captured.rs:17:9
+   |
+LL |         m[1] += 40;
+   |         ^
+
+error: Min Capture analysis includes:
+  --> $DIR/arrays-completely-captured.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         m[0] += 10;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture m[] -> MutBorrow
+  --> $DIR/arrays-completely-captured.rs:14:9
+   |
+LL |         m[0] += 10;
+   |         ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/by_value.rs b/tests/ui/closures/2229_closure_analysis/by_value.rs
new file mode 100644
index 00000000000..d8d3bbee200
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/by_value.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+// Test that we handle derferences properly when only some of the captures are being moved with
+// `capture_disjoint_fields` enabled.
+#![feature(rustc_attrs)]
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+// Ensure that we don't capture any derefs when moving captures into the closures,
+// i.e. only data from the enclosing stack is moved.
+fn big_box() {
+    let s = MuchLargerType(Default::default());
+    let b = Box::new(s);
+    let t = (b, 10);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let p = t.0.0;
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+        println!("{} {:?}", t.1, p);
+        //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
+    };
+
+    c();
+}
+
+fn main() {
+    big_box();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/by_value.stderr b/tests/ui/closures/2229_closure_analysis/by_value.stderr
new file mode 100644
index 00000000000..097462253aa
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/by_value.stderr
@@ -0,0 +1,58 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/by_value.rs:18:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/by_value.rs:21:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let p = t.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+  --> $DIR/by_value.rs:24:17
+   |
+LL |         let p = t.0.0;
+   |                 ^^^^^
+note: Capturing t[(1, 0)] -> ImmBorrow
+  --> $DIR/by_value.rs:27:29
+   |
+LL |         println!("{} {:?}", t.1, p);
+   |                             ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/by_value.rs:21:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let p = t.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/by_value.rs:24:17
+   |
+LL |         let p = t.0.0;
+   |                 ^^^^^
+note: Min Capture t[(1, 0)] -> ImmBorrow
+  --> $DIR/by_value.rs:27:29
+   |
+LL |         println!("{} {:?}", t.1, p);
+   |                             ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs
new file mode 100644
index 00000000000..dc53b31768e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let p = Point { x: 10, y: 10 };
+    let q = Point { x: 10, y: 10 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ImmBorrow
+        println!("{:?}", p.x);
+        //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+
+        println!("{:?}", q.x);
+        //~^ NOTE: Capturing q[(0, 0)] -> ImmBorrow
+        println!("{:?}", q);
+        //~^ NOTE: Capturing q[] -> ImmBorrow
+        //~| NOTE: Min Capture q[] -> ImmBorrow
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-1.stderr b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
new file mode 100644
index 00000000000..fceafb9c84e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
@@ -0,0 +1,68 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-1.rs:15:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-1.rs:18:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", p);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:21:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+note: Capturing p[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:24:26
+   |
+LL |         println!("{:?}", p.x);
+   |                          ^^^
+note: Capturing q[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:27:26
+   |
+LL |         println!("{:?}", q.x);
+   |                          ^^^
+note: Capturing q[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:29:26
+   |
+LL |         println!("{:?}", q);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-1.rs:18:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", p);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:21:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+note: Min Capture q[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:29:26
+   |
+LL |         println!("{:?}", q);
+   |                          ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs
new file mode 100644
index 00000000000..99d12f8d8f1
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.rs
@@ -0,0 +1,28 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+    x: String,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: String::new(), y: 10 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = p.x;
+        //~^ NOTE: Capturing p[(0, 0)] -> ByValue
+        //~| NOTE: p[] captured as ByValue here
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ByValue
+        //~| NOTE: p[] used here
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-2.stderr b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
new file mode 100644
index 00000000000..cb44ca26652
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-2.rs:14:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-2.rs:17:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-2.rs:20:18
+   |
+LL |         let _x = p.x;
+   |                  ^^^
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/capture-analysis-2.rs:23:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-2.rs:17:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ByValue
+  --> $DIR/capture-analysis-2.rs:20:18
+   |
+LL |         let _x = p.x;
+   |                  ^^^ p[] captured as ByValue here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs
new file mode 100644
index 00000000000..3f337097dbd
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+    c: String,
+    d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+    b: Child,
+}
+
+fn main() {
+    let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = a.b.c;
+        //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ByValue
+        //~| NOTE: a[(0, 0)] captured as ByValue here
+        println!("{:?}", a.b);
+        //~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+        //~| NOTE: a[(0, 0)] used here
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-3.stderr b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
new file mode 100644
index 00000000000..71e7bdc354f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-3.rs:19:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-3.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b.c;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(0, 0),(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-3.rs:25:18
+   |
+LL |         let _x = a.b.c;
+   |                  ^^^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-3.rs:28:26
+   |
+LL |         println!("{:?}", a.b);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-3.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b.c;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-3.rs:25:18
+   |
+LL |         let _x = a.b.c;
+   |                  ^^^^^ a[(0, 0)] captured as ByValue here
+...
+LL |         println!("{:?}", a.b);
+   |                          ^^^ a[(0, 0)] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs
new file mode 100644
index 00000000000..bc46ec99736
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.rs
@@ -0,0 +1,31 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+    c: String,
+    d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+    b: Child,
+}
+
+fn main() {
+    let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = a.b;
+        //~^ NOTE: Capturing a[(0, 0)] -> ByValue
+        //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+        println!("{:?}", a.b.c);
+        //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-analysis-4.stderr b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
new file mode 100644
index 00000000000..7e6e625bc7d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
@@ -0,0 +1,53 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-4.rs:19:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-4.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-4.rs:25:18
+   |
+LL |         let _x = a.b;
+   |                  ^^^
+note: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-4.rs:28:26
+   |
+LL |         println!("{:?}", a.b.c);
+   |                          ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-4.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-4.rs:25:18
+   |
+LL |         let _x = a.b;
+   |                  ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs
new file mode 100644
index 00000000000..6fd15155331
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.rs
@@ -0,0 +1,29 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 10 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        println!("{}", p.x);
+        //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+    };
+
+    // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+    let py = &mut p.y;
+
+    c();
+    *py = 20;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr
new file mode 100644
index 00000000000..0f64ecf3a0c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-struct.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-disjoint-field-struct.rs:13:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-disjoint-field-struct.rs:16:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", p.x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-disjoint-field-struct.rs:19:24
+   |
+LL |         println!("{}", p.x);
+   |                        ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-disjoint-field-struct.rs:16:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", p.x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-disjoint-field-struct.rs:19:24
+   |
+LL |         println!("{}", p.x);
+   |                        ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs
new file mode 100644
index 00000000000..8d3bb3262fb
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.rs
@@ -0,0 +1,24 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+fn main() {
+    let mut t = (10, 10);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        println!("{}", t.0);
+        //~^ NOTE: Capturing t[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
+    };
+
+    // `c` only captures t.0, therefore mutating t.1 is allowed.
+    let t1 = &mut t.1;
+
+    c();
+    *t1 = 20;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr
new file mode 100644
index 00000000000..a8ca9622a6a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-disjoint-field-tuple.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-disjoint-field-tuple.rs:8:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-disjoint-field-tuple.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", t.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-disjoint-field-tuple.rs:14:24
+   |
+LL |         println!("{}", t.0);
+   |                        ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-disjoint-field-tuple.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", t.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-disjoint-field-tuple.rs:14:24
+   |
+LL |         println!("{}", t.0);
+   |                        ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs b/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs
new file mode 100644
index 00000000000..bbe3aa31a98
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-enum-field.rs
@@ -0,0 +1,27 @@
+// edition:2021
+// run-pass
+
+#[derive(Debug, PartialEq, Eq)]
+pub enum Color {
+    RGB(u8, u8, u8),
+}
+
+fn main() {
+    let mut color = Color::RGB(0, 0, 0);
+    let mut red = |v| {
+        let Color::RGB(ref mut r, _, _) = color;
+        *r = v;
+    };
+    let mut green = |v| {
+        let Color::RGB(_, ref mut g, _) = color;
+        *g = v;
+    };
+    let mut blue = |v| {
+        let Color::RGB(_, _, ref mut b) = color;
+        *b = v;
+    };
+    red(1);
+    green(2);
+    blue(3);
+    assert_eq!(Color::RGB(1, 2, 3), color);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.rs b/tests/ui/closures/2229_closure_analysis/capture-enums.rs
new file mode 100644
index 00000000000..322ae99b861
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-enums.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+enum Info {
+    Point(i32, i32, String),
+    Meta(String, Vec<(i32, i32)>)
+}
+
+fn multi_variant_enum() {
+    let point = Info::Point(10, -10, "1".into());
+
+    let vec = Vec::new();
+    let meta = Info::Meta("meta".into(), vec);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        if let Info::Point(_, _, str) = point {
+            //~^ NOTE: Capturing point[] -> ImmBorrow
+            //~| NOTE: Capturing point[(2, 0)] -> ByValue
+            //~| NOTE: Min Capture point[] -> ByValue
+            println!("{}", str);
+        }
+
+        if let Info::Meta(_, v) = meta {
+            //~^ NOTE: Capturing meta[] -> ImmBorrow
+            //~| NOTE: Capturing meta[(1, 1)] -> ByValue
+            //~| NOTE: Min Capture meta[] -> ByValue
+            println!("{:?}", v);
+        }
+    };
+
+    c();
+}
+
+enum SingleVariant {
+    Point(i32, i32, String),
+}
+
+fn single_variant_enum() {
+    let point = SingleVariant::Point(10, -10, "1".into());
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let SingleVariant::Point(_, _, str) = point;
+        //~^ NOTE: Capturing point[(2, 0)] -> ByValue
+        //~| NOTE: Min Capture point[(2, 0)] -> ByValue
+        println!("{}", str);
+    };
+
+    c();
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/capture-enums.stderr b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr
new file mode 100644
index 00000000000..8a6ba8444a8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/capture-enums.stderr
@@ -0,0 +1,113 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-enums.rs:16:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-enums.rs:47:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/capture-enums.rs:19:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         if let Info::Point(_, _, str) = point {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Capturing point[] -> ImmBorrow
+  --> $DIR/capture-enums.rs:22:41
+   |
+LL |         if let Info::Point(_, _, str) = point {
+   |                                         ^^^^^
+note: Capturing point[(2, 0)] -> ByValue
+  --> $DIR/capture-enums.rs:22:41
+   |
+LL |         if let Info::Point(_, _, str) = point {
+   |                                         ^^^^^
+note: Capturing meta[] -> ImmBorrow
+  --> $DIR/capture-enums.rs:29:35
+   |
+LL |         if let Info::Meta(_, v) = meta {
+   |                                   ^^^^
+note: Capturing meta[(1, 1)] -> ByValue
+  --> $DIR/capture-enums.rs:29:35
+   |
+LL |         if let Info::Meta(_, v) = meta {
+   |                                   ^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-enums.rs:19:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         if let Info::Point(_, _, str) = point {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Min Capture point[] -> ByValue
+  --> $DIR/capture-enums.rs:22:41
+   |
+LL |         if let Info::Point(_, _, str) = point {
+   |                                         ^^^^^
+note: Min Capture meta[] -> ByValue
+  --> $DIR/capture-enums.rs:29:35
+   |
+LL |         if let Info::Meta(_, v) = meta {
+   |                                   ^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/capture-enums.rs:50:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let SingleVariant::Point(_, _, str) = point;
+...  |
+LL | |         println!("{}", str);
+LL | |     };
+   | |_____^
+   |
+note: Capturing point[(2, 0)] -> ByValue
+  --> $DIR/capture-enums.rs:53:47
+   |
+LL |         let SingleVariant::Point(_, _, str) = point;
+   |                                               ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-enums.rs:50:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let SingleVariant::Point(_, _, str) = point;
+...  |
+LL | |         println!("{}", str);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture point[(2, 0)] -> ByValue
+  --> $DIR/capture-enums.rs:53:47
+   |
+LL |         let SingleVariant::Point(_, _, str) = point;
+   |                                               ^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs b/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
new file mode 100644
index 00000000000..3341166e22b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
@@ -0,0 +1,50 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+#[derive(Debug)]
+struct Line {
+    p: Point,
+    q: Point
+}
+#[derive(Debug)]
+struct Plane {
+    a: Line,
+    b: Line,
+}
+
+fn main() {
+    let mut p = Plane {
+        a: Line {
+            p: Point { x: 1,y: 2 },
+            q: Point { x: 3,y: 4 },
+        },
+        b: Line {
+            p: Point { x: 1,y: 2 },
+            q: Point { x: 3,y: 4 },
+        }
+    };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = &p.a.p.x;
+        //~^ NOTE: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+        p.b.q.y = 9;
+        //~^ NOTE: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+        //~| NOTE: p[] captured as MutBorrow here
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> MutBorrow
+        //~| NOTE: p[] used here
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr b/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
new file mode 100644
index 00000000000..29e1af0431e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
@@ -0,0 +1,61 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/deep-multilevel-struct.rs:34:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/deep-multilevel-struct.rs:37:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &p.a.p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/deep-multilevel-struct.rs:40:18
+   |
+LL |         let x = &p.a.p.x;
+   |                  ^^^^^^^
+note: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+  --> $DIR/deep-multilevel-struct.rs:42:9
+   |
+LL |         p.b.q.y = 9;
+   |         ^^^^^^^
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/deep-multilevel-struct.rs:45:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/deep-multilevel-struct.rs:37:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &p.a.p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> MutBorrow
+  --> $DIR/deep-multilevel-struct.rs:42:9
+   |
+LL |         p.b.q.y = 9;
+   |         ^^^^^^^ p[] captured as MutBorrow here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs b/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
new file mode 100644
index 00000000000..34b0132f3cb
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
@@ -0,0 +1,24 @@
+// edition:2021
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+fn main() {
+    let mut t = (((1,2),(3,4)),((5,6),(7,8)));
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = &t.0.0.0;
+        //~^ NOTE: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+        t.1.1.1 = 9;
+        //~^ NOTE: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+        //~| NOTE: t[] captured as MutBorrow here
+        println!("{:?}", t);
+        //~^ NOTE: Min Capture t[] -> MutBorrow
+        //~| NOTE: Capturing t[] -> ImmBorrow
+        //~| NOTE: t[] used here
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr b/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
new file mode 100644
index 00000000000..e917516765c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
@@ -0,0 +1,61 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/deep-multilevel-tuple.rs:8:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/deep-multilevel-tuple.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &t.0.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/deep-multilevel-tuple.rs:14:18
+   |
+LL |         let x = &t.0.0.0;
+   |                  ^^^^^^^
+note: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+  --> $DIR/deep-multilevel-tuple.rs:16:9
+   |
+LL |         t.1.1.1 = 9;
+   |         ^^^^^^^
+note: Capturing t[] -> ImmBorrow
+  --> $DIR/deep-multilevel-tuple.rs:19:26
+   |
+LL |         println!("{:?}", t);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/deep-multilevel-tuple.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &t.0.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[] -> MutBorrow
+  --> $DIR/deep-multilevel-tuple.rs:16:9
+   |
+LL |         t.1.1.1 = 9;
+   |         ^^^^^^^ t[] captured as MutBorrow here
+...
+LL |         println!("{:?}", t);
+   |                          ^ t[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/destructure_patterns.rs b/tests/ui/closures/2229_closure_analysis/destructure_patterns.rs
new file mode 100644
index 00000000000..6c65a7bf87b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/destructure_patterns.rs
@@ -0,0 +1,77 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure Index projections are handled properly during capture analysis
+// The array should be moved in entirety, even though only some elements are used.
+fn arrays() {
+    let arr: [String; 5] = [format!("A"), format!("B"), format!("C"), format!("D"), format!("E")];
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let [a, b, .., e] = arr;
+        //~^ NOTE: Capturing arr[Index] -> ByValue
+        //~| NOTE: Capturing arr[Index] -> ByValue
+        //~| NOTE: Capturing arr[Index] -> ByValue
+        //~| NOTE: Min Capture arr[] -> ByValue
+        assert_eq!(a, "A");
+        assert_eq!(b, "B");
+        assert_eq!(e, "E");
+    };
+
+    c();
+}
+
+struct Point {
+    x: i32,
+    y: i32,
+    id: String,
+}
+
+fn structs() {
+    let mut p = Point { x: 10, y: 10, id: String::new() };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let Point { x: ref mut x, y: _, id: moved_id } = p;
+        //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
+        //~| NOTE: Capturing p[(2, 0)] -> ByValue
+        //~| NOTE: Min Capture p[(0, 0)] -> MutBorrow
+        //~| NOTE: Min Capture p[(2, 0)] -> ByValue
+
+        println!("{}, {}", x, moved_id);
+    };
+    c();
+}
+
+fn tuples() {
+    let mut t = (10, String::new(), (String::new(), 42));
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let (ref mut x, ref ref_str, (moved_s, _)) = t;
+        //~^ NOTE: Capturing t[(0, 0)] -> MutBorrow
+        //~| NOTE: Capturing t[(1, 0)] -> ImmBorrow
+        //~| NOTE: Capturing t[(2, 0),(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> MutBorrow
+        //~| NOTE: Min Capture t[(1, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(2, 0),(0, 0)] -> ByValue
+
+        println!("{}, {} {}", x, ref_str, moved_s);
+    };
+    c();
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/destructure_patterns.stderr b/tests/ui/closures/2229_closure_analysis/destructure_patterns.stderr
new file mode 100644
index 00000000000..44fbe6d8158
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/destructure_patterns.stderr
@@ -0,0 +1,178 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/destructure_patterns.rs:10:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/destructure_patterns.rs:38:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/destructure_patterns.rs:58:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/destructure_patterns.rs:13:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let [a, b, .., e] = arr;
+...  |
+LL | |         assert_eq!(e, "E");
+LL | |     };
+   | |_____^
+   |
+note: Capturing arr[Index] -> ByValue
+  --> $DIR/destructure_patterns.rs:16:29
+   |
+LL |         let [a, b, .., e] = arr;
+   |                             ^^^
+note: Capturing arr[Index] -> ByValue
+  --> $DIR/destructure_patterns.rs:16:29
+   |
+LL |         let [a, b, .., e] = arr;
+   |                             ^^^
+note: Capturing arr[Index] -> ByValue
+  --> $DIR/destructure_patterns.rs:16:29
+   |
+LL |         let [a, b, .., e] = arr;
+   |                             ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/destructure_patterns.rs:13:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let [a, b, .., e] = arr;
+...  |
+LL | |         assert_eq!(e, "E");
+LL | |     };
+   | |_____^
+   |
+note: Min Capture arr[] -> ByValue
+  --> $DIR/destructure_patterns.rs:16:29
+   |
+LL |         let [a, b, .., e] = arr;
+   |                             ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/destructure_patterns.rs:41:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+...  |
+LL | |         println!("{}, {}", x, moved_id);
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> MutBorrow
+  --> $DIR/destructure_patterns.rs:44:58
+   |
+LL |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+   |                                                          ^
+note: Capturing p[(2, 0)] -> ByValue
+  --> $DIR/destructure_patterns.rs:44:58
+   |
+LL |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+   |                                                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/destructure_patterns.rs:41:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+...  |
+LL | |         println!("{}, {}", x, moved_id);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[(0, 0)] -> MutBorrow
+  --> $DIR/destructure_patterns.rs:44:58
+   |
+LL |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+   |                                                          ^
+note: Min Capture p[(2, 0)] -> ByValue
+  --> $DIR/destructure_patterns.rs:44:58
+   |
+LL |         let Point { x: ref mut x, y: _, id: moved_id } = p;
+   |                                                          ^
+
+error: First Pass analysis includes:
+  --> $DIR/destructure_patterns.rs:61:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+...  |
+LL | |         println!("{}, {} {}", x, ref_str, moved_s);
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0)] -> MutBorrow
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+note: Capturing t[(1, 0)] -> ImmBorrow
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+note: Capturing t[(2, 0),(0, 0)] -> ByValue
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+
+error: Min Capture analysis includes:
+  --> $DIR/destructure_patterns.rs:61:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+...  |
+LL | |         println!("{}, {} {}", x, ref_str, moved_s);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> MutBorrow
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+note: Min Capture t[(1, 0)] -> ImmBorrow
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+note: Min Capture t[(2, 0),(0, 0)] -> ByValue
+  --> $DIR/destructure_patterns.rs:64:54
+   |
+LL |         let (ref mut x, ref ref_str, (moved_s, _)) = t;
+   |                                                      ^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs
new file mode 100644
index 00000000000..f97e60daf43
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs
@@ -0,0 +1,85 @@
+// edition:2021
+
+// Test that arrays are completely captured by closures by relying on the borrow check diagnostics
+
+fn arrays_1() {
+    let mut arr = [1, 2, 3, 4, 5];
+
+    let mut c = || {
+        arr[0] += 10;
+    };
+
+    // c will capture `arr` completely, therefore another index into the
+    // array can't be modified here
+    arr[1] += 10;
+    //~^ ERROR: cannot use `arr` because it was mutably borrowed
+    //~| ERROR: cannot use `arr[_]` because it was mutably borrowed
+    c();
+}
+
+fn arrays_2() {
+    let mut arr = [1, 2, 3, 4, 5];
+
+    let c = || {
+        println!("{:#?}", &arr[3..4]);
+    };
+
+    // c will capture `arr` completely, therefore another index into the
+    // array can't be modified here
+    arr[1] += 10;
+    //~^ ERROR: cannot assign to `arr[_]` because it is borrowed
+    c();
+}
+
+fn arrays_3() {
+    let mut arr = [1, 2, 3, 4, 5];
+
+    let c = || {
+        println!("{}", arr[3]);
+    };
+
+    // c will capture `arr` completely, therefore another index into the
+    // array can't be modified here
+    arr[1] += 10;
+    //~^ ERROR: cannot assign to `arr[_]` because it is borrowed
+    c();
+}
+
+fn arrays_4() {
+    let mut arr = [1, 2, 3, 4, 5];
+
+    let mut c = || {
+        arr[1] += 10;
+    };
+
+    // c will capture `arr` completely, therefore we cannot borrow another index
+    // into the array.
+    println!("{}", arr[3]);
+    //~^ ERROR: cannot use `arr` because it was mutably borrowed
+    //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
+
+    c();
+}
+
+fn arrays_5() {
+    let mut arr = [1, 2, 3, 4, 5];
+
+    let mut c = || {
+        arr[1] += 10;
+    };
+
+    // c will capture `arr` completely, therefore we cannot borrow other indices
+    // into the array.
+    println!("{:#?}", &arr[3..2]);
+    //~^ ERROR: cannot borrow `arr` as immutable because it is also borrowed as mutable
+
+    c();
+}
+
+fn main() {
+    arrays_1();
+    arrays_2();
+    arrays_3();
+    arrays_4();
+    arrays_5();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
new file mode 100644
index 00000000000..4f41060dc98
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr
@@ -0,0 +1,104 @@
+error[E0503]: cannot use `arr` because it was mutably borrowed
+  --> $DIR/arrays.rs:14:5
+   |
+LL |     let mut c = || {
+   |                 -- borrow of `arr` occurs here
+LL |         arr[0] += 10;
+   |         --- borrow occurs due to use of `arr` in closure
+...
+LL |     arr[1] += 10;
+   |     ^^^^^^ use of borrowed `arr`
+...
+LL |     c();
+   |     - borrow later used here
+
+error[E0503]: cannot use `arr[_]` because it was mutably borrowed
+  --> $DIR/arrays.rs:14:5
+   |
+LL |     let mut c = || {
+   |                 -- borrow of `arr` occurs here
+LL |         arr[0] += 10;
+   |         --- borrow occurs due to use of `arr` in closure
+...
+LL |     arr[1] += 10;
+   |     ^^^^^^^^^^^^ use of borrowed `arr`
+...
+LL |     c();
+   |     - borrow later used here
+
+error[E0506]: cannot assign to `arr[_]` because it is borrowed
+  --> $DIR/arrays.rs:29:5
+   |
+LL |     let c = || {
+   |             -- borrow of `arr[_]` occurs here
+LL |         println!("{:#?}", &arr[3..4]);
+   |                            --- borrow occurs due to use in closure
+...
+LL |     arr[1] += 10;
+   |     ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+LL |
+LL |     c();
+   |     - borrow later used here
+
+error[E0506]: cannot assign to `arr[_]` because it is borrowed
+  --> $DIR/arrays.rs:43:5
+   |
+LL |     let c = || {
+   |             -- borrow of `arr[_]` occurs here
+LL |         println!("{}", arr[3]);
+   |                        --- borrow occurs due to use in closure
+...
+LL |     arr[1] += 10;
+   |     ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+LL |
+LL |     c();
+   |     - borrow later used here
+
+error[E0503]: cannot use `arr` because it was mutably borrowed
+  --> $DIR/arrays.rs:57:20
+   |
+LL |     let mut c = || {
+   |                 -- borrow of `arr` occurs here
+LL |         arr[1] += 10;
+   |         --- borrow occurs due to use of `arr` in closure
+...
+LL |     println!("{}", arr[3]);
+   |                    ^^^^^^ use of borrowed `arr`
+...
+LL |     c();
+   |     - borrow later used here
+
+error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable
+  --> $DIR/arrays.rs:57:20
+   |
+LL |     let mut c = || {
+   |                 -- mutable borrow occurs here
+LL |         arr[1] += 10;
+   |         --- first borrow occurs due to use of `arr` in closure
+...
+LL |     println!("{}", arr[3]);
+   |                    ^^^^^^ immutable borrow occurs here
+...
+LL |     c();
+   |     - mutable borrow later used here
+   |
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable
+  --> $DIR/arrays.rs:73:24
+   |
+LL |     let mut c = || {
+   |                 -- mutable borrow occurs here
+LL |         arr[1] += 10;
+   |         --- first borrow occurs due to use of `arr` in closure
+...
+LL |     println!("{:#?}", &arr[3..2]);
+   |                        ^^^ immutable borrow occurs here
+...
+LL |     c();
+   |     - mutable borrow later used here
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0502, E0503, E0506.
+For more information about an error, try `rustc --explain E0502`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs
new file mode 100644
index 00000000000..3664d76c203
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+fn main() {
+    let mut p = Point {x: 1, y: 2 };
+
+    let y = &mut p.y;
+    let mut c = || {
+    //~^ ERROR cannot borrow `p` as mutable more than once at a time
+       let x = &mut p.x;
+       println!("{:?}", p);
+    };
+    c();
+    *y+=1;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr
new file mode 100644
index 00000000000..341d2bc6563
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-1.stderr
@@ -0,0 +1,19 @@
+error[E0499]: cannot borrow `p` as mutable more than once at a time
+  --> $DIR/borrowck-1.rs:12:17
+   |
+LL |     let y = &mut p.y;
+   |             -------- first mutable borrow occurs here
+LL |     let mut c = || {
+   |                 ^^ second mutable borrow occurs here
+LL |
+LL |        let x = &mut p.x;
+   |                     --- capture is mutable because of use here
+LL |        println!("{:?}", p);
+   |                         - second borrow occurs due to use of `p` in closure
+...
+LL |     *y+=1;
+   |     ----- first borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs
new file mode 100644
index 00000000000..ae416bab65e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+fn main() {
+    let mut p = Point {x: 1, y: 2 };
+
+    let y = &p.y;
+    let mut c = || {
+    //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable
+       println!("{:?}", p);
+       let x = &mut p.x;
+    };
+    c();
+    println!("{}", y);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr
new file mode 100644
index 00000000000..584bb862b2c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-2.stderr
@@ -0,0 +1,19 @@
+error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-2.rs:12:17
+   |
+LL |     let y = &p.y;
+   |             ---- immutable borrow occurs here
+LL |     let mut c = || {
+   |                 ^^ mutable borrow occurs here
+LL |
+LL |        println!("{:?}", p);
+   |                         - second borrow occurs due to use of `p` in closure
+LL |        let x = &mut p.x;
+   |                     --- capture is mutable because of use here
+...
+LL |     println!("{}", y);
+   |                    - immutable borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs
new file mode 100644
index 00000000000..00f50c33e1c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.rs
@@ -0,0 +1,17 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+    x: String,
+    y: String,
+}
+fn main() {
+    let mut c = {
+        let mut p = Point {x: "1".to_string(), y: "2".to_string() };
+        || { //~ ERROR closure may outlive the current block, but it borrows `p`
+           let x = &mut p.x;
+           println!("{:?}", p);
+        }
+    };
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr
new file mode 100644
index 00000000000..ee923804786
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-3.stderr
@@ -0,0 +1,22 @@
+error[E0373]: closure may outlive the current block, but it borrows `p`, which is owned by the current block
+  --> $DIR/borrowck-3.rs:11:9
+   |
+LL |         || {
+   |         ^^ may outlive borrowed value `p`
+LL |            let x = &mut p.x;
+LL |            println!("{:?}", p);
+   |                             - `p` is borrowed here
+   |
+note: block requires argument type to outlive `'1`
+  --> $DIR/borrowck-3.rs:9:9
+   |
+LL |     let mut c = {
+   |         ^^^^^
+help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
+   |
+LL |         move || {
+   |         ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0373`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
new file mode 100644
index 00000000000..a2290d85020
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.rs
@@ -0,0 +1,20 @@
+// edition:2021
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+fn foo () -> impl FnMut()->() {
+    let mut p = Point {x: 1, y: 2 };
+    let mut c = || {
+    //~^ ERROR closure may outlive the current function, but it borrows `p`
+       p.x+=5;
+       println!("{:?}", p);
+    };
+    c
+}
+fn main() {
+    let c = foo();
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
new file mode 100644
index 00000000000..46379a3815a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-4.stderr
@@ -0,0 +1,22 @@
+error[E0373]: closure may outlive the current function, but it borrows `p`, which is owned by the current function
+  --> $DIR/borrowck-4.rs:10:17
+   |
+LL |     let mut c = || {
+   |                 ^^ may outlive borrowed value `p`
+...
+LL |        println!("{:?}", p);
+   |                         - `p` is borrowed here
+   |
+note: closure is returned here
+  --> $DIR/borrowck-4.rs:15:5
+   |
+LL |     c
+   |     ^
+help: to force the closure to take ownership of `p` (and any other referenced variables), use the `move` keyword
+   |
+LL |     let mut c = move || {
+   |                 ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0373`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs
new file mode 100644
index 00000000000..5ff7b1242db
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.rs
@@ -0,0 +1,26 @@
+// edition:2021
+
+
+
+// Tests that two closures cannot simultaneously have mutable
+// and immutable access to the variable. Issue #6801.
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn a() {
+    let mut p = Point {x: 3, y:4};
+    let c2 = || p.y * 5;
+    let c1 = || {
+    //~^ ERROR cannot borrow `p` as mutable because it is also borrowed as immutable
+        dbg!(&p);
+        p.x = 4;
+    };
+    drop(c2);
+}
+
+fn main() {
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr
new file mode 100644
index 00000000000..5f1dae2972f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/borrowck/borrowck-closures-mut-and-imm.stderr
@@ -0,0 +1,21 @@
+error[E0502]: cannot borrow `p` as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-closures-mut-and-imm.rs:17:14
+   |
+LL |     let c2 = || p.y * 5;
+   |              -- --- first borrow occurs due to use of `p.y` in closure
+   |              |
+   |              immutable borrow occurs here
+LL |     let c1 = || {
+   |              ^^ mutable borrow occurs here
+LL |
+LL |         dbg!(&p);
+   |               - second borrow occurs due to use of `p` in closure
+LL |         p.x = 4;
+   |         --- capture is mutable because of use here
+LL |     };
+LL |     drop(c2);
+   |          -- immutable borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/box.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/box.rs
new file mode 100644
index 00000000000..a110fa4e2cb
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/box.rs
@@ -0,0 +1,64 @@
+// edition:2021
+
+// Test borrow checker when we precise capture when using boxes
+
+struct MetaData { x: String, name: String }
+struct Data { m: MetaData }
+struct BoxedData(Box<Data>);
+struct EvenMoreBoxedData(Box<BoxedData>);
+
+// Check diagnostics when the same path is mutated both inside and outside the closure
+fn box_1() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let mut c = || {
+        e.0.0.m.x = format!("not-x");
+    };
+
+    e.0.0.m.x = format!("not-x");
+    //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
+    c();
+}
+
+// Check diagnostics when a path is mutated inside a closure while attempting to read it outside
+// the closure.
+fn box_2() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let mut c = || {
+        e.0.0.m.x = format!("not-x");
+    };
+
+    println!("{}", e.0.0.m.x);
+    //~^ ERROR: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
+    c();
+}
+
+// Check diagnostics when a path is read inside a closure while attempting to mutate it outside
+// the closure.
+fn box_3() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let c = || {
+        println!("{}", e.0.0.m.x);
+    };
+
+    e.0.0.m.x = format!("not-x");
+    //~^ ERROR: cannot assign to `e.0.0.m.x` because it is borrowed
+    c();
+}
+
+fn main() {
+    box_1();
+    box_2();
+    box_3();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr
new file mode 100644
index 00000000000..f8b17875235
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/box.stderr
@@ -0,0 +1,48 @@
+error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
+  --> $DIR/box.rs:21:5
+   |
+LL |     let mut c = || {
+   |                 -- borrow of `e.0.0.m.x` occurs here
+LL |         e.0.0.m.x = format!("not-x");
+   |         --------- borrow occurs due to use in closure
+...
+LL |     e.0.0.m.x = format!("not-x");
+   |     ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+LL |
+LL |     c();
+   |     - borrow later used here
+
+error[E0502]: cannot borrow `e.0.0.m.x` as immutable because it is also borrowed as mutable
+  --> $DIR/box.rs:38:20
+   |
+LL |     let mut c = || {
+   |                 -- mutable borrow occurs here
+LL |         e.0.0.m.x = format!("not-x");
+   |         --------- first borrow occurs due to use of `e.0.0.m.x` in closure
+...
+LL |     println!("{}", e.0.0.m.x);
+   |                    ^^^^^^^^^ immutable borrow occurs here
+LL |
+LL |     c();
+   |     - mutable borrow later used here
+   |
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0506]: cannot assign to `e.0.0.m.x` because it is borrowed
+  --> $DIR/box.rs:55:5
+   |
+LL |     let c = || {
+   |             -- borrow of `e.0.0.m.x` occurs here
+LL |         println!("{}", e.0.0.m.x);
+   |                        --------- borrow occurs due to use in closure
+...
+LL |     e.0.0.m.x = format!("not-x");
+   |     ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+LL |
+LL |     c();
+   |     - borrow later used here
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0502, E0506.
+For more information about an error, try `rustc --explain E0502`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
new file mode 100644
index 00000000000..77effcb0065
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.rs
@@ -0,0 +1,19 @@
+// edition:2021
+
+// Test that if we deref an immutable borrow to access a Place,
+// then we can't mutate the final place.
+
+fn main() {
+    let mut x = (format!(""), format!("X2"));
+    let mut y = (&x, "Y");
+    let z = (&mut y, "Z");
+
+    // `x.0` is mutable but we access `x` via `*z.0.0`, which is an immutable reference and
+    // therefore can't be mutated.
+    let mut c = || {
+    //~^ ERROR: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
+        z.0.0.0 = format!("X1");
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
new file mode 100644
index 00000000000..38c530b809a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm-borrow.stderr
@@ -0,0 +1,12 @@
+error[E0596]: cannot borrow `*z.0.0` as mutable, as it is behind a `&` reference
+  --> $DIR/cant-mutate-imm-borrow.rs:13:17
+   |
+LL |     let mut c = || {
+   |                 ^^ cannot borrow as mutable
+LL |
+LL |         z.0.0.0 = format!("X1");
+   |         ------- mutable borrow occurs due to use of `*z.0.0` in closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
new file mode 100644
index 00000000000..25ee9a1490e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.rs
@@ -0,0 +1,34 @@
+// edition:2021
+
+// Ensure that diagnostics for mutability error (because the root variable
+// isn't mutable) work with `capture_disjoint_fields` enabled.
+
+fn mut_error_struct() {
+    let x = (10, 10);
+    let y = (x, 10);
+    let z = (y, 10);
+
+    let mut c = || {
+        z.0.0.0 = 20;
+        //~^ ERROR: cannot assign to `z.0.0.0`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn mut_error_box() {
+    let x = (10, 10);
+    let bx = Box::new(x);
+
+    let mut c = || {
+        bx.0 = 20;
+        //~^ ERROR: cannot assign to `*bx.0`, as it is not declared as mutable
+    };
+
+    c();
+}
+
+fn main() {
+    mut_error_struct();
+    mut_error_box();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
new file mode 100644
index 00000000000..98414fa8a3d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/cant-mutate-imm.stderr
@@ -0,0 +1,21 @@
+error[E0594]: cannot assign to `z.0.0.0`, as it is not declared as mutable
+  --> $DIR/cant-mutate-imm.rs:12:9
+   |
+LL |     let z = (y, 10);
+   |         - help: consider changing this to be mutable: `mut z`
+...
+LL |         z.0.0.0 = 20;
+   |         ^^^^^^^^^^^^ cannot assign
+
+error[E0594]: cannot assign to `*bx.0`, as it is not declared as mutable
+  --> $DIR/cant-mutate-imm.rs:24:9
+   |
+LL |     let bx = Box::new(x);
+   |         -- help: consider changing this to be mutable: `mut bx`
+...
+LL |         bx.0 = 20;
+   |         ^^^^^^^^^ cannot assign
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs
new file mode 100644
index 00000000000..f3be542e40d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+// Test that array access is not stored as part of closure kind origin
+
+fn expect_fn<F: Fn()>(_f: F) {}
+
+fn main() {
+    let s = [format!("s"), format!("s")];
+    let c = || { //~ ERROR expected a closure that implements the `Fn`
+        let [_, _s] = s;
+    };
+    expect_fn(c);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr
new file mode 100644
index 00000000000..309c63e5293
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr
@@ -0,0 +1,22 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+  --> $DIR/closure-origin-array-diagnostics.rs:9:13
+   |
+LL |     let c = || {
+   |             ^^ this closure implements `FnOnce`, not `Fn`
+LL |         let [_, _s] = s;
+   |                       - closure is `FnOnce` because it moves the variable `s` out of its environment
+LL |     };
+LL |     expect_fn(c);
+   |     --------- - the requirement to implement `Fn` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `expect_fn`
+  --> $DIR/closure-origin-array-diagnostics.rs:5:17
+   |
+LL | fn expect_fn<F: Fn()>(_f: F) {}
+   |                 ^^^^ required by this bound in `expect_fn`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs
new file mode 100644
index 00000000000..aa85b55b15c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.rs
@@ -0,0 +1,27 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+enum MultiVariant {
+    Point(i32, i32),
+    Meta(i32)
+}
+
+fn main() {
+    let mut point = MultiVariant::Point(10, -10,);
+
+    let mut meta = MultiVariant::Meta(1);
+
+    let c = || {
+        if let MultiVariant::Point(ref mut x, _) = point {
+            *x += 1;
+        }
+
+        if let MultiVariant::Meta(ref mut v) = meta {
+            *v += 1;
+        }
+    };
+
+    let a = c;
+    let b = c; //~ ERROR use of moved value: `c` [E0382]
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr
new file mode 100644
index 00000000000..83d282aadb9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-multi-variant-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `c`
+  --> $DIR/closure-origin-multi-variant-diagnostics.rs:26:13
+   |
+LL |     let a = c;
+   |             - value moved here
+LL |     let b = c;
+   |             ^ value used here after move
+   |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+  --> $DIR/closure-origin-multi-variant-diagnostics.rs:16:52
+   |
+LL |         if let MultiVariant::Point(ref mut x, _) = point {
+   |                                                    ^^^^^
+help: consider mutably borrowing `c`
+   |
+LL |     let a = &mut c;
+   |             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs
new file mode 100644
index 00000000000..bedb103cc4c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.rs
@@ -0,0 +1,18 @@
+// edition:2021
+
+
+enum SingleVariant {
+    Point(i32, i32),
+}
+
+fn main() {
+    let mut point = SingleVariant::Point(10, -10);
+
+    let c = || {
+        let SingleVariant::Point(ref mut x, _) = point;
+        *x += 1;
+    };
+
+    let b = c;
+    let a = c; //~ ERROR use of moved value: `c` [E0382]
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr
new file mode 100644
index 00000000000..46323b75210
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-single-variant-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `c`
+  --> $DIR/closure-origin-single-variant-diagnostics.rs:17:13
+   |
+LL |     let b = c;
+   |             - value moved here
+LL |     let a = c;
+   |             ^ value used here after move
+   |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `point.0` out of its environment
+  --> $DIR/closure-origin-single-variant-diagnostics.rs:12:50
+   |
+LL |         let SingleVariant::Point(ref mut x, _) = point;
+   |                                                  ^^^^^
+help: consider mutably borrowing `c`
+   |
+LL |     let b = &mut c;
+   |             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs
new file mode 100644
index 00000000000..3277a83c4e1
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.rs
@@ -0,0 +1,22 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+struct Y {
+    y: X
+}
+
+struct X {
+    a: u32,
+    b: u32,
+}
+
+fn main() {
+    let mut x = Y { y: X { a: 5, b: 0 } };
+    let hello = || {
+        x.y.a += 1;
+    };
+
+    let b = hello;
+    let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr
new file mode 100644
index 00000000000..25029cc7bd8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-struct-diagnostics.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `hello`
+  --> $DIR/closure-origin-struct-diagnostics.rs:21:13
+   |
+LL |     let b = hello;
+   |             ----- value moved here
+LL |     let c = hello;
+   |             ^^^^^ value used here after move
+   |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.y.a` out of its environment
+  --> $DIR/closure-origin-struct-diagnostics.rs:17:9
+   |
+LL |         x.y.a += 1;
+   |         ^^^^^
+help: consider mutably borrowing `hello`
+   |
+LL |     let b = &mut hello;
+   |             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs
new file mode 100644
index 00000000000..dc3a57ae793
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+// Check that precise paths are being reported back in the error message.
+
+fn main() {
+    let mut x = (5, 0);
+    let hello = || {
+        x.0 += 1;
+    };
+
+    let b = hello;
+    let c = hello; //~ ERROR use of moved value: `hello` [E0382]
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr
new file mode 100644
index 00000000000..06ef7baf9c0
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics-1.stderr
@@ -0,0 +1,21 @@
+error[E0382]: use of moved value: `hello`
+  --> $DIR/closure-origin-tuple-diagnostics-1.rs:12:13
+   |
+LL |     let b = hello;
+   |             ----- value moved here
+LL |     let c = hello;
+   |             ^^^^^ value used here after move
+   |
+note: closure cannot be moved more than once as it is not `Copy` due to moving the variable `x.0` out of its environment
+  --> $DIR/closure-origin-tuple-diagnostics-1.rs:8:9
+   |
+LL |         x.0 += 1;
+   |         ^^^
+help: consider mutably borrowing `hello`
+   |
+LL |     let b = &mut hello;
+   |             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs
new file mode 100644
index 00000000000..fa1328013db
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.rs
@@ -0,0 +1,13 @@
+// edition:2021
+
+struct S(String, String);
+
+fn expect_fn<F: Fn()>(_f: F) {}
+
+fn main() {
+    let s = S(format!("s"), format!("s"));
+    let c = || { //~ ERROR expected a closure that implements the `Fn`
+        let s = s.1;
+    };
+    expect_fn(c);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr
new file mode 100644
index 00000000000..3e77635f9e0
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/closure-origin-tuple-diagnostics.stderr
@@ -0,0 +1,22 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+  --> $DIR/closure-origin-tuple-diagnostics.rs:9:13
+   |
+LL |     let c = || {
+   |             ^^ this closure implements `FnOnce`, not `Fn`
+LL |         let s = s.1;
+   |                 --- closure is `FnOnce` because it moves the variable `s.1` out of its environment
+LL |     };
+LL |     expect_fn(c);
+   |     --------- - the requirement to implement `Fn` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `expect_fn`
+  --> $DIR/closure-origin-tuple-diagnostics.rs:5:17
+   |
+LL | fn expect_fn<F: Fn()>(_f: F) {}
+   |                 ^^^^ required by this bound in `expect_fn`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs
new file mode 100644
index 00000000000..3399bc0018e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.rs
@@ -0,0 +1,92 @@
+// edition:2021
+
+// check-pass
+#![allow(unreachable_code)]
+#![warn(unused)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+pub fn f() {
+    let mut a = 1;
+    let mut c = Point{ x:1, y:0 };
+
+    // Captured by value, but variable is dead on entry.
+    (move || {
+        // This will not trigger a warning for unused variable as
+        // c.x will be treated as a Non-tracked place
+        c.x = 1;
+        println!("{}", c.x);
+        a = 1; //~ WARN value captured by `a` is never read
+        println!("{}", a);
+    })();
+
+    // Read and written to, but never actually used.
+    (move || {
+        // This will not trigger a warning for unused variable as
+        // c.x will be treated as a Non-tracked place
+        c.x += 1;
+        a += 1; //~ WARN unused variable: `a`
+    })();
+
+    (move || {
+        println!("{}", c.x);
+        // Value is read by closure itself on later invocations.
+        // This will not trigger a warning for unused variable as
+        // c.x will be treated as a Non-tracked place
+        c.x += 1;
+        println!("{}", a);
+        a += 1;
+    })();
+    let b = Box::new(42);
+    (move || {
+        println!("{}", c.x);
+        // Never read because this is FnOnce closure.
+        // This will not trigger a warning for unused variable as
+        // c.x will be treated as a Non-tracked place
+        c.x += 1;
+        println!("{}", a);
+        a += 1; //~ WARN value assigned to `a` is never read
+        drop(b);
+    })();
+}
+
+#[derive(Debug)]
+struct MyStruct<'a>  {
+    x: Option<& 'a str>,
+    y: i32,
+}
+
+pub fn nested() {
+    let mut a : Option<& str>;
+    a = None;
+    let mut b : Option<& str>;
+    b = None;
+    let mut d = MyStruct{ x: None, y: 1};
+    let mut e = MyStruct{ x: None, y: 1};
+    (|| {
+        (|| {
+            // This will not trigger a warning for unused variable as
+            // d.x will be treated as a Non-tracked place
+            d.x = Some("d1");
+            d.x = Some("d2");
+            a = Some("d1"); //~ WARN value assigned to `a` is never read
+            a = Some("d2");
+        })();
+        (move || {
+            // This will not trigger a warning for unused variable as
+            //e.x will be treated as a Non-tracked place
+            e.x = Some("e1");
+            e.x = Some("e2");
+            b = Some("e1"); //~ WARN value assigned to `b` is never read
+                            //~| WARN unused variable: `b`
+            b = Some("e2"); //~ WARN value assigned to `b` is never read
+        })();
+    })();
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr
new file mode 100644
index 00000000000..cf414adc0b9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness.stderr
@@ -0,0 +1,65 @@
+warning: value captured by `a` is never read
+  --> $DIR/liveness.rs:24:9
+   |
+LL |         a = 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+note: the lint level is defined here
+  --> $DIR/liveness.rs:5:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `a`
+  --> $DIR/liveness.rs:33:9
+   |
+LL |         a += 1;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: value assigned to `a` is never read
+  --> $DIR/liveness.rs:53:9
+   |
+LL |         a += 1;
+   |         ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `a` is never read
+  --> $DIR/liveness.rs:77:13
+   |
+LL |             a = Some("d1");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `b` is never read
+  --> $DIR/liveness.rs:85:13
+   |
+LL |             b = Some("e1");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: value assigned to `b` is never read
+  --> $DIR/liveness.rs:87:13
+   |
+LL |             b = Some("e2");
+   |             ^
+   |
+   = help: maybe it is overwritten before being read?
+
+warning: unused variable: `b`
+  --> $DIR/liveness.rs:85:13
+   |
+LL |             b = Some("e1");
+   |             ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 7 warnings emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs
new file mode 100644
index 00000000000..465c9476ba6
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.rs
@@ -0,0 +1,43 @@
+// edition:2021
+
+// check-pass
+#![warn(unused)]
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct MyStruct {
+    a: i32,
+    b: i32,
+}
+
+pub fn unintentional_copy_one() {
+    let mut a = 1;
+    let mut last = MyStruct{ a: 1, b: 1};
+    let mut f = move |s| {
+        // This will not trigger a warning for unused variable
+        // as last.a will be treated as a Non-tracked place
+        last.a = s;
+        a = s;
+        //~^ WARN value assigned to `a` is never read
+        //~| WARN unused variable: `a`
+    };
+    f(2);
+    f(3);
+    f(4);
+}
+
+pub fn unintentional_copy_two() {
+    let mut a = 1;
+    let mut sum = MyStruct{ a: 1, b: 0};
+    (1..10).for_each(move |x| {
+        // This will not trigger a warning for unused variable
+        // as sum.b will be treated as a Non-tracked place
+        sum.b += x;
+        a += x; //~ WARN unused variable: `a`
+    });
+}
+
+fn main() {
+    unintentional_copy_one();
+    unintentional_copy_two();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr
new file mode 100644
index 00000000000..0410de4c798
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/liveness_unintentional_copy.stderr
@@ -0,0 +1,33 @@
+warning: value assigned to `a` is never read
+  --> $DIR/liveness_unintentional_copy.rs:20:9
+   |
+LL |         a = s;
+   |         ^
+   |
+   = help: maybe it is overwritten before being read?
+note: the lint level is defined here
+  --> $DIR/liveness_unintentional_copy.rs:4:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_assignments)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `a`
+  --> $DIR/liveness_unintentional_copy.rs:20:9
+   |
+LL |         a = s;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `a`
+  --> $DIR/liveness_unintentional_copy.rs:36:9
+   |
+LL |         a += x;
+   |         ^
+   |
+   = help: did you mean to capture by reference instead?
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs
new file mode 100644
index 00000000000..fa73ff23f9c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.rs
@@ -0,0 +1,28 @@
+// edition:2021
+
+// Test that when a borrow checker diagnostics are emitted, it's as precise
+// as the capture by the closure.
+
+#![allow(unused)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    let mut c = || {
+        w.p.x += 20;
+    };
+
+    let py = &mut w.p.x;
+    //~^ ERROR: cannot borrow `w.p.x` as mutable more than once at a time
+    c();
+
+    *py = 20
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr
new file mode 100644
index 00000000000..ac4c9c93769
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/multilevel-path.stderr
@@ -0,0 +1,17 @@
+error[E0499]: cannot borrow `w.p.x` as mutable more than once at a time
+  --> $DIR/multilevel-path.rs:23:14
+   |
+LL |     let mut c = || {
+   |                 -- first mutable borrow occurs here
+LL |         w.p.x += 20;
+   |         ----- first borrow occurs due to use of `w.p.x` in closure
+...
+LL |     let py = &mut w.p.x;
+   |              ^^^^^^^^^^ second mutable borrow occurs here
+LL |
+LL |     c();
+   |     - first borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
new file mode 100644
index 00000000000..3d5a31e8b8e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+// Test that we can't mutate a place if we need to deref an imm-borrow
+// to reach it.
+
+fn imm_mut_ref() {
+    let mut x = String::new();
+    let y = String::new();
+    let mref_x = &mut x;
+    let ref_mref_x = &mref_x;
+
+    let c = || {
+    //~^ ERROR: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+        **ref_mref_x = y;
+    };
+
+    c();
+}
+
+fn mut_imm_ref() {
+    let x = String::new();
+    let y = String::new();
+    let mut ref_x = &x;
+    let mref_ref_x = &mut ref_x;
+
+    let c = || {
+    //~^ ERROR: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+        **mref_ref_x = y;
+    };
+
+    c();
+}
+
+fn main() {
+    imm_mut_ref();
+    mut_imm_ref();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
new file mode 100644
index 00000000000..95f36fc042c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/mut_ref.stderr
@@ -0,0 +1,26 @@
+error[E0596]: cannot borrow `**ref_mref_x` as mutable, as it is behind a `&` reference
+  --> $DIR/mut_ref.rs:12:13
+   |
+LL |     let c = || {
+   |             ^^ `ref_mref_x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+LL |
+LL |         **ref_mref_x = y;
+   |         ------------ mutable borrow occurs due to use of `**ref_mref_x` in closure
+   |
+help: consider changing this to be a mutable reference
+   |
+LL |     let ref_mref_x = &mut mref_x;
+   |                      ~~~~~~~~~~~
+
+error[E0596]: cannot borrow `**mref_ref_x` as mutable, as it is behind a `&` reference
+  --> $DIR/mut_ref.rs:26:13
+   |
+LL |     let c = || {
+   |             ^^ cannot borrow as mutable
+LL |
+LL |         **mref_ref_x = y;
+   |         ------------ mutable borrow occurs due to use of `**mref_ref_x` in closure
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs
new file mode 100644
index 00000000000..1488f329648
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.rs
@@ -0,0 +1,32 @@
+// edition:2021
+
+// Given how the closure desugaring is implemented (at least at the time of writing this test),
+// we don't need to truncate the captured path to a reference into a packed-struct if the field
+// being referenced will be moved into the closure, since it's safe to move out a field from a
+// packed-struct.
+//
+// However to avoid surprises for the user, or issues when the closure is
+// inlined we will truncate the capture to access just the struct regardless of if the field
+// might get moved into the closure.
+//
+// It is possible for someone to try writing the code that relies on the desugaring to create a ref
+// into a packed-struct. Here we test that the compiler still detects that case.
+fn test_missing_unsafe_warning_on_repr_packed() {
+    #[repr(packed)]
+    struct Foo { x: String }
+
+    let foo = Foo { x: String::new() };
+
+    let c = || {
+        println!("{}", foo.x);
+        //~^ ERROR: reference to packed field is unaligned
+        //~| WARNING: this was previously accepted by the compiler but is being phased out
+        let _z = foo.x;
+    };
+
+    c();
+}
+
+fn main() {
+    test_missing_unsafe_warning_on_repr_packed();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
new file mode 100644
index 00000000000..508c4b911b7
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/repr_packed.stderr
@@ -0,0 +1,29 @@
+error: reference to packed field is unaligned
+  --> $DIR/repr_packed.rs:21:24
+   |
+LL |         println!("{}", foo.x);
+   |                        ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
+   = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+   = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+   = note: `#[deny(unaligned_references)]` on by default
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+Future incompatibility report: Future breakage diagnostic:
+error: reference to packed field is unaligned
+  --> $DIR/repr_packed.rs:21:24
+   |
+LL |         println!("{}", foo.x);
+   |                        ^^^^^
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #82523 <https://github.com/rust-lang/rust/issues/82523>
+   = note: fields of packed structs are not properly aligned, and creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
+   = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)
+   = note: `#[deny(unaligned_references)]` on by default
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs
new file mode 100644
index 00000000000..ed2d9a3de00
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.rs
@@ -0,0 +1,25 @@
+// edition:2021
+
+// Test that borrow checker error is accurate and that min capture pass of the
+// closure analysis is working as expected.
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 20 };
+
+    // `p` is captured via mutable borrow.
+    let mut c = || {
+        p.x += 10;
+        println!("{:?}", p);
+    };
+
+
+    println!("{:?}", p);
+    //~^ ERROR: cannot borrow `p` as immutable because it is also borrowed as mutable
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
new file mode 100644
index 00000000000..06157b2af7a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/simple-struct-min-capture.stderr
@@ -0,0 +1,21 @@
+error[E0502]: cannot borrow `p` as immutable because it is also borrowed as mutable
+  --> $DIR/simple-struct-min-capture.rs:22:22
+   |
+LL |     let mut c = || {
+   |                 -- mutable borrow occurs here
+LL |         p.x += 10;
+   |         --- capture is mutable because of use here
+LL |         println!("{:?}", p);
+   |                          - first borrow occurs due to use of `p` in closure
+...
+LL |     println!("{:?}", p);
+   |                      ^ immutable borrow occurs here
+LL |
+LL |     c();
+   |     - mutable borrow later used here
+   |
+   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0502`.
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs
new file mode 100644
index 00000000000..46b54846e32
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/union.rs
@@ -0,0 +1,25 @@
+// edition:2021
+
+// Test that we point to the correct location that results a union being captured.
+// Union is special because it can't be disjointly captured.
+
+union A {
+    y: u32,
+    x: (),
+}
+
+fn main() {
+    let mut a = A { y: 1 };
+    let mut c = || {
+    //~^ borrow of `a.y` occurs here
+        let _ = unsafe { &a.y };
+        let _ = &mut a;
+        //~^ borrow occurs due to use in closure
+        let _ = unsafe { &mut a.y };
+    };
+    a.y = 1;
+    //~^ cannot assign to `a.y` because it is borrowed [E0506]
+    //~| assignment to borrowed `a.y` occurs here
+    c();
+    //~^ borrow later used here
+}
diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/union.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/union.stderr
new file mode 100644
index 00000000000..7c34e2336c8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/diagnostics/union.stderr
@@ -0,0 +1,18 @@
+error[E0506]: cannot assign to `a.y` because it is borrowed
+  --> $DIR/union.rs:20:5
+   |
+LL |     let mut c = || {
+   |                 -- borrow of `a.y` occurs here
+...
+LL |         let _ = &mut a;
+   |                      - borrow occurs due to use in closure
+...
+LL |     a.y = 1;
+   |     ^^^^^^^ assignment to borrowed `a.y` occurs here
+...
+LL |     c();
+   |     - borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs b/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs
new file mode 100644
index 00000000000..269cf76e673
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.rs
@@ -0,0 +1,18 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+fn main() {
+    let s = format!("s");
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("This uses new capture analyysis to capture s={}", s);
+        //~^ NOTE: Capturing s[] -> ImmBorrow
+        //~| NOTE: Min Capture s[] -> ImmBorrow
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr b/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr
new file mode 100644
index 00000000000..b936c5ee35a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/feature-gate-capture_disjoint_fields.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/feature-gate-capture_disjoint_fields.rs:8:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("This uses new capture analyysis to capture s={}", s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing s[] -> ImmBorrow
+  --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69
+   |
+LL |         println!("This uses new capture analyysis to capture s={}", s);
+   |                                                                     ^
+
+error: Min Capture analysis includes:
+  --> $DIR/feature-gate-capture_disjoint_fields.rs:11:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("This uses new capture analyysis to capture s={}", s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture s[] -> ImmBorrow
+  --> $DIR/feature-gate-capture_disjoint_fields.rs:14:69
+   |
+LL |         println!("This uses new capture analyysis to capture s={}", s);
+   |                                                                     ^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.rs b/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.rs
new file mode 100644
index 00000000000..bfa3ebcd6d2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.rs
@@ -0,0 +1,41 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Filter {
+    div: i32,
+}
+impl Filter {
+    fn allowed(&self, x: i32) -> bool {
+        x % self.div == 1
+    }
+}
+
+struct Data {
+    filter: Filter,
+    list: Vec<i32>,
+}
+impl Data {
+    fn update(&mut self) {
+        // The closure passed to filter only captures self.filter,
+        // therefore mutating self.list is allowed.
+        self.list.retain(
+            #[rustc_capture_analysis]
+            |v| self.filter.allowed(*v),
+            //~^ ERROR: First Pass analysis includes:
+            //~| ERROR: Min Capture analysis includes:
+            //~| NOTE: Capturing self[Deref,(0, 0)] -> ImmBorrow
+            //~| NOTE: Min Capture self[Deref,(0, 0)] -> ImmBorrow
+        );
+    }
+}
+
+fn main() {
+    let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() };
+
+    for i in 1..10 {
+        d.list.push(i);
+    }
+
+    d.update();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr b/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr
new file mode 100644
index 00000000000..10e0d076b42
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/filter-on-struct-member.stderr
@@ -0,0 +1,26 @@
+error: First Pass analysis includes:
+  --> $DIR/filter-on-struct-member.rs:24:13
+   |
+LL |             |v| self.filter.allowed(*v),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Capturing self[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/filter-on-struct-member.rs:24:17
+   |
+LL |             |v| self.filter.allowed(*v),
+   |                 ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/filter-on-struct-member.rs:24:13
+   |
+LL |             |v| self.filter.allowed(*v),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Min Capture self[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/filter-on-struct-member.rs:24:17
+   |
+LL |             |v| self.filter.allowed(*v),
+   |                 ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/issue-87378.rs b/tests/ui/closures/2229_closure_analysis/issue-87378.rs
new file mode 100644
index 00000000000..75901a5718b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-87378.rs
@@ -0,0 +1,26 @@
+#![feature(rustc_attrs)]
+
+// edition:2021
+
+// Test that any precise capture on a union is truncated because it's unsafe to do so.
+
+union Union {
+    value: u64,
+}
+
+fn main() {
+    let u = Union { value: 42 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+       unsafe { u.value }
+        //~^ NOTE: Capturing u[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture u[] -> ImmBorrow
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-87378.stderr b/tests/ui/closures/2229_closure_analysis/issue-87378.stderr
new file mode 100644
index 00000000000..16c3f7c976d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-87378.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/issue-87378.rs:14:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/issue-87378.rs:17:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |        unsafe { u.value }
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing u[(0, 0)] -> ImmBorrow
+  --> $DIR/issue-87378.rs:20:17
+   |
+LL |        unsafe { u.value }
+   |                 ^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/issue-87378.rs:17:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |        unsafe { u.value }
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture u[] -> ImmBorrow
+  --> $DIR/issue-87378.rs:20:17
+   |
+LL |        unsafe { u.value }
+   |                 ^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/issue-87987.rs b/tests/ui/closures/2229_closure_analysis/issue-87987.rs
new file mode 100644
index 00000000000..d26343c33cf
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-87987.rs
@@ -0,0 +1,27 @@
+// run-pass
+// edition:2021
+
+struct Props {
+    field_1: u32, //~ WARNING: fields `field_1` and `field_2` are never read
+    field_2: u32,
+}
+
+fn main() {
+    // Test 1
+    let props_2 = Props { field_1: 1, field_2: 1 };
+
+    let _ = || {
+        let _: Props = props_2;
+    };
+
+    // Test 2
+    let mut arr = [1, 3, 4, 5];
+
+    let mref = &mut arr;
+
+    let _c = || match arr {
+        [_, _, _, _] => println!("A"),
+    };
+
+    println!("{:#?}", mref);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-87987.stderr b/tests/ui/closures/2229_closure_analysis/issue-87987.stderr
new file mode 100644
index 00000000000..5696a010c3f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-87987.stderr
@@ -0,0 +1,14 @@
+warning: fields `field_1` and `field_2` are never read
+  --> $DIR/issue-87987.rs:5:5
+   |
+LL | struct Props {
+   |        ----- fields in this struct
+LL |     field_1: u32,
+   |     ^^^^^^^
+LL |     field_2: u32,
+   |     ^^^^^^^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/issue-88118-2.rs b/tests/ui/closures/2229_closure_analysis/issue-88118-2.rs
new file mode 100644
index 00000000000..0cfb1a55bf2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-88118-2.rs
@@ -0,0 +1,24 @@
+// edition:2021
+// run-pass
+#![feature(if_let_guard)]
+#[allow(unused_must_use)]
+#[allow(dead_code)]
+
+fn print_error_count(registry: &Registry) {
+    |x: &Registry| {
+        match &x {
+            Registry if let _ = registry.try_find_description() => { }
+            //~^ WARNING: irrefutable `if let` guard pattern
+            _ => {}
+        }
+    };
+}
+
+struct Registry;
+impl Registry {
+    pub fn try_find_description(&self) {
+        unimplemented!()
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-88118-2.stderr b/tests/ui/closures/2229_closure_analysis/issue-88118-2.stderr
new file mode 100644
index 00000000000..b3cb558f976
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-88118-2.stderr
@@ -0,0 +1,12 @@
+warning: irrefutable `if let` guard pattern
+  --> $DIR/issue-88118-2.rs:10:29
+   |
+LL |             Registry if let _ = registry.try_find_description() => { }
+   |                             ^
+   |
+   = note: this pattern will always match, so the guard is useless
+   = help: consider removing the guard and adding a `let` inside the match arm
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/issue-88476.rs b/tests/ui/closures/2229_closure_analysis/issue-88476.rs
new file mode 100644
index 00000000000..f5906d30600
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-88476.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test that we can't move out of struct that impls `Drop`.
+
+
+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<i32>);
+
+    impl Drop for Foo {
+        fn drop(self: &mut Foo) {}
+    }
+
+    let f = Foo(Rc::new(1));
+    let x = #[rustc_capture_analysis] move || {
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{:?}", f.0);
+        //~^ NOTE: Capturing f[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture f[] -> ByValue
+    };
+
+    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`.
+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 = #[rustc_capture_analysis] move || {
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", character.hp)
+        //~^ NOTE: Capturing character[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture character[(0, 0)] -> ByValue
+    };
+
+    c();
+
+    println!("{}", character.name);
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-88476.stderr b/tests/ui/closures/2229_closure_analysis/issue-88476.stderr
new file mode 100644
index 00000000000..c7c9ecbbb0e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-88476.stderr
@@ -0,0 +1,97 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/issue-88476.rs:20:13
+   |
+LL |     let x = #[rustc_capture_analysis] move || {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/issue-88476.rs:47:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/issue-88476.rs:20:39
+   |
+LL |       let x = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing f[(0, 0)] -> ImmBorrow
+  --> $DIR/issue-88476.rs:25:26
+   |
+LL |         println!("{:?}", f.0);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/issue-88476.rs:20:39
+   |
+LL |       let x = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture f[] -> ByValue
+  --> $DIR/issue-88476.rs:25:26
+   |
+LL |         println!("{:?}", f.0);
+   |                          ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/issue-88476.rs:47:39
+   |
+LL |       let c = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing character[(0, 0)] -> ImmBorrow
+  --> $DIR/issue-88476.rs:52:24
+   |
+LL |         println!("{}", character.hp)
+   |                        ^^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/issue-88476.rs:47:39
+   |
+LL |       let c = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture character[(0, 0)] -> ByValue
+  --> $DIR/issue-88476.rs:52:24
+   |
+LL |         println!("{}", character.hp)
+   |                        ^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/issue-89606.rs b/tests/ui/closures/2229_closure_analysis/issue-89606.rs
new file mode 100644
index 00000000000..1bb6aa40f06
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-89606.rs
@@ -0,0 +1,40 @@
+// Regression test for #89606. Used to ICE.
+//
+// check-pass
+// revisions: twenty_eighteen twenty_twentyone
+// [twenty_eighteen]compile-flags: --edition 2018
+// [twenty_twentyone]compile-flags: --edition 2021
+
+struct S<'a>(Option<&'a mut i32>);
+
+fn by_ref(s: &mut S<'_>) {
+    (|| {
+        let S(_o) = s;
+        s.0 = None;
+    })();
+}
+
+fn by_value(s: S<'_>) {
+    (|| {
+        let S(ref _o) = s;
+        let _g = s.0;
+    })();
+}
+
+struct V<'a>((Option<&'a mut i32>,));
+
+fn nested(v: &mut V<'_>) {
+    (|| {
+        let V((_o,)) = v;
+        v.0 = (None, );
+    })();
+}
+
+fn main() {
+    let mut s = S(None);
+    by_ref(&mut s);
+    by_value(s);
+
+    let mut v = V((None, ));
+    nested(&mut v);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/issue-90465.fixed b/tests/ui/closures/2229_closure_analysis/issue-90465.fixed
new file mode 100644
index 00000000000..4e0b18e7233
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-90465.fixed
@@ -0,0 +1,35 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+    struct Foo(u32);
+    impl Drop for Foo {
+        fn drop(&mut self) {
+            println!("dropped {}", self.0);
+        }
+    }
+
+    let f0 = Foo(0);
+    let f1 = Foo(1);
+
+    let c0 = move || {
+        let _ = &f0;
+        //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+        //~| NOTE for more information
+        let _ = f0;
+        //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+    };
+
+    let c1 = move || {
+        let _ = &f1;
+    };
+
+    println!("dropping 0");
+    drop(c0);
+    println!("dropping 1");
+    drop(c1);
+    println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/tests/ui/closures/2229_closure_analysis/issue-90465.rs b/tests/ui/closures/2229_closure_analysis/issue-90465.rs
new file mode 100644
index 00000000000..466e6dbabc5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-90465.rs
@@ -0,0 +1,34 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE lint level is defined here
+
+fn main() {
+    struct Foo(u32);
+    impl Drop for Foo {
+        fn drop(&mut self) {
+            println!("dropped {}", self.0);
+        }
+    }
+
+    let f0 = Foo(0);
+    let f1 = Foo(1);
+
+    let c0 = move || {
+        //~^ ERROR changes to closure capture in Rust 2021 will affect drop order
+        //~| NOTE for more information
+        let _ = f0;
+        //~^ NOTE in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+    };
+
+    let c1 = move || {
+        let _ = &f1;
+    };
+
+    println!("dropping 0");
+    drop(c0);
+    println!("dropping 1");
+    drop(c1);
+    println!("dropped all");
+}
+//~^ NOTE in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
diff --git a/tests/ui/closures/2229_closure_analysis/issue-90465.stderr b/tests/ui/closures/2229_closure_analysis/issue-90465.stderr
new file mode 100644
index 00000000000..c1679c6b637
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-90465.stderr
@@ -0,0 +1,26 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/issue-90465.rs:17:14
+   |
+LL |     let c0 = move || {
+   |              ^^^^^^^
+...
+LL |         let _ = f0;
+   |                 -- in Rust 2018, this causes the closure to capture `f0`, but in Rust 2021, it has no effect
+...
+LL | }
+   | - in Rust 2018, `f0` is dropped here along with the closure, but in Rust 2021 `f0` is not part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/issue-90465.rs:3:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `f0` to be fully captured
+   |
+LL ~     let c0 = move || {
+LL +         let _ = &f0;
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs b/tests/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
new file mode 100644
index 00000000000..a3b17755fac
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue-92724-needsdrop-query-cycle.rs
@@ -0,0 +1,14 @@
+// ICEs if checking if there is a significant destructor causes a query cycle
+// check-pass
+
+#![warn(rust_2021_incompatible_closure_captures)]
+pub struct Foo(Bar);
+pub struct Bar(Baz);
+pub struct Baz(Vec<Foo>);
+
+impl Foo {
+    pub fn baz(self, v: Baz) -> Baz {
+        (|| v)()
+    }
+}
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/issue_88118.rs b/tests/ui/closures/2229_closure_analysis/issue_88118.rs
new file mode 100644
index 00000000000..453b7e04a36
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/issue_88118.rs
@@ -0,0 +1,15 @@
+// Regression test for #88118. Used to ICE.
+//
+// check-pass
+
+#![allow(incomplete_features)]
+#![feature(capture_disjoint_fields)]
+
+fn foo<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) {
+    Box::new(move |value| {
+        (|_| handler.clone()())(value);
+        None
+    }) as Box<dyn Fn(i32) -> Option<i32>>;
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs b/tests/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs
new file mode 100644
index 00000000000..4060c409355
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/auxiliary/match_non_exhaustive_lib.rs
@@ -0,0 +1,10 @@
+#[non_exhaustive]
+pub enum E1 {}
+
+#[non_exhaustive]
+pub enum E2 { A, B }
+
+#[non_exhaustive]
+pub enum E3 { C }
+
+pub enum E4 { D }
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-87097.rs b/tests/ui/closures/2229_closure_analysis/match/issue-87097.rs
new file mode 100644
index 00000000000..815fc0a719c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-87097.rs
@@ -0,0 +1,35 @@
+// run-pass
+// edition:2021
+
+enum Variant {
+    A,
+    B, //~ WARNING: variant `B` is never constructed
+}
+
+struct A {
+    field: Variant,
+}
+
+fn discriminant_is_a_ref() {
+    let here = A { field: Variant::A };
+    let out_ref = &here.field;
+
+    || match out_ref { //~ WARNING: unused closure that must be used
+        Variant::A => (),
+        Variant::B => (),
+    };
+}
+
+fn discriminant_is_a_field() {
+    let here = A { field: Variant::A };
+
+    || match here.field { //~ WARNING: unused closure that must be used
+        Variant::A => (),
+        Variant::B => (),
+    };
+}
+
+fn main() {
+    discriminant_is_a_ref();
+    discriminant_is_a_field();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-87097.stderr b/tests/ui/closures/2229_closure_analysis/match/issue-87097.stderr
new file mode 100644
index 00000000000..39ec71ba22a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-87097.stderr
@@ -0,0 +1,36 @@
+warning: variant `B` is never constructed
+  --> $DIR/issue-87097.rs:6:5
+   |
+LL | enum Variant {
+   |      ------- variant in this enum
+LL |     A,
+LL |     B,
+   |     ^
+   |
+   = note: `#[warn(dead_code)]` on by default
+
+warning: unused closure that must be used
+  --> $DIR/issue-87097.rs:17:5
+   |
+LL | /     || match out_ref {
+LL | |         Variant::A => (),
+LL | |         Variant::B => (),
+LL | |     };
+   | |_____^
+   |
+   = note: closures are lazy and do nothing unless called
+   = note: `#[warn(unused_must_use)]` on by default
+
+warning: unused closure that must be used
+  --> $DIR/issue-87097.rs:26:5
+   |
+LL | /     || match here.field {
+LL | |         Variant::A => (),
+LL | |         Variant::B => (),
+LL | |     };
+   | |_____^
+   |
+   = note: closures are lazy and do nothing unless called
+
+warning: 3 warnings emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-87426.rs b/tests/ui/closures/2229_closure_analysis/match/issue-87426.rs
new file mode 100644
index 00000000000..74506979a28
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-87426.rs
@@ -0,0 +1,14 @@
+// run-pass
+// edition:2021
+
+pub fn foo() {
+    let ref_x_ck = 123;
+    let _y = || match ref_x_ck {
+        2_000_000..=3_999_999 => { println!("A")}
+        _ => { println!("B")}
+    };
+}
+
+fn main() {
+    foo();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-87988.rs b/tests/ui/closures/2229_closure_analysis/match/issue-87988.rs
new file mode 100644
index 00000000000..27e7fabf11a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-87988.rs
@@ -0,0 +1,19 @@
+// run-pass
+// edition:2021
+
+const LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: i32 = 0x01;
+const LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: i32 = 0x02;
+
+pub fn hotplug_callback(event: i32) {
+    let _ = || {
+        match event {
+            LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED => (),
+            LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT => (),
+            _ => (),
+        };
+    };
+}
+
+fn main() {
+    hotplug_callback(1);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-88331.rs b/tests/ui/closures/2229_closure_analysis/match/issue-88331.rs
new file mode 100644
index 00000000000..0a6d71c68ae
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-88331.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode(pub u8);
+
+impl Opcode {
+    pub const OP1: Opcode = Opcode(0x1);
+}
+
+pub fn example1(msg_type: Opcode) -> impl FnMut(&[u8]) {
+    move |i| match msg_type {
+    //~^ ERROR: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+        Opcode::OP1 => unimplemented!(),
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Opcode2(Opcode);
+
+impl Opcode2 {
+    pub const OP2: Opcode2 = Opcode2(Opcode(0x1));
+}
+
+
+pub fn example2(msg_type: Opcode2) -> impl FnMut(&[u8]) {
+
+    move |i| match msg_type {
+    //~^ ERROR: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+        Opcode2::OP2=> unimplemented!(),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/match/issue-88331.stderr b/tests/ui/closures/2229_closure_analysis/match/issue-88331.stderr
new file mode 100644
index 00000000000..7e22defa98d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/issue-88331.stderr
@@ -0,0 +1,39 @@
+error[E0004]: non-exhaustive patterns: `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+  --> $DIR/issue-88331.rs:11:20
+   |
+LL |     move |i| match msg_type {
+   |                    ^^^^^^^^ patterns `Opcode(0_u8)` and `Opcode(2_u8..=u8::MAX)` not covered
+   |
+note: `Opcode` defined here
+  --> $DIR/issue-88331.rs:4:12
+   |
+LL | pub struct Opcode(pub u8);
+   |            ^^^^^^
+   = note: the matched value is of type `Opcode`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~         Opcode::OP1 => unimplemented!(),
+LL ~         Opcode(0_u8) | Opcode(2_u8..=u8::MAX) => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+  --> $DIR/issue-88331.rs:27:20
+   |
+LL |     move |i| match msg_type {
+   |                    ^^^^^^^^ patterns `Opcode2(Opcode(0_u8))` and `Opcode2(Opcode(2_u8..=u8::MAX))` not covered
+   |
+note: `Opcode2` defined here
+  --> $DIR/issue-88331.rs:18:12
+   |
+LL | pub struct Opcode2(Opcode);
+   |            ^^^^^^^
+   = note: the matched value is of type `Opcode2`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+   |
+LL ~         Opcode2::OP2=> unimplemented!(),
+LL ~         Opcode2(Opcode(0_u8)) | Opcode2(Opcode(2_u8..=u8::MAX)) => todo!(),
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs
new file mode 100644
index 00000000000..914ebbe26a5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_1.rs
@@ -0,0 +1,44 @@
+// run-pass
+// edition:2021
+
+const PATTERN_REF: &str = "Hello World";
+const NUMBER: i32 = 30;
+const NUMBER_POINTER: *const i32 = &NUMBER;
+
+pub fn edge_case_ref(event: &str) {
+    let _ = || {
+        match event {
+            PATTERN_REF => (),
+            _ => (),
+        };
+    };
+}
+
+pub fn edge_case_str(event: String) {
+    let _ = || {
+        match event.as_str() {
+            "hello" => (),
+            _ => (),
+        };
+    };
+}
+
+pub fn edge_case_raw_ptr(event: *const i32) {
+    let _ = || {
+        match event {
+            NUMBER_POINTER => (),
+            _ => (),
+        };
+    };
+}
+
+pub fn edge_case_char(event: char) {
+    let _ = || {
+        match event {
+            'a' => (),
+            _ => (),
+        };
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs
new file mode 100644
index 00000000000..ae724f9c3cc
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.rs
@@ -0,0 +1,37 @@
+// edition:2021
+
+enum SingleVariant {
+    A
+}
+
+struct TestStruct {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
+fn edge_case_if() {
+    let sv = SingleVariant::A;
+    let condition = true;
+    // sv should not be captured as it is a SingleVariant
+    let _a = || {
+        match sv {
+            SingleVariant::A if condition => (),
+            _ => ()
+        }
+    };
+    let mut mut_sv = sv;
+    _a();
+
+    // ts should be captured
+    let ts = TestStruct { x: 1, y: 1, z: 1 };
+    let _b = || { match ts {
+        TestStruct{ x: 1, .. } => (),
+        _ => ()
+    }};
+    let mut mut_ts = ts;
+    //~^ ERROR: cannot move out of `ts` because it is borrowed
+    _b();
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr
new file mode 100644
index 00000000000..1e42d73c62b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/match-edge-cases_2.stderr
@@ -0,0 +1,17 @@
+error[E0505]: cannot move out of `ts` because it is borrowed
+  --> $DIR/match-edge-cases_2.rs:32:22
+   |
+LL |     let _b = || { match ts {
+   |              --         -- borrow occurs due to use in closure
+   |              |
+   |              borrow of `ts` occurs here
+...
+LL |     let mut mut_ts = ts;
+   |                      ^^ move out of `ts` occurs here
+LL |
+LL |     _b();
+   |     -- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0505`.
diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs
new file mode 100644
index 00000000000..972c24c23b0
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.rs
@@ -0,0 +1,54 @@
+// edition:2021
+
+// aux-build:match_non_exhaustive_lib.rs
+
+/* The error message for non-exhaustive matches on non-local enums
+ * marked as non-exhaustive should mention the fact that the enum
+ * is marked as non-exhaustive (issue #85227).
+ */
+
+// Ignore non_exhaustive in the same crate
+#[non_exhaustive]
+enum L1 { A, B }
+enum L2 { C }
+
+extern crate match_non_exhaustive_lib;
+use match_non_exhaustive_lib::{E1, E2, E3, E4};
+
+fn foo() -> (L1, L2) {todo!()}
+fn bar() -> (E1, E2, E3, E4) {todo!()}
+
+fn main() {
+    let (l1, l2) = foo();
+    // No error for enums defined in this crate
+    let _a = || { match l1 { L1::A => (), L1::B => () } };
+    // (except if the match is already non-exhaustive)
+    let _b = || { match l1 { L1::A => () } };
+    //~^ ERROR: non-exhaustive patterns: `L1::B` not covered [E0004]
+
+    // l2 should not be captured as it is a non-exhaustive SingleVariant
+    // defined in this crate
+    let _c = || { match l2 { L2::C => (), _ => () }  };
+    let mut mut_l2 = l2;
+    _c();
+
+    // E1 is not visibly uninhabited from here
+    let (e1, e2, e3, e4) = bar();
+    let _d = || { match e1 {} };
+    //~^ ERROR: non-exhaustive patterns: type `E1` is non-empty [E0004]
+    let _e = || { match e2 { E2::A => (), E2::B => () } };
+    //~^ ERROR: non-exhaustive patterns: `_` not covered [E0004]
+    let _f = || { match e2 { E2::A => (), E2::B => (), _ => () }  };
+
+    // e3 should be captured as it is a non-exhaustive SingleVariant
+    // defined in another crate
+    let _g = || { match e3 { E3::C => (), _ => () }  };
+    let mut mut_e3 = e3;
+    //~^ ERROR: cannot move out of `e3` because it is borrowed
+    _g();
+
+    // e4 should not be captured as it is a SingleVariant
+    let _h = || { match e4 { E4::D => (), _ => () }  };
+    let mut mut_e4 = e4;
+    _h();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
new file mode 100644
index 00000000000..3a5fad15421
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr
@@ -0,0 +1,70 @@
+error[E0004]: non-exhaustive patterns: `L1::B` not covered
+  --> $DIR/non-exhaustive-match.rs:26:25
+   |
+LL |     let _b = || { match l1 { L1::A => () } };
+   |                         ^^ pattern `L1::B` not covered
+   |
+note: `L1` defined here
+  --> $DIR/non-exhaustive-match.rs:12:14
+   |
+LL | enum L1 { A, B }
+   |      --      ^ not covered
+   = note: the matched value is of type `L1`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |     let _b = || { match l1 { L1::A => (), L1::B => todo!() } };
+   |                                         ++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: type `E1` is non-empty
+  --> $DIR/non-exhaustive-match.rs:37:25
+   |
+LL |     let _d = || { match e1 {} };
+   |                         ^^
+   |
+note: `E1` defined here
+  --> $DIR/auxiliary/match_non_exhaustive_lib.rs:2:1
+   |
+LL | pub enum E1 {}
+   | ^^^^^^^^^^^
+   = note: the matched value is of type `E1`, which is marked as non-exhaustive
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     let _d = || { match e1 {
+LL +         _ => todo!(),
+LL ~     } };
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/non-exhaustive-match.rs:39:25
+   |
+LL |     let _e = || { match e2 { E2::A => (), E2::B => () } };
+   |                         ^^ pattern `_` not covered
+   |
+note: `E2` defined here
+  --> $DIR/auxiliary/match_non_exhaustive_lib.rs:5:1
+   |
+LL | pub enum E2 { A, B }
+   | ^^^^^^^^^^^
+   = note: the matched value is of type `E2`, which is marked as non-exhaustive
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+   |
+LL |     let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } };
+   |                                                      ++++++++++++++
+
+error[E0505]: cannot move out of `e3` because it is borrowed
+  --> $DIR/non-exhaustive-match.rs:46:22
+   |
+LL |     let _g = || { match e3 { E3::C => (), _ => () }  };
+   |              --         -- borrow occurs due to use in closure
+   |              |
+   |              borrow of `e3` occurs here
+LL |     let mut mut_e3 = e3;
+   |                      ^^ move out of `e3` occurs here
+LL |
+LL |     _g();
+   |     -- borrow later used here
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0004, E0505.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs
new file mode 100644
index 00000000000..69cf920de94
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.rs
@@ -0,0 +1,79 @@
+// edition:2021
+
+#![feature(never_type)]
+
+// Should fake read the discriminant and throw an error
+fn test1() {
+    let x: !;
+    let c1 = || match x { };
+    //~^ ERROR E0381
+}
+
+// Should fake read the discriminant and throw an error
+fn test2() {
+    let x: !;
+    let c2 = || match x { _ => () };
+    //~^ ERROR E0381
+}
+
+// Testing single variant patterns
+enum SingleVariant {
+    Points(u32)
+}
+
+// Should fake read the discriminant and throw an error
+fn test3() {
+    let variant: !;
+    let c = || {
+    //~^ ERROR E0381
+        match variant {
+            SingleVariant::Points(_) => {}
+        }
+    };
+    c();
+}
+
+// Should fake read the discriminant and throw an error
+fn test4() {
+    let variant: !;
+    let c = || { //~ ERROR E0381
+        match variant {
+            SingleVariant::Points(a) => {
+                println!("{:?}", a);
+            }
+        }
+    };
+    c();
+}
+
+fn test5() {
+    let t: !;
+    let g: !;
+
+    let a = || {
+        match g { }; //~ ERROR E0381
+        let c = ||  {
+            match t { }; //~ ERROR E0381
+        };
+
+        c();
+    };
+
+}
+
+// Should fake read the discriminant and throw an error
+fn test6() {
+    let x: u8;
+    let c1 = || match x { };
+    //~^ ERROR E0381
+    //~| ERROR: non-exhaustive patterns: type `u8` is non-empty
+}
+
+fn main() {
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+    test6();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
new file mode 100644
index 00000000000..ad061d93cb2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/pattern-matching-should-fail.stderr
@@ -0,0 +1,88 @@
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+  --> $DIR/pattern-matching-should-fail.rs:67:23
+   |
+LL |     let c1 = || match x { };
+   |                       ^
+   |
+   = note: the matched value is of type `u8`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     let c1 = || match x {
+LL +         _ => todo!(),
+LL ~     };
+   |
+
+error[E0381]: used binding `x` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:8:23
+   |
+LL |     let x: !;
+   |         - binding declared here but left uninitialized
+LL |     let c1 = || match x { };
+   |                       ^ `x` used here but it isn't initialized
+
+error[E0381]: used binding `x` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:15:14
+   |
+LL |     let x: !;
+   |         - binding declared here but left uninitialized
+LL |     let c2 = || match x { _ => () };
+   |              ^^       - borrow occurs due to use in closure
+   |              |
+   |              `x` used here but it isn't initialized
+
+error[E0381]: used binding `variant` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:27:13
+   |
+LL |     let variant: !;
+   |         ------- binding declared here but left uninitialized
+LL |     let c = || {
+   |             ^^ `variant` used here but it isn't initialized
+LL |
+LL |         match variant {
+   |               ------- borrow occurs due to use in closure
+
+error[E0381]: used binding `variant` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:39:13
+   |
+LL |     let variant: !;
+   |         ------- binding declared here but left uninitialized
+LL |     let c = || {
+   |             ^^ `variant` used here but it isn't initialized
+LL |         match variant {
+   |               ------- borrow occurs due to use in closure
+
+error[E0381]: used binding `g` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:54:15
+   |
+LL |     let g: !;
+   |         - binding declared here but left uninitialized
+...
+LL |         match g { };
+   |               ^ `g` used here but it isn't initialized
+
+error[E0381]: used binding `t` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:56:19
+   |
+LL |     let t: !;
+   |         - binding declared here but left uninitialized
+...
+LL |             match t { };
+   |                   ^ `t` used here but it isn't initialized
+
+error[E0381]: used binding `x` isn't initialized
+  --> $DIR/pattern-matching-should-fail.rs:67:23
+   |
+LL |     let x: u8;
+   |         - binding declared here but left uninitialized
+LL |     let c1 = || match x { };
+   |                       ^ `x` used here but it isn't initialized
+   |
+help: consider assigning a value
+   |
+LL |     let x: u8 = 0;
+   |               +++
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0004, E0381.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs
new file mode 100644
index 00000000000..56f5ac44db0
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.rs
@@ -0,0 +1,137 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Should capture the discriminant since a variant of a multivariant enum is
+// mentioned in the match arm; the discriminant is captured by the closure regardless
+// of if it creates a binding
+fn test_1_should_capture() {
+    let variant = Some(2229);
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        match variant {
+        //~^ NOTE: Capturing variant[] -> ImmBorrow
+        //~| NOTE: Min Capture variant[] -> ImmBorrow
+            Some(_) => {}
+            _ => {}
+        }
+    };
+    c();
+}
+
+// Should not capture the discriminant since only a wildcard is mentioned in the
+// match arm
+fn test_2_should_not_capture() {
+    let variant = Some(2229);
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+        match variant {
+            _ => {}
+        }
+    };
+    c();
+}
+
+// Testing single variant patterns
+enum SingleVariant {
+    Points(u32)
+}
+
+// Should not capture the discriminant since the single variant mentioned
+// in the match arm does not trigger a binding
+fn test_3_should_not_capture_single_variant() {
+    let variant = SingleVariant::Points(1);
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+        match variant {
+            SingleVariant::Points(_) => {}
+        }
+    };
+    c();
+}
+
+// Should not capture the discriminant since the single variant mentioned
+// in the match arm does not trigger a binding
+fn test_6_should_capture_single_variant() {
+    let variant = SingleVariant::Points(1);
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        match variant {
+            //~^ NOTE: Capturing variant[] -> ImmBorrow
+            //~| NOTE: Capturing variant[(0, 0)] -> ImmBorrow
+            //~| NOTE: Min Capture variant[] -> ImmBorrow
+            SingleVariant::Points(a) => {
+                println!("{:?}", a);
+            }
+        }
+    };
+    c();
+}
+
+// Should not capture the discriminant since only wildcards are mentioned in the
+// match arm
+fn test_4_should_not_capture_array() {
+    let array: [i32; 3] = [0; 3];
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+        match array {
+            [_,_,_] => {}
+        }
+    };
+    c();
+}
+
+// Testing MultiVariant patterns
+enum MVariant {
+    A,
+    B,
+    C,
+}
+
+// Should capture the discriminant since a variant of the multi variant enum is
+// mentioned in the match arm; the discriminant is captured by the closure
+// regardless of if it creates a binding
+fn test_5_should_capture_multi_variant() {
+    let variant = MVariant::A;
+    let c =  #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        match variant {
+        //~^ NOTE: Capturing variant[] -> ImmBorrow
+        //~| NOTE: Min Capture variant[] -> ImmBorrow
+            MVariant::A => {}
+            _ => {}
+        }
+    };
+    c();
+}
+
+fn main() {
+    test_1_should_capture();
+    test_2_should_not_capture();
+    test_3_should_not_capture_single_variant();
+    test_6_should_capture_single_variant();
+    test_4_should_not_capture_array();
+    test_5_should_capture_multi_variant();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr
new file mode 100644
index 00000000000..46081333395
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/match/patterns-capture-analysis.stderr
@@ -0,0 +1,203 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:10:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:31:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:52:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:68:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:90:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/patterns-capture-analysis.rs:114:14
+   |
+LL |     let c =  #[rustc_capture_analysis]
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:14:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Capturing variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:17:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:14:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Min Capture variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:17:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:34:5
+   |
+LL | /     || {
+LL | |
+LL | |         match variant {
+LL | |             _ => {}
+LL | |         }
+LL | |     };
+   | |_____^
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:55:5
+   |
+LL | /     || {
+LL | |
+LL | |         match variant {
+LL | |             SingleVariant::Points(_) => {}
+LL | |         }
+LL | |     };
+   | |_____^
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:71:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Capturing variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:74:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+note: Capturing variant[(0, 0)] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:74:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:71:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Min Capture variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:74:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:93:5
+   |
+LL | /     || {
+LL | |
+LL | |         match array {
+LL | |             [_,_,_] => {}
+LL | |         }
+LL | |     };
+   | |_____^
+
+error: First Pass analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:117:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Capturing variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:120:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/patterns-capture-analysis.rs:117:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         match variant {
+...  |
+LL | |         }
+LL | |     };
+   | |_____^
+   |
+note: Min Capture variant[] -> ImmBorrow
+  --> $DIR/patterns-capture-analysis.rs:120:15
+   |
+LL |         match variant {
+   |               ^^^^^^^
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
new file mode 100644
index 00000000000..26703fbf811
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed
@@ -0,0 +1,88 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+/* Test Send Trait Migration */
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+fn test_send_trait() {
+    let mut f = 10;
+    let fptr = SendPointer(&mut f as *mut i32);
+    thread::spawn(move || { let _ = &fptr; unsafe {
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr` to be fully captured
+        *fptr.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+    } });
+}
+
+/* Test Sync Trait Migration */
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_sync_trait() {
+    let mut f = 10;
+    let f = CustomInt(&mut f as *mut i32);
+    let fptr = SyncPointer(f);
+    thread::spawn(move || { let _ = &fptr; unsafe {
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr` to be fully captured
+        *fptr.0.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+    } });
+}
+
+/* Test Clone Trait Migration */
+struct S(Foo);
+struct T(i32);
+
+struct U(S, T);
+
+impl Clone for U {
+    fn clone(&self) -> Self {
+        U(S(Foo(0)), T(0))
+    }
+}
+
+fn test_clone_trait() {
+    let f = U(S(Foo(0)), T(0));
+    let c = || {
+        let _ = &f;
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f` to be fully captured
+        let f_1 = f.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+        println!("{:?}", f_1.0);
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+
+fn main() {
+    test_send_trait();
+    test_sync_trait();
+    test_clone_trait();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
new file mode 100644
index 00000000000..932db51d437
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.rs
@@ -0,0 +1,87 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+/* Test Send Trait Migration */
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+fn test_send_trait() {
+    let mut f = 10;
+    let fptr = SendPointer(&mut f as *mut i32);
+    thread::spawn(move || unsafe {
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr` to be fully captured
+        *fptr.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+    });
+}
+
+/* Test Sync Trait Migration */
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_sync_trait() {
+    let mut f = 10;
+    let f = CustomInt(&mut f as *mut i32);
+    let fptr = SyncPointer(f);
+    thread::spawn(move || unsafe {
+        //~^ ERROR: changes to closure capture
+        //~| NOTE: in Rust 2018, this closure implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr` to be fully captured
+        *fptr.0.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+    });
+}
+
+/* Test Clone Trait Migration */
+struct S(Foo);
+struct T(i32);
+
+struct U(S, T);
+
+impl Clone for U {
+    fn clone(&self) -> Self {
+        U(S(Foo(0)), T(0))
+    }
+}
+
+fn test_clone_trait() {
+    let f = U(S(Foo(0)), T(0));
+    let c = || {
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f` to be fully captured
+        let f_1 = f.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+        println!("{:?}", f_1.0);
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+
+fn main() {
+    test_send_trait();
+    test_sync_trait();
+    test_clone_trait();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
new file mode 100644
index 00000000000..3a42cc8b843
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr
@@ -0,0 +1,67 @@
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/auto_traits.rs:22:19
+   |
+LL |     thread::spawn(move || unsafe {
+   |                   ^^^^^^^ in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0` does not implement `Send`
+...
+LL |         *fptr.0 = 20;
+   |         ------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/auto_traits.rs:2:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `fptr` to be fully captured
+   |
+LL ~     thread::spawn(move || { let _ = &fptr; unsafe {
+LL |
+ ...
+LL |
+LL ~     } });
+   |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/auto_traits.rs:42:19
+   |
+LL |     thread::spawn(move || unsafe {
+   |                   ^^^^^^^
+   |                   |
+   |                   in Rust 2018, this closure implements `Send` as `fptr` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr` is not fully captured and `fptr.0.0` does not implement `Send`
+   |                   in Rust 2018, this closure implements `Sync` as `fptr` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr` is not fully captured and `fptr.0.0` does not implement `Sync`
+...
+LL |         *fptr.0.0 = 20;
+   |         --------- in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `fptr` to be fully captured
+   |
+LL ~     thread::spawn(move || { let _ = &fptr; unsafe {
+LL |
+ ...
+LL |
+LL ~     } });
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/auto_traits.rs:67:13
+   |
+LL |     let c = || {
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f` is not fully captured and `f.1` does not implement `Clone`
+...
+LL |         let f_1 = f.1;
+   |                   --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.1`
+...
+LL | }
+   | - in Rust 2018, `f` is dropped here, but in Rust 2021, only `f.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &f;
+   |
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
new file mode 100644
index 00000000000..9a6db588c8b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.fixed
@@ -0,0 +1,33 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(rust_2021_compatibility)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+macro_rules! m {
+    (@ $body:expr) => {{
+        let f = || $body;
+        //~^ WARNING: drop order
+        f();
+    }};
+    ($body:block) => {{
+        m!(@ $body);
+    }};
+}
+
+fn main() {
+    let a = (Foo(0), Foo(1));
+    m!({
+        let _ = &a;
+        //~^ HELP: add a dummy
+        let x = a.0;
+        println!("{:?}", x);
+    });
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
new file mode 100644
index 00000000000..08cc24b4b3f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.rs
@@ -0,0 +1,32 @@
+// run-rustfix
+// edition:2018
+// check-pass
+#![warn(rust_2021_compatibility)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+macro_rules! m {
+    (@ $body:expr) => {{
+        let f = || $body;
+        //~^ WARNING: drop order
+        f();
+    }};
+    ($body:block) => {{
+        m!(@ $body);
+    }};
+}
+
+fn main() {
+    let a = (Foo(0), Foo(1));
+    m!({
+        //~^ HELP: add a dummy
+        let x = a.0;
+        println!("{:?}", x);
+    });
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
new file mode 100644
index 00000000000..bb17e3a34af
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/closure-body-macro-fragment.stderr
@@ -0,0 +1,33 @@
+warning: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/closure-body-macro-fragment.rs:16:17
+   |
+LL |           let f = || $body;
+   |                   ^^
+...
+LL |       }};
+   |       - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
+...
+LL | /     m!({
+LL | |
+LL | |         let x = a.0;
+   | |                 --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
+LL | |         println!("{:?}", x);
+LL | |     });
+   | |______- in this macro invocation
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/closure-body-macro-fragment.rs:4:9
+   |
+LL | #![warn(rust_2021_compatibility)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `#[warn(rust_2021_incompatible_closure_captures)]` implied by `#[warn(rust_2021_compatibility)]`
+   = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: add a dummy let to cause `a` to be fully captured
+   |
+LL ~     m!({
+LL +         let _ = &a;
+   |
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed
new file mode 100644
index 00000000000..2652bf5988e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed
@@ -0,0 +1,38 @@
+// run-pass
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused)]
+
+// Test cases for types that implement an insignificant drop (stlib defined)
+
+macro_rules! test_insig_dtor_for_type {
+    ($t: ty, $disambiguator: ident) => {
+        mod $disambiguator {
+            use std::collections::*;
+            use std::rc::Rc;
+            use std::sync::Mutex;
+
+            fn test_for_type(t: $t) {
+                let tup = (Mutex::new(0), t);
+
+                let _c = || tup.0;
+            }
+        }
+    };
+}
+
+test_insig_dtor_for_type!(i32, prim_i32);
+test_insig_dtor_for_type!(Vec<i32>, vec_i32);
+test_insig_dtor_for_type!(String, string);
+test_insig_dtor_for_type!(Vec<String>, vec_string);
+test_insig_dtor_for_type!(HashMap<String, String>, hash_map);
+test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map);
+test_insig_dtor_for_type!(LinkedList<String>, linked_list);
+test_insig_dtor_for_type!(Rc<i32>, rc_i32);
+test_insig_dtor_for_type!(Rc<String>, rc_string);
+test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter);
+test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter);
+test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter);
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
new file mode 100644
index 00000000000..2652bf5988e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs
@@ -0,0 +1,38 @@
+// run-pass
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused)]
+
+// Test cases for types that implement an insignificant drop (stlib defined)
+
+macro_rules! test_insig_dtor_for_type {
+    ($t: ty, $disambiguator: ident) => {
+        mod $disambiguator {
+            use std::collections::*;
+            use std::rc::Rc;
+            use std::sync::Mutex;
+
+            fn test_for_type(t: $t) {
+                let tup = (Mutex::new(0), t);
+
+                let _c = || tup.0;
+            }
+        }
+    };
+}
+
+test_insig_dtor_for_type!(i32, prim_i32);
+test_insig_dtor_for_type!(Vec<i32>, vec_i32);
+test_insig_dtor_for_type!(String, string);
+test_insig_dtor_for_type!(Vec<String>, vec_string);
+test_insig_dtor_for_type!(HashMap<String, String>, hash_map);
+test_insig_dtor_for_type!(BTreeMap<String, i32>, btree_map);
+test_insig_dtor_for_type!(LinkedList<String>, linked_list);
+test_insig_dtor_for_type!(Rc<i32>, rc_i32);
+test_insig_dtor_for_type!(Rc<String>, rc_string);
+test_insig_dtor_for_type!(std::vec::IntoIter<String>, vec_into_iter);
+test_insig_dtor_for_type!(btree_map::IntoIter<String, String>, btree_map_into_iter);
+test_insig_dtor_for_type!(std::array::IntoIter<String, 5>, array_into_iter);
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed
new file mode 100644
index 00000000000..d985e3bb9ec
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.fixed
@@ -0,0 +1,76 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+use std::sync::Mutex;
+
+    #[rustc_insignificant_dtor]
+struct InsignificantDropPoint {
+    x: i32,
+    y: Mutex<i32>,
+}
+
+impl Drop for InsignificantDropPoint {
+    fn drop(&mut self) {}
+}
+
+struct SigDrop;
+
+impl Drop for SigDrop {
+    fn drop(&mut self) {}
+}
+
+#[rustc_insignificant_dtor]
+struct GenericStruct<T>(T, T);
+
+impl<T> Drop for GenericStruct<T> {
+    fn drop(&mut self) {}
+}
+
+struct Wrapper<T>(GenericStruct<T>, i32);
+
+// `SigDrop` implements drop and therefore needs to be migrated.
+fn significant_drop_needs_migration() {
+    let t = (SigDrop {}, SigDrop {});
+
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Even if a type implements an insignificant drop, if it's
+// elements have a significant drop then the overall type is
+// consdered to have an significant drop. Since the elements
+// of `GenericStruct` implement drop, migration is required.
+fn generic_struct_with_significant_drop_needs_migration() {
+    let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
+
+    // move is used to force i32 to be copied instead of being a ref
+    let c = move || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+fn main() {
+    significant_drop_needs_migration();
+    generic_struct_with_significant_drop_needs_migration();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs
new file mode 100644
index 00000000000..f95d34eeb29
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.rs
@@ -0,0 +1,74 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+use std::sync::Mutex;
+
+    #[rustc_insignificant_dtor]
+struct InsignificantDropPoint {
+    x: i32,
+    y: Mutex<i32>,
+}
+
+impl Drop for InsignificantDropPoint {
+    fn drop(&mut self) {}
+}
+
+struct SigDrop;
+
+impl Drop for SigDrop {
+    fn drop(&mut self) {}
+}
+
+#[rustc_insignificant_dtor]
+struct GenericStruct<T>(T, T);
+
+impl<T> Drop for GenericStruct<T> {
+    fn drop(&mut self) {}
+}
+
+struct Wrapper<T>(GenericStruct<T>, i32);
+
+// `SigDrop` implements drop and therefore needs to be migrated.
+fn significant_drop_needs_migration() {
+    let t = (SigDrop {}, SigDrop {});
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Even if a type implements an insignificant drop, if it's
+// elements have a significant drop then the overall type is
+// consdered to have an significant drop. Since the elements
+// of `GenericStruct` implement drop, migration is required.
+fn generic_struct_with_significant_drop_needs_migration() {
+    let t = Wrapper(GenericStruct(SigDrop {}, SigDrop {}), 5);
+
+    // move is used to force i32 to be copied instead of being a ref
+    let c = move || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+fn main() {
+    significant_drop_needs_migration();
+    generic_struct_with_significant_drop_needs_migration();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr
new file mode 100644
index 00000000000..a0795c12928
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_migrations.stderr
@@ -0,0 +1,45 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/insignificant_drop_attr_migrations.rs:39:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/insignificant_drop_attr_migrations.rs:3:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/insignificant_drop_attr_migrations.rs:59:13
+   |
+LL |     let c = move || {
+   |             ^^^^^^^
+...
+LL |         let _t = t.1;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = move || {
+LL +         let _ = &t;
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs
new file mode 100644
index 00000000000..3f184a67fba
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/insignificant_drop_attr_no_migrations.rs
@@ -0,0 +1,45 @@
+// run-pass
+
+#![deny(rust_2021_incompatible_closure_captures)]
+#![feature(rustc_attrs)]
+#![allow(unused)]
+#[rustc_insignificant_dtor]
+
+struct InsignificantDropPoint {
+    x: i32,
+    y: i32,
+}
+
+impl Drop for InsignificantDropPoint {
+    fn drop(&mut self) {}
+}
+
+struct GenericStruct<T>(T, T);
+
+// No drop reordering is required as the elements of `t` implement insignificant drop
+fn insignificant_drop_does_not_need_migration() {
+    let t = (InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 });
+
+    let c = || {
+        let _t = t.0;
+    };
+
+    c();
+}
+
+// Generic struct whose elements don't have significant drops don't need drop reordering
+fn generic_struct_with_insignificant_drop_does_not_need_migration() {
+    let t =
+        GenericStruct(InsignificantDropPoint { x: 4, y: 9 }, InsignificantDropPoint { x: 4, y: 9 });
+
+    let c = || {
+        let _t = t.0;
+    };
+
+    c();
+}
+
+fn main() {
+    insignificant_drop_does_not_need_migration();
+    generic_struct_with_insignificant_drop_does_not_need_migration();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs
new file mode 100644
index 00000000000..ff5d284614b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.rs
@@ -0,0 +1,10 @@
+// run-pass
+
+#![warn(rust_2021_incompatible_closure_captures)]
+
+fn main() {
+    if let a = "" {
+        //~^ WARNING: irrefutable `if let` pattern
+        drop(|_: ()| drop(a));
+    }
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
new file mode 100644
index 00000000000..36a80e694e8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
@@ -0,0 +1,12 @@
+warning: irrefutable `if let` pattern
+  --> $DIR/issue-78720.rs:6:8
+   |
+LL |     if let a = "" {
+   |        ^^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `if let` is useless
+   = help: consider replacing the `if let` with a `let`
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/issue-86753.rs b/tests/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
new file mode 100644
index 00000000000..fce9cac627b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
@@ -0,0 +1,34 @@
+// edition:2018
+// check-pass
+
+#![warn(rust_2021_compatibility)]
+
+use std::future::Future;
+
+struct Runtime;
+
+impl Runtime {
+    pub fn block_on<F: Future>(&self, _future: F) -> F::Output {
+        unimplemented!()
+    }
+}
+
+pub fn http<F, Fut>(_func: F)
+where
+    F: Fn() -> Fut,
+    Fut: Future<Output = ()>,
+{
+    let rt = Runtime {};
+    let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) });
+    let _ = || rt.block_on(async { srv });
+}
+
+pub struct Server<S> {
+    _marker: std::marker::PhantomData<S>,
+}
+
+pub fn serve<S>(_new_service: S) -> Server<S> {
+    unimplemented!()
+}
+
+fn main() { }
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs b/tests/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs
new file mode 100644
index 00000000000..ed8cb042b3e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/issue-90024-adt-correct-subst.rs
@@ -0,0 +1,37 @@
+// Test that rustc doesn't ICE as in #90024.
+// check-pass
+// edition=2018
+
+#![warn(rust_2021_incompatible_closure_captures)]
+
+// Checks there's no double-subst into the generic args, otherwise we get OOB
+// MCVE by @lqd
+pub struct Graph<N, E, Ix> {
+    _edges: E,
+    _nodes: N,
+    _ix: Vec<Ix>,
+}
+fn graph<N, E>() -> Graph<N, E, i32> {
+    todo!()
+}
+fn first_ice() {
+    let g = graph::<i32, i32>();
+    let _ = || g;
+}
+
+// Checks that there is a subst into the fields, otherwise we get normalization error
+// MCVE by @cuviper
+use std::iter::Empty;
+struct Foo<I: Iterator> {
+    data: Vec<I::Item>,
+}
+pub fn second_ice() {
+    let v = Foo::<Empty<()>> { data: vec![] };
+
+    (|| v.data[0])();
+}
+
+pub fn main() {
+    first_ice();
+    second_ice();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/macro.fixed b/tests/ui/closures/2229_closure_analysis/migrations/macro.fixed
new file mode 100644
index 00000000000..31fe494dc79
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/macro.fixed
@@ -0,0 +1,25 @@
+// run-rustfix
+
+// See https://github.com/rust-lang/rust/issues/87955
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+fn main() {
+    let a = (Foo(0), Foo(1));
+    let _ = || { let _ = &a; dbg!(a.0) };
+    //~^ ERROR: drop order
+    //~| NOTE: will only capture `a.0`
+    //~| NOTE: for more information, see
+    //~| HELP: add a dummy let to cause `a` to be fully captured
+}
+//~^ NOTE: dropped here
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/macro.rs b/tests/ui/closures/2229_closure_analysis/migrations/macro.rs
new file mode 100644
index 00000000000..0f0c4974922
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/macro.rs
@@ -0,0 +1,25 @@
+// run-rustfix
+
+// See https://github.com/rust-lang/rust/issues/87955
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+fn main() {
+    let a = (Foo(0), Foo(1));
+    let _ = || dbg!(a.0);
+    //~^ ERROR: drop order
+    //~| NOTE: will only capture `a.0`
+    //~| NOTE: for more information, see
+    //~| HELP: add a dummy let to cause `a` to be fully captured
+}
+//~^ NOTE: dropped here
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/macro.stderr b/tests/ui/closures/2229_closure_analysis/migrations/macro.stderr
new file mode 100644
index 00000000000..c17edce72f9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/macro.stderr
@@ -0,0 +1,22 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/macro.rs:19:13
+   |
+LL |     let _ = || dbg!(a.0);
+   |             ^^      --- in Rust 2018, this closure captures all of `a`, but in Rust 2021, it will only capture `a.0`
+...
+LL | }
+   | - in Rust 2018, `a` is dropped here, but in Rust 2021, only `a.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/macro.rs:5:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `a` to be fully captured
+   |
+LL |     let _ = || { let _ = &a; dbg!(a.0) };
+   |                +++++++++++++           +
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed
new file mode 100644
index 00000000000..ce8b6072595
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed
@@ -0,0 +1,47 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test the two possible cases for automated migartion using rustfix
+// - Closure contains a block i.e.  `|| { .. };`
+// - Closure contains just an expr `|| ..;`
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+fn closure_contains_block() {
+    let t = (Foo(0), Foo(0));
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn closure_doesnt_contain_block() {
+    let t = (Foo(0), Foo(0));
+    let c = || { let _ = &t; t.0 };
+    //~^ ERROR: drop order
+    //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    //~| NOTE: for more information, see
+    //~| HELP: add a dummy let to cause `t` to be fully captured
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn main() {
+    closure_contains_block();
+    closure_doesnt_contain_block();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs
new file mode 100644
index 00000000000..2237bebd788
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs
@@ -0,0 +1,46 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test the two possible cases for automated migartion using rustfix
+// - Closure contains a block i.e.  `|| { .. };`
+// - Closure contains just an expr `|| ..;`
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+fn closure_contains_block() {
+    let t = (Foo(0), Foo(0));
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn closure_doesnt_contain_block() {
+    let t = (Foo(0), Foo(0));
+    let c = || t.0;
+    //~^ ERROR: drop order
+    //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    //~| NOTE: for more information, see
+    //~| HELP: add a dummy let to cause `t` to be fully captured
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+fn main() {
+    closure_contains_block();
+    closure_doesnt_contain_block();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr
new file mode 100644
index 00000000000..94526487e67
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr
@@ -0,0 +1,41 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/migrations_rustfix.rs:19:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/migrations_rustfix.rs:2:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/migrations_rustfix.rs:33:13
+   |
+LL |     let c = || t.0;
+   |             ^^ --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL |     let c = || { let _ = &t; t.0 };
+   |                +++++++++++++     +
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
new file mode 100644
index 00000000000..ff2244a8e31
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed
@@ -0,0 +1,50 @@
+// run-rustfix
+// needs-unwind
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(fn_traits)]
+#![feature(never_type)]
+
+use std::panic;
+
+fn foo_diverges() -> ! {
+    panic!()
+}
+
+fn assert_panics<F>(f: F)
+where
+    F: FnOnce(),
+{
+    let f = panic::AssertUnwindSafe(f);
+    let result = panic::catch_unwind(move || {
+        let _ = &f;
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+        //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f` to be fully captured
+        f.0()
+        //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+    });
+    if let Ok(..) = result {
+        panic!("diverging function returned");
+    }
+}
+
+fn test_fn_ptr_panic<T>(mut t: T)
+where
+    T: Fn() -> !,
+{
+    let as_fn = <T as Fn<()>>::call;
+    assert_panics(|| as_fn(&t, ()));
+    let as_fn_mut = <T as FnMut<()>>::call_mut;
+    assert_panics(|| as_fn_mut(&mut t, ()));
+    let as_fn_once = <T as FnOnce<()>>::call_once;
+    assert_panics(|| as_fn_once(t, ()));
+}
+
+fn main() {
+    test_fn_ptr_panic(foo_diverges);
+    test_fn_ptr_panic(foo_diverges as fn() -> !);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
new file mode 100644
index 00000000000..52e96d013a2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs
@@ -0,0 +1,49 @@
+// run-rustfix
+// needs-unwind
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+#![feature(fn_traits)]
+#![feature(never_type)]
+
+use std::panic;
+
+fn foo_diverges() -> ! {
+    panic!()
+}
+
+fn assert_panics<F>(f: F)
+where
+    F: FnOnce(),
+{
+    let f = panic::AssertUnwindSafe(f);
+    let result = panic::catch_unwind(move || {
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `UnwindSafe`
+        //~| NOTE: in Rust 2018, this closure implements `RefUnwindSafe`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f` to be fully captured
+        f.0()
+        //~^ NOTE: in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+    });
+    if let Ok(..) = result {
+        panic!("diverging function returned");
+    }
+}
+
+fn test_fn_ptr_panic<T>(mut t: T)
+where
+    T: Fn() -> !,
+{
+    let as_fn = <T as Fn<()>>::call;
+    assert_panics(|| as_fn(&t, ()));
+    let as_fn_mut = <T as FnMut<()>>::call_mut;
+    assert_panics(|| as_fn_mut(&mut t, ()));
+    let as_fn_once = <T as FnOnce<()>>::call_once;
+    assert_panics(|| as_fn_once(t, ()));
+}
+
+fn main() {
+    test_fn_ptr_panic(foo_diverges);
+    test_fn_ptr_panic(foo_diverges as fn() -> !);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
new file mode 100644
index 00000000000..e10898f9844
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr
@@ -0,0 +1,26 @@
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/mir_calls_to_shims.rs:20:38
+   |
+LL |     let result = panic::catch_unwind(move || {
+   |                                      ^^^^^^^
+   |                                      |
+   |                                      in Rust 2018, this closure implements `RefUnwindSafe` as `f` implements `RefUnwindSafe`, but in Rust 2021, this closure will no longer implement `RefUnwindSafe` because `f` is not fully captured and `f.0` does not implement `RefUnwindSafe`
+   |                                      in Rust 2018, this closure implements `UnwindSafe` as `f` implements `UnwindSafe`, but in Rust 2021, this closure will no longer implement `UnwindSafe` because `f` is not fully captured and `f.0` does not implement `UnwindSafe`
+...
+LL |         f.0()
+   |         --- in Rust 2018, this closure captures all of `f`, but in Rust 2021, it will only capture `f.0`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/mir_calls_to_shims.rs:4:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `f` to be fully captured
+   |
+LL ~     let result = panic::catch_unwind(move || {
+LL +         let _ = &f;
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
new file mode 100644
index 00000000000..173dd2e2cff
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.fixed
@@ -0,0 +1,157 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(String);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+impl Foo {
+    fn from(s: &str) -> Self {
+        Self(String::from(s))
+    }
+}
+
+struct S(#[allow(unused_tuple_struct_fields)] Foo);
+
+#[derive(Clone)]
+struct T(#[allow(unused_tuple_struct_fields)] i32);
+
+struct U(S, T);
+
+impl Clone for U {
+    fn clone(&self) -> Self {
+        U(S(Foo::from("Hello World")), T(0))
+    }
+}
+
+fn test_multi_issues() {
+    let f1 = U(S(Foo::from("foo")), T(0));
+    let f2 = U(S(Foo::from("bar")), T(0));
+    let c = || {
+        let _ = (&f1, &f2);
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
+        let _f_1 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f2.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+
+fn test_capturing_all_disjoint_fields_individually() {
+    let f1 = U(S(Foo::from("foo")), T(0));
+    let c = || {
+        let _ = &f1;
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_1 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f1.1;
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+
+struct U1(S, T, S);
+
+impl Clone for U1 {
+    fn clone(&self) -> Self {
+        U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")))
+    }
+}
+
+fn test_capturing_several_disjoint_fields_individually_1() {
+    let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+    let c = || {
+        let _ = &f1;
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_0 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f1.2;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+
+fn test_capturing_several_disjoint_fields_individually_2() {
+    let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+    let c = || {
+        let _ = &f1;
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_0 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_1 = f1.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_multi_traits_issues() {
+    let mut f1 = 10;
+    let f1 = CustomInt(&mut f1 as *mut i32);
+    let fptr1 = SyncPointer(f1);
+
+    let mut f2 = 10;
+    let fptr2 = SendPointer(&mut f2 as *mut i32);
+    thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+        *fptr1.0.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+        *fptr2.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+    } });
+}
+
+fn main() {
+    test_multi_issues();
+    test_capturing_all_disjoint_fields_individually();
+    test_capturing_several_disjoint_fields_individually_1();
+    test_capturing_several_disjoint_fields_individually_2();
+    test_multi_traits_issues();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
new file mode 100644
index 00000000000..cfc4555ca03
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.rs
@@ -0,0 +1,153 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+use std::thread;
+
+#[derive(Debug)]
+struct Foo(String);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+impl Foo {
+    fn from(s: &str) -> Self {
+        Self(String::from(s))
+    }
+}
+
+struct S(#[allow(unused_tuple_struct_fields)] Foo);
+
+#[derive(Clone)]
+struct T(#[allow(unused_tuple_struct_fields)] i32);
+
+struct U(S, T);
+
+impl Clone for U {
+    fn clone(&self) -> Self {
+        U(S(Foo::from("Hello World")), T(0))
+    }
+}
+
+fn test_multi_issues() {
+    let f1 = U(S(Foo::from("foo")), T(0));
+    let f2 = U(S(Foo::from("bar")), T(0));
+    let c = || {
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1`, `f2` to be fully captured
+        let _f_1 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f2.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+
+fn test_capturing_all_disjoint_fields_individually() {
+    let f1 = U(S(Foo::from("foo")), T(0));
+    let c = || {
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_1 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f1.1;
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+
+struct U1(S, T, S);
+
+impl Clone for U1 {
+    fn clone(&self) -> Self {
+        U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")))
+    }
+}
+
+fn test_capturing_several_disjoint_fields_individually_1() {
+    let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+    let c = || {
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect which traits the closure implements [rust_2021_incompatible_closure_captures]
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_0 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_2 = f1.2;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+
+fn test_capturing_several_disjoint_fields_individually_2() {
+    let f1 = U1(S(Foo::from("foo")), T(0), S(Foo::from("bar")));
+    let c = || {
+        //~^ ERROR: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+        //~| NOTE: in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `f1` to be fully captured
+        let _f_0 = f1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+        let _f_1 = f1.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+    };
+
+    let c_clone = c.clone();
+
+    c_clone();
+}
+//~^ NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+
+struct SendPointer(*mut i32);
+unsafe impl Send for SendPointer {}
+
+struct CustomInt(*mut i32);
+struct SyncPointer(CustomInt);
+unsafe impl Sync for SyncPointer {}
+unsafe impl Send for CustomInt {}
+
+fn test_multi_traits_issues() {
+    let mut f1 = 10;
+    let f1 = CustomInt(&mut f1 as *mut i32);
+    let fptr1 = SyncPointer(f1);
+
+    let mut f2 = 10;
+    let fptr2 = SendPointer(&mut f2 as *mut i32);
+    thread::spawn(move || unsafe {
+        //~^ ERROR: changes to closure capture in Rust 2021
+        //~| NOTE: in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`
+        //~| NOTE: in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+        *fptr1.0.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+        *fptr2.0 = 20;
+        //~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+    });
+}
+
+fn main() {
+    test_multi_issues();
+    test_capturing_all_disjoint_fields_individually();
+    test_capturing_several_disjoint_fields_individually_1();
+    test_capturing_several_disjoint_fields_individually_2();
+    test_multi_traits_issues();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
new file mode 100644
index 00000000000..efb264447f6
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/multi_diagnostics.stderr
@@ -0,0 +1,118 @@
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:37:13
+   |
+LL |     let c = || {
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL |         let _f_1 = f1.0;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL |         let _f_2 = f2.1;
+   |                    ---- in Rust 2018, this closure captures all of `f2`, but in Rust 2021, it will only capture `f2.1`
+...
+LL | }
+   | - in Rust 2018, `f2` is dropped here, but in Rust 2021, only `f2.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/multi_diagnostics.rs:2:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `f1`, `f2` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = (&f1, &f2);
+   |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:56:13
+   |
+LL |     let c = || {
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL |         let _f_1 = f1.0;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &f1;
+   |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:81:13
+   |
+LL |     let c = || {
+   |             ^^
+   |             |
+   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+   |             in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.2` does not implement `Clone`
+...
+LL |         let _f_0 = f1.0;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL |         let _f_2 = f1.2;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.2`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &f1;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:100:13
+   |
+LL |     let c = || {
+   |             ^^ in Rust 2018, this closure implements `Clone` as `f1` implements `Clone`, but in Rust 2021, this closure will no longer implement `Clone` because `f1` is not fully captured and `f1.0` does not implement `Clone`
+...
+LL |         let _f_0 = f1.0;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.0`
+LL |
+LL |         let _f_1 = f1.1;
+   |                    ---- in Rust 2018, this closure captures all of `f1`, but in Rust 2021, it will only capture `f1.1`
+...
+LL | }
+   | -
+   | |
+   | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.0` will be dropped here as part of the closure
+   | in Rust 2018, `f1` is dropped here, but in Rust 2021, only `f1.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `f1` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &f1;
+   |
+
+error: changes to closure capture in Rust 2021 will affect which traits the closure implements
+  --> $DIR/multi_diagnostics.rs:133:19
+   |
+LL |     thread::spawn(move || unsafe {
+   |                   ^^^^^^^
+   |                   |
+   |                   in Rust 2018, this closure implements `Send` as `fptr1` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Send`
+   |                   in Rust 2018, this closure implements `Sync` as `fptr1` implements `Sync`, but in Rust 2021, this closure will no longer implement `Sync` because `fptr1` is not fully captured and `fptr1.0.0` does not implement `Sync`
+   |                   in Rust 2018, this closure implements `Send` as `fptr2` implements `Send`, but in Rust 2021, this closure will no longer implement `Send` because `fptr2` is not fully captured and `fptr2.0` does not implement `Send`
+...
+LL |         *fptr1.0.0 = 20;
+   |         ---------- in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
+LL |
+LL |         *fptr2.0 = 20;
+   |         -------- in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `fptr1`, `fptr2` to be fully captured
+   |
+LL ~     thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
+LL |
+ ...
+LL |
+LL ~     } });
+   |
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/no_migrations.rs b/tests/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
new file mode 100644
index 00000000000..8b75e226ab5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/no_migrations.rs
@@ -0,0 +1,81 @@
+// run-pass
+
+// 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();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/old_name.rs b/tests/ui/closures/2229_closure_analysis/migrations/old_name.rs
new file mode 100644
index 00000000000..16e3cca7b77
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/old_name.rs
@@ -0,0 +1,9 @@
+// check-pass
+
+// Ensure that the old name for `rust_2021_incompatible_closure_captures` is still
+// accepted by the compiler
+
+#![allow(disjoint_capture_migration)]
+//~^ WARN lint `disjoint_capture_migration` has been renamed
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/old_name.stderr b/tests/ui/closures/2229_closure_analysis/migrations/old_name.stderr
new file mode 100644
index 00000000000..47cb689fa01
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/old_name.stderr
@@ -0,0 +1,10 @@
+warning: lint `disjoint_capture_migration` has been renamed to `rust_2021_incompatible_closure_captures`
+  --> $DIR/old_name.rs:6:10
+   |
+LL | #![allow(disjoint_capture_migration)]
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `rust_2021_incompatible_closure_captures`
+   |
+   = note: `#[warn(renamed_and_removed_lints)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/precise.fixed b/tests/ui/closures/2229_closure_analysis/migrations/precise.fixed
new file mode 100644
index 00000000000..7892a72c765
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/precise.fixed
@@ -0,0 +1,69 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that lint is triggered if a path that implements Drop is not captured by move
+fn test_precise_analysis_drop_paths_not_captured_by_move() {
+    let t = ConstainsDropField(Foo(10), Foo(20));
+
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t = &t.1;
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+struct S;
+impl Drop for S {
+    fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test precise analysis for the lint works with paths longer than one.
+fn test_precise_analysis_long_path_missing() {
+    let u = U(T(S, S), T(S, S));
+
+    let c = || {
+        let _ = &u;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `u` to be fully captured
+        let _x = u.0.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+        let _x = u.0.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+        let _x = u.1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+
+
+fn main() {
+    test_precise_analysis_drop_paths_not_captured_by_move();
+    test_precise_analysis_long_path_missing();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/precise.rs b/tests/ui/closures/2229_closure_analysis/migrations/precise.rs
new file mode 100644
index 00000000000..f5e99002bd0
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/precise.rs
@@ -0,0 +1,67 @@
+// run-rustfix
+
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that lint is triggered if a path that implements Drop is not captured by move
+fn test_precise_analysis_drop_paths_not_captured_by_move() {
+    let t = ConstainsDropField(Foo(10), Foo(20));
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t = &t.1;
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+struct S;
+impl Drop for S {
+    fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test precise analysis for the lint works with paths longer than one.
+fn test_precise_analysis_long_path_missing() {
+    let u = U(T(S, S), T(S, S));
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `u` to be fully captured
+        let _x = u.0.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+        let _x = u.0.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+        let _x = u.1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+
+
+fn main() {
+    test_precise_analysis_drop_paths_not_captured_by_move();
+    test_precise_analysis_long_path_missing();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/precise.stderr b/tests/ui/closures/2229_closure_analysis/migrations/precise.stderr
new file mode 100644
index 00000000000..eff26a4d6f5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/precise.stderr
@@ -0,0 +1,55 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/precise.rs:20:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/precise.rs:3:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/precise.rs:45:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _x = u.0.0;
+   |                  ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.0`
+LL |
+LL |         let _x = u.0.1;
+   |                  ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.0.1`
+LL |
+LL |         let _x = u.1.0;
+   |                  ----- in Rust 2018, this closure captures all of `u`, but in Rust 2021, it will only capture `u.1.0`
+...
+LL | }
+   | -
+   | |
+   | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.0` will be dropped here as part of the closure
+   | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.0.1` will be dropped here as part of the closure
+   | in Rust 2018, `u` is dropped here, but in Rust 2021, only `u.1.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `u` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &u;
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs b/tests/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs
new file mode 100644
index 00000000000..587d71c40fc
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs
@@ -0,0 +1,104 @@
+// run-pass
+
+#![deny(rust_2021_incompatible_closure_captures)]
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+struct ConstainsDropField(Foo, Foo);
+
+// Test that if all paths starting at root variable that implement Drop are captured
+// then it doesn't trigger the lint.
+fn test_precise_analysis_simple_1() {
+    let t = (Foo(10), Foo(20), Foo(30));
+
+    let c = || {
+        let _t = t.0;
+        let _t = t.1;
+        let _t = t.2;
+    };
+
+    c();
+}
+
+// Test that if all paths starting at root variable that implement Drop are captured
+// then it doesn't trigger the lint.
+fn test_precise_analysis_simple_2() {
+    let t = ConstainsDropField(Foo(10), Foo(20));
+
+    let c = || {
+        let _t = t.0;
+        let _t = t.1;
+    };
+
+    c();
+}
+
+#[derive(Debug)]
+struct ContainsAndImplsDrop(Foo);
+impl Drop for ContainsAndImplsDrop {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+// If a path isn't directly captured but requires Drop, then this tests that migrations aren't
+// needed if the a parent to that path is captured.
+fn test_precise_analysis_parent_captured_1() {
+    let t = ConstainsDropField(Foo(10), Foo(20));
+
+    let c = || {
+        let _t = t;
+    };
+
+    c();
+}
+
+// If a path isn't directly captured but requires Drop, then this tests that migrations aren't
+// needed if the a parent to that path is captured.
+fn test_precise_analysis_parent_captured_2() {
+    let t = ContainsAndImplsDrop(Foo(10));
+
+    let c = || {
+        let _t = t;
+    };
+
+    c();
+}
+
+struct S;
+impl Drop for S {
+    fn drop(&mut self) {}
+}
+
+struct T(S, S);
+struct U(T, T);
+
+// Test that if the path is longer than just one element, precise analysis works correctly.
+fn test_precise_analysis_long_path() {
+    let u = U(T(S, S), T(S, S));
+
+    let c = || {
+        let _x = u.0.0;
+        let _x = u.0.1;
+        let _x = u.1.0;
+        let _x = u.1.1;
+    };
+
+    c();
+}
+
+fn main() {
+    test_precise_analysis_simple_1();
+    test_precise_analysis_simple_2();
+
+    test_precise_analysis_parent_captured_1();
+    test_precise_analysis_parent_captured_2();
+
+    test_precise_analysis_long_path();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed
new file mode 100644
index 00000000000..e99dbb5ab3a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed
@@ -0,0 +1,229 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a significant drop (user defined)
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+#[derive(Debug)]
+struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo);
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+        let _ = (&t, &t1, &t2);
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t1 = t1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+        let _t2 = t2.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+        let _ = (&t, &t1);
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t1 = t1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+        let _t2 = t2;
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        println!("{:?}", t1.1);
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// The root variable might not implement drop themselves but some path starting
+// at the root variable might implement Drop.
+//
+// If this path isn't captured we need to migrate for the root variable.
+fn test4_type_contains_drop_need_migration() {
+    let t = ConstainsDropField(Foo(0), Foo(0));
+
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test5_drop_non_drop_aggregate_need_migration() {
+    let t = (Foo(0), Foo(0), 0i32);
+
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Significant and Insignificant Drop aggregates.
+fn test6_significant_insignificant_drop_aggregate_need_migration() {
+    let t = (Foo(0), String::new());
+
+    let c = || {
+        let _ = &t;
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test7_move_closures_non_copy_types_might_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0), Foo(0));
+
+    let c = move || {
+        let _ = (&t1, &t);
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured
+        println!("{:?} {:?}", t1.1, t.1);
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+        //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+
+
+fn test8_drop_order_and_blocks() {
+    {
+        let tuple =
+          (Foo(0), Foo(1));
+        {
+            let c = || {
+                let _ = &tuple;
+                //~^ ERROR: drop order
+                //~| NOTE: for more information, see
+                //~| HELP: add a dummy let to cause `tuple` to be fully captured
+                tuple.0;
+                //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+            };
+
+            c();
+        }
+        //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+    }
+}
+
+fn test9_drop_order_and_nested_closures() {
+    let tuple =
+        (Foo(0), Foo(1));
+    let b = || {
+        let c = || {
+            let _ = &tuple;
+            //~^ ERROR: drop order
+            //~| NOTE: for more information, see
+            //~| HELP: add a dummy let to cause `tuple` to be fully captured
+            tuple.0;
+            //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+        };
+
+        c();
+    };
+    //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+
+    b();
+}
+
+// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type
+fn test10_vec_of_significant_drop_type() {
+
+        let tup = (Foo(0), vec![Foo(3)]);
+
+        let _c = || { let _ = &tup; tup.0 };
+            //~^ ERROR: drop order
+            //~| NOTE: for more information, see
+            //~| HELP: add a dummy let to cause `tup` to be fully captured
+            //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+}
+//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+
+fn main() {
+    test1_all_need_migration();
+    test2_only_precise_paths_need_migration();
+    test3_only_by_value_need_migration();
+    test4_type_contains_drop_need_migration();
+    test5_drop_non_drop_aggregate_need_migration();
+    test6_significant_insignificant_drop_aggregate_need_migration();
+    test7_move_closures_non_copy_types_might_need_migration();
+    test8_drop_order_and_blocks();
+    test9_drop_order_and_nested_closures();
+    test10_vec_of_significant_drop_type();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
new file mode 100644
index 00000000000..62a984c9eeb
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.rs
@@ -0,0 +1,220 @@
+// run-rustfix
+#![deny(rust_2021_incompatible_closure_captures)]
+//~^ NOTE: the lint level is defined here
+
+// Test cases for types that implement a significant drop (user defined)
+
+#[derive(Debug)]
+struct Foo(i32);
+impl Drop for Foo {
+    fn drop(&mut self) {
+        println!("{:?} dropped", self.0);
+    }
+}
+
+#[derive(Debug)]
+struct ConstainsDropField(Foo, #[allow(unused_tuple_struct_fields)] Foo);
+
+// `t` needs Drop because one of its elements needs drop,
+// therefore precise capture might affect drop ordering
+fn test1_all_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t1 = t1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+        let _t2 = t2.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+
+// String implements drop and therefore should be migrated.
+// But in this test cases, `t2` is completely captured and when it is dropped won't be affected
+fn test2_only_precise_paths_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let t2 = (Foo(0), Foo(0));
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t`, `t1` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        let _t1 = t1.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+        let _t2 = t2;
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+
+// If a variable would've not been captured by value then it would've not been
+// dropped with the closure and therefore doesn't need migration.
+fn test3_only_by_value_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0));
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+        println!("{:?}", t1.1);
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// The root variable might not implement drop themselves but some path starting
+// at the root variable might implement Drop.
+//
+// If this path isn't captured we need to migrate for the root variable.
+fn test4_type_contains_drop_need_migration() {
+    let t = ConstainsDropField(Foo(0), Foo(0));
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Drop + Non Drop aggregates.
+// Note we need migration here only because the non-copy (because Drop type) is captured,
+// otherwise we won't need to, since we can get away with just by ref capture in that case.
+fn test5_drop_non_drop_aggregate_need_migration() {
+    let t = (Foo(0), Foo(0), 0i32);
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.0;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+
+// Test migration analysis in case of Significant and Insignificant Drop aggregates.
+fn test6_significant_insignificant_drop_aggregate_need_migration() {
+    let t = (Foo(0), String::new());
+
+    let c = || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t` to be fully captured
+        let _t = t.1;
+        //~^ NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+
+// Since we are using a move closure here, both `t` and `t1` get moved
+// even though they are being used by ref inside the closure.
+fn test7_move_closures_non_copy_types_might_need_migration() {
+    let t = (Foo(0), Foo(0));
+    let t1 = (Foo(0), Foo(0), Foo(0));
+
+    let c = move || {
+        //~^ ERROR: drop order
+        //~| NOTE: for more information, see
+        //~| HELP: add a dummy let to cause `t1`, `t` to be fully captured
+        println!("{:?} {:?}", t1.1, t.1);
+        //~^ NOTE: in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+        //~| NOTE: in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+    };
+
+    c();
+}
+//~^ NOTE: in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+//~| NOTE: in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+
+
+fn test8_drop_order_and_blocks() {
+    {
+        let tuple =
+          (Foo(0), Foo(1));
+        {
+            let c = || {
+                //~^ ERROR: drop order
+                //~| NOTE: for more information, see
+                //~| HELP: add a dummy let to cause `tuple` to be fully captured
+                tuple.0;
+                //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+            };
+
+            c();
+        }
+        //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+    }
+}
+
+fn test9_drop_order_and_nested_closures() {
+    let tuple =
+        (Foo(0), Foo(1));
+    let b = || {
+        let c = || {
+            //~^ ERROR: drop order
+            //~| NOTE: for more information, see
+            //~| HELP: add a dummy let to cause `tuple` to be fully captured
+            tuple.0;
+            //~^ NOTE: in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+        };
+
+        c();
+    };
+    //~^ NOTE: in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+
+    b();
+}
+
+// Test that we migrate if drop order of Vec<T> would be affected if T is a significant drop type
+fn test10_vec_of_significant_drop_type() {
+
+        let tup = (Foo(0), vec![Foo(3)]);
+
+        let _c = || tup.0;
+            //~^ ERROR: drop order
+            //~| NOTE: for more information, see
+            //~| HELP: add a dummy let to cause `tup` to be fully captured
+            //~| NOTE: in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+}
+//~^ NOTE: in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+
+fn main() {
+    test1_all_need_migration();
+    test2_only_precise_paths_need_migration();
+    test3_only_by_value_need_migration();
+    test4_type_contains_drop_need_migration();
+    test5_drop_non_drop_aggregate_need_migration();
+    test6_significant_insignificant_drop_aggregate_need_migration();
+    test7_move_closures_non_copy_types_might_need_migration();
+    test8_drop_order_and_blocks();
+    test9_drop_order_and_nested_closures();
+    test10_vec_of_significant_drop_type();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
new file mode 100644
index 00000000000..54ad20f8983
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr
@@ -0,0 +1,214 @@
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:25:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+LL |
+LL |         let _t1 = t1.0;
+   |                   ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+LL |
+LL |         let _t2 = t2.0;
+   |                   ---- in Rust 2018, this closure captures all of `t2`, but in Rust 2021, it will only capture `t2.0`
+...
+LL | }
+   | -
+   | |
+   | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+   | in Rust 2018, `t2` is dropped here, but in Rust 2021, only `t2.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+note: the lint level is defined here
+  --> $DIR/significant_drop.rs:2:9
+   |
+LL | #![deny(rust_2021_incompatible_closure_captures)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = (&t, &t1, &t2);
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:50:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+LL |
+LL |         let _t1 = t1.0;
+   |                   ---- in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.0`
+...
+LL | }
+   | -
+   | |
+   | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t`, `t1` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = (&t, &t1);
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:71:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:91:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:109:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.0;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.0`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:125:13
+   |
+LL |     let c = || {
+   |             ^^
+...
+LL |         let _t = t.1;
+   |                  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+...
+LL | }
+   | - in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t` to be fully captured
+   |
+LL ~     let c = || {
+LL +         let _ = &t;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:143:13
+   |
+LL |     let c = move || {
+   |             ^^^^^^^
+...
+LL |         println!("{:?} {:?}", t1.1, t.1);
+   |                               ----  --- in Rust 2018, this closure captures all of `t`, but in Rust 2021, it will only capture `t.1`
+   |                               |
+   |                               in Rust 2018, this closure captures all of `t1`, but in Rust 2021, it will only capture `t1.1`
+...
+LL | }
+   | -
+   | |
+   | in Rust 2018, `t1` is dropped here, but in Rust 2021, only `t1.1` will be dropped here as part of the closure
+   | in Rust 2018, `t` is dropped here, but in Rust 2021, only `t.1` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `t1`, `t` to be fully captured
+   |
+LL ~     let c = move || {
+LL +         let _ = (&t1, &t);
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:163:21
+   |
+LL |             let c = || {
+   |                     ^^
+...
+LL |                 tuple.0;
+   |                 ------- in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+...
+LL |         }
+   |         - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tuple` to be fully captured
+   |
+LL ~             let c = || {
+LL +                 let _ = &tuple;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:181:17
+   |
+LL |         let c = || {
+   |                 ^^
+...
+LL |             tuple.0;
+   |             ------- in Rust 2018, this closure captures all of `tuple`, but in Rust 2021, it will only capture `tuple.0`
+...
+LL |     };
+   |     - in Rust 2018, `tuple` is dropped here, but in Rust 2021, only `tuple.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tuple` to be fully captured
+   |
+LL ~         let c = || {
+LL +             let _ = &tuple;
+   |
+
+error: changes to closure capture in Rust 2021 will affect drop order
+  --> $DIR/significant_drop.rs:201:18
+   |
+LL |         let _c = || tup.0;
+   |                  ^^ ----- in Rust 2018, this closure captures all of `tup`, but in Rust 2021, it will only capture `tup.0`
+...
+LL | }
+   | - in Rust 2018, `tup` is dropped here, but in Rust 2021, only `tup.0` will be dropped here as part of the closure
+   |
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>
+help: add a dummy let to cause `tup` to be fully captured
+   |
+LL |         let _c = || { let _ = &tup; tup.0 };
+   |                     +++++++++++++++       +
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs b/tests/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs
new file mode 100644
index 00000000000..39cf82053f7
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/migrations/unpin_no_migration.rs
@@ -0,0 +1,13 @@
+//run-pass
+#![deny(rust_2021_incompatible_closure_captures)]
+#![allow(unused_must_use)]
+
+fn filter_try_fold(
+    predicate: &mut impl FnMut() -> bool,
+) -> impl FnMut() -> bool + '_ {
+    move || predicate()
+}
+
+fn main() {
+    filter_try_fold(&mut || true);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/move_closure.rs b/tests/ui/closures/2229_closure_analysis/move_closure.rs
new file mode 100644
index 00000000000..b542fa2430c
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/move_closure.rs
@@ -0,0 +1,222 @@
+// edition:2021
+
+// Test that move closures drop derefs with `capture_disjoint_fields` enabled.
+
+#![feature(rustc_attrs)]
+
+fn simple_move_closure() {
+    struct S(String);
+    struct T(S);
+
+    let t = T(S("s".into()));
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        t.0.0 = "new S".into();
+        //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> MutBorrow
+        //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue
+    };
+    c();
+}
+
+// Test move closure use reborrows when using references
+fn simple_ref() {
+    let mut s = 10;
+    let ref_s = &mut s;
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        *ref_s += 10;
+        //~^ NOTE: Capturing ref_s[Deref] -> MutBorrow
+        //~| NOTE: Min Capture ref_s[] -> ByValue
+    };
+    c();
+}
+
+// Test move closure use reborrows when using references
+fn struct_contains_ref_to_another_struct_1() {
+    struct S(String);
+    struct T<'a>(&'a mut S);
+
+    let mut s = S("s".into());
+    let t = T(&mut s);
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        t.0.0 = "new s".into();
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+    };
+
+    c();
+}
+
+// Test that we can use reborrows to read data of Copy types
+// i.e. without truncating derefs
+fn struct_contains_ref_to_another_struct_2() {
+    struct S(i32);
+    struct T<'a>(&'a S);
+
+    let s = S(0);
+    let t = T(&s);
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let _t = t.0.0;
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+    };
+
+    c();
+}
+
+// Test that we can use truncate to move out of !Copy types
+fn struct_contains_ref_to_another_struct_3() {
+    struct S(String);
+    struct T<'a>(&'a S);
+
+    let s = S("s".into());
+    let t = T(&s);
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let _t = t.0.0;
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+    };
+
+    c();
+}
+
+// Test that derefs of box are truncated in move closures
+fn truncate_box_derefs() {
+    struct S(i32);
+
+
+    // Content within the box is moved within the closure
+    let b = Box::new(S(10));
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let _t = b.0;
+        //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture b[] -> ByValue
+    };
+
+    c();
+
+    // Content within the box is used by a shared ref and the box is the root variable
+    let b = Box::new(S(10));
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", b.0);
+        //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture b[] -> ByValue
+    };
+
+    c();
+
+    // Content within the box is used by a shared ref and the box is not the root variable
+    let b = Box::new(S(10));
+    let t = (0, b);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    move || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", t.1.0);
+        //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(1, 0)] -> ByValue
+    };
+}
+
+struct Foo { x: i32 }
+
+// Ensure that even in move closures, if the data is not owned by the root variable
+// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
+fn box_mut_1() {
+    let mut foo = Foo { x: 0 } ;
+
+    let p_foo = &mut foo;
+    let box_p_foo = Box::new(p_foo);
+
+    let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| First Pass analysis includes:
+    //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+    //~| Min Capture analysis includes:
+    //~| NOTE: Min Capture box_p_foo[] -> ByValue
+}
+
+// Ensure that even in move closures, if the data is not owned by the root variable
+// then we don't truncate the derefs or a ByValue capture, rather do a reborrow
+fn box_mut_2() {
+    let foo = Foo { x: 0 } ;
+
+    let mut box_foo = Box::new(foo);
+    let p_foo = &mut box_foo;
+
+    let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| First Pass analysis includes:
+    //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+    //~| Min Capture analysis includes:
+    //~| NOTE: Min Capture p_foo[] -> ByValue
+}
+
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+    let x = 10;
+
+    let c = #[rustc_capture_analysis] move || x;
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| First Pass analysis includes:
+    //~| NOTE: Capturing x[] -> ImmBorrow
+    //~| Min Capture analysis includes:
+    //~| NOTE: Min Capture x[] -> ByValue
+
+    c
+}
+
+fn main() {
+    simple_move_closure();
+    simple_ref();
+    struct_contains_ref_to_another_struct_1();
+    struct_contains_ref_to_another_struct_2();
+    struct_contains_ref_to_another_struct_3();
+    truncate_box_derefs();
+    box_mut_2();
+    box_mut_1();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/move_closure.stderr b/tests/ui/closures/2229_closure_analysis/move_closure.stderr
new file mode 100644
index 00000000000..fd80e05c689
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/move_closure.stderr
@@ -0,0 +1,462 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:12:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:30:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:51:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:74:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:96:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:117:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:133:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:150:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:172:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:189:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/move_closure.rs:202:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:202:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                       ^^^^^^^^^
+   |
+note: Capturing x[] -> ImmBorrow
+  --> $DIR/move_closure.rs:202:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                               ^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:202:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                       ^^^^^^^^^
+   |
+note: Min Capture x[] -> ByValue
+  --> $DIR/move_closure.rs:202:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || x;
+   |                                               ^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:15:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),(0, 0)] -> MutBorrow
+  --> $DIR/move_closure.rs:18:9
+   |
+LL |         t.0.0 = "new S".into();
+   |         ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:15:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new S".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0),(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:18:9
+   |
+LL |         t.0.0 = "new S".into();
+   |         ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:33:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         *ref_s += 10;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing ref_s[Deref] -> MutBorrow
+  --> $DIR/move_closure.rs:36:9
+   |
+LL |         *ref_s += 10;
+   |         ^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:33:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         *ref_s += 10;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture ref_s[] -> ByValue
+  --> $DIR/move_closure.rs:36:9
+   |
+LL |         *ref_s += 10;
+   |         ^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:54:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> MutBorrow
+  --> $DIR/move_closure.rs:57:9
+   |
+LL |         t.0.0 = "new s".into();
+   |         ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:54:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         t.0.0 = "new s".into();
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:57:9
+   |
+LL |         t.0.0 = "new s".into();
+   |         ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:77:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = t.0.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/move_closure.rs:80:18
+   |
+LL |         let _t = t.0.0;
+   |                  ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:77:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = t.0.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:80:18
+   |
+LL |         let _t = t.0.0;
+   |                  ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:99:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = t.0.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:102:18
+   |
+LL |         let _t = t.0.0;
+   |                  ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:99:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = t.0.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/move_closure.rs:102:18
+   |
+LL |         let _t = t.0.0;
+   |                  ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:120:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = b.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing b[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/move_closure.rs:123:18
+   |
+LL |         let _t = b.0;
+   |                  ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:120:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         let _t = b.0;
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture b[] -> ByValue
+  --> $DIR/move_closure.rs:123:18
+   |
+LL |         let _t = b.0;
+   |                  ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:136:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         println!("{}", b.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing b[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/move_closure.rs:139:24
+   |
+LL |         println!("{}", b.0);
+   |                        ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:136:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         println!("{}", b.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture b[] -> ByValue
+  --> $DIR/move_closure.rs:139:24
+   |
+LL |         println!("{}", b.0);
+   |                        ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:153:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         println!("{}", t.1.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/move_closure.rs:156:24
+   |
+LL |         println!("{}", t.1.0);
+   |                        ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:153:5
+   |
+LL | /     move || {
+LL | |
+LL | |
+LL | |         println!("{}", t.1.0);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(1, 0)] -> ByValue
+  --> $DIR/move_closure.rs:156:24
+   |
+LL |         println!("{}", t.1.0);
+   |                        ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:172:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Capturing box_p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+  --> $DIR/move_closure.rs:172:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+   |                                               ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:172:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Min Capture box_p_foo[] -> ByValue
+  --> $DIR/move_closure.rs:172:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || box_p_foo.x += 10;
+   |                                               ^^^^^^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/move_closure.rs:189:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Capturing p_foo[Deref,Deref,(0, 0)] -> MutBorrow
+  --> $DIR/move_closure.rs:189:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+   |                                               ^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/move_closure.rs:189:39
+   |
+LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+   |                                       ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: Min Capture p_foo[] -> ByValue
+  --> $DIR/move_closure.rs:189:47
+   |
+LL |     let c = #[rustc_capture_analysis] move || p_foo.x += 10;
+   |                                               ^^^^^^^
+
+error: aborting due to 33 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/multilevel-path-1.rs b/tests/ui/closures/2229_closure_analysis/multilevel-path-1.rs
new file mode 100644
index 00000000000..a8a2acfa78d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/multilevel-path-1.rs
@@ -0,0 +1,39 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    // Only paths that appears within the closure that directly start off
+    // a variable defined outside the closure are captured.
+    //
+    // Therefore `w.p` is captured
+    // Note that `wp.x` doesn't start off a variable defined outside the closure.
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let wp = &w.p;
+        //~^ NOTE: Capturing w[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture w[(0, 0)] -> ImmBorrow
+        println!("{}", wp.x);
+    };
+
+    // Since `c` captures `w.p` by an ImmBorrow, `w.p.y` can't be mutated.
+    let py = &mut w.p.y;
+    c();
+
+    *py = 20
+}
diff --git a/tests/ui/closures/2229_closure_analysis/multilevel-path-1.stderr b/tests/ui/closures/2229_closure_analysis/multilevel-path-1.stderr
new file mode 100644
index 00000000000..29ad1c59198
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/multilevel-path-1.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/multilevel-path-1.rs:22:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/multilevel-path-1.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let wp = &w.p;
+...  |
+LL | |         println!("{}", wp.x);
+LL | |     };
+   | |_____^
+   |
+note: Capturing w[(0, 0)] -> ImmBorrow
+  --> $DIR/multilevel-path-1.rs:28:19
+   |
+LL |         let wp = &w.p;
+   |                   ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/multilevel-path-1.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let wp = &w.p;
+...  |
+LL | |         println!("{}", wp.x);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture w[(0, 0)] -> ImmBorrow
+  --> $DIR/multilevel-path-1.rs:28:19
+   |
+LL |         let wp = &w.p;
+   |                   ^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/multilevel-path-2.rs b/tests/ui/closures/2229_closure_analysis/multilevel-path-2.rs
new file mode 100644
index 00000000000..e21fe318cd1
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/multilevel-path-2.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    let c = #[rustc_capture_analysis]
+        //~^ ERROR: attributes on expressions are experimental
+        //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", w.p.x);
+        //~^ NOTE: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
+    };
+
+    // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+    let py = &mut w.p.y;
+    c();
+
+    *py = 20
+}
diff --git a/tests/ui/closures/2229_closure_analysis/multilevel-path-2.stderr b/tests/ui/closures/2229_closure_analysis/multilevel-path-2.stderr
new file mode 100644
index 00000000000..929cba11314
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/multilevel-path-2.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/multilevel-path-2.rs:17:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/multilevel-path-2.rs:20:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", w.p.x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing w[(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/multilevel-path-2.rs:23:24
+   |
+LL |         println!("{}", w.p.x);
+   |                        ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/multilevel-path-2.rs:20:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", w.p.x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture w[(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/multilevel-path-2.rs:23:24
+   |
+LL |         println!("{}", w.p.x);
+   |                        ^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/nested-closure.rs b/tests/ui/closures/2229_closure_analysis/nested-closure.rs
new file mode 100644
index 00000000000..22eae744b80
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/nested-closure.rs
@@ -0,0 +1,53 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+// This testcase ensures that nested closures are handles properly
+// - The nested closure is analyzed first.
+// - The capture kind of the nested closure is accounted for by the enclosing closure
+// - Any captured path by the nested closure that starts off a local variable in the enclosing
+// closure is not listed as a capture of the enclosing closure.
+
+fn main() {
+    let mut p = Point { x: 5, y: 20 };
+
+    let mut c1 = #[rustc_capture_analysis]
+        //~^ ERROR: attributes on expressions are experimental
+        //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", p.x);
+        //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+        let incr = 10;
+        let mut c2 = #[rustc_capture_analysis]
+        //~^ ERROR: attributes on expressions are experimental
+        //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+        || p.y += incr;
+        //~^ ERROR: First Pass analysis includes:
+        //~| ERROR: Min Capture analysis includes:
+        //~| NOTE: Capturing p[(1, 0)] -> MutBorrow
+        //~| NOTE: Capturing incr[] -> ImmBorrow
+        //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
+        //~| NOTE: Min Capture incr[] -> ImmBorrow
+        //~| NOTE: Capturing p[(1, 0)] -> MutBorrow
+        //~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
+        c2();
+        println!("{}", p.y);
+        //~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow
+    };
+
+    c1();
+
+    let px = &p.x;
+
+    println!("{}", px);
+
+    c1();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/nested-closure.stderr b/tests/ui/closures/2229_closure_analysis/nested-closure.stderr
new file mode 100644
index 00000000000..a50d0c6a182
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/nested-closure.stderr
@@ -0,0 +1,106 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/nested-closure.rs:19:18
+   |
+LL |     let mut c1 = #[rustc_capture_analysis]
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/nested-closure.rs:29:22
+   |
+LL |         let mut c2 = #[rustc_capture_analysis]
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/nested-closure.rs:32:9
+   |
+LL |         || p.y += incr;
+   |         ^^^^^^^^^^^^^^
+   |
+note: Capturing p[(1, 0)] -> MutBorrow
+  --> $DIR/nested-closure.rs:32:12
+   |
+LL |         || p.y += incr;
+   |            ^^^
+note: Capturing incr[] -> ImmBorrow
+  --> $DIR/nested-closure.rs:32:19
+   |
+LL |         || p.y += incr;
+   |                   ^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/nested-closure.rs:32:9
+   |
+LL |         || p.y += incr;
+   |         ^^^^^^^^^^^^^^
+   |
+note: Min Capture p[(1, 0)] -> MutBorrow
+  --> $DIR/nested-closure.rs:32:12
+   |
+LL |         || p.y += incr;
+   |            ^^^
+note: Min Capture incr[] -> ImmBorrow
+  --> $DIR/nested-closure.rs:32:19
+   |
+LL |         || p.y += incr;
+   |                   ^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/nested-closure.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", p.x);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> ImmBorrow
+  --> $DIR/nested-closure.rs:25:24
+   |
+LL |         println!("{}", p.x);
+   |                        ^^^
+note: Capturing p[(1, 0)] -> MutBorrow
+  --> $DIR/nested-closure.rs:32:12
+   |
+LL |         || p.y += incr;
+   |            ^^^
+note: Capturing p[(1, 0)] -> ImmBorrow
+  --> $DIR/nested-closure.rs:42:24
+   |
+LL |         println!("{}", p.y);
+   |                        ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/nested-closure.rs:22:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", p.x);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+  --> $DIR/nested-closure.rs:25:24
+   |
+LL |         println!("{}", p.x);
+   |                        ^^^
+note: Min Capture p[(1, 0)] -> MutBorrow
+  --> $DIR/nested-closure.rs:32:12
+   |
+LL |         || p.y += incr;
+   |            ^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/tests/ui/closures/2229_closure_analysis/optimization/edge_case.rs
new file mode 100644
index 00000000000..e7edc0bbce3
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/optimization/edge_case.rs
@@ -0,0 +1,34 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+
+struct MyStruct<'a> {
+   a: &'static Int,
+   b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+    let c = #[rustc_capture_analysis] || drop(&m.a.0);
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+    //~| NOTE: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
+    //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+    c
+}
+
+fn main() {
+    let t = 0;
+    let s = MyStruct { a: REF_I, b: B(&t) };
+    let _ = foo(&s);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/tests/ui/closures/2229_closure_analysis/optimization/edge_case.stderr
new file mode 100644
index 00000000000..87d5d5bee07
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/optimization/edge_case.stderr
@@ -0,0 +1,36 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/edge_case.rs:20:13
+   |
+LL |     let c = #[rustc_capture_analysis] || drop(&m.a.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/edge_case.rs:20:39
+   |
+LL |     let c = #[rustc_capture_analysis] || drop(&m.a.0);
+   |                                       ^^^^^^^^^^^^^^^
+   |
+note: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/edge_case.rs:20:48
+   |
+LL |     let c = #[rustc_capture_analysis] || drop(&m.a.0);
+   |                                                ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/edge_case.rs:20:39
+   |
+LL |     let c = #[rustc_capture_analysis] || drop(&m.a.0);
+   |                                       ^^^^^^^^^^^^^^^
+   |
+note: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow
+  --> $DIR/edge_case.rs:20:48
+   |
+LL |     let c = #[rustc_capture_analysis] || drop(&m.a.0);
+   |                                                ^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs b/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs
new file mode 100644
index 00000000000..033fd6f1775
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/optimization/edge_case_run_pass.rs
@@ -0,0 +1,27 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+#![allow(dead_code)]
+
+struct Int(i32);
+struct B<'a>(&'a i32);
+
+const I : Int = Int(0);
+const REF_I : &'static Int = &I;
+
+struct MyStruct<'a> {
+   a: &'static Int,
+   b: B<'a>,
+}
+
+fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+    let c = || drop(&m.a.0);
+    c
+}
+
+fn main() {
+    let t = 0;
+    let s = MyStruct { a: REF_I, b: B(&t) };
+    let _ = foo(&s);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/path-with-array-access.rs b/tests/ui/closures/2229_closure_analysis/path-with-array-access.rs
new file mode 100644
index 00000000000..0c10319314a
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/path-with-array-access.rs
@@ -0,0 +1,33 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+struct Point {
+    x: f32,
+    y: f32,
+}
+
+struct Pentagon {
+    points: [Point; 5],
+}
+
+fn main() {
+    let p1 = Point { x: 10.0, y: 10.0 };
+    let p2 = Point { x: 7.5, y: 12.5 };
+    let p3 = Point { x: 15.0, y: 15.0 };
+    let p4 = Point { x: 12.5, y: 12.5 };
+    let p5 = Point { x: 20.0, y: 10.0 };
+
+    let pent = Pentagon { points: [p1, p2, p3, p4, p5] };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", pent.points[5].x);
+        //~^ NOTE: Capturing pent[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture pent[(0, 0)] -> ImmBorrow
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/path-with-array-access.stderr b/tests/ui/closures/2229_closure_analysis/path-with-array-access.stderr
new file mode 100644
index 00000000000..124b7bf6fe2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/path-with-array-access.stderr
@@ -0,0 +1,48 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/path-with-array-access.rs:23:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/path-with-array-access.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", pent.points[5].x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing pent[(0, 0)] -> ImmBorrow
+  --> $DIR/path-with-array-access.rs:29:24
+   |
+LL |         println!("{}", pent.points[5].x);
+   |                        ^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/path-with-array-access.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", pent.points[5].x);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture pent[(0, 0)] -> ImmBorrow
+  --> $DIR/path-with-array-access.rs:29:24
+   |
+LL |         println!("{}", pent.points[5].x);
+   |                        ^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs
new file mode 100644
index 00000000000..2f8cddc06ba
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs
@@ -0,0 +1,101 @@
+// edition:2021
+
+// Tests that in cases where we individually capture all the fields of a type,
+// we still drop them in the order they would have been dropped in the 2018 edition.
+
+// NOTE: It is *critical* that the order of the min capture NOTES in the stderr output
+//       does *not* change!
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct HasDrop;
+impl Drop for HasDrop {
+    fn drop(&mut self) {
+        println!("dropped");
+    }
+}
+
+fn test_one() {
+    let a = (HasDrop, HasDrop);
+    let b = (HasDrop, HasDrop);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+        //~^ ERROR: Min Capture analysis includes:
+        //~| ERROR
+        println!("{:?}", a.0);
+        //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", a.1);
+        //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+        //~| NOTE
+
+        println!("{:?}", b.0);
+        //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", b.1);
+        //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+        //~| NOTE
+    };
+}
+
+fn test_two() {
+    let a = (HasDrop, HasDrop);
+    let b = (HasDrop, HasDrop);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+        //~^ ERROR: Min Capture analysis includes:
+        //~| ERROR
+        println!("{:?}", a.1);
+        //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", a.0);
+        //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+        //~| NOTE
+
+        println!("{:?}", b.1);
+        //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", b.0);
+        //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+        //~| NOTE
+    };
+}
+
+fn test_three() {
+    let a = (HasDrop, HasDrop);
+    let b = (HasDrop, HasDrop);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+        //~^ ERROR: Min Capture analysis includes:
+        //~| ERROR
+        println!("{:?}", b.1);
+        //~^ NOTE: Min Capture b[(1, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", a.1);
+        //~^ NOTE: Min Capture a[(1, 0)] -> ImmBorrow
+        //~| NOTE
+        println!("{:?}", a.0);
+        //~^ NOTE: Min Capture a[(0, 0)] -> ImmBorrow
+        //~| NOTE
+
+        println!("{:?}", b.0);
+        //~^ NOTE: Min Capture b[(0, 0)] -> ImmBorrow
+        //~| NOTE
+    };
+}
+
+fn main() {
+    test_one();
+    test_two();
+    test_three();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr
new file mode 100644
index 00000000000..2d1dc8727c2
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.stderr
@@ -0,0 +1,228 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/preserve_field_drop_order.rs:23:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/preserve_field_drop_order.rs:49:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/preserve_field_drop_order.rs:75:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", a.0);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:29:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Capturing a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:32:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:36:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+note: Capturing b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:39:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", a.0);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:29:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:32:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+note: Min Capture b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:36:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:39:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:52:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", a.1);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:55:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:58:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Capturing b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:62:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:65:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:52:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", a.1);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:58:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:55:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+note: Min Capture b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:65:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:62:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:78:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", b.1);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:81:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+note: Capturing a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:84:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:87:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Capturing b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:91:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/preserve_field_drop_order.rs:78:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", b.1);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture b[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:91:26
+   |
+LL |         println!("{:?}", b.0);
+   |                          ^^^
+note: Min Capture b[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:81:26
+   |
+LL |         println!("{:?}", b.1);
+   |                          ^^^
+note: Min Capture a[(0, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:87:26
+   |
+LL |         println!("{:?}", a.0);
+   |                          ^^^
+note: Min Capture a[(1, 0)] -> ImmBorrow
+  --> $DIR/preserve_field_drop_order.rs:84:26
+   |
+LL |         println!("{:?}", a.1);
+   |                          ^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs
new file mode 100644
index 00000000000..1cae776dd68
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.rs
@@ -0,0 +1,58 @@
+// run-pass
+// check-run-results
+// revisions: twenty_eighteen twenty_twentyone
+// [twenty_eighteen]compile-flags: --edition 2018
+// [twenty_twentyone]compile-flags: --edition 2021
+
+#[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();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout
new file mode 100644
index 00000000000..557d047c1d5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_eighteen.run.stdout
@@ -0,0 +1,13 @@
+Dropable("y") Dropable("x")
+Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x")
+Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x")
+Dropping r.d.x
+Dropping r.d.y
+Dropping r.c.x
+Dropping r.c.y
+Dropping b.c.x
+Dropping b.c.y
+Dropping b.d.x
+Dropping b.d.y
+Dropping x
+Dropping y
diff --git a/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout
new file mode 100644
index 00000000000..557d047c1d5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/preserve_field_drop_order2.twenty_twentyone.run.stdout
@@ -0,0 +1,13 @@
+Dropable("y") Dropable("x")
+Dropable("b.d.y") Dropable("b.d.x") Dropable("b.c.y") Dropable("b.c.x")
+Dropable("r.d.y") Dropable("r.d.x") Dropable("r.c.y") Dropable("r.c.x")
+Dropping r.d.x
+Dropping r.d.y
+Dropping r.c.x
+Dropping r.c.y
+Dropping b.c.x
+Dropping b.c.y
+Dropping b.d.x
+Dropping b.d.y
+Dropping x
+Dropping y
diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.rs b/tests/ui/closures/2229_closure_analysis/repr_packed.rs
new file mode 100644
index 00000000000..f23670f63ac
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/repr_packed.rs
@@ -0,0 +1,101 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// `u8` aligned at a byte and are unaffected by repr(packed).
+// Therefore we can precisely (and safely) capture references to both the fields.
+fn test_alignment_not_affected() {
+    #[repr(packed)]
+    struct Foo { x: u8, y: u8 }
+
+    let mut foo = Foo { x: 0, y: 0 };
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let z1: &u8 = &foo.x;
+        //~^ NOTE: Capturing foo[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture foo[(0, 0)] -> ImmBorrow
+        let z2: &mut u8 = &mut foo.y;
+        //~^ NOTE: Capturing foo[(1, 0)] -> MutBorrow
+        //~| NOTE: Min Capture foo[(1, 0)] -> MutBorrow
+
+        *z2 = 42;
+
+        println!("({}, {})", z1, z2);
+    };
+
+    c();
+}
+
+// `String`, `u16` are not aligned at a one byte boundary and are thus affected by repr(packed).
+//
+// Here we test that the closure doesn't capture a reference point to `foo.x` but
+// rather capture `foo` entirely.
+fn test_alignment_affected() {
+    #[repr(packed)]
+    struct Foo { x: String, y: u16 }
+
+    let mut foo = Foo { x: String::new(), y: 0 };
+
+    let mut c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let z1: &String = &foo.x;
+        //~^ NOTE: Capturing foo[] -> ImmBorrow
+        let z2: &mut u16 = &mut foo.y;
+        //~^ NOTE: Capturing foo[] -> MutBorrow
+        //~| NOTE: Min Capture foo[] -> MutBorrow
+
+
+        *z2 = 42;
+
+        println!("({}, {})", z1, z2);
+    };
+
+    c();
+}
+
+// Given how the closure desugaring is implemented (at least at the time of writing this test),
+// we don't need to truncate the captured path to a reference into a packed-struct if the field
+// being referenced will be moved into the closure, since it's safe to move out a field from a
+// packed-struct.
+//
+// However to avoid surprises for the user, or issues when the closure is
+// inlined we will truncate the capture to access just the struct regardless of if the field
+// might get moved into the closure.
+fn test_truncation_when_ref_and_move() {
+    #[repr(packed)]
+    struct Foo { x: String }
+
+    let mut foo = Foo { x: String::new() };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", foo.x);
+        //~^ NOTE: Capturing foo[] -> ImmBorrow
+        //~| NOTE: Min Capture foo[] -> ByValue
+        //~| NOTE: foo[] used here
+        let _z = foo.x;
+        //~^ NOTE: Capturing foo[(0, 0)] -> ByValue
+        //~| NOTE: foo[] captured as ByValue here
+    };
+
+    c();
+}
+
+fn main() {
+    test_truncation_when_ref_and_move();
+    test_alignment_affected();
+    test_alignment_not_affected();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/repr_packed.stderr b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr
new file mode 100644
index 00000000000..580061ebc6e
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/repr_packed.stderr
@@ -0,0 +1,161 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/repr_packed.rs:13:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/repr_packed.rs:44:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/repr_packed.rs:79:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/repr_packed.rs:16:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let z1: &u8 = &foo.x;
+...  |
+LL | |         println!("({}, {})", z1, z2);
+LL | |     };
+   | |_____^
+   |
+note: Capturing foo[(0, 0)] -> ImmBorrow
+  --> $DIR/repr_packed.rs:19:24
+   |
+LL |         let z1: &u8 = &foo.x;
+   |                        ^^^^^
+note: Capturing foo[(1, 0)] -> MutBorrow
+  --> $DIR/repr_packed.rs:22:32
+   |
+LL |         let z2: &mut u8 = &mut foo.y;
+   |                                ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/repr_packed.rs:16:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let z1: &u8 = &foo.x;
+...  |
+LL | |         println!("({}, {})", z1, z2);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture foo[(0, 0)] -> ImmBorrow
+  --> $DIR/repr_packed.rs:19:24
+   |
+LL |         let z1: &u8 = &foo.x;
+   |                        ^^^^^
+note: Min Capture foo[(1, 0)] -> MutBorrow
+  --> $DIR/repr_packed.rs:22:32
+   |
+LL |         let z2: &mut u8 = &mut foo.y;
+   |                                ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/repr_packed.rs:47:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let z1: &String = &foo.x;
+...  |
+LL | |         println!("({}, {})", z1, z2);
+LL | |     };
+   | |_____^
+   |
+note: Capturing foo[] -> ImmBorrow
+  --> $DIR/repr_packed.rs:50:28
+   |
+LL |         let z1: &String = &foo.x;
+   |                            ^^^^^
+note: Capturing foo[] -> MutBorrow
+  --> $DIR/repr_packed.rs:52:33
+   |
+LL |         let z2: &mut u16 = &mut foo.y;
+   |                                 ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/repr_packed.rs:47:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let z1: &String = &foo.x;
+...  |
+LL | |         println!("({}, {})", z1, z2);
+LL | |     };
+   | |_____^
+   |
+note: Min Capture foo[] -> MutBorrow
+  --> $DIR/repr_packed.rs:52:33
+   |
+LL |         let z2: &mut u16 = &mut foo.y;
+   |                                 ^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/repr_packed.rs:82:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", foo.x);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing foo[] -> ImmBorrow
+  --> $DIR/repr_packed.rs:85:24
+   |
+LL |         println!("{}", foo.x);
+   |                        ^^^^^
+note: Capturing foo[(0, 0)] -> ByValue
+  --> $DIR/repr_packed.rs:89:18
+   |
+LL |         let _z = foo.x;
+   |                  ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/repr_packed.rs:82:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{}", foo.x);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture foo[] -> ByValue
+  --> $DIR/repr_packed.rs:85:24
+   |
+LL |         println!("{}", foo.x);
+   |                        ^^^^^ foo[] used here
+...
+LL |         let _z = foo.x;
+   |                  ^^^^^ foo[] captured as ByValue here
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/box.rs b/tests/ui/closures/2229_closure_analysis/run_pass/box.rs
new file mode 100644
index 00000000000..73aca288faa
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/box.rs
@@ -0,0 +1,92 @@
+// edition:2021
+// run-pass
+
+// Test precise capture when using boxes
+
+struct MetaData { x: String, name: String }
+struct Data { m: MetaData }
+struct BoxedData(Box<Data>);
+struct EvenMoreBoxedData(Box<BoxedData>);
+
+// Mutate disjoint paths, one inside one outside the closure
+fn box_1() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let mut c = || {
+        e.0.0.m.x = format!("not-x");
+    };
+
+    e.0.0.m.name = format!("not-name");
+    c();
+}
+
+// Mutate a path inside the closure and read a disjoint path outside the closure
+fn box_2() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let mut c = || {
+        e.0.0.m.x = format!("not-x");
+    };
+
+    println!("{}", e.0.0.m.name);
+    c();
+}
+
+// Read a path inside the closure and mutate a disjoint path outside the closure
+fn box_3() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let mut e = EvenMoreBoxedData(Box::new(b));
+
+    let c = || {
+        println!("{}", e.0.0.m.name);
+    };
+
+    e.0.0.m.x = format!("not-x");
+    c();
+}
+
+// Read disjoint paths, one inside the closure and one outside the closure.
+fn box_4() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let e = EvenMoreBoxedData(Box::new(b));
+
+    let c = || {
+        println!("{}", e.0.0.m.name);
+    };
+
+    println!("{}", e.0.0.m.x);
+    c();
+}
+
+// Read the same path, once inside the closure and once outside the closure.
+fn box_5() {
+    let m = MetaData { x: format!("x"), name: format!("name") };
+    let d = Data { m };
+    let b = BoxedData(Box::new(d));
+    let e = EvenMoreBoxedData(Box::new(b));
+
+    let c = || {
+        println!("{}", e.0.0.m.name);
+    };
+
+    println!("{}", e.0.0.m.name);
+    c();
+}
+
+fn main() {
+    box_1();
+    box_2();
+    box_3();
+    box_4();
+    box_5();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/by_value.rs b/tests/ui/closures/2229_closure_analysis/run_pass/by_value.rs
new file mode 100644
index 00000000000..f8752fe1cec
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/by_value.rs
@@ -0,0 +1,26 @@
+// edition:2021
+// run-pass
+
+// Test that ByValue captures compile successfully especially when the captures are
+// dereferenced within the closure.
+
+#[derive(Debug, Default)]
+struct SomeLargeType;
+struct MuchLargerType([SomeLargeType; 32]);
+
+fn big_box() {
+    let s = MuchLargerType(Default::default());
+    let b = Box::new(s);
+    let t = (b, 10);
+
+    let c = || {
+        let p = t.0.0;
+        println!("{} {:?}", t.1, p);
+    };
+
+    c();
+}
+
+fn main() {
+    big_box();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs
new file mode 100644
index 00000000000..3cb1eb32952
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-struct.rs
@@ -0,0 +1,24 @@
+// edition:2021
+// run-pass
+
+// Test that we can immutably borrow field of an instance of a structure from within a closure,
+// while having a mutable borrow to another field of the same instance outside the closure.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 10 };
+
+    let c = || {
+        println!("{}", p.x);
+    };
+
+    // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+    let py = &mut p.y;
+
+    c();
+    *py = 20;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs
new file mode 100644
index 00000000000..0f79b7ae7b8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple-mut.rs
@@ -0,0 +1,20 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate an element of a tuple from within a closure
+// while immutably borrowing another element of the same tuple outside the closure.
+
+#![feature(rustc_attrs)]
+
+fn main() {
+    let mut t = (10, 10);
+
+    let mut c = || {
+        let t1 = &mut t.1;
+        *t1 = 20;
+    };
+
+    // Test that `c` only captures t.1, therefore reading t.0 is allowed.
+    println!("{}", t.0);
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs
new file mode 100644
index 00000000000..81f0328b9ba
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/capture-disjoint-field-tuple.rs
@@ -0,0 +1,21 @@
+// edition:2021
+// run-pass
+
+// Test that we can immutably borrow an element of a tuple from within a closure,
+// while having a mutable borrow to another element of the same tuple outside the closure.
+
+#![feature(rustc_attrs)]
+
+fn main() {
+    let mut t = (10, 10);
+
+    let c = || {
+        println!("{}", t.0);
+    };
+
+    // `c` only captures t.0, therefore mutating t.1 is allowed.
+    let t1 = &mut t.1;
+
+    c();
+    *t1 = 20;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs b/tests/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs
new file mode 100644
index 00000000000..cea02fbe15d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/capture_with_wildcard_match.rs
@@ -0,0 +1,27 @@
+// edition:2021
+//check-pass
+
+fn test1() {
+    let foo : [Vec<u8>; 3] = ["String".into(), "String".into(), "String".into()];
+    let c = || {
+        match foo { _ => () };
+    };
+    drop(foo);
+    c();
+}
+
+fn test2() {
+    let foo : Option<[Vec<u8>; 3]> = Some(["String".into(), "String".into(), "String".into()]);
+    let c = || {
+        match foo {
+            Some(_) => 1,
+            _ => 2
+        };
+    };
+    c();
+}
+
+fn main() {
+    test1();
+    test2();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs
new file mode 100644
index 00000000000..5c278bff90b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.rs
@@ -0,0 +1,21 @@
+// edition:2021
+// check-pass
+#![warn(unused)]
+
+fn main() {
+    let t = (String::from("Hello"), String::from("World"));
+    let g = (String::from("Mr"), String::from("Goose"));
+
+    let a = || {
+        let (_, g2) = g;
+        //~^ WARN unused variable: `g2`
+        let c = ||  {
+            let (_, t2) = t;
+            //~^ WARN unused variable: `t2`
+        };
+
+        c();
+    };
+
+    a();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr
new file mode 100644
index 00000000000..cf8bd7a0a27
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/destructure-pattern-closure-within-closure.stderr
@@ -0,0 +1,21 @@
+warning: unused variable: `g2`
+  --> $DIR/destructure-pattern-closure-within-closure.rs:10:17
+   |
+LL |         let (_, g2) = g;
+   |                 ^^ help: if this is intentional, prefix it with an underscore: `_g2`
+   |
+note: the lint level is defined here
+  --> $DIR/destructure-pattern-closure-within-closure.rs:3:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `t2`
+  --> $DIR/destructure-pattern-closure-within-closure.rs:13:21
+   |
+LL |             let (_, t2) = t;
+   |                     ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs b/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs
new file mode 100644
index 00000000000..dacc2c616b8
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.rs
@@ -0,0 +1,119 @@
+// edition:2021
+// check-pass
+#![warn(unused)]
+
+struct Point {
+    x: u32,
+    y: u32,
+}
+
+fn test1() {
+    let t = (String::from("Hello"), String::from("World"));
+
+    let c = ||  {
+        let (t1, t2) = t;
+        //~^ WARN unused variable: `t1`
+        //~| WARN unused variable: `t2`
+    };
+
+    c();
+}
+
+fn test2() {
+    let t = (String::from("Hello"), String::from("World"));
+
+    let c = ||  {
+        let (t1, _) = t;
+        //~^ WARN unused variable: `t1`
+    };
+
+    c();
+}
+
+fn test3() {
+    let t = (String::from("Hello"), String::from("World"));
+
+    let c = ||  {
+        let (_, t2) = t;
+        //~^ WARN unused variable: `t2`
+    };
+
+    c();
+}
+
+fn test4() {
+    let t = (String::from("Hello"), String::from("World"));
+
+    let c = ||  {
+        let (_, _) = t;
+    };
+
+    c();
+}
+
+fn test5() {
+    let t = (String::new(), String::new());
+    let _c = ||  {
+        let _a = match t {
+            (t1, _) => t1,
+        };
+    };
+}
+
+fn test6() {
+    let t = (String::new(), String::new());
+    let _c = ||  {
+        let _a = match t {
+            (_, t2) => t2,
+        };
+    };
+}
+
+fn test7() {
+    let t = (String::new(), String::new());
+    let _c = ||  {
+        let _a = match t {
+            (t1, t2) => (t1, t2),
+        };
+    };
+}
+
+fn test8() {
+    let x = 0;
+    let tup = (1, 2);
+    let p = Point { x: 10, y: 20 };
+
+    let c = || {
+        let _ = x;
+        let Point { x, y } = p;
+        //~^ WARN unused variable: `x`
+        println!("{}", y);
+        let (_, _) = tup;
+    };
+
+    c();
+}
+
+fn test9() {
+    let _z = 9;
+    let t = (String::from("Hello"), String::from("World"));
+
+    let c = ||  {
+        let (_, t) = t;
+        println!("{}", t);
+    };
+
+    c();
+}
+
+fn main() {
+    test1();
+    test2();
+    test3();
+    test4();
+    test5();
+    test6();
+    test7();
+    test8();
+    test9();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr b/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr
new file mode 100644
index 00000000000..7706f68ba5b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/destructure_patterns.stderr
@@ -0,0 +1,39 @@
+warning: unused variable: `t1`
+  --> $DIR/destructure_patterns.rs:14:14
+   |
+LL |         let (t1, t2) = t;
+   |              ^^ help: if this is intentional, prefix it with an underscore: `_t1`
+   |
+note: the lint level is defined here
+  --> $DIR/destructure_patterns.rs:3:9
+   |
+LL | #![warn(unused)]
+   |         ^^^^^^
+   = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]`
+
+warning: unused variable: `t2`
+  --> $DIR/destructure_patterns.rs:14:18
+   |
+LL |         let (t1, t2) = t;
+   |                  ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+
+warning: unused variable: `t1`
+  --> $DIR/destructure_patterns.rs:26:14
+   |
+LL |         let (t1, _) = t;
+   |              ^^ help: if this is intentional, prefix it with an underscore: `_t1`
+
+warning: unused variable: `t2`
+  --> $DIR/destructure_patterns.rs:37:17
+   |
+LL |         let (_, t2) = t;
+   |                 ^^ help: if this is intentional, prefix it with an underscore: `_t2`
+
+warning: unused variable: `x`
+  --> $DIR/destructure_patterns.rs:88:21
+   |
+LL |         let Point { x, y } = p;
+   |                     ^ help: try ignoring the field: `x: _`
+
+warning: 5 warnings emitted
+
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs b/tests/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs
new file mode 100644
index 00000000000..03400e0ee8d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/disjoint-capture-in-same-closure.rs
@@ -0,0 +1,23 @@
+// edition:2021
+// run-pass
+
+// Tests that if a closure uses individual fields of the same object
+// then that case is handled properly.
+
+#![allow(unused)]
+
+struct Struct {
+    x: i32,
+    y: i32,
+    s: String,
+}
+
+fn main() {
+    let mut s = Struct { x: 10, y: 10, s: String::new() };
+
+    let mut c = {
+        s.x += 10;
+        s.y += 42;
+        s.s = String::from("new");
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs b/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs
new file mode 100644
index 00000000000..477fdd613f5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/drop_then_use_fake_reads.rs
@@ -0,0 +1,11 @@
+// edition:2021
+// check-pass
+#![feature(rustc_attrs)]
+
+fn main() {
+    let mut x = 1;
+    let c = || {
+        drop(&mut x);
+        match x { _ => () }
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/edition.rs b/tests/ui/closures/2229_closure_analysis/run_pass/edition.rs
new file mode 100644
index 00000000000..20bbe1d89e4
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/edition.rs
@@ -0,0 +1,23 @@
+// edition:2021
+// run-pass
+
+// Test that edition 2021 enables disjoint capture by default.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 10 };
+
+    let c = || {
+        println!("{}", p.x);
+    };
+
+    // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+    let py = &mut p.y;
+
+    c();
+    *py = 20;
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs b/tests/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs
new file mode 100644
index 00000000000..e19f5ff1bae
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/filter-on-struct-member.rs
@@ -0,0 +1,37 @@
+// edition:2021
+// run-pass
+
+// Test disjoint capture within an impl block
+
+struct Filter {
+    div: i32,
+}
+impl Filter {
+    fn allowed(&self, x: i32) -> bool {
+        x % self.div == 1
+    }
+}
+
+struct Data {
+    filter: Filter,
+    list: Vec<i32>,
+}
+impl Data {
+    fn update(&mut self) {
+        // The closure passed to filter only captures self.filter,
+        // therefore mutating self.list is allowed.
+        self.list.retain(
+            |v| self.filter.allowed(*v),
+        );
+    }
+}
+
+fn main() {
+    let mut d = Data { filter: Filter { div: 3 }, list: Vec::new() };
+
+    for i in 1..10 {
+        d.list.push(i);
+    }
+
+    d.update();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs b/tests/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs
new file mode 100644
index 00000000000..1286613cb13
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/fru_syntax.rs
@@ -0,0 +1,42 @@
+// edition:2021
+// run-pass
+
+// Test that functional record update/struct update syntax works inside
+// a closure when the feature `capture_disjoint_fields` is enabled.
+
+#[derive(Clone)]
+struct S {
+    a: String,
+    b: String,
+}
+
+struct T {
+    a: String,
+    s: S,
+}
+
+fn main() {
+    let a = String::new();
+    let b = String::new();
+    let c = String::new();
+    let s = S {a, b};
+    let t = T {
+        a: c,
+        s: s.clone()
+    };
+
+    let c = || {
+        let s2 = S {
+            a: format!("New s2"),
+            ..s
+        };
+        let s3 = S {
+            a: format!("New s3"),
+            ..t.s
+        };
+        println!("{} {}", s2.a, s2.b);
+        println!("{} {} {}", s3.a, s3.b, t.a);
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs b/tests/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs
new file mode 100644
index 00000000000..c64475fda43
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/issue-87378.rs
@@ -0,0 +1,16 @@
+// edition:2021
+// check-pass
+
+union Union {
+    value: u64,
+}
+
+fn main() {
+    let u = Union { value: 42 };
+
+    let c = || {
+       unsafe { u.value }
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs
new file mode 100644
index 00000000000..25fbb6cb906
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88372.rs
@@ -0,0 +1,19 @@
+// edition:2021
+// run-pass
+
+
+fn solve<F>(validate: F) -> Option<u64>
+where
+    F: Fn(&mut [i8; 1]),
+{
+    let mut position: [i8; 1] = [1];
+    Some(0).map(|_| {
+        validate(&mut position);
+        let [_x] = position;
+        0
+    })
+}
+
+fn main() {
+    solve(|_| ());
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs
new file mode 100644
index 00000000000..99962053077
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88431.rs
@@ -0,0 +1,59 @@
+// edition:2021
+// check-pass
+
+use std::collections::HashMap;
+use std::future::Future;
+use std::pin::Pin;
+
+pub struct GameMode {}
+
+struct GameStateManager<'a> {
+    gamestate_stack: Vec<Box<dyn GameState<'a> + 'a>>,
+}
+
+pub trait GameState<'a> {}
+
+async fn construct_gamestate_replay<'a>(
+    _gamemode: &GameMode,
+    _factory: &mut GameStateManager<'a>,
+) -> Box<dyn GameState<'a> + 'a> {
+    unimplemented!()
+}
+
+type FutureGameState<'a, 'b> = Pin<Box<dyn Future<Output = Box<dyn GameState<'a> + 'a>> + 'b>>;
+
+struct MenuOption<'a> {
+    command: Box<dyn for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a>,
+}
+
+impl<'a> MenuOption<'a> {
+    fn new(
+        _command: impl for<'b> Fn(&'b mut GameStateManager<'a>) -> FutureGameState<'a, 'b> + 'a,
+    ) -> Self {
+        unimplemented!()
+    }
+}
+
+struct MenuState<'a> {
+    options: Vec<MenuOption<'a>>,
+}
+
+impl<'a> GameState<'a> for MenuState<'a> {}
+
+pub async fn get_replay_menu<'a>(
+    gamemodes: &'a HashMap<&str, GameMode>,
+) -> Box<dyn GameState<'a> + 'a> {
+    let recordings: Vec<String> = vec![];
+    let _ = recordings
+        .into_iter()
+        .map(|entry| {
+            MenuOption::new(move |f| {
+                Box::pin(construct_gamestate_replay(&gamemodes[entry.as_str()], f))
+            })
+        })
+        .collect::<Vec<_>>();
+
+    todo!()
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs
new file mode 100644
index 00000000000..f44c2af803b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs
@@ -0,0 +1,47 @@
+// check-pass
+// edition:2021
+
+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<i32>);
+
+    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() {}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
new file mode 100644
index 00000000000..d2375aa69ec
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs
@@ -0,0 +1,30 @@
+// edition:2021
+//check-pass
+#![warn(unused)]
+#![feature(rustc_attrs)]
+#![feature(btree_drain_filter)]
+
+use std::collections::BTreeMap;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+
+fn main() {
+    let mut map = BTreeMap::new();
+    map.insert("a", ());
+    map.insert("b", ());
+    map.insert("c", ());
+
+    {
+        let mut it = map.drain_filter(|_, _| true);
+        catch_unwind(AssertUnwindSafe(|| while it.next().is_some() {})).unwrap_err();
+        let result = catch_unwind(AssertUnwindSafe(|| it.next()));
+        assert!(matches!(result, Ok(None)));
+    }
+
+    {
+        let mut it = map.drain_filter(|_, _| true);
+        catch_unwind(AssertUnwindSafe(|| while let Some(_) = it.next() {})).unwrap_err();
+        let result = catch_unwind(AssertUnwindSafe(|| it.next()));
+        assert!(matches!(result, Ok(None)));
+    }
+
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/tests/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
new file mode 100644
index 00000000000..f76965bdd3f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/move_closure.rs
@@ -0,0 +1,93 @@
+// edition:2021
+// run-pass
+
+// Test that move closures compile properly with `capture_disjoint_fields` enabled.
+
+#![allow(unused)]
+
+fn simple_ref() {
+    let mut s = 10;
+    let ref_s = &mut s;
+
+    let mut c = move || {
+        *ref_s += 10;
+    };
+    c();
+}
+
+fn struct_contains_ref_to_another_struct() {
+    struct S(String);
+    struct T<'a>(&'a mut S);
+
+    let mut s = S("s".into());
+    let t = T(&mut s);
+
+    let mut c = move || {
+        t.0.0 = "new s".into();
+    };
+
+    c();
+}
+
+#[derive(Debug)]
+struct S(String);
+
+#[derive(Debug)]
+struct T(S);
+
+fn no_ref() {
+    let mut t = T(S("s".into()));
+    let mut c = move || {
+        t.0.0 = "new S".into();
+    };
+    c();
+}
+
+fn no_ref_nested() {
+    let mut t = T(S("s".into()));
+    let c = || {
+        println!("{:?}", t.0);
+        let mut c = move || {
+            t.0.0 = "new S".into();
+            println!("{:?}", t.0.0);
+        };
+        c();
+    };
+    c();
+}
+
+// Test that even if a path is moved into the closure, the closure is not FnOnce
+// if the path is not moved by the closure call.
+fn data_moved_but_not_fn_once() {
+    let x = Box::new(10i32);
+
+    let c = move || {
+        // *x has type i32 which is Copy. So even though the box `x` will be moved
+        // into the closure, `x` is never moved when the closure is called, i.e. the
+        // ownership stays with the closure and therefore we can call the function multiple times.
+        let _x = *x;
+    };
+
+    c();
+    c();
+}
+
+// Test that move closures can take ownership of Copy type
+fn returned_closure_owns_copy_type_data() -> impl Fn() -> i32 {
+    let x = 10;
+
+    let c = move || x;
+
+    c
+}
+
+fn main() {
+    simple_ref();
+    struct_contains_ref_to_another_struct();
+    no_ref();
+    no_ref_nested();
+
+    data_moved_but_not_fn_once();
+
+    returned_closure_owns_copy_type_data();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs
new file mode 100644
index 00000000000..624e0ff2256
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-1.rs
@@ -0,0 +1,33 @@
+// edition:2021
+// run-pass
+
+// Test that closures can capture paths that are more precise than just one level
+// from the root variable.
+//
+// If the closures can handle such precision we should be able to mutate one path in the closure
+// while being able to mutate another path outside the closure, where the two paths are disjoint
+// after applying two projections on the root variable.
+
+#![allow(unused)]
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    let mut c = || {
+        w.p.x += 20;
+    };
+
+    // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+    let py = &mut w.p.y;
+    c();
+
+    *py = 20
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs
new file mode 100644
index 00000000000..bd8addd3781
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-2.rs
@@ -0,0 +1,31 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+
+// If the closures can handle such precision we should be able to read one path in the closure
+// while being able mutate another path outside the closure, where the two paths are disjoint
+// after applying two projections on the root variable.
+
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    let c = || {
+        println!("{}", w.p.x);
+    };
+
+    // `c` only captures `w.p.x`, therefore it's safe to mutate `w.p.y`.
+    let py = &mut w.p.y;
+    c();
+
+    *py = 20
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs
new file mode 100644
index 00000000000..8fc0efb60b7
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/multilevel-path-3.rs
@@ -0,0 +1,28 @@
+// edition:2021
+// run-pass
+
+#![allow(unused)]
+
+// Test that when `capture_disjoint_fields` is enabled we can read a path
+// both inside and outside the closure at the same time.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+struct Wrapper {
+    p: Point,
+}
+
+fn main() {
+    let mut w = Wrapper { p: Point { x: 10, y: 10 } };
+
+    let c = || {
+        println!("{}", w.p.x);
+    };
+
+    let px = &w.p.x;
+    c();
+
+    println!("{}", px);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs b/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
new file mode 100644
index 00000000000..9f0c4d96aa5
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref.rs
@@ -0,0 +1,54 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+
+// Check that we can mutate when one deref is required
+fn mut_ref_1() {
+    let mut x = String::new();
+    let rx = &mut x;
+
+    let mut c = || {
+        *rx = String::new();
+    };
+
+    c();
+}
+
+// Similar example as mut_ref_1, we don't deref the imm-borrow here,
+// and so we are allowed to mutate.
+fn mut_ref_2() {
+    let x = String::new();
+    let y = String::new();
+    let mut ref_x = &x;
+    let m_ref_x = &mut ref_x;
+
+    let mut c = || {
+        *m_ref_x = &y;
+    };
+
+    c();
+}
+
+// Check that we can mutate when multiple derefs of mut-borrows are required to reach
+// the target place.
+// It works because all derefs are mutable, if either of them was an immutable
+// borrow, then we would not be able to deref.
+fn mut_mut_ref() {
+    let mut x = String::new();
+    let mut mref_x = &mut x;
+    let m_mref_x = &mut mref_x;
+
+    let mut c = || {
+        **m_mref_x = String::new();
+    };
+
+    c();
+}
+
+fn main() {
+    mut_ref_1();
+    mut_ref_2();
+    mut_mut_ref();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs b/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
new file mode 100644
index 00000000000..a85335438a9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/mut_ref_struct_mem.rs
@@ -0,0 +1,43 @@
+// edition:2021
+// run-pass
+
+// Test that we can mutate a place through a mut-borrow
+// that is captured by the closure
+
+// More specifically we test that the if the mutable reference isn't root variable of a capture
+// but rather accessed while accessing the precise capture.
+
+fn mut_tuple() {
+    let mut t = (10, 10);
+
+    let t1 = (&mut t, 10);
+
+    let mut c = || {
+        // Mutable because (*t.0) is mutable
+        t1.0.0 += 10;
+    };
+
+    c();
+}
+
+fn mut_tuple_nested() {
+    let mut t = (10, 10);
+
+    let t1 = (&mut t, 10);
+
+    let mut c = || {
+        let mut c = || {
+            // Mutable because (*t.0) is mutable
+            t1.0.0 += 10;
+        };
+
+        c();
+    };
+
+    c();
+}
+
+fn main() {
+    mut_tuple();
+    mut_tuple_nested();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs b/tests/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs
new file mode 100644
index 00000000000..a80b40bb469
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/nested-closure.rs
@@ -0,0 +1,36 @@
+// edition:2021
+// run-pass
+
+// Test whether if we can do precise capture when using nested clsoure.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 5, y: 20 };
+
+    // c1 should capture `p.x` via immutable borrow and
+    // `p.y` via mutable borrow.
+    let mut c1 = || {
+        println!("{}", p.x);
+
+        let incr = 10;
+
+        let mut c2 = || p.y += incr;
+        c2();
+
+        println!("{}", p.y);
+    };
+
+    c1();
+
+    // This should not throw an error because `p.x` is borrowed via Immutable borrow,
+    // and multiple immutable borrow of the same place are allowed.
+    let px = &p.x;
+
+    println!("{}", px);
+
+    c1();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs
new file mode 100644
index 00000000000..ed222b3148f
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/struct-pattern-matching-with-methods.rs
@@ -0,0 +1,49 @@
+// edition:2021
+//check-pass
+#![warn(unused)]
+#![allow(dead_code)]
+#![feature(rustc_attrs)]
+
+#[derive(Debug, Clone, Copy)]
+enum PointType {
+    TwoD { x: u32, y: u32 },
+
+    ThreeD{ x: u32, y: u32, z: u32 }
+}
+
+// Testing struct patterns
+struct Points {
+    points: Vec<PointType>,
+}
+
+impl Points {
+    pub fn test1(&mut self) -> Vec<usize> {
+        (0..self.points.len())
+            .filter_map(|i| {
+                let idx = i as usize;
+                match self.test2(idx) {
+                    PointType::TwoD { .. } => Some(i),
+                    PointType::ThreeD { .. } => None,
+                }
+            })
+            .collect()
+    }
+
+    pub fn test2(&mut self, i: usize) -> PointType {
+        self.points[i]
+    }
+}
+
+fn main() {
+    let mut points = Points {
+        points: Vec::<PointType>::new()
+    };
+
+    points.points.push(PointType::ThreeD { x:0, y:0, z:0 });
+    points.points.push(PointType::TwoD{ x:0, y:0 });
+    points.points.push(PointType::ThreeD{ x:0, y:0, z:0 });
+    points.points.push(PointType::TwoD{ x:0, y:0 });
+
+    println!("{:?}", points.test1());
+    println!("{:?}", points.points);
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs
new file mode 100644
index 00000000000..f3f44433ccf
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/tuple-struct-pattern-matching-with-methods.rs
@@ -0,0 +1,43 @@
+// edition:2021
+//check-pass
+
+#[derive(Copy, Clone)]
+enum PointType {
+    TwoD(u32, u32),
+    ThreeD(u32, u32, u32)
+}
+
+// Testing tuple struct patterns
+struct Points {
+    points: Vec<PointType>,
+}
+
+impl Points {
+    pub fn test1(&mut self) -> Vec<usize> {
+        (0..self.points.len())
+            .filter_map(|i| {
+                match self.test2(i) {
+                    PointType::TwoD (..) => Some(i),
+                    PointType::ThreeD (..) => None,
+                }
+            })
+            .collect()
+    }
+
+    pub fn test2(&mut self, i: usize) -> PointType {
+        self.points[i]
+    }
+}
+
+fn main() {
+    let mut points = Points {
+        points: Vec::<PointType>::new()
+    };
+
+    points.points.push(PointType::ThreeD(0,0,0));
+    points.points.push(PointType::TwoD(0,0));
+    points.points.push(PointType::ThreeD(0,0,1));
+    points.points.push(PointType::TwoD(0,1));
+
+    println!("{:?}", points.test1());
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs b/tests/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
new file mode 100644
index 00000000000..3f7ddf93f06
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/unsafe_ptr.rs
@@ -0,0 +1,47 @@
+// edition:2021
+// run-pass
+
+// Test that we can use raw ptrs when using `capture_disjoint_fields`.
+
+#![allow(dead_code)]
+
+#[derive(Debug)]
+struct S {
+    s: String,
+    t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+    let s = "".into();
+    let t = "".into();
+    let my_speed: Box<S> = Box::new(S { s, t });
+
+    let p : *const S = Box::into_raw(my_speed);
+    let t = T(p);
+
+    let c = || unsafe {
+        println!("{:?}", (*t.0).s);
+    };
+
+    c();
+}
+
+fn unsafe_mut() {
+    let s = "".into();
+    let t = "".into();
+    let mut my_speed: Box<S> = Box::new(S { s, t });
+    let p : *mut S = &mut *my_speed;
+
+    let c = || {
+        let x = unsafe { &mut (*p).s };
+        *x = "s".into();
+    };
+    c();
+}
+
+fn main() {
+    unsafe_mut();
+    unsafe_imm();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs b/tests/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs
new file mode 100644
index 00000000000..0206927cc59
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/run_pass/use_of_mutable_borrow_and_fake_reads.rs
@@ -0,0 +1,11 @@
+// edition:2021
+//check-pass
+#![feature(rustc_attrs)]
+
+fn main() {
+    let mut x = 0;
+    let c = || {
+        &mut x; // mutable borrow of `x`
+        match x { _ => () } // fake read of `x`
+    };
+}
diff --git a/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
new file mode 100644
index 00000000000..563095d440d
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
@@ -0,0 +1,39 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure that min analysis meets capture kind for all paths captured.
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 20 };
+
+    //
+    // Requirements:
+    // p.x -> MutBoorrow
+    // p   -> ImmBorrow
+    //
+    // Requirements met when p is captured via MutBorrow
+    //
+    let mut c = #[rustc_capture_analysis]
+        //~^ ERROR: attributes on expressions are experimental
+        //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        p.x += 10;
+        //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
+        //~| NOTE: p[] captured as MutBorrow here
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> MutBorrow
+        //~| NOTE: p[] used here
+    };
+
+    c();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
new file mode 100644
index 00000000000..05d79797ab3
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
@@ -0,0 +1,56 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/simple-struct-min-capture.rs:23:17
+   |
+LL |     let mut c = #[rustc_capture_analysis]
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/simple-struct-min-capture.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         p.x += 10;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> MutBorrow
+  --> $DIR/simple-struct-min-capture.rs:29:9
+   |
+LL |         p.x += 10;
+   |         ^^^
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/simple-struct-min-capture.rs:32:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/simple-struct-min-capture.rs:26:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         p.x += 10;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> MutBorrow
+  --> $DIR/simple-struct-min-capture.rs:29:9
+   |
+LL |         p.x += 10;
+   |         ^^^ p[] captured as MutBorrow here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/unsafe_ptr.rs b/tests/ui/closures/2229_closure_analysis/unsafe_ptr.rs
new file mode 100644
index 00000000000..eab9f9d08a9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/unsafe_ptr.rs
@@ -0,0 +1,62 @@
+// edition:2021
+
+// Test that we restrict precision of a capture when we access a raw ptr,
+// i.e. the capture doesn't deref the raw ptr.
+
+
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct S {
+    s: String,
+    t: String,
+}
+
+struct T(*const S);
+
+fn unsafe_imm() {
+    let s = "".into();
+    let t = "".into();
+    let my_speed: Box<S> = Box::new(S { s, t });
+
+    let p : *const S = Box::into_raw(my_speed);
+    let t = T(p);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+     || unsafe {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{:?}", (*t.0).s);
+        //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture t[(0, 0)] -> ImmBorrow
+    };
+
+    c();
+}
+
+fn unsafe_mut() {
+    let s = "".into();
+    let t = "".into();
+    let mut my_speed: Box<S> = Box::new(S { s, t });
+    let p : *mut S = &mut *my_speed;
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = unsafe { &mut (*p).s };
+        //~^ NOTE: Capturing p[Deref,(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ImmBorrow
+        *x = "s".into();
+    };
+    c();
+}
+
+fn main() {
+    unsafe_mut();
+    unsafe_imm();
+}
diff --git a/tests/ui/closures/2229_closure_analysis/unsafe_ptr.stderr b/tests/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
new file mode 100644
index 00000000000..e740a4d2d6b
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/unsafe_ptr.stderr
@@ -0,0 +1,93 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/unsafe_ptr.rs:25:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/unsafe_ptr.rs:45:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/unsafe_ptr.rs:28:6
+   |
+LL | /      || unsafe {
+LL | |
+LL | |
+LL | |         println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:31:26
+   |
+LL |         println!("{:?}", (*t.0).s);
+   |                          ^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/unsafe_ptr.rs:28:6
+   |
+LL | /      || unsafe {
+LL | |
+LL | |
+LL | |         println!("{:?}", (*t.0).s);
+LL | |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:31:26
+   |
+LL |         println!("{:?}", (*t.0).s);
+   |                          ^^^^^^^^
+
+error: First Pass analysis includes:
+  --> $DIR/unsafe_ptr.rs:48:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = unsafe { &mut (*p).s };
+...  |
+LL | |         *x = "s".into();
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[Deref,(0, 0)] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:51:31
+   |
+LL |         let x = unsafe { &mut (*p).s };
+   |                               ^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/unsafe_ptr.rs:48:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = unsafe { &mut (*p).s };
+...  |
+LL | |         *x = "s".into();
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ImmBorrow
+  --> $DIR/unsafe_ptr.rs:51:31
+   |
+LL |         let x = unsafe { &mut (*p).s };
+   |                               ^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/2229_closure_analysis/wild_patterns.rs b/tests/ui/closures/2229_closure_analysis/wild_patterns.rs
new file mode 100644
index 00000000000..a795088a1d9
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/wild_patterns.rs
@@ -0,0 +1,73 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test to ensure that we can handle cases where
+// let statements create no bindings are initialized
+// using a Place expression
+//
+// Note: Currently when feature `capture_disjoint_fields` is enabled
+// we can't handle such cases. So the test current use `_x` instead of
+// `_` until the issue is resolved.
+// Check rust-lang/project-rfc-2229#24 for status.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn wild_struct() {
+    let p = Point { x: 10, y: 20 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        // FIXME(arora-aman): Change `_x` to `_`
+        let Point { x: _x, y: _ } = p;
+        //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture p[(0, 0)] -> ImmBorrow
+    };
+
+    c();
+}
+
+fn wild_tuple() {
+    let t = (String::new(), 10);
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        // FIXME(arora-aman): Change `_x` to `_`
+        let (_x, _) = t;
+        //~^ NOTE: Capturing t[(0, 0)] -> ByValue
+        //~| NOTE: Min Capture t[(0, 0)] -> ByValue
+    };
+
+    c();
+}
+
+fn wild_arr() {
+    let arr = [String::new(), String::new()];
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        // FIXME(arora-aman): Change `_x` to `_`
+        let [_x, _] = arr;
+        //~^ NOTE: Capturing arr[Index] -> ByValue
+        //~| NOTE: Min Capture arr[] -> ByValue
+    };
+
+    c();
+}
+
+fn main() {}
diff --git a/tests/ui/closures/2229_closure_analysis/wild_patterns.stderr b/tests/ui/closures/2229_closure_analysis/wild_patterns.stderr
new file mode 100644
index 00000000000..c64378091e6
--- /dev/null
+++ b/tests/ui/closures/2229_closure_analysis/wild_patterns.stderr
@@ -0,0 +1,138 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/wild_patterns.rs:22:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/wild_patterns.rs:40:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/wild_patterns.rs:58:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error: First Pass analysis includes:
+  --> $DIR/wild_patterns.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> ImmBorrow
+  --> $DIR/wild_patterns.rs:29:37
+   |
+LL |         let Point { x: _x, y: _ } = p;
+   |                                     ^
+
+error: Min Capture analysis includes:
+  --> $DIR/wild_patterns.rs:25:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[(0, 0)] -> ImmBorrow
+  --> $DIR/wild_patterns.rs:29:37
+   |
+LL |         let Point { x: _x, y: _ } = p;
+   |                                     ^
+
+error: First Pass analysis includes:
+  --> $DIR/wild_patterns.rs:43:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0)] -> ByValue
+  --> $DIR/wild_patterns.rs:47:23
+   |
+LL |         let (_x, _) = t;
+   |                       ^
+
+error: Min Capture analysis includes:
+  --> $DIR/wild_patterns.rs:43:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[(0, 0)] -> ByValue
+  --> $DIR/wild_patterns.rs:47:23
+   |
+LL |         let (_x, _) = t;
+   |                       ^
+
+error: First Pass analysis includes:
+  --> $DIR/wild_patterns.rs:61:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing arr[Index] -> ByValue
+  --> $DIR/wild_patterns.rs:65:23
+   |
+LL |         let [_x, _] = arr;
+   |                       ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/wild_patterns.rs:61:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         // FIXME(arora-aman): Change `_x` to `_`
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture arr[] -> ByValue
+  --> $DIR/wild_patterns.rs:65:23
+   |
+LL |         let [_x, _] = arr;
+   |                       ^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/closures/add_semicolon_non_block_closure.rs b/tests/ui/closures/add_semicolon_non_block_closure.rs
new file mode 100644
index 00000000000..3ae91be60c5
--- /dev/null
+++ b/tests/ui/closures/add_semicolon_non_block_closure.rs
@@ -0,0 +1,11 @@
+fn foo(_f: impl Fn()) {}
+
+fn bar() -> i32 {
+    1
+}
+
+fn main() {
+    foo(|| bar())
+    //~^ ERROR mismatched types [E0308]
+    //~| HELP consider using a semicolon here
+}
diff --git a/tests/ui/closures/add_semicolon_non_block_closure.stderr b/tests/ui/closures/add_semicolon_non_block_closure.stderr
new file mode 100644
index 00000000000..ed829fc98f8
--- /dev/null
+++ b/tests/ui/closures/add_semicolon_non_block_closure.stderr
@@ -0,0 +1,16 @@
+error[E0308]: mismatched types
+  --> $DIR/add_semicolon_non_block_closure.rs:8:12
+   |
+LL | fn main() {
+   |           - expected `()` because of default return type
+LL |     foo(|| bar())
+   |            ^^^^^ expected `()`, found `i32`
+   |
+help: consider using a semicolon here
+   |
+LL |     foo(|| { bar(); })
+   |            +      +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/binder/async-closure-with-binder.rs b/tests/ui/closures/binder/async-closure-with-binder.rs
new file mode 100644
index 00000000000..4fa599d37cb
--- /dev/null
+++ b/tests/ui/closures/binder/async-closure-with-binder.rs
@@ -0,0 +1,8 @@
+// edition:2021
+#![feature(closure_lifetime_binder)]
+#![feature(async_closure)]
+fn main() {
+    for<'a> async || ();
+    //~^ ERROR `for<...>` binders on `async` closures are not currently supported
+    //~^^ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+}
diff --git a/tests/ui/closures/binder/async-closure-with-binder.stderr b/tests/ui/closures/binder/async-closure-with-binder.stderr
new file mode 100644
index 00000000000..1d4628b1a49
--- /dev/null
+++ b/tests/ui/closures/binder/async-closure-with-binder.stderr
@@ -0,0 +1,16 @@
+error: `for<...>` binders on `async` closures are not currently supported
+  --> $DIR/async-closure-with-binder.rs:5:5
+   |
+LL |     for<'a> async || ();
+   |     ^^^^^^^
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/async-closure-with-binder.rs:5:5
+   |
+LL |     for<'a> async || ();
+   |     -------^^^^^^^^^
+   |     |
+   |     `for<...>` is here
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/binder/disallow-const.rs b/tests/ui/closures/binder/disallow-const.rs
new file mode 100644
index 00000000000..72ad6185d37
--- /dev/null
+++ b/tests/ui/closures/binder/disallow-const.rs
@@ -0,0 +1,6 @@
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    for<const N: i32> || -> () {};
+    //~^ ERROR only lifetime parameters can be used in this context
+}
diff --git a/tests/ui/closures/binder/disallow-const.stderr b/tests/ui/closures/binder/disallow-const.stderr
new file mode 100644
index 00000000000..3c3b43d8cf3
--- /dev/null
+++ b/tests/ui/closures/binder/disallow-const.stderr
@@ -0,0 +1,8 @@
+error: only lifetime parameters can be used in this context
+  --> $DIR/disallow-const.rs:4:15
+   |
+LL |     for<const N: i32> || -> () {};
+   |               ^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/binder/disallow-ty.rs b/tests/ui/closures/binder/disallow-ty.rs
new file mode 100644
index 00000000000..bbe3d8488d9
--- /dev/null
+++ b/tests/ui/closures/binder/disallow-ty.rs
@@ -0,0 +1,6 @@
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    for<T> || -> () {};
+    //~^ ERROR only lifetime parameters can be used in this context
+}
diff --git a/tests/ui/closures/binder/disallow-ty.stderr b/tests/ui/closures/binder/disallow-ty.stderr
new file mode 100644
index 00000000000..51b6773edea
--- /dev/null
+++ b/tests/ui/closures/binder/disallow-ty.stderr
@@ -0,0 +1,8 @@
+error: only lifetime parameters can be used in this context
+  --> $DIR/disallow-ty.rs:4:9
+   |
+LL |     for<T> || -> () {};
+   |         ^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/binder/implicit-return.rs b/tests/ui/closures/binder/implicit-return.rs
new file mode 100644
index 00000000000..d34e5721d91
--- /dev/null
+++ b/tests/ui/closures/binder/implicit-return.rs
@@ -0,0 +1,6 @@
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    let _f = for<'a> |_: &'a ()| {};
+    //~^ implicit types in closure signatures are forbidden when `for<...>` is present
+}
diff --git a/tests/ui/closures/binder/implicit-return.stderr b/tests/ui/closures/binder/implicit-return.stderr
new file mode 100644
index 00000000000..5bfb9711334
--- /dev/null
+++ b/tests/ui/closures/binder/implicit-return.stderr
@@ -0,0 +1,10 @@
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-return.rs:4:34
+   |
+LL |     let _f = for<'a> |_: &'a ()| {};
+   |              -------             ^
+   |              |
+   |              `for<...>` is here
+
+error: aborting due to previous error
+
diff --git a/tests/ui/closures/binder/implicit-stuff.rs b/tests/ui/closures/binder/implicit-stuff.rs
new file mode 100644
index 00000000000..09e4c747afe
--- /dev/null
+++ b/tests/ui/closures/binder/implicit-stuff.rs
@@ -0,0 +1,27 @@
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    // Implicit types
+    let _ = for<> || {};                                      //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> || -> &'a _ { &() };                      //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x| -> &'a () { x };                      //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x: &'a _| -> &'a () { x };               //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x }; //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x };     //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x: &'a _| -> &'a &'a () { x };           //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+    let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ {            //~ ERROR implicit types in closure signatures are forbidden when `for<...>` is present
+        let _: &u8 = x;
+        let _: u32 = y;
+        let _: i32 = z;
+        x
+    };
+
+    // Lifetime elision
+    let _ = for<> |_: &()| -> () {};           //~ ERROR `&` without an explicit lifetime name cannot be used here
+    let _ = for<> |x: &()| -> &() { x };       //~ ERROR `&` without an explicit lifetime name cannot be used here
+                                               //~| ERROR `&` without an explicit lifetime name cannot be used here
+    let _ = for<> |x: &'_ ()| -> &'_ () { x }; //~ ERROR `'_` cannot be used here
+                                               //~| ERROR `'_` cannot be used here
+    let _ = for<'a> |x: &()| -> &'a () { x };  //~ ERROR `&` without an explicit lifetime name cannot be used here
+    let _ = for<'a> |x: &'a ()| -> &() { x };  //~ ERROR `&` without an explicit lifetime name cannot be used here
+}
diff --git a/tests/ui/closures/binder/implicit-stuff.stderr b/tests/ui/closures/binder/implicit-stuff.stderr
new file mode 100644
index 00000000000..779a08a44e5
--- /dev/null
+++ b/tests/ui/closures/binder/implicit-stuff.stderr
@@ -0,0 +1,107 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/implicit-stuff.rs:20:23
+   |
+LL |     let _ = for<> |_: &()| -> () {};
+   |                       ^ explicit lifetime name needed here
+
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/implicit-stuff.rs:21:23
+   |
+LL |     let _ = for<> |x: &()| -> &() { x };
+   |                       ^ explicit lifetime name needed here
+
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/implicit-stuff.rs:21:31
+   |
+LL |     let _ = for<> |x: &()| -> &() { x };
+   |                               ^ explicit lifetime name needed here
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/implicit-stuff.rs:23:24
+   |
+LL |     let _ = for<> |x: &'_ ()| -> &'_ () { x };
+   |                        ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/implicit-stuff.rs:23:35
+   |
+LL |     let _ = for<> |x: &'_ ()| -> &'_ () { x };
+   |                                   ^^ `'_` is a reserved lifetime name
+
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/implicit-stuff.rs:25:25
+   |
+LL |     let _ = for<'a> |x: &()| -> &'a () { x };
+   |                         ^ explicit lifetime name needed here
+
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/implicit-stuff.rs:26:36
+   |
+LL |     let _ = for<'a> |x: &'a ()| -> &() { x };
+   |                                    ^ explicit lifetime name needed here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:5:22
+   |
+LL |     let _ = for<> || {};
+   |             -----    ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:6:31
+   |
+LL |     let _ = for<'a> || -> &'a _ { &() };
+   |             -------           ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:7:22
+   |
+LL |     let _ = for<'a> |x| -> &'a () { x };
+   |             -------  ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:8:29
+   |
+LL |     let _ = for<'a> |x: &'a _| -> &'a () { x };
+   |             -------         ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:9:35
+   |
+LL |     let _ = for<'a> |x: &'a Vec::<_>| -> &'a Vec::<()> { x };
+   |             -------               ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:10:49
+   |
+LL |     let _ = for<'a> |x: &'a Vec<()>| -> &'a Vec<_> { x };
+   |             ------- `for<...>` is here          ^
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:11:29
+   |
+LL |     let _ = for<'a> |x: &'a _| -> &'a &'a () { x };
+   |             -------         ^
+   |             |
+   |             `for<...>` is here
+
+error: implicit types in closure signatures are forbidden when `for<...>` is present
+  --> $DIR/implicit-stuff.rs:12:29
+   |
+LL |     let _ = for<'a> |x: &'a _, y, z: _| -> &'a _ {
+   |             -------         ^  ^     ^         ^
+   |             |
+   |             `for<...>` is here
+
+error: aborting due to 15 previous errors
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/tests/ui/closures/binder/late-bound-in-body.rs b/tests/ui/closures/binder/late-bound-in-body.rs
new file mode 100644
index 00000000000..bb5c7552fda
--- /dev/null
+++ b/tests/ui/closures/binder/late-bound-in-body.rs
@@ -0,0 +1,9 @@
+// check-pass
+
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    let _ = for<'a> || -> () {
+        let _: &'a bool = &true;
+    };
+}
diff --git a/tests/ui/closures/binder/nested-closures-regions.rs b/tests/ui/closures/binder/nested-closures-regions.rs
new file mode 100644
index 00000000000..6bfc6c80b78
--- /dev/null
+++ b/tests/ui/closures/binder/nested-closures-regions.rs
@@ -0,0 +1,9 @@
+// check-pass
+
+#![feature(closure_lifetime_binder)]
+#![feature(rustc_attrs)]
+
+#[rustc_regions]
+fn main() {
+    for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
+}
diff --git a/tests/ui/closures/binder/nested-closures-regions.stderr b/tests/ui/closures/binder/nested-closures-regions.stderr
new file mode 100644
index 00000000000..b385e0ed6e0
--- /dev/null
+++ b/tests/ui/closures/binder/nested-closures-regions.stderr
@@ -0,0 +1,38 @@
+note: external requirements
+  --> $DIR/nested-closures-regions.rs:8:24
+   |
+LL |     for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: main::{closure#0}::{closure#0} with closure substs [
+               i8,
+               extern "rust-call" fn((&(),)),
+               (),
+           ]
+   = note: late-bound region is '_#4r
+   = note: late-bound region is '_#2r
+   = note: number of external vids: 3
+   = note: where '_#1r: '_#2r
+   = note: where '_#2r: '_#1r
+
+note: no external requirements
+  --> $DIR/nested-closures-regions.rs:8:5
+   |
+LL |     for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
+   |     ^^^^^^^^^^^^^^^^
+   |
+   = note: defining type: main::{closure#0} with closure substs [
+               i8,
+               extern "rust-call" fn(()),
+               (),
+           ]
+   = note: late-bound region is '_#2r
+
+note: no external requirements
+  --> $DIR/nested-closures-regions.rs:7:1
+   |
+LL | fn main() {
+   | ^^^^^^^^^
+   |
+   = note: defining type: main
+
diff --git a/tests/ui/closures/binder/nested-closures.rs b/tests/ui/closures/binder/nested-closures.rs
new file mode 100644
index 00000000000..b3c36e7eebb
--- /dev/null
+++ b/tests/ui/closures/binder/nested-closures.rs
@@ -0,0 +1,7 @@
+// check-pass
+
+#![feature(closure_lifetime_binder)]
+
+fn main() {
+    for<'a> || -> () { for<'c> |_: &'a ()| -> () {}; };
+}
diff --git a/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs b/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs
new file mode 100644
index 00000000000..b476dd50cc9
--- /dev/null
+++ b/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.rs
@@ -0,0 +1,7 @@
+#![feature(closure_lifetime_binder)]
+fn main() {
+    for<> |_: &'a ()| -> () {};
+    //~^ ERROR use of undeclared lifetime name `'a`
+    for<'a> |_: &'b ()| -> () {};
+    //~^ ERROR use of undeclared lifetime name `'b`
+}
diff --git a/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr b/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr
new file mode 100644
index 00000000000..1381acc15ca
--- /dev/null
+++ b/tests/ui/closures/binder/suggestion-for-introducing-lifetime-into-binder.stderr
@@ -0,0 +1,33 @@
+error[E0261]: use of undeclared lifetime name `'a`
+  --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:3:16
+   |
+LL |     for<> |_: &'a ()| -> () {};
+   |                ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'a` here
+   |
+LL |     for<'a, > |_: &'a ()| -> () {};
+   |         +++
+help: consider introducing lifetime `'a` here
+   |
+LL | fn main<'a>() {
+   |        ++++
+
+error[E0261]: use of undeclared lifetime name `'b`
+  --> $DIR/suggestion-for-introducing-lifetime-into-binder.rs:5:18
+   |
+LL |     for<'a> |_: &'b ()| -> () {};
+   |                  ^^ undeclared lifetime
+   |
+help: consider introducing lifetime `'b` here
+   |
+LL |     for<'b, 'a> |_: &'b ()| -> () {};
+   |         +++
+help: consider introducing lifetime `'b` here
+   |
+LL | fn main<'b>() {
+   |        ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0261`.
diff --git a/tests/ui/closures/closure-array-break-length.rs b/tests/ui/closures/closure-array-break-length.rs
new file mode 100644
index 00000000000..fda590fda02
--- /dev/null
+++ b/tests/ui/closures/closure-array-break-length.rs
@@ -0,0 +1,7 @@
+fn main() {
+    |_: [_; continue]| {}; //~ ERROR: `continue` outside of a loop
+
+    while |_: [_; continue]| {} {} //~ ERROR: `continue` outside of a loop
+
+    while |_: [_; break]| {} {} //~ ERROR: `break` outside of a loop
+}
diff --git a/tests/ui/closures/closure-array-break-length.stderr b/tests/ui/closures/closure-array-break-length.stderr
new file mode 100644
index 00000000000..7e0b0027a6f
--- /dev/null
+++ b/tests/ui/closures/closure-array-break-length.stderr
@@ -0,0 +1,21 @@
+error[E0268]: `continue` outside of a loop
+  --> $DIR/closure-array-break-length.rs:2:13
+   |
+LL |     |_: [_; continue]| {};
+   |             ^^^^^^^^ cannot `continue` outside of a loop
+
+error[E0268]: `continue` outside of a loop
+  --> $DIR/closure-array-break-length.rs:4:19
+   |
+LL |     while |_: [_; continue]| {} {}
+   |                   ^^^^^^^^ cannot `continue` outside of a loop
+
+error[E0268]: `break` outside of a loop or labeled block
+  --> $DIR/closure-array-break-length.rs:6:19
+   |
+LL |     while |_: [_; break]| {} {}
+   |                   ^^^^^ cannot `break` outside of a loop or labeled block
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0268`.
diff --git a/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs b/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs
new file mode 100644
index 00000000000..039cf3e0464
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.rs
@@ -0,0 +1,11 @@
+struct X<F> where F: FnOnce() + 'static + Send {
+    field: F,
+}
+
+fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static {
+    //~^ ERROR `F` cannot be sent between threads safely
+    return X { field: blk };
+}
+
+fn main() {
+}
diff --git a/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr b/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr
new file mode 100644
index 00000000000..bf6ec5c36e4
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-cant-promote-superkind-in-struct.stderr
@@ -0,0 +1,19 @@
+error[E0277]: `F` cannot be sent between threads safely
+  --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:5:22
+   |
+LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static {
+   |                      ^^^^ `F` cannot be sent between threads safely
+   |
+note: required by a bound in `X`
+  --> $DIR/closure-bounds-cant-promote-superkind-in-struct.rs:1:43
+   |
+LL | struct X<F> where F: FnOnce() + 'static + Send {
+   |                                           ^^^^ required by this bound in `X`
+help: consider further restricting this bound
+   |
+LL | fn foo<F>(blk: F) -> X<F> where F: FnOnce() + 'static + std::marker::Send {
+   |                                                       +++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.rs b/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.rs
new file mode 100644
index 00000000000..7327d825668
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.rs
@@ -0,0 +1,13 @@
+fn bar<F>(blk: F) where F: FnOnce() + 'static {
+}
+
+fn foo(x: &()) {
+    bar(|| {
+        //~^ ERROR borrowed data escapes
+        //~| ERROR closure may outlive
+        let _ = x;
+    })
+}
+
+fn main() {
+}
diff --git a/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr b/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr
new file mode 100644
index 00000000000..85df5c1e5b3
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-static-cant-capture-borrowed.stderr
@@ -0,0 +1,44 @@
+error[E0521]: borrowed data escapes outside of function
+  --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:5
+   |
+LL |   fn foo(x: &()) {
+   |          -  - let's call the lifetime of this reference `'1`
+   |          |
+   |          `x` is a reference that is only valid in the function body
+LL | /     bar(|| {
+LL | |
+LL | |
+LL | |         let _ = x;
+LL | |     })
+   | |      ^
+   | |      |
+   | |______`x` escapes the function body here
+   |        argument requires that `'1` must outlive `'static`
+
+error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
+  --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:9
+   |
+LL |     bar(|| {
+   |         ^^ may outlive borrowed value `x`
+...
+LL |         let _ = x;
+   |                 - `x` is borrowed here
+   |
+note: function requires argument type to outlive `'static`
+  --> $DIR/closure-bounds-static-cant-capture-borrowed.rs:5:5
+   |
+LL | /     bar(|| {
+LL | |
+LL | |
+LL | |         let _ = x;
+LL | |     })
+   | |______^
+help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
+   |
+LL |     bar(move || {
+   |         ++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0373, E0521.
+For more information about an error, try `rustc --explain E0373`.
diff --git a/tests/ui/closures/closure-bounds-subtype.rs b/tests/ui/closures/closure-bounds-subtype.rs
new file mode 100644
index 00000000000..4888cbfcc1f
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-subtype.rs
@@ -0,0 +1,16 @@
+fn take_any<F>(_: F) where F: FnOnce() {
+}
+
+fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send {
+}
+
+fn give_any<F>(f: F) where F: FnOnce() {
+    take_any(f);
+}
+
+fn give_owned<F>(f: F) where F: FnOnce() + Send {
+    take_any(f);
+    take_const_owned(f); //~ ERROR `F` cannot be shared between threads safely [E0277]
+}
+
+fn main() {}
diff --git a/tests/ui/closures/closure-bounds-subtype.stderr b/tests/ui/closures/closure-bounds-subtype.stderr
new file mode 100644
index 00000000000..8ad8273fc2b
--- /dev/null
+++ b/tests/ui/closures/closure-bounds-subtype.stderr
@@ -0,0 +1,25 @@
+error[E0277]: `F` cannot be shared between threads safely
+  --> $DIR/closure-bounds-subtype.rs:13:22
+   |
+LL |     take_const_owned(f);
+   |     ---------------- ^ `F` cannot be shared between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `take_const_owned`
+  --> $DIR/closure-bounds-subtype.rs:4:50
+   |
+LL | fn take_const_owned<F>(_: F) where F: FnOnce() + Sync + Send {
+   |                                                  ^^^^ required by this bound in `take_const_owned`
+help: use parentheses to call this type parameter
+   |
+LL |     take_const_owned(f());
+   |                       ++
+help: consider further restricting this bound
+   |
+LL | fn give_owned<F>(f: F) where F: FnOnce() + Send + std::marker::Sync {
+   |                                                 +++++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr
new file mode 100644
index 00000000000..8846ccef34e
--- /dev/null
+++ b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr
@@ -0,0 +1,37 @@
+error: lifetime may not live long enough
+  --> $DIR/expect-region-supply-region-2.rs:14:30
+   |
+LL | fn expect_bound_supply_named<'x>() {
+   |                              -- lifetime `'x` defined here
+...
+LL |     closure_expecting_bound(|x: &'x u32| {
+   |                              ^  - let's call the lifetime of this reference `'1`
+   |                              |
+   |                              requires that `'1` must outlive `'x`
+
+error[E0521]: borrowed data escapes outside of closure
+  --> $DIR/expect-region-supply-region-2.rs:20:9
+   |
+LL |     let mut f: Option<&u32> = None;
+   |         ----- `f` declared here, outside of the closure body
+...
+LL |     closure_expecting_bound(|x: &'x u32| {
+   |                              - `x` is a reference that is only valid in the closure body
+...
+LL |         f = Some(x);
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
+
+error: lifetime may not live long enough
+  --> $DIR/expect-region-supply-region-2.rs:14:30
+   |
+LL | fn expect_bound_supply_named<'x>() {
+   |                              -- lifetime `'x` defined here
+...
+LL |     closure_expecting_bound(|x: &'x u32| {
+   |                              ^ requires that `'x` must outlive `'static`
+   |
+   = help: consider replacing `'x` with `'static`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.rs b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.rs
new file mode 100644
index 00000000000..9b51bbd58a3
--- /dev/null
+++ b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.rs
@@ -0,0 +1,24 @@
+#![allow(warnings)]
+
+fn closure_expecting_bound<F>(_: F)
+where
+    F: FnOnce(&u32),
+{
+}
+
+fn expect_bound_supply_named<'x>() {
+    let mut f: Option<&u32> = None;
+
+    // Here we give a type annotation that `x` should be free. We get
+    // an error because of that.
+    closure_expecting_bound(|x: &'x u32| {
+        //~^ ERROR lifetime may not live long enough
+        //~| ERROR lifetime may not live long enough
+
+        // Borrowck doesn't get a chance to run, but if it did it should error
+        // here.
+        f = Some(x);
+    });
+}
+
+fn main() {}
diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr
new file mode 100644
index 00000000000..9aab51c986c
--- /dev/null
+++ b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.stderr
@@ -0,0 +1,22 @@
+error: lifetime may not live long enough
+  --> $DIR/expect-region-supply-region-2.rs:14:30
+   |
+LL | fn expect_bound_supply_named<'x>() {
+   |                              -- lifetime `'x` defined here
+...
+LL |     closure_expecting_bound(|x: &'x u32| {
+   |                              ^  - let's call the lifetime of this reference `'1`
+   |                              |
+   |                              requires that `'1` must outlive `'x`
+
+error: lifetime may not live long enough
+  --> $DIR/expect-region-supply-region-2.rs:14:30
+   |
+LL | fn expect_bound_supply_named<'x>() {
+   |                              -- lifetime `'x` defined here
+...
+LL |     closure_expecting_bound(|x: &'x u32| {
+   |                              ^ requires that `'x` must outlive `'static`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region.rs b/tests/ui/closures/closure-expected-type/expect-region-supply-region.rs
new file mode 100644
index 00000000000..55c6aa795c2
--- /dev/null
+++ b/tests/ui/closures/closure-expected-type/expect-region-supply-region.rs
@@ -0,0 +1,57 @@
+#![allow(warnings)]
+
+fn closure_expecting_bound<F>(_: F)
+where
+    F: FnOnce(&u32),
+{
+}
+
+fn closure_expecting_free<'a, F>(_: F)
+where
+    F: FnOnce(&'a u32),
+{
+}
+
+fn expect_bound_supply_nothing() {
+    // Because `x` is inferred to have a bound region, we cannot allow
+    // it to escape into `f`:
+    let mut f: Option<&u32> = None;
+    closure_expecting_bound(|x| {
+        f = Some(x); //~ ERROR borrowed data escapes outside of closure
+    });
+}
+
+fn expect_bound_supply_bound() {
+    // Because `x` is inferred to have a bound region, we cannot allow
+    // it to escape into `f`, even with an explicit type annotation on
+    // closure:
+    let mut f: Option<&u32> = None;
+    closure_expecting_bound(|x: &u32| {
+        f = Some(x); //~ ERROR borrowed data escapes outside of closure
+    });
+}
+
+fn expect_free_supply_nothing() {
+    let mut f: Option<&u32> = None;
+    closure_expecting_free(|x| f = Some(x)); // OK
+}
+
+fn expect_free_supply_bound() {
+    let mut f: Option<&u32> = None;
+
+    // Here, even though the annotation `&u32` could be seen as being
+    // bound in the closure, we permit it to be defined as a free
+    // region (which is inferred to something in the fn body).
+    closure_expecting_free(|x: &u32| f = Some(x)); // OK
+}
+
+fn expect_free_supply_named<'x>() {
+    let mut f: Option<&u32> = None;
+
+    // Here, even though the annotation `&u32` could be seen as being
+    // bound in the closure, we permit it to be defined as a free
+    // region (which is inferred to something in the fn body).
+    closure_expecting_free(|x: &'x u32| f = Some(x)); // OK
+}
+
+fn main() {}
diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region.stderr b/tests/ui/closures/closure-expected-type/expect-region-supply-region.stderr
new file mode 100644
index 00000000000..0d97fa7e230
--- /dev/null
+++ b/tests/ui/closures/closure-expected-type/expect-region-supply-region.stderr
@@ -0,0 +1,23 @@
+error[E0521]: borrowed data escapes outside of closure
+  --> $DIR/expect-region-supply-region.rs:20:9
+   |
+LL |     let mut f: Option<&u32> = None;
+   |         ----- `f` declared here, outside of the closure body
+LL |     closure_expecting_bound(|x| {
+   |                              - `x` is a reference that is only valid in the closure body
+LL |         f = Some(x);
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
+
+error[E0521]: borrowed data escapes outside of closure
+  --> $DIR/expect-region-supply-region.rs:30:9
+   |
+LL |     let mut f: Option<&u32> = None;
+   |         ----- `f` declared here, outside of the closure body
+LL |     closure_expecting_bound(|x: &u32| {
+   |                              - `x` is a reference that is only valid in the closure body
+LL |         f = Some(x);
+   |         ^^^^^^^^^^^ `x` escapes the closure body here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0521`.
diff --git a/tests/ui/closures/closure-expected.rs b/tests/ui/closures/closure-expected.rs
new file mode 100644
index 00000000000..68cac3dd85e
--- /dev/null
+++ b/tests/ui/closures/closure-expected.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let x = Some(1);
+    let y = x.or_else(4);
+    //~^ ERROR expected a `FnOnce<()>` closure, found `{integer}`
+}
diff --git a/tests/ui/closures/closure-expected.stderr b/tests/ui/closures/closure-expected.stderr
new file mode 100644
index 00000000000..87a5d67a420
--- /dev/null
+++ b/tests/ui/closures/closure-expected.stderr
@@ -0,0 +1,16 @@
+error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
+  --> $DIR/closure-expected.rs:3:23
+   |
+LL |     let y = x.or_else(4);
+   |               ------- ^ expected an `FnOnce<()>` closure, found `{integer}`
+   |               |
+   |               required by a bound introduced by this call
+   |
+   = help: the trait `FnOnce<()>` is not implemented for `{integer}`
+   = note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
+note: required by a bound in `Option::<T>::or_else`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/closure-immutable-outer-variable.fixed b/tests/ui/closures/closure-immutable-outer-variable.fixed
new file mode 100644
index 00000000000..1b0feede34e
--- /dev/null
+++ b/tests/ui/closures/closure-immutable-outer-variable.fixed
@@ -0,0 +1,13 @@
+// run-rustfix
+
+// Point at the captured immutable outer variable
+
+fn foo(mut f: Box<dyn FnMut()>) {
+    f();
+}
+
+fn main() {
+    let mut y = true;
+    foo(Box::new(move || y = !y) as Box<_>);
+    //~^ ERROR cannot assign to `y`, as it is not declared as mutable
+}
diff --git a/tests/ui/closures/closure-immutable-outer-variable.rs b/tests/ui/closures/closure-immutable-outer-variable.rs
new file mode 100644
index 00000000000..50ec1c6148a
--- /dev/null
+++ b/tests/ui/closures/closure-immutable-outer-variable.rs
@@ -0,0 +1,13 @@
+// run-rustfix
+
+// Point at the captured immutable outer variable
+
+fn foo(mut f: Box<dyn FnMut()>) {
+    f();
+}
+
+fn main() {
+    let y = true;
+    foo(Box::new(move || y = !y) as Box<_>);
+    //~^ ERROR cannot assign to `y`, as it is not declared as mutable
+}
diff --git a/tests/ui/closures/closure-immutable-outer-variable.rs.fixed b/tests/ui/closures/closure-immutable-outer-variable.rs.fixed
new file mode 100644
index 00000000000..5c6358beb24
--- /dev/null
+++ b/tests/ui/closures/closure-immutable-outer-variable.rs.fixed
@@ -0,0 +1,10 @@
+// Point at the captured immutable outer variable
+
+fn foo(mut f: Box<FnMut()>) {
+    f();
+}
+
+fn main() {
+    let mut y = true;
+    foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
+}
diff --git a/tests/ui/closures/closure-immutable-outer-variable.stderr b/tests/ui/closures/closure-immutable-outer-variable.stderr
new file mode 100644
index 00000000000..799097889cd
--- /dev/null
+++ b/tests/ui/closures/closure-immutable-outer-variable.stderr
@@ -0,0 +1,11 @@
+error[E0594]: cannot assign to `y`, as it is not declared as mutable
+  --> $DIR/closure-immutable-outer-variable.rs:11:26
+   |
+LL |     let y = true;
+   |         - help: consider changing this to be mutable: `mut y`
+LL |     foo(Box::new(move || y = !y) as Box<_>);
+   |                          ^^^^^^ cannot assign
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/tests/ui/closures/closure-move-sync.rs b/tests/ui/closures/closure-move-sync.rs
new file mode 100644
index 00000000000..ea2d1434c4a
--- /dev/null
+++ b/tests/ui/closures/closure-move-sync.rs
@@ -0,0 +1,22 @@
+use std::thread;
+use std::sync::mpsc::channel;
+
+fn bar() {
+    let (send, recv) = channel();
+    let t = thread::spawn(|| {
+        recv.recv().unwrap();
+        //~^^ ERROR `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
+    });
+
+    send.send(());
+
+    t.join().unwrap();
+}
+
+fn foo() {
+    let (tx, _rx) = channel();
+    thread::spawn(|| tx.send(()).unwrap());
+    //~^ ERROR `Sender<()>` cannot be shared between threads safely
+}
+
+fn main() {}
diff --git a/tests/ui/closures/closure-move-sync.stderr b/tests/ui/closures/closure-move-sync.stderr
new file mode 100644
index 00000000000..64e3b51ea71
--- /dev/null
+++ b/tests/ui/closures/closure-move-sync.stderr
@@ -0,0 +1,43 @@
+error[E0277]: `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
+  --> $DIR/closure-move-sync.rs:6:27
+   |
+LL |       let t = thread::spawn(|| {
+   |  _____________-------------_^
+   | |             |
+   | |             required by a bound introduced by this call
+LL | |         recv.recv().unwrap();
+LL | |
+LL | |     });
+   | |_____^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
+   |
+   = help: the trait `Sync` is not implemented for `std::sync::mpsc::Receiver<()>`
+   = note: required for `&std::sync::mpsc::Receiver<()>` to implement `Send`
+note: required because it's used within this closure
+  --> $DIR/closure-move-sync.rs:6:27
+   |
+LL |     let t = thread::spawn(|| {
+   |                           ^^
+note: required by a bound in `spawn`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+
+error[E0277]: `Sender<()>` cannot be shared between threads safely
+  --> $DIR/closure-move-sync.rs:18:19
+   |
+LL |     thread::spawn(|| tx.send(()).unwrap());
+   |     ------------- ^^^^^^^^^^^^^^^^^^^^^^^ `Sender<()>` cannot be shared between threads safely
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Sync` is not implemented for `Sender<()>`
+   = note: required for `&Sender<()>` to implement `Send`
+note: required because it's used within this closure
+  --> $DIR/closure-move-sync.rs:18:19
+   |
+LL |     thread::spawn(|| tx.send(()).unwrap());
+   |                   ^^
+note: required by a bound in `spawn`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/closure-no-fn-1.rs b/tests/ui/closures/closure-no-fn-1.rs
new file mode 100644
index 00000000000..48c3e472520
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-1.rs
@@ -0,0 +1,8 @@
+// Ensure that capturing closures are never coerced to fns
+// Especially interesting as non-capturing closures can be.
+
+fn main() {
+    let mut a = 0u8;
+    let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/closures/closure-no-fn-1.stderr b/tests/ui/closures/closure-no-fn-1.stderr
new file mode 100644
index 00000000000..eab7482e6c4
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-1.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-no-fn-1.rs:6:29
+   |
+LL |     let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+   |              ------------   ^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
+   |              |
+   |              expected due to this
+   |
+   = note: expected fn pointer `fn(u8) -> u8`
+                 found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:36]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-no-fn-1.rs:6:39
+   |
+LL |     let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+   |                                       ^ `a` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-no-fn-2.rs b/tests/ui/closures/closure-no-fn-2.rs
new file mode 100644
index 00000000000..f3066f7a330
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-2.rs
@@ -0,0 +1,8 @@
+// Ensure that capturing closures are never coerced to fns
+// Especially interesting as non-capturing closures can be.
+
+fn main() {
+    let b = 0u8;
+    let bar: fn() -> u8 = || { b };
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/closures/closure-no-fn-2.stderr b/tests/ui/closures/closure-no-fn-2.stderr
new file mode 100644
index 00000000000..e1f0143abfe
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-2.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-no-fn-2.rs:6:27
+   |
+LL |     let bar: fn() -> u8 = || { b };
+   |              ----------   ^^^^^^^^ expected fn pointer, found closure
+   |              |
+   |              expected due to this
+   |
+   = note: expected fn pointer `fn() -> u8`
+                 found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:29]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-no-fn-2.rs:6:32
+   |
+LL |     let bar: fn() -> u8 = || { b };
+   |                                ^ `b` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-no-fn-3.rs b/tests/ui/closures/closure-no-fn-3.rs
new file mode 100644
index 00000000000..53217c2f199
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-3.rs
@@ -0,0 +1,8 @@
+// Ensure that capturing closures are never coerced to fns
+// Especially interesting as non-capturing closures can be.
+
+fn main() {
+    let b = 0u8;
+    let baz: fn() -> u8 = (|| { b }) as fn() -> u8;
+    //~^ ERROR non-primitive cast
+}
diff --git a/tests/ui/closures/closure-no-fn-3.stderr b/tests/ui/closures/closure-no-fn-3.stderr
new file mode 100644
index 00000000000..6009389b1bb
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-3.stderr
@@ -0,0 +1,9 @@
+error[E0605]: non-primitive cast: `[closure@$DIR/closure-no-fn-3.rs:6:28: 6:30]` as `fn() -> u8`
+  --> $DIR/closure-no-fn-3.rs:6:27
+   |
+LL |     let baz: fn() -> u8 = (|| { b }) as fn() -> u8;
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^ invalid cast
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0605`.
diff --git a/tests/ui/closures/closure-no-fn-4.rs b/tests/ui/closures/closure-no-fn-4.rs
new file mode 100644
index 00000000000..275bff645db
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-4.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let b = 2;
+    let _: fn(usize) -> usize = match true {
+        true => |a| a + 1,
+        false => |a| a - b,
+        //~^ ERROR `match` arms have incompatible types
+    };
+}
diff --git a/tests/ui/closures/closure-no-fn-4.stderr b/tests/ui/closures/closure-no-fn-4.stderr
new file mode 100644
index 00000000000..d1b7048841a
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-4.stderr
@@ -0,0 +1,24 @@
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure-no-fn-4.rs:5:18
+   |
+LL |       let _: fn(usize) -> usize = match true {
+   |  _________________________________-
+LL | |         true => |a| a + 1,
+   | |                 --------- this is found to be of type `fn(usize) -> usize`
+LL | |         false => |a| a - b,
+   | |                  ^^^^^^^^^ expected fn pointer, found closure
+LL | |
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected fn pointer `fn(usize) -> usize`
+                 found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:21]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-no-fn-4.rs:5:26
+   |
+LL |         false => |a| a - b,
+   |                          ^ `b` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-no-fn-5.rs b/tests/ui/closures/closure-no-fn-5.rs
new file mode 100644
index 00000000000..43e3e977e34
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-5.rs
@@ -0,0 +1,12 @@
+// When providing diagnostics about not being able to coerce a capturing-closure
+// to fn type, we want to report only upto 4 captures.
+
+fn main() {
+    let a = 0u8;
+    let b = 0u8;
+    let c = 0u8;
+    let d = 0u8;
+    let e = 0u8;
+    let bar: fn() -> u8 = || { a; b; c; d; e };
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/closures/closure-no-fn-5.stderr b/tests/ui/closures/closure-no-fn-5.stderr
new file mode 100644
index 00000000000..a33b847ea92
--- /dev/null
+++ b/tests/ui/closures/closure-no-fn-5.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-no-fn-5.rs:10:27
+   |
+LL |     let bar: fn() -> u8 = || { a; b; c; d; e };
+   |              ----------   ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
+   |              |
+   |              expected due to this
+   |
+   = note: expected fn pointer `fn() -> u8`
+                 found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:29]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-no-fn-5.rs:10:32
+   |
+LL |     let bar: fn() -> u8 = || { a; b; c; d; e };
+   |                                ^  ^  ^  ^ `d` captured here
+   |                                |  |  |
+   |                                |  |  `c` captured here
+   |                                |  `b` captured here
+   |                                `a` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-referencing-itself-issue-25954.rs b/tests/ui/closures/closure-referencing-itself-issue-25954.rs
new file mode 100644
index 00000000000..7dd0e51797d
--- /dev/null
+++ b/tests/ui/closures/closure-referencing-itself-issue-25954.rs
@@ -0,0 +1,18 @@
+// Regression test for #25954: detect and reject a closure type that
+// references itself.
+
+use std::cell::{Cell, RefCell};
+
+struct A<T: Fn()> {
+    x: RefCell<Option<T>>,
+    b: Cell<i32>,
+}
+
+fn main() {
+    let mut p = A{x: RefCell::new(None), b: Cell::new(4i32)};
+
+    // This is an error about types of infinite size:
+    let q = || p.b.set(5i32); //~ ERROR mismatched types
+
+    *(p.x.borrow_mut()) = Some(q);
+}
diff --git a/tests/ui/closures/closure-referencing-itself-issue-25954.stderr b/tests/ui/closures/closure-referencing-itself-issue-25954.stderr
new file mode 100644
index 00000000000..8ca43cd1cff
--- /dev/null
+++ b/tests/ui/closures/closure-referencing-itself-issue-25954.stderr
@@ -0,0 +1,9 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-referencing-itself-issue-25954.rs:15:13
+   |
+LL |     let q = || p.b.set(5i32);
+   |             ^^^^^^^^^^^^^^^^ cyclic type of infinite size
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-reform-bad.rs b/tests/ui/closures/closure-reform-bad.rs
new file mode 100644
index 00000000000..0ba48ab5184
--- /dev/null
+++ b/tests/ui/closures/closure-reform-bad.rs
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+fn call_bare(f: fn(&str)) {
+    f("Hello ");
+}
+
+fn main() {
+    let string = "world!";
+    let f = |s: &str| println!("{}{}", s, string);
+    call_bare(f)    //~ ERROR mismatched types
+}
diff --git a/tests/ui/closures/closure-reform-bad.stderr b/tests/ui/closures/closure-reform-bad.stderr
new file mode 100644
index 00000000000..4c40f70b9d0
--- /dev/null
+++ b/tests/ui/closures/closure-reform-bad.stderr
@@ -0,0 +1,26 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-reform-bad.rs:11:15
+   |
+LL |     let f = |s: &str| println!("{}{}", s, string);
+   |             --------- the found closure
+LL |     call_bare(f)
+   |     --------- ^ expected fn pointer, found closure
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected fn pointer `for<'a> fn(&'a str)`
+                 found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:22]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-reform-bad.rs:10:43
+   |
+LL |     let f = |s: &str| println!("{}{}", s, string);
+   |                                           ^^^^^^ `string` captured here
+note: function defined here
+  --> $DIR/closure-reform-bad.rs:4:4
+   |
+LL | fn call_bare(f: fn(&str)) {
+   |    ^^^^^^^^^ -----------
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-return-type-mismatch.rs b/tests/ui/closures/closure-return-type-mismatch.rs
new file mode 100644
index 00000000000..1631bb303e5
--- /dev/null
+++ b/tests/ui/closures/closure-return-type-mismatch.rs
@@ -0,0 +1,17 @@
+fn main() {
+    || {
+        if false {
+            return "test";
+        }
+        let a = true;
+        a //~ ERROR mismatched types
+    };
+
+    || -> bool {
+        if false {
+            return "hello" //~ ERROR mismatched types
+        };
+        let b = true;
+        b
+    };
+}
diff --git a/tests/ui/closures/closure-return-type-mismatch.stderr b/tests/ui/closures/closure-return-type-mismatch.stderr
new file mode 100644
index 00000000000..3a89d30a05d
--- /dev/null
+++ b/tests/ui/closures/closure-return-type-mismatch.stderr
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-return-type-mismatch.rs:7:9
+   |
+LL |         a
+   |         ^ expected `&str`, found `bool`
+   |
+note: return type inferred to be `&str` here
+  --> $DIR/closure-return-type-mismatch.rs:4:20
+   |
+LL |             return "test";
+   |                    ^^^^^^
+
+error[E0308]: mismatched types
+  --> $DIR/closure-return-type-mismatch.rs:12:20
+   |
+LL |             return "hello"
+   |                    ^^^^^^^ expected `bool`, found `&str`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure-return-type-must-be-sized.rs b/tests/ui/closures/closure-return-type-must-be-sized.rs
new file mode 100644
index 00000000000..8cfa0291229
--- /dev/null
+++ b/tests/ui/closures/closure-return-type-must-be-sized.rs
@@ -0,0 +1,74 @@
+#![feature(unboxed_closures)]
+
+trait A {
+    fn a() where Self: Sized;
+}
+
+mod a {
+    use crate::A;
+
+    pub fn foo<F: FnOnce<()>>() where F::Output: A {
+        F::Output::a()
+    }
+
+    pub fn bar<F: FnOnce() -> R, R: ?Sized>() {}
+
+    pub fn baz<F: FnOnce<()>>() where F::Output: A, F::Output: Sized {
+        F::Output::a()
+    }
+}
+
+mod b {
+    use crate::A;
+
+    pub fn foo<F: Fn<()>>() where F::Output: A {
+        F::Output::a()
+    }
+
+    pub fn bar<F: Fn() -> R, R: ?Sized>() {}
+
+    pub fn baz<F: Fn<()>>() where F::Output: A, F::Output: Sized {
+        F::Output::a()
+    }
+}
+
+mod c {
+    use crate::A;
+
+    pub fn foo<F: FnMut<()>>() where F::Output: A {
+        F::Output::a()
+    }
+
+    pub fn bar<F: FnMut() -> R, R: ?Sized>() {}
+
+    pub fn baz<F: FnMut<()>>() where F::Output: A, F::Output: Sized {
+        F::Output::a()
+    }
+}
+
+impl A for Box<dyn A> {
+    fn a() {}
+}
+
+fn main() {
+    a::foo::<fn() -> dyn A>();         //~ ERROR E0277
+    a::bar::<fn() -> dyn A, _>();      //~ ERROR E0277
+    a::baz::<fn() -> dyn A>();         //~ ERROR E0277
+    a::foo::<fn() -> Box<dyn A>>();    //  ok
+    a::bar::<fn() -> Box<dyn A>, _>(); //  ok
+    a::baz::<fn() -> Box<dyn A>>();    //  ok
+
+    b::foo::<fn() -> dyn A>();         //~ ERROR E0277
+    b::bar::<fn() -> dyn A, _>();      //~ ERROR E0277
+    b::baz::<fn() -> dyn A>();         //~ ERROR E0277
+    b::foo::<fn() -> Box<dyn A>>();    //  ok
+    b::bar::<fn() -> Box<dyn A>, _>(); //  ok
+    b::baz::<fn() -> Box<dyn A>>();    //  ok
+
+    c::foo::<fn() -> dyn A>();         //~ ERROR E0277
+    c::bar::<fn() -> dyn A, _>();      //~ ERROR E0277
+    c::baz::<fn() -> dyn A>();         //~ ERROR E0277
+    c::foo::<fn() -> Box<dyn A>>();    //  ok
+    c::bar::<fn() -> Box<dyn A>, _>(); //  ok
+    c::baz::<fn() -> Box<dyn A>>();    //  ok
+}
diff --git a/tests/ui/closures/closure-return-type-must-be-sized.stderr b/tests/ui/closures/closure-return-type-must-be-sized.stderr
new file mode 100644
index 00000000000..d4fc723fa81
--- /dev/null
+++ b/tests/ui/closures/closure-return-type-must-be-sized.stderr
@@ -0,0 +1,99 @@
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:54:5
+   |
+LL |     a::foo::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:55:14
+   |
+LL |     a::bar::<fn() -> dyn A, _>();
+   |              ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+note: required by a bound in `a::bar`
+  --> $DIR/closure-return-type-must-be-sized.rs:14:19
+   |
+LL |     pub fn bar<F: FnOnce() -> R, R: ?Sized>() {}
+   |                   ^^^^^^^^^^^^^ required by this bound in `bar`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:56:5
+   |
+LL |     a::baz::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:61:5
+   |
+LL |     b::foo::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:62:14
+   |
+LL |     b::bar::<fn() -> dyn A, _>();
+   |              ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+note: required by a bound in `b::bar`
+  --> $DIR/closure-return-type-must-be-sized.rs:28:19
+   |
+LL |     pub fn bar<F: Fn() -> R, R: ?Sized>() {}
+   |                   ^^^^^^^^^ required by this bound in `bar`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:63:5
+   |
+LL |     b::baz::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:68:5
+   |
+LL |     c::foo::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:69:14
+   |
+LL |     c::bar::<fn() -> dyn A, _>();
+   |              ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+note: required by a bound in `c::bar`
+  --> $DIR/closure-return-type-must-be-sized.rs:42:19
+   |
+LL |     pub fn bar<F: FnMut() -> R, R: ?Sized>() {}
+   |                   ^^^^^^^^^^^^ required by this bound in `bar`
+
+error[E0277]: the size for values of type `dyn A` cannot be known at compilation time
+  --> $DIR/closure-return-type-must-be-sized.rs:70:5
+   |
+LL |     c::baz::<fn() -> dyn A>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: within `fn() -> dyn A`, the trait `Sized` is not implemented for `dyn A`
+   = note: required because it appears within the type `fn() -> dyn A`
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/closure-wrong-kind.rs b/tests/ui/closures/closure-wrong-kind.rs
new file mode 100644
index 00000000000..9bf38bfb66b
--- /dev/null
+++ b/tests/ui/closures/closure-wrong-kind.rs
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+struct X;
+fn foo<T>(_: T) {}
+fn bar<T: Fn(u32)>(_: T) {}
+
+fn main() {
+    let x = X;
+    let closure = |_| foo(x);  //~ ERROR E0525
+    bar(closure);
+}
diff --git a/tests/ui/closures/closure-wrong-kind.stderr b/tests/ui/closures/closure-wrong-kind.stderr
new file mode 100644
index 00000000000..9ea55d764f3
--- /dev/null
+++ b/tests/ui/closures/closure-wrong-kind.stderr
@@ -0,0 +1,21 @@
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+  --> $DIR/closure-wrong-kind.rs:10:19
+   |
+LL |     let closure = |_| foo(x);
+   |                   ^^^     - closure is `FnOnce` because it moves the variable `x` out of its environment
+   |                   |
+   |                   this closure implements `FnOnce`, not `Fn`
+LL |     bar(closure);
+   |     --- ------- the requirement to implement `Fn` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `bar`
+  --> $DIR/closure-wrong-kind.rs:6:11
+   |
+LL | fn bar<T: Fn(u32)>(_: T) {}
+   |           ^^^^^^^ required by this bound in `bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/tests/ui/closures/closure_cap_coerce_many_fail.rs b/tests/ui/closures/closure_cap_coerce_many_fail.rs
new file mode 100644
index 00000000000..9133a292103
--- /dev/null
+++ b/tests/ui/closures/closure_cap_coerce_many_fail.rs
@@ -0,0 +1,39 @@
+fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We shouldn't coerce capturing closure to a function
+    let cap = 0;
+    let _ = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+
+    // We shouldn't coerce capturing closure to a non-capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b) as i32,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+
+    // We shouldn't coerce non-capturing closure to a capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b + cap) as i32,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+
+    // We shouldn't coerce capturing closure to a capturing closure
+    let _ = match "+" {
+        "+" => |a, b| (a + b + cap) as i32,
+        "-" => |a, b| (a - b + cap) as i32,
+        _ => unimplemented!(),
+    };
+    //~^^^ ERROR `match` arms have incompatible types
+}
diff --git a/tests/ui/closures/closure_cap_coerce_many_fail.stderr b/tests/ui/closures/closure_cap_coerce_many_fail.stderr
new file mode 100644
index 00000000000..ca8a43328a9
--- /dev/null
+++ b/tests/ui/closures/closure_cap_coerce_many_fail.stderr
@@ -0,0 +1,82 @@
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:9:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => add,
+   | |                --- this is found to be of type `fn(i32, i32) -> i32 {add}`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected fn item `fn(i32, i32) -> i32 {add}`
+              found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:9:16: 9:22]`
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:18:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b) as i32,
+   | |                ---------------------
+   | |                |
+   | |                the expected closure
+   | |                this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:22]`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:17:16: 17:22]`
+              found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:18:16: 18:22]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:27:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b + cap) as i32,
+   | |                ---------------------------
+   | |                |
+   | |                the expected closure
+   | |                this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:22]`
+LL | |         "-" => |a, b| (a - b) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:26:16: 26:22]`
+              found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:27:16: 27:22]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/closure_cap_coerce_many_fail.rs:35:16
+   |
+LL |       let _ = match "+" {
+   |  _____________-
+LL | |         "+" => |a, b| (a + b + cap) as i32,
+   | |                ---------------------------
+   | |                |
+   | |                the expected closure
+   | |                this is found to be of type `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:22]`
+LL | |         "-" => |a, b| (a - b + cap) as i32,
+   | |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
+LL | |         _ => unimplemented!(),
+LL | |     };
+   | |_____- `match` arms have incompatible types
+   |
+   = note: expected closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:34:16: 34:22]`
+              found closure `[closure@$DIR/closure_cap_coerce_many_fail.rs:35:16: 35:22]`
+   = note: no two closures, even if identical, have the same type
+   = help: consider boxing your closure and/or using it as a trait object
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_check_pass.rs b/tests/ui/closures/closure_no_cap_coerce_many_check_pass.rs
new file mode 100644
index 00000000000..ce461810ec9
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_check_pass.rs
@@ -0,0 +1,166 @@
+// check-pass
+// Ensure non-capturing Closure passes CoerceMany.
+fn foo(x: usize) -> usize {
+    0
+}
+
+fn bar(x: usize) -> usize {
+    1
+}
+
+fn main() {
+    // One FnDef and one non-capturing Closure
+    let _ = match 0 {
+        0 => foo,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, |a| 2];
+    let _ = [|a| 2, foo];
+
+
+
+    // Two FnDefs and one non-capturing Closure
+    let _ = match 0 {
+        0 => foo,
+        1 => bar,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        0 => foo,
+        2 => |a| 2,
+        1 => bar,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo,
+        1 => bar,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, bar, |a| 2];
+    let _ = [foo, |a| 2, bar];
+    let _ = [|a| 2, foo, bar];
+
+
+
+    // One FnDef and two non-capturing Closures
+    let _ = match 0 {
+        0 => foo,
+        1 => |a| 1,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        1 => |a| 1,
+        0 => foo,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = match 0 {
+        1 => |a| 1,
+        2 => |a| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+
+    let _ = [foo, |a| 1, |a| 2];
+    let _ = [|a| 1, foo, |a| 2];
+    let _ = [|a| 1, |a| 2, foo];
+
+
+
+    // Three non-capturing Closures
+    let _ = match 0 {
+        0 => |a: usize| 0,
+        1 => |a| 1,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+
+    let _ = [|a: usize| 0, |a| 1, |a| 2];
+
+
+
+    // Three non-capturing Closures variable
+    let clo0 = |a: usize| 0;
+    let clo1 = |a| 1;
+    let clo2 = |a| 2;
+    let _ = match 0 {
+        0 => clo0,
+        1 => clo1,
+        2 => clo2,
+        _ => unimplemented!(),
+    };
+
+    let clo0 = |a: usize| 0;
+    let clo1 = |a| 1;
+    let clo2 = |a| 2;
+    let _ = [clo0, clo1, clo2];
+
+
+
+    // --- Function pointer related part
+
+    // Closure is not in a variable
+    type FnPointer = fn(usize) -> usize;
+
+    let _ = match 0 {
+        0 => foo as FnPointer,
+        2 => |a| 2,
+        _ => unimplemented!(),
+    };
+    let _ = match 0 {
+        2 => |a| 2,
+        0 => foo as FnPointer,
+        _ => unimplemented!(),
+    };
+    let _ = [foo as FnPointer, |a| 2];
+    let _ = [|a| 2, foo as FnPointer];
+    let _ = [foo, bar, |x| x];
+    let _ = [foo as FnPointer, bar, |x| x];
+    let _ = [foo, bar as FnPointer, |x| x];
+    let _ = [foo, bar, (|x| x) as FnPointer];
+    let _ = [foo as FnPointer, bar as FnPointer, |x| x];
+
+    // Closure is in a variable
+    let x = |a| 2;
+    let _ = match 0 {
+        0 => foo as FnPointer,
+        2 => x,
+        _ => unimplemented!(),
+    };
+    let x = |a| 2;
+    let _ = match 0 {
+        2 => x,
+        0 => foo as FnPointer,
+        _ => unimplemented!(),
+    };
+    let x = |a| 2;
+    let _ = [foo as FnPointer, x];
+    let _ = [x, foo as FnPointer];
+
+    let x = |a| 2;
+    let _ = [foo, bar, x];
+    let x: FnPointer = |a| 2;
+    let _ = [foo, bar, x];
+    let x = |a| 2;
+    let _ = [foo, bar as FnPointer, x];
+    let x = |a| 2;
+    let _ = [foo as FnPointer, bar, x];
+    let x = |a| 2;
+    let _ = [foo as FnPointer, bar as FnPointer, x];
+}
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_run_pass.rs b/tests/ui/closures/closure_no_cap_coerce_many_run_pass.rs
new file mode 100644
index 00000000000..3c5fe8a5502
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_run_pass.rs
@@ -0,0 +1,59 @@
+// run-pass
+// Ensure non-capturing Closure passing CoerceMany work correctly.
+fn foo(_: usize) -> usize {
+    0
+}
+
+fn bar(_: usize) -> usize {
+    1
+}
+
+fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+fn main() {
+    // Coerce result check
+
+    type FnPointer = fn(usize) -> usize;
+
+    let c = |x| x;
+    let c_pointer: FnPointer = c;
+    assert_eq!(c_pointer(42), 42);
+
+    let f = match 0 {
+        0 => foo,
+        1 => |_| 1,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 0);
+
+    let f = match 2 {
+        2 => |_| 2,
+        0 => foo,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 2);
+
+    let f = match 1 {
+        0 => foo,
+        1 => bar,
+        2 => |_| 2,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 1);
+
+    let clo0 = |_: usize| 0;
+    let clo1 = |_| 1;
+    let clo2 = |_| 2;
+    let f = match 0 {
+        0 => clo0,
+        1 => clo1,
+        2 => clo2,
+        _ => unimplemented!(),
+    };
+    assert_eq!(f(42), 0);
+
+    let funcs = [add, |a, b| (a - b) as i32];
+    assert_eq!([funcs[0](5, 5), funcs[1](5, 5)], [10, 0]);
+}
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr
new file mode 100644
index 00000000000..2f9c7973b5a
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.mir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
new file mode 100644
index 00000000000..bdb3eb23c38
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.rs
@@ -0,0 +1,25 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+// Ensure we get unsafe function after coercion
+unsafe fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We can coerce non-capturing closure to unsafe function
+    let foo = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
+
+
+    // We can coerce unsafe function to non-capturing closure
+    let foo = match "+" {
+        "-" => |a, b| (a - b) as i32,
+        "+" => add,
+        _ => unimplemented!(),
+    };
+    let result: i32 = foo(5, 5); //~ ERROR call to unsafe function
+}
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr
new file mode 100644
index 00000000000..2f9c7973b5a
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_0.thir.stderr
@@ -0,0 +1,19 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:15:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/closure_no_cap_coerce_many_unsafe_0.rs:24:23
+   |
+LL |     let result: i32 = foo(5, 5);
+   |                       ^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs
new file mode 100644
index 00000000000..a6d6125a1b9
--- /dev/null
+++ b/tests/ui/closures/closure_no_cap_coerce_many_unsafe_1.rs
@@ -0,0 +1,23 @@
+// run-pass
+// Ensure we get correct unsafe function after coercion
+unsafe fn add(a: i32, b: i32) -> i32 {
+    a + b
+}
+fn main() {
+    // We can coerce non-capturing closure to unsafe function
+    let foo = match "+" {
+        "+" => add,
+        "-" => |a, b| (a - b) as i32,
+        _ => unimplemented!(),
+    };
+    assert_eq!(unsafe { foo(5, 5) }, 10);
+
+
+    // We can coerce unsafe function to non-capturing closure
+    let foo = match "-" {
+        "-" => |a, b| (a - b) as i32,
+        "+" => add,
+        _ => unimplemented!(),
+    };
+    assert_eq!(unsafe { foo(5, 5) }, 0);
+}
diff --git a/tests/ui/closures/closure_promotion.rs b/tests/ui/closures/closure_promotion.rs
new file mode 100644
index 00000000000..47a8fc0902d
--- /dev/null
+++ b/tests/ui/closures/closure_promotion.rs
@@ -0,0 +1,5 @@
+// build-pass (FIXME(62277): could be check-pass?)
+
+fn main() {
+    let x: &'static _ = &|| { let z = 3; z };
+}
diff --git a/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr
new file mode 100644
index 00000000000..a60100ddaea
--- /dev/null
+++ b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.mir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+  --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
+   |
+LL |     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs
new file mode 100644
index 00000000000..57358fbdd84
--- /dev/null
+++ b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.rs
@@ -0,0 +1,8 @@
+// revisions: mir thir
+// [thir]compile-flags: -Z thir-unsafeck
+
+fn main() {
+    let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
+    //~^ ERROR E0133
+    let _: unsafe fn() = || unsafe { ::std::pin::Pin::new_unchecked(&0_u8); }; // OK
+}
diff --git a/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
new file mode 100644
index 00000000000..8c516e8900c
--- /dev/null
+++ b/tests/ui/closures/coerce-unsafe-closure-to-unsafe-fn-ptr.thir.stderr
@@ -0,0 +1,11 @@
+error[E0133]: call to unsafe function `Pin::<P>::new_unchecked` is unsafe and requires unsafe function or block
+  --> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
+   |
+LL |     let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/closures/coerce-unsafe-to-closure.rs b/tests/ui/closures/coerce-unsafe-to-closure.rs
new file mode 100644
index 00000000000..78bdd36f9cc
--- /dev/null
+++ b/tests/ui/closures/coerce-unsafe-to-closure.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let x: Option<&[u8]> = Some("foo").map(std::mem::transmute);
+    //~^ ERROR E0277
+}
diff --git a/tests/ui/closures/coerce-unsafe-to-closure.stderr b/tests/ui/closures/coerce-unsafe-to-closure.stderr
new file mode 100644
index 00000000000..449cd0b3177
--- /dev/null
+++ b/tests/ui/closures/coerce-unsafe-to-closure.stderr
@@ -0,0 +1,16 @@
+error[E0277]: expected a `FnOnce<(&str,)>` closure, found `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
+  --> $DIR/coerce-unsafe-to-closure.rs:2:44
+   |
+LL |     let x: Option<&[u8]> = Some("foo").map(std::mem::transmute);
+   |                                        --- ^^^^^^^^^^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+   |                                        |
+   |                                        required by a bound introduced by this call
+   |
+   = help: the trait `FnOnce<(&str,)>` is not implemented for fn item `unsafe extern "rust-intrinsic" fn(_) -> _ {transmute::<_, _>}`
+   = note: unsafe function cannot be called generically without an unsafe block
+note: required by a bound in `Option::<T>::map`
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/deeply-nested_closures.rs b/tests/ui/closures/deeply-nested_closures.rs
new file mode 100644
index 00000000000..a02684ee1de
--- /dev/null
+++ b/tests/ui/closures/deeply-nested_closures.rs
@@ -0,0 +1,23 @@
+// Check that this can be compiled in a reasonable time.
+
+// build-pass
+
+fn main() {
+    // 96 nested closures
+    let x = ();
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+    || || || || || || || ||
+    [&(), &x];
+}
diff --git a/tests/ui/closures/diverging-closure.rs b/tests/ui/closures/diverging-closure.rs
new file mode 100644
index 00000000000..1213a883ef0
--- /dev/null
+++ b/tests/ui/closures/diverging-closure.rs
@@ -0,0 +1,10 @@
+// run-fail
+// error-pattern:oops
+// ignore-emscripten no processes
+
+fn main() {
+    let func = || -> ! {
+        panic!("oops");
+    };
+    func();
+}
diff --git a/tests/ui/closures/issue-101696.rs b/tests/ui/closures/issue-101696.rs
new file mode 100644
index 00000000000..0a358bd1643
--- /dev/null
+++ b/tests/ui/closures/issue-101696.rs
@@ -0,0 +1,36 @@
+// check-pass
+
+use std::marker::PhantomData;
+
+#[derive(Default)]
+struct MyType<'a> {
+    field: usize,
+    _phantom: PhantomData<&'a ()>,
+}
+
+#[derive(Default)]
+struct MyTypeVariant<'a> {
+    field: usize,
+    _phantom: PhantomData<&'a ()>,
+}
+
+trait AsVariantTrait {
+    type Type;
+}
+
+impl<'a> AsVariantTrait for MyType<'a> {
+    type Type = MyTypeVariant<'a>;
+}
+
+type Variant<G> = <G as AsVariantTrait>::Type;
+
+fn foo<T: Default, F: FnOnce(T)>(f: F) {
+    let input = T::default();
+    f(input);
+}
+
+fn main() {
+    foo(|a: <MyType as AsVariantTrait>::Type| {
+        a.field;
+    });
+}
diff --git a/tests/ui/closures/issue-102089-multiple-opaque-cast.rs b/tests/ui/closures/issue-102089-multiple-opaque-cast.rs
new file mode 100644
index 00000000000..043bf06a1f5
--- /dev/null
+++ b/tests/ui/closures/issue-102089-multiple-opaque-cast.rs
@@ -0,0 +1,17 @@
+// edition:2021
+// check-pass
+
+pub struct Example<'a, T> {
+  a: T,
+  b: &'a T,
+}
+
+impl<'a, T> Example<'a, T> {
+  pub fn error_trying_to_destructure_self_in_closure(self) {
+    let closure = || {
+      let Self { a, b } = self;
+    };
+  }
+}
+
+fn main() {}
diff --git a/tests/ui/closures/issue-10398.rs b/tests/ui/closures/issue-10398.rs
new file mode 100644
index 00000000000..f76d09cd0b2
--- /dev/null
+++ b/tests/ui/closures/issue-10398.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let x: Box<_> = Box::new(1);
+    let f = move|| {
+        let _a = x;
+        drop(x);
+        //~^ ERROR: use of moved value: `x`
+    };
+    f();
+}
diff --git a/tests/ui/closures/issue-10398.stderr b/tests/ui/closures/issue-10398.stderr
new file mode 100644
index 00000000000..423b79dafcc
--- /dev/null
+++ b/tests/ui/closures/issue-10398.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of moved value: `x`
+  --> $DIR/issue-10398.rs:5:14
+   |
+LL |         let _a = x;
+   |                  - value moved here
+LL |         drop(x);
+   |              ^ value used here after move
+   |
+   = note: move occurs because `x` has type `Box<i32>`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/issue-23012-supertrait-signature-inference.rs b/tests/ui/closures/issue-23012-supertrait-signature-inference.rs
new file mode 100644
index 00000000000..5899b703e7c
--- /dev/null
+++ b/tests/ui/closures/issue-23012-supertrait-signature-inference.rs
@@ -0,0 +1,29 @@
+// check-pass
+// Checks that we can infer a closure signature even if the `FnOnce` bound is
+// a supertrait of the obligations we have currently registered for the Ty var.
+
+pub trait Receive<T, E>: FnOnce(Result<T, E>) {
+    fn receive(self, res: Result<T, E>);
+}
+
+impl<T, E, F: FnOnce(Result<T, E>)> Receive<T, E> for F {
+    fn receive(self, res: Result<T, E>) {
+        self(res)
+    }
+}
+
+pub trait Async<T, E> {
+    fn receive<F: Receive<T, E>>(self, f: F);
+}
+
+impl<T, E> Async<T, E> for Result<T, E> {
+    fn receive<F: Receive<T, E>>(self, f: F) {
+        f(self)
+    }
+}
+
+pub fn main() {
+    Ok::<u32, ()>(123).receive(|res| {
+        res.unwrap();
+    });
+}
diff --git a/tests/ui/closures/issue-41366.rs b/tests/ui/closures/issue-41366.rs
new file mode 100644
index 00000000000..acc1c6ae122
--- /dev/null
+++ b/tests/ui/closures/issue-41366.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+trait T<'x> {
+    type V;
+}
+
+impl<'g> T<'g> for u32 {
+    type V = u16;
+}
+
+fn main() {
+    (&|_| ()) as &dyn for<'x> Fn(<u32 as T<'x>>::V);
+}
diff --git a/tests/ui/closures/issue-42463.rs b/tests/ui/closures/issue-42463.rs
new file mode 100644
index 00000000000..51d6ea3f7a8
--- /dev/null
+++ b/tests/ui/closures/issue-42463.rs
@@ -0,0 +1,32 @@
+// run-pass
+use std::ops::{Deref, DerefMut};
+
+struct CheckedDeref<T, F> {
+    value: T,
+    check: F
+}
+
+impl<F: Fn(&T) -> bool, T> Deref for CheckedDeref<T, F> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        assert!((self.check)(&self.value));
+        &self.value
+    }
+}
+
+impl<F: Fn(&T) -> bool, T> DerefMut for CheckedDeref<T, F> {
+    fn deref_mut(&mut self) -> &mut T {
+        assert!((self.check)(&self.value));
+        &mut self.value
+    }
+}
+
+
+fn main() {
+    let mut v = CheckedDeref {
+        value: vec![0],
+        check: |v: &Vec<_>| !v.is_empty()
+    };
+    v.push(1);
+    assert_eq!(*v, vec![0, 1]);
+}
diff --git a/tests/ui/closures/issue-46742.rs b/tests/ui/closures/issue-46742.rs
new file mode 100644
index 00000000000..cd8dc486906
--- /dev/null
+++ b/tests/ui/closures/issue-46742.rs
@@ -0,0 +1,9 @@
+// check-pass
+fn main() {
+    let _: i32 = (match "" {
+        "+" => ::std::ops::Add::add,
+        "-" => ::std::ops::Sub::sub,
+        "<" => |a,b| (a < b) as i32,
+        _ => unimplemented!(),
+    })(5, 5);
+}
diff --git a/tests/ui/closures/issue-48109.rs b/tests/ui/closures/issue-48109.rs
new file mode 100644
index 00000000000..ce1f2a03647
--- /dev/null
+++ b/tests/ui/closures/issue-48109.rs
@@ -0,0 +1,14 @@
+// check-pass
+fn useful(i: usize) -> usize {
+    i
+}
+
+fn useful2(i: usize) -> usize {
+    i
+}
+
+fn main() {
+    for f in &[useful, useful2, |x| x] {
+        println!("{}", f(6));
+    }
+}
diff --git a/tests/ui/closures/issue-52437.rs b/tests/ui/closures/issue-52437.rs
new file mode 100644
index 00000000000..6ac5380a5aa
--- /dev/null
+++ b/tests/ui/closures/issue-52437.rs
@@ -0,0 +1,5 @@
+fn main() {
+    [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
+    //~^ ERROR: invalid label name `'static`
+    //~| ERROR: type annotations needed
+}
diff --git a/tests/ui/closures/issue-52437.stderr b/tests/ui/closures/issue-52437.stderr
new file mode 100644
index 00000000000..9ba24c7a886
--- /dev/null
+++ b/tests/ui/closures/issue-52437.stderr
@@ -0,0 +1,20 @@
+error: invalid label name `'static`
+  --> $DIR/issue-52437.rs:2:13
+   |
+LL |     [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
+   |             ^^^^^^^
+
+error[E0282]: type annotations needed
+  --> $DIR/issue-52437.rs:2:30
+   |
+LL |     [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
+   |                              ^
+   |
+help: consider giving this closure parameter an explicit type
+   |
+LL |     [(); &(&'static: loop { |x: /* Type */| {}; }) as *const _ as usize]
+   |                               ++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/closures/issue-67123.rs b/tests/ui/closures/issue-67123.rs
new file mode 100644
index 00000000000..014c530e6d7
--- /dev/null
+++ b/tests/ui/closures/issue-67123.rs
@@ -0,0 +1,5 @@
+fn foo<T>(t: T) {
+    || { t; t; }; //~ ERROR: use of moved value
+}
+
+fn main() {}
diff --git a/tests/ui/closures/issue-67123.stderr b/tests/ui/closures/issue-67123.stderr
new file mode 100644
index 00000000000..7877c7334d7
--- /dev/null
+++ b/tests/ui/closures/issue-67123.stderr
@@ -0,0 +1,17 @@
+error[E0382]: use of moved value: `t`
+  --> $DIR/issue-67123.rs:2:13
+   |
+LL |     || { t; t; };
+   |          -  ^ value used here after move
+   |          |
+   |          value moved here
+   |
+   = note: move occurs because `t` has type `T`, which does not implement the `Copy` trait
+help: consider restricting type parameter `T`
+   |
+LL | fn foo<T: Copy>(t: T) {
+   |         ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/issue-6801.rs b/tests/ui/closures/issue-6801.rs
new file mode 100644
index 00000000000..cecb08f006f
--- /dev/null
+++ b/tests/ui/closures/issue-6801.rs
@@ -0,0 +1,21 @@
+// Creating a stack closure which references a box and then
+// transferring ownership of the box before invoking the stack
+// closure results in a crash.
+
+
+
+fn twice(x: Box<usize>) -> usize {
+     *x * 2
+}
+
+fn invoke<F>(f: F) where F: FnOnce() -> usize {
+     f();
+}
+
+fn main() {
+      let x  : Box<usize>  = Box::new(9);
+      let sq =  || { *x * *x };
+
+      twice(x); //~ ERROR: cannot move out of
+      invoke(sq);
+}
diff --git a/tests/ui/closures/issue-6801.stderr b/tests/ui/closures/issue-6801.stderr
new file mode 100644
index 00000000000..6a40db0d51d
--- /dev/null
+++ b/tests/ui/closures/issue-6801.stderr
@@ -0,0 +1,16 @@
+error[E0505]: cannot move out of `x` because it is borrowed
+  --> $DIR/issue-6801.rs:19:13
+   |
+LL |       let sq =  || { *x * *x };
+   |                 --   -- borrow occurs due to use in closure
+   |                 |
+   |                 borrow of `x` occurs here
+LL |
+LL |       twice(x);
+   |             ^ move out of `x` occurs here
+LL |       invoke(sq);
+   |              -- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0505`.
diff --git a/tests/ui/closures/issue-68025.rs b/tests/ui/closures/issue-68025.rs
new file mode 100644
index 00000000000..261bfd60aae
--- /dev/null
+++ b/tests/ui/closures/issue-68025.rs
@@ -0,0 +1,12 @@
+// check-pass
+
+fn foo<F, G>(_: G, _: Box<F>)
+where
+    F: Fn(),
+    G: Fn(Box<F>),
+{
+}
+
+fn main() {
+    foo(|f| (*f)(), Box::new(|| {}));
+}
diff --git a/tests/ui/closures/issue-72408-nested-closures-exponential.rs b/tests/ui/closures/issue-72408-nested-closures-exponential.rs
new file mode 100644
index 00000000000..2d6ba936572
--- /dev/null
+++ b/tests/ui/closures/issue-72408-nested-closures-exponential.rs
@@ -0,0 +1,59 @@
+// build-pass
+
+// 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));
+}
diff --git a/tests/ui/closures/issue-78720.rs b/tests/ui/closures/issue-78720.rs
new file mode 100644
index 00000000000..4cdb9f49113
--- /dev/null
+++ b/tests/ui/closures/issue-78720.rs
@@ -0,0 +1,19 @@
+fn server() -> impl {
+//~^ ERROR at least one trait must be specified
+    ().map2(|| "")
+}
+
+trait FilterBase2 {
+    fn map2<F>(self, f: F) -> Map2<F> {}
+    //~^ ERROR mismatched types
+    //~^^ ERROR the size for values of type `Self` cannot be known at compilation time
+}
+
+struct Map2<Segment2> {
+    _func: F,
+    //~^ ERROR cannot find type `F` in this scope
+}
+
+impl<F> FilterBase2 for F {}
+
+fn main() {}
diff --git a/tests/ui/closures/issue-78720.stderr b/tests/ui/closures/issue-78720.stderr
new file mode 100644
index 00000000000..1e860d32b2a
--- /dev/null
+++ b/tests/ui/closures/issue-78720.stderr
@@ -0,0 +1,53 @@
+error: at least one trait must be specified
+  --> $DIR/issue-78720.rs:1:16
+   |
+LL | fn server() -> impl {
+   |                ^^^^
+
+error[E0412]: cannot find type `F` in this scope
+  --> $DIR/issue-78720.rs:13:12
+   |
+LL |     _func: F,
+   |            ^
+  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+   = note: similarly named trait `Fn` defined here
+   |
+help: a trait with a similar name exists
+   |
+LL |     _func: Fn,
+   |            ~~
+help: you might be missing a type parameter
+   |
+LL | struct Map2<Segment2, F> {
+   |                     +++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-78720.rs:7:39
+   |
+LL |     fn map2<F>(self, f: F) -> Map2<F> {}
+   |                                       ^^ expected struct `Map2`, found `()`
+   |
+   = note: expected struct `Map2<F>`
+           found unit type `()`
+
+error[E0277]: the size for values of type `Self` cannot be known at compilation time
+  --> $DIR/issue-78720.rs:7:16
+   |
+LL |     fn map2<F>(self, f: F) -> Map2<F> {}
+   |                ^^^^ doesn't have a size known at compile-time
+   |
+   = help: unsized fn params are gated as an unstable feature
+help: consider further restricting `Self`
+   |
+LL |     fn map2<F>(self, f: F) -> Map2<F> where Self: Sized {}
+   |                                       +++++++++++++++++
+help: function arguments must have a statically known size, borrowed types always have a known size
+   |
+LL |     fn map2<F>(&self, f: F) -> Map2<F> {}
+   |                +
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0277, E0308, E0412.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/closures/issue-80313-mutable-borrow-in-closure.rs b/tests/ui/closures/issue-80313-mutable-borrow-in-closure.rs
new file mode 100644
index 00000000000..ff210ae06a3
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutable-borrow-in-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = || {
+        &mut my_var;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/tests/ui/closures/issue-80313-mutable-borrow-in-closure.stderr b/tests/ui/closures/issue-80313-mutable-borrow-in-closure.stderr
new file mode 100644
index 00000000000..239f071ca92
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutable-borrow-in-closure.stderr
@@ -0,0 +1,17 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutable-borrow-in-closure.rs:6:5
+   |
+LL |         &mut my_var;
+   |              ------ calling `callback` requires mutable binding due to mutable borrow of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut callback = || {
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs b/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs
new file mode 100644
index 00000000000..8f2d8a67630
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = move || {
+        &mut my_var;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr b/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr
new file mode 100644
index 00000000000..1ec279f03ef
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr
@@ -0,0 +1,17 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutable-borrow-in-move-closure.rs:6:5
+   |
+LL |         &mut my_var;
+   |              ------ calling `callback` requires mutable binding due to possible mutation of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut callback = move || {
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-80313-mutation-in-closure.rs b/tests/ui/closures/issue-80313-mutation-in-closure.rs
new file mode 100644
index 00000000000..e082ea562ef
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutation-in-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = || {
+        my_var = true;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/tests/ui/closures/issue-80313-mutation-in-closure.stderr b/tests/ui/closures/issue-80313-mutation-in-closure.stderr
new file mode 100644
index 00000000000..22a62ce7350
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutation-in-closure.stderr
@@ -0,0 +1,17 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutation-in-closure.rs:6:5
+   |
+LL |         my_var = true;
+   |         ------ calling `callback` requires mutable binding due to mutable borrow of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut callback = || {
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-80313-mutation-in-move-closure.rs b/tests/ui/closures/issue-80313-mutation-in-move-closure.rs
new file mode 100644
index 00000000000..f66bf4e0628
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutation-in-move-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = move || {
+        my_var = true;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/tests/ui/closures/issue-80313-mutation-in-move-closure.stderr b/tests/ui/closures/issue-80313-mutation-in-move-closure.stderr
new file mode 100644
index 00000000000..a2222f8cc95
--- /dev/null
+++ b/tests/ui/closures/issue-80313-mutation-in-move-closure.stderr
@@ -0,0 +1,17 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutation-in-move-closure.rs:6:5
+   |
+LL |         my_var = true;
+   |         ------ calling `callback` requires mutable binding due to possible mutation of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut callback = move || {
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-81700-mut-borrow.rs b/tests/ui/closures/issue-81700-mut-borrow.rs
new file mode 100644
index 00000000000..a27a6160142
--- /dev/null
+++ b/tests/ui/closures/issue-81700-mut-borrow.rs
@@ -0,0 +1,5 @@
+fn foo(x: &mut u32) {
+    let bar = || { foo(x); };
+    bar(); //~ ERROR cannot borrow
+}
+fn main() {}
diff --git a/tests/ui/closures/issue-81700-mut-borrow.stderr b/tests/ui/closures/issue-81700-mut-borrow.stderr
new file mode 100644
index 00000000000..03b18c3f70c
--- /dev/null
+++ b/tests/ui/closures/issue-81700-mut-borrow.stderr
@@ -0,0 +1,16 @@
+error[E0596]: cannot borrow `bar` as mutable, as it is not declared as mutable
+  --> $DIR/issue-81700-mut-borrow.rs:3:5
+   |
+LL |     let bar = || { foo(x); };
+   |                        - calling `bar` requires mutable binding due to mutable borrow of `x`
+LL |     bar();
+   |     ^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut bar = || { foo(x); };
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-82438-mut-without-upvar.rs b/tests/ui/closures/issue-82438-mut-without-upvar.rs
new file mode 100644
index 00000000000..5d88e1e77d4
--- /dev/null
+++ b/tests/ui/closures/issue-82438-mut-without-upvar.rs
@@ -0,0 +1,28 @@
+use std::error::Error;
+struct A {
+}
+
+impl A {
+    pub fn new() -> A {
+        A {
+        }
+    }
+
+    pub fn f<'a>(
+        &'a self,
+        team_name: &'a str,
+        c: &'a mut dyn FnMut(String, String, u64, u64)
+    ) -> Result<(), Box<dyn Error>> {
+        Ok(())
+    }
+}
+
+
+fn main() {
+    let A = A::new();
+    let participant_name = "A";
+
+    let c = |a, b, c, d| {};
+
+    A.f(participant_name, &mut c); //~ ERROR cannot borrow
+}
diff --git a/tests/ui/closures/issue-82438-mut-without-upvar.stderr b/tests/ui/closures/issue-82438-mut-without-upvar.stderr
new file mode 100644
index 00000000000..f0951b7d108
--- /dev/null
+++ b/tests/ui/closures/issue-82438-mut-without-upvar.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `c` as mutable, as it is not declared as mutable
+  --> $DIR/issue-82438-mut-without-upvar.rs:27:27
+   |
+LL |     A.f(participant_name, &mut c);
+   |                           ^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut c = |a, b, c, d| {};
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-84044-drop-non-mut.rs b/tests/ui/closures/issue-84044-drop-non-mut.rs
new file mode 100644
index 00000000000..aed7750f1b9
--- /dev/null
+++ b/tests/ui/closures/issue-84044-drop-non-mut.rs
@@ -0,0 +1,6 @@
+// #84044: This used to ICE.
+
+fn main() {
+    let f = || {};
+    drop(&mut f); //~ ERROR cannot borrow `f` as mutable, as it is not declared as mutable
+}
diff --git a/tests/ui/closures/issue-84044-drop-non-mut.stderr b/tests/ui/closures/issue-84044-drop-non-mut.stderr
new file mode 100644
index 00000000000..5335a056cd8
--- /dev/null
+++ b/tests/ui/closures/issue-84044-drop-non-mut.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable
+  --> $DIR/issue-84044-drop-non-mut.rs:5:10
+   |
+LL |     drop(&mut f);
+   |          ^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut f = || {};
+   |         +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/closures/issue-84128.rs b/tests/ui/closures/issue-84128.rs
new file mode 100644
index 00000000000..30733871b85
--- /dev/null
+++ b/tests/ui/closures/issue-84128.rs
@@ -0,0 +1,16 @@
+// test for issue 84128
+// missing suggestion for similar ADT type with diffetent generic parameter
+// on closure ReturnNoExpression
+
+struct Foo<T>(T);
+
+fn main() {
+    || {
+        if false {
+            return Foo(0);
+        }
+
+        Foo(())
+        //~^ ERROR mismatched types [E0308]
+    };
+}
diff --git a/tests/ui/closures/issue-84128.stderr b/tests/ui/closures/issue-84128.stderr
new file mode 100644
index 00000000000..59607afec8f
--- /dev/null
+++ b/tests/ui/closures/issue-84128.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-84128.rs:13:13
+   |
+LL |         Foo(())
+   |         --- ^^ expected integer, found `()`
+   |         |
+   |         arguments to this struct are incorrect
+   |
+note: tuple struct defined here
+  --> $DIR/issue-84128.rs:5:8
+   |
+LL | struct Foo<T>(T);
+   |        ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/issue-87461.rs b/tests/ui/closures/issue-87461.rs
new file mode 100644
index 00000000000..0151080eeb4
--- /dev/null
+++ b/tests/ui/closures/issue-87461.rs
@@ -0,0 +1,29 @@
+// Regression test for #87461.
+
+// edition:2021
+
+async fn func() -> Result<u16, u64> {
+    let _ = async {
+        Err(42u64)
+    }.await?;
+
+    Ok(())
+    //~^ ERROR: mismatched types [E0308]
+}
+
+async fn func2() -> Result<u16, u64> {
+    Err(42u64)?;
+
+    Ok(())
+    //~^ ERROR: mismatched types [E0308]
+}
+
+fn main() {
+    || -> Result<u16, u64> {
+        if true {
+            return Err(42u64);
+        }
+        Ok(())
+        //~^ ERROR: mismatched types [E0308]
+    };
+}
diff --git a/tests/ui/closures/issue-87461.stderr b/tests/ui/closures/issue-87461.stderr
new file mode 100644
index 00000000000..72337892734
--- /dev/null
+++ b/tests/ui/closures/issue-87461.stderr
@@ -0,0 +1,36 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-87461.rs:10:8
+   |
+LL |     Ok(())
+   |     -- ^^ expected `u16`, found `()`
+   |     |
+   |     arguments to this enum variant are incorrect
+   |
+note: tuple variant defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+
+error[E0308]: mismatched types
+  --> $DIR/issue-87461.rs:17:8
+   |
+LL |     Ok(())
+   |     -- ^^ expected `u16`, found `()`
+   |     |
+   |     arguments to this enum variant are incorrect
+   |
+note: tuple variant defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+
+error[E0308]: mismatched types
+  --> $DIR/issue-87461.rs:26:12
+   |
+LL |         Ok(())
+   |         -- ^^ expected `u16`, found `()`
+   |         |
+   |         arguments to this enum variant are incorrect
+   |
+note: tuple variant defined here
+  --> $SRC_DIR/core/src/result.rs:LL:COL
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/issue-87814-1.rs b/tests/ui/closures/issue-87814-1.rs
new file mode 100644
index 00000000000..5cf01ddf5d7
--- /dev/null
+++ b/tests/ui/closures/issue-87814-1.rs
@@ -0,0 +1,8 @@
+// check-pass
+fn main() {
+    let mut schema_all = vec![];
+    (0..42).for_each(|_x| match Err(()) as Result<(), _> {
+        Ok(()) => schema_all.push(()),
+        Err(_) => (),
+    });
+}
diff --git a/tests/ui/closures/issue-87814-2.rs b/tests/ui/closures/issue-87814-2.rs
new file mode 100644
index 00000000000..efe77f90f06
--- /dev/null
+++ b/tests/ui/closures/issue-87814-2.rs
@@ -0,0 +1,10 @@
+// check-pass
+
+fn main() {
+    let mut schema_all: (Vec<String>, Vec<String>) = (vec![], vec![]);
+
+    let _c = || match schema_all.0.try_reserve(1) as Result<(), _> {
+        Ok(()) => (),
+        Err(_) => (),
+    };
+}
diff --git a/tests/ui/closures/issue-90871.rs b/tests/ui/closures/issue-90871.rs
new file mode 100644
index 00000000000..7ce061cd388
--- /dev/null
+++ b/tests/ui/closures/issue-90871.rs
@@ -0,0 +1,7 @@
+#![feature(type_ascription)]
+
+fn main() {
+    type_ascribe!(2, n([u8; || 1]))
+    //~^ ERROR cannot find type `n` in this scope
+    //~| ERROR mismatched types
+}
diff --git a/tests/ui/closures/issue-90871.stderr b/tests/ui/closures/issue-90871.stderr
new file mode 100644
index 00000000000..4a578b4d7f5
--- /dev/null
+++ b/tests/ui/closures/issue-90871.stderr
@@ -0,0 +1,26 @@
+error[E0412]: cannot find type `n` in this scope
+  --> $DIR/issue-90871.rs:4:22
+   |
+LL |     type_ascribe!(2, n([u8; || 1]))
+   |                      ^ help: a trait with a similar name exists: `Fn`
+  --> $SRC_DIR/core/src/ops/function.rs:LL:COL
+   |
+   = note: similarly named trait `Fn` defined here
+
+error[E0308]: mismatched types
+  --> $DIR/issue-90871.rs:4:29
+   |
+LL |     type_ascribe!(2, n([u8; || 1]))
+   |                             ^^^^ expected `usize`, found closure
+   |
+   = note: expected type `usize`
+           found closure `[closure@$DIR/issue-90871.rs:4:29: 4:31]`
+help: use parentheses to call this closure
+   |
+LL |     type_ascribe!(2, n([u8; (|| 1)()]))
+   |                             +    +++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0412.
+For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/issue-97607.rs b/tests/ui/closures/issue-97607.rs
new file mode 100644
index 00000000000..74c910ad0bb
--- /dev/null
+++ b/tests/ui/closures/issue-97607.rs
@@ -0,0 +1,12 @@
+// check-pass
+#[allow(unused)]
+
+fn test<T, F, U>(f: F) -> Box<dyn Fn(T) -> 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() {}
diff --git a/tests/ui/closures/issue-99565.rs b/tests/ui/closures/issue-99565.rs
new file mode 100644
index 00000000000..3a30d2ee034
--- /dev/null
+++ b/tests/ui/closures/issue-99565.rs
@@ -0,0 +1,7 @@
+#![crate_type = "lib"]
+
+fn foo<T, U>(_: U) {}
+
+fn bar() {
+    foo(|| {}); //~ ERROR type annotations needed
+}
diff --git a/tests/ui/closures/issue-99565.stderr b/tests/ui/closures/issue-99565.stderr
new file mode 100644
index 00000000000..0d940aa9a2f
--- /dev/null
+++ b/tests/ui/closures/issue-99565.stderr
@@ -0,0 +1,14 @@
+error[E0282]: type annotations needed
+  --> $DIR/issue-99565.rs:6:5
+   |
+LL |     foo(|| {});
+   |     ^^^ cannot infer type of the type parameter `T` declared on the function `foo`
+   |
+help: consider specifying the generic arguments
+   |
+LL |     foo::<T, _>(|| {});
+   |        ++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/closures/local-type-mix.rs b/tests/ui/closures/local-type-mix.rs
new file mode 100644
index 00000000000..006e6f490f0
--- /dev/null
+++ b/tests/ui/closures/local-type-mix.rs
@@ -0,0 +1,17 @@
+// Check that using the parameter name in its type does not ICE.
+// edition:2018
+
+#![feature(async_closure)]
+
+fn main() {
+    let _ = |x: x| x; //~ ERROR expected type
+    let _ = |x: bool| -> x { x }; //~ ERROR expected type
+    let _ = async move |x: x| x; //~ ERROR expected type
+    let _ = async move |x: bool| -> x { x }; //~ ERROR expected type
+}
+
+fn foo(x: x) {} //~ ERROR expected type
+fn foo_ret(x: bool) -> x {} //~ ERROR expected type
+
+async fn async_foo(x: x) {} //~ ERROR expected type
+async fn async_foo_ret(x: bool) -> x {} //~ ERROR expected type
diff --git a/tests/ui/closures/local-type-mix.stderr b/tests/ui/closures/local-type-mix.stderr
new file mode 100644
index 00000000000..68c320a065d
--- /dev/null
+++ b/tests/ui/closures/local-type-mix.stderr
@@ -0,0 +1,51 @@
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:7:17
+   |
+LL |     let _ = |x: x| x;
+   |                 ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:8:26
+   |
+LL |     let _ = |x: bool| -> x { x };
+   |                          ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:9:28
+   |
+LL |     let _ = async move |x: x| x;
+   |                            ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:10:37
+   |
+LL |     let _ = async move |x: bool| -> x { x };
+   |                                     ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:13:11
+   |
+LL | fn foo(x: x) {}
+   |           ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:14:24
+   |
+LL | fn foo_ret(x: bool) -> x {}
+   |                        ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:16:23
+   |
+LL | async fn async_foo(x: x) {}
+   |                       ^ not a type
+
+error[E0573]: expected type, found local variable `x`
+  --> $DIR/local-type-mix.rs:17:36
+   |
+LL | async fn async_foo_ret(x: bool) -> x {}
+   |                                    ^ not a type
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0573`.
diff --git a/tests/ui/closures/multiple-fn-bounds.rs b/tests/ui/closures/multiple-fn-bounds.rs
new file mode 100644
index 00000000000..6bb4098e2bb
--- /dev/null
+++ b/tests/ui/closures/multiple-fn-bounds.rs
@@ -0,0 +1,15 @@
+fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
+    //~^ NOTE required by a bound in `foo`
+    //~| NOTE required by this bound in `foo`
+    //~| NOTE closure inferred to have a different signature due to this bound
+    todo!();
+}
+
+fn main() {
+    let v = true;
+    foo(move |x| v);
+    //~^ ERROR type mismatch in closure arguments
+    //~| NOTE expected closure signature
+    //~| NOTE expected due to this
+    //~| NOTE found signature defined here
+}
diff --git a/tests/ui/closures/multiple-fn-bounds.stderr b/tests/ui/closures/multiple-fn-bounds.stderr
new file mode 100644
index 00000000000..da26302c9d8
--- /dev/null
+++ b/tests/ui/closures/multiple-fn-bounds.stderr
@@ -0,0 +1,26 @@
+error[E0631]: type mismatch in closure arguments
+  --> $DIR/multiple-fn-bounds.rs:10:5
+   |
+LL |     foo(move |x| v);
+   |     ^^^ --------
+   |     |   |     |
+   |     |   |     help: do not borrow the argument: `char`
+   |     |   found signature defined here
+   |     expected due to this
+   |
+   = note: expected closure signature `fn(char) -> _`
+              found closure signature `for<'a> fn(&'a char) -> _`
+note: closure inferred to have a different signature due to this bound
+  --> $DIR/multiple-fn-bounds.rs:1:11
+   |
+LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
+   |           ^^^^^^^^^^^^^^^^^
+note: required by a bound in `foo`
+  --> $DIR/multiple-fn-bounds.rs:1:31
+   |
+LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
+   |                               ^^^^^^^^^^^^^^^^ required by this bound in `foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0631`.
diff --git a/tests/ui/closures/old-closure-arg-call-as.rs b/tests/ui/closures/old-closure-arg-call-as.rs
new file mode 100644
index 00000000000..87cf3a487bf
--- /dev/null
+++ b/tests/ui/closures/old-closure-arg-call-as.rs
@@ -0,0 +1,12 @@
+// run-pass
+
+#![allow(non_snake_case)]
+
+fn asBlock<F>(f: F) -> usize where F: FnOnce() -> usize {
+   return f();
+}
+
+pub fn main() {
+   let x = asBlock(|| 22);
+   assert_eq!(x, 22);
+}
diff --git a/tests/ui/closures/old-closure-arg.rs b/tests/ui/closures/old-closure-arg.rs
new file mode 100644
index 00000000000..bd1385e5c33
--- /dev/null
+++ b/tests/ui/closures/old-closure-arg.rs
@@ -0,0 +1,11 @@
+// run-pass
+// Check usage and precedence of block arguments in expressions:
+pub fn main() {
+    let v = vec![-1.0f64, 0.0, 1.0, 2.0, 3.0];
+
+    // Statement form does not require parentheses:
+    for i in &v {
+        println!("{}", *i);
+    }
+
+}
diff --git a/tests/ui/closures/old-closure-explicit-types.rs b/tests/ui/closures/old-closure-explicit-types.rs
new file mode 100644
index 00000000000..860fcc8df21
--- /dev/null
+++ b/tests/ui/closures/old-closure-explicit-types.rs
@@ -0,0 +1,6 @@
+// run-pass
+
+pub fn main() {
+    fn as_buf<T, F>(s: String, f: F) -> T where F: FnOnce(String) -> T { f(s) }
+    as_buf("foo".to_string(), |foo: String| -> () { println!("{}", foo) });
+}
diff --git a/tests/ui/closures/old-closure-expr-precedence.rs b/tests/ui/closures/old-closure-expr-precedence.rs
new file mode 100644
index 00000000000..13b2fe9c3d1
--- /dev/null
+++ b/tests/ui/closures/old-closure-expr-precedence.rs
@@ -0,0 +1,62 @@
+// run-pass
+
+#![allow(unused_must_use)]
+#![allow(unused_parens)]
+// This test has some extra semis in it that the pretty-printer won't
+// reproduce so we don't want to automatically reformat it
+
+// no-reformat
+
+
+/*
+ *
+ *  When you write a block-expression thing followed by
+ *  a lone unary operator, you can get a surprising parse:
+ *
+ *  if (...) { ... }
+ *  -num;
+ *
+ * for example, or:
+ *
+ *  if (...) { ... }
+ *  *box;
+ *
+ * These will parse as subtraction and multiplication binops.
+ * To get them to parse "the way you want" you need to brace
+ * the leading unops:
+
+ *  if (...) { ... }
+ *  {-num};
+ *
+ * or alternatively, semi-separate them:
+ *
+ *  if (...) { ... };
+ *  -num;
+ *
+ * This seems a little wonky, but the alternative is to lower
+ * precedence of such block-like exprs to the point where
+ * you have to parenthesize them to get them to occur in the
+ * RHS of a binop. For example, you'd have to write:
+ *
+ *   12 + (if (foo) { 13 } else { 14 });
+ *
+ * rather than:
+ *
+ *   12 + if (foo) { 13 } else { 14 };
+ *
+ * Since we want to maintain the ability to write the latter,
+ * we leave the parens-burden on the trailing unop case.
+ *
+ */
+
+pub fn main() {
+
+  let num = 12;
+
+  assert_eq!(if (true) { 12 } else { 12 } - num, 0);
+  assert_eq!(12 - if (true) { 12 } else { 12 }, 0);
+  if (true) { 12; } {-num};
+  if (true) { 12; }; {-num};
+  if (true) { 12; };;; -num;
+  //~^ WARNING unnecessary trailing semicolons
+}
diff --git a/tests/ui/closures/old-closure-expr-precedence.stderr b/tests/ui/closures/old-closure-expr-precedence.stderr
new file mode 100644
index 00000000000..fabece1ad4a
--- /dev/null
+++ b/tests/ui/closures/old-closure-expr-precedence.stderr
@@ -0,0 +1,10 @@
+warning: unnecessary trailing semicolons
+  --> $DIR/old-closure-expr-precedence.rs:60:21
+   |
+LL |   if (true) { 12; };;; -num;
+   |                     ^^ help: remove these semicolons
+   |
+   = note: `#[warn(redundant_semicolons)]` on by default
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/closures/old-closure-expression-remove-semicolon.fixed b/tests/ui/closures/old-closure-expression-remove-semicolon.fixed
new file mode 100644
index 00000000000..8aa9e952b99
--- /dev/null
+++ b/tests/ui/closures/old-closure-expression-remove-semicolon.fixed
@@ -0,0 +1,12 @@
+// run-rustfix
+
+fn foo() -> i32 {
+    0
+}
+
+fn main() {
+    let _x: i32 = {
+        //~^ ERROR mismatched types
+        foo() //~ HELP remove this semicolon to return this value
+    };
+}
diff --git a/tests/ui/closures/old-closure-expression-remove-semicolon.rs b/tests/ui/closures/old-closure-expression-remove-semicolon.rs
new file mode 100644
index 00000000000..912c7a3314a
--- /dev/null
+++ b/tests/ui/closures/old-closure-expression-remove-semicolon.rs
@@ -0,0 +1,12 @@
+// run-rustfix
+
+fn foo() -> i32 {
+    0
+}
+
+fn main() {
+    let _x: i32 = {
+        //~^ ERROR mismatched types
+        foo(); //~ HELP remove this semicolon to return this value
+    };
+}
diff --git a/tests/ui/closures/old-closure-expression-remove-semicolon.stderr b/tests/ui/closures/old-closure-expression-remove-semicolon.stderr
new file mode 100644
index 00000000000..bc54ab4d511
--- /dev/null
+++ b/tests/ui/closures/old-closure-expression-remove-semicolon.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/old-closure-expression-remove-semicolon.rs:8:19
+   |
+LL |       let _x: i32 = {
+   |  ___________________^
+LL | |
+LL | |         foo();
+   | |              - help: remove this semicolon to return this value
+LL | |     };
+   | |_____^ expected `i32`, found `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/old-closure-fn-coerce.rs b/tests/ui/closures/old-closure-fn-coerce.rs
new file mode 100644
index 00000000000..d993ad99459
--- /dev/null
+++ b/tests/ui/closures/old-closure-fn-coerce.rs
@@ -0,0 +1,11 @@
+// run-pass
+#![allow(unused_braces)]
+
+fn force<F>(f: F) -> isize where F: FnOnce() -> isize { return f(); }
+
+pub fn main() {
+    fn f() -> isize { return 7; }
+    assert_eq!(force(f), 7);
+    let g = {||force(f)};
+    assert_eq!(g(), 7);
+}
diff --git a/tests/ui/closures/old-closure-iter-1.rs b/tests/ui/closures/old-closure-iter-1.rs
new file mode 100644
index 00000000000..caf0266cff1
--- /dev/null
+++ b/tests/ui/closures/old-closure-iter-1.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+fn iter_vec<T, F>(v: Vec<T> , mut f: F) where F: FnMut(&T) { for x in &v { f(x); } }
+
+pub fn main() {
+    let v = vec![1, 2, 3, 4, 5, 6, 7];
+    let mut odds = 0;
+    iter_vec(v, |i| {
+        if *i % 2 == 1 {
+            odds += 1;
+        }
+    });
+    println!("{}", odds);
+    assert_eq!(odds, 4);
+}
diff --git a/tests/ui/closures/old-closure-iter-2.rs b/tests/ui/closures/old-closure-iter-2.rs
new file mode 100644
index 00000000000..e90c1ee815a
--- /dev/null
+++ b/tests/ui/closures/old-closure-iter-2.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+fn iter_vec<T, F>(v: Vec<T>, mut f: F) where F: FnMut(&T) { for x in &v { f(x); } }
+
+pub fn main() {
+    let v = vec![1, 2, 3, 4, 5];
+    let mut sum = 0;
+    iter_vec(v.clone(), |i| {
+        iter_vec(v.clone(), |j| {
+            sum += *i * *j;
+        });
+    });
+    println!("{}", sum);
+    assert_eq!(sum, 225);
+}
diff --git a/tests/ui/closures/once-move-out-on-heap.rs b/tests/ui/closures/once-move-out-on-heap.rs
new file mode 100644
index 00000000000..4e2e400cec0
--- /dev/null
+++ b/tests/ui/closures/once-move-out-on-heap.rs
@@ -0,0 +1,18 @@
+// run-pass
+// Testing guarantees provided by once functions.
+
+
+
+use std::sync::Arc;
+
+fn foo<F:FnOnce()>(blk: F) {
+    blk();
+}
+
+pub fn main() {
+    let x = Arc::new(true);
+    foo(move|| {
+        assert!(*x);
+        drop(x);
+    });
+}
diff --git a/tests/ui/closures/print/closure-print-generic-1.rs b/tests/ui/closures/print/closure-print-generic-1.rs
new file mode 100644
index 00000000000..504b4adbeb9
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-1.rs
@@ -0,0 +1,23 @@
+fn to_fn_once<F: FnOnce()>(f: F) -> F {
+    f
+}
+
+fn f<T: std::fmt::Display>(y: T) {
+    struct Foo<U: std::fmt::Display> {
+        x: U,
+    };
+
+    let foo = Foo { x: "x" };
+
+    let c = to_fn_once(move || {
+        println!("{} {}", foo.x, y);
+    });
+
+    c();
+    c();
+    //~^ ERROR use of moved value
+}
+
+fn main() {
+    f("S");
+}
diff --git a/tests/ui/closures/print/closure-print-generic-1.stderr b/tests/ui/closures/print/closure-print-generic-1.stderr
new file mode 100644
index 00000000000..b21734f0257
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-1.stderr
@@ -0,0 +1,20 @@
+error[E0382]: use of moved value: `c`
+  --> $DIR/closure-print-generic-1.rs:17:5
+   |
+LL |     let c = to_fn_once(move || {
+   |         - move occurs because `c` has type `[closure@$DIR/closure-print-generic-1.rs:12:24: 12:31]`, which does not implement the `Copy` trait
+...
+LL |     c();
+   |     --- `c` moved due to this call
+LL |     c();
+   |     ^ value used here after move
+   |
+note: this value implements `FnOnce`, which causes it to be moved when called
+  --> $DIR/closure-print-generic-1.rs:16:5
+   |
+LL |     c();
+   |     ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/print/closure-print-generic-2.rs b/tests/ui/closures/print/closure-print-generic-2.rs
new file mode 100644
index 00000000000..3f77fd26b17
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-2.rs
@@ -0,0 +1,13 @@
+mod mod1 {
+    pub fn f<T: std::fmt::Display>(t: T) {
+        let x = 20;
+
+        let c = || println!("{} {}", t, x);
+        let c1: () = c;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn main() {
+    mod1::f(5i32);
+}
diff --git a/tests/ui/closures/print/closure-print-generic-2.stderr b/tests/ui/closures/print/closure-print-generic-2.stderr
new file mode 100644
index 00000000000..e53277a9396
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-2.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-print-generic-2.rs:6:22
+   |
+LL |         let c = || println!("{} {}", t, x);
+   |                 -- the found closure
+LL |         let c1: () = c;
+   |                 --   ^ expected `()`, found closure
+   |                 |
+   |                 expected due to this
+   |
+   = note: expected unit type `()`
+                found closure `[closure@$DIR/closure-print-generic-2.rs:5:17: 5:19]`
+help: use parentheses to call this closure
+   |
+LL |         let c1: () = c();
+   |                       ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs
new file mode 100644
index 00000000000..07bf8fe4c00
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.rs
@@ -0,0 +1,16 @@
+// compile-flags: -Ztrim-diagnostic-paths=off -Zverbose
+
+mod mod1 {
+    pub fn f<T: std::fmt::Display>(t: T)
+    {
+        let x = 20;
+
+        let c = || println!("{} {}", t, x);
+        let c1 : () = c;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn main() {
+    mod1::f(5i32);
+}
diff --git a/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr
new file mode 100644
index 00000000000..ff89dd34034
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-print-generic-trim-off-verbose-2.rs:9:23
+   |
+LL |         let c = || println!("{} {}", t, x);
+   |                 -- the found closure
+LL |         let c1 : () = c;
+   |                  --   ^ expected `()`, found closure
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected unit type `()`
+                found closure `[mod1::f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]`
+help: use parentheses to call this closure
+   |
+LL |         let c1 : () = c();
+   |                        ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/print/closure-print-generic-verbose-1.rs b/tests/ui/closures/print/closure-print-generic-verbose-1.rs
new file mode 100644
index 00000000000..67d37f1c59b
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-verbose-1.rs
@@ -0,0 +1,24 @@
+// compile-flags: -Zverbose
+
+fn to_fn_once<F:FnOnce()>(f: F) -> F { f }
+
+fn f<T: std::fmt::Display>(y: T) {
+    struct Foo<U: std::fmt::Display> {
+        x: U
+    };
+
+    let foo =  Foo{ x: "x" };
+
+    let c = to_fn_once(move|| {
+        println!("{} {}", foo.x, y);
+    });
+
+    c();
+    c();
+    //~^ ERROR use of moved value
+}
+
+
+fn main() {
+    f("S");
+}
diff --git a/tests/ui/closures/print/closure-print-generic-verbose-1.stderr b/tests/ui/closures/print/closure-print-generic-verbose-1.stderr
new file mode 100644
index 00000000000..3ab7c66d11f
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-verbose-1.stderr
@@ -0,0 +1,20 @@
+error[E0382]: use of moved value: `c`
+  --> $DIR/closure-print-generic-verbose-1.rs:17:5
+   |
+LL |     let c = to_fn_once(move|| {
+   |         - move occurs because `c` has type `[f<T>::{closure#0} closure_kind_ty=i32 closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=(Foo<&'_#9r str>, T)]`, which does not implement the `Copy` trait
+...
+LL |     c();
+   |     --- `c` moved due to this call
+LL |     c();
+   |     ^ value used here after move
+   |
+note: this value implements `FnOnce`, which causes it to be moved when called
+  --> $DIR/closure-print-generic-verbose-1.rs:16:5
+   |
+LL |     c();
+   |     ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/closures/print/closure-print-generic-verbose-2.rs b/tests/ui/closures/print/closure-print-generic-verbose-2.rs
new file mode 100644
index 00000000000..f460fedffb7
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-verbose-2.rs
@@ -0,0 +1,16 @@
+// compile-flags: -Zverbose
+
+mod mod1 {
+    pub fn f<T: std::fmt::Display>(t: T)
+    {
+        let x = 20;
+
+        let c = || println!("{} {}", t, x);
+        let c1 : () = c;
+        //~^ ERROR mismatched types
+    }
+}
+
+fn main() {
+    mod1::f(5i32);
+}
diff --git a/tests/ui/closures/print/closure-print-generic-verbose-2.stderr b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr
new file mode 100644
index 00000000000..5bbf84f963d
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-generic-verbose-2.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-print-generic-verbose-2.rs:9:23
+   |
+LL |         let c = || println!("{} {}", t, x);
+   |                 -- the found closure
+LL |         let c1 : () = c;
+   |                  --   ^ expected `()`, found closure
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected unit type `()`
+                found closure `[f<T>::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]`
+help: use parentheses to call this closure
+   |
+LL |         let c1 : () = c();
+   |                        ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/print/closure-print-verbose.rs b/tests/ui/closures/print/closure-print-verbose.rs
new file mode 100644
index 00000000000..4b0438a91ed
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-verbose.rs
@@ -0,0 +1,12 @@
+// compile-flags: -Zverbose
+
+// Same as closure-coerce-fn-1.rs
+
+// Ensure that capturing closures are never coerced to fns
+// Especially interesting as non-capturing closures can be.
+
+fn main() {
+    let mut a = 0u8;
+    let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+    //~^ ERROR mismatched types
+}
diff --git a/tests/ui/closures/print/closure-print-verbose.stderr b/tests/ui/closures/print/closure-print-verbose.stderr
new file mode 100644
index 00000000000..083717b3334
--- /dev/null
+++ b/tests/ui/closures/print/closure-print-verbose.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/closure-print-verbose.rs:10:29
+   |
+LL |     let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+   |              ------------   ^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
+   |              |
+   |              expected due to this
+   |
+   = note: expected fn pointer `fn(u8) -> u8`
+                 found closure `[main::{closure#0} closure_substs=(unavailable) substs=[i8, extern "rust-call" fn((u8,)) -> u8, _#6t]]`
+note: closures can only be coerced to `fn` types if they do not capture any variables
+  --> $DIR/closure-print-verbose.rs:10:39
+   |
+LL |     let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
+   |                                       ^ `a` captured here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/closures/semistatement-in-lambda.rs b/tests/ui/closures/semistatement-in-lambda.rs
new file mode 100644
index 00000000000..ebd55e0ba02
--- /dev/null
+++ b/tests/ui/closures/semistatement-in-lambda.rs
@@ -0,0 +1,12 @@
+// run-pass
+
+#![allow(unused_must_use)]
+
+pub fn main() {
+    // Test that lambdas behave as unary expressions with block-like expressions
+    -if true { 1 } else { 2 } * 3;
+    || if true { 1 } else { 2 } * 3;
+
+    // The following is invalid and parses as `if true { 1 } else { 2 }; *3`
+    // if true { 1 } else { 2 } * 3
+}
diff --git a/tests/ui/closures/supertrait-hint-cycle-2.rs b/tests/ui/closures/supertrait-hint-cycle-2.rs
new file mode 100644
index 00000000000..fda81b18d1e
--- /dev/null
+++ b/tests/ui/closures/supertrait-hint-cycle-2.rs
@@ -0,0 +1,18 @@
+// check-pass
+
+trait Foo<'a> {
+    type Input;
+}
+
+impl<F: Fn(u32)> Foo<'_> for F {
+    type Input = u32;
+}
+
+trait SuperFn: for<'a> Foo<'a> + for<'a> Fn(<Self as Foo<'a>>::Input) {}
+impl<T> SuperFn for T where T: for<'a> Fn(<Self as Foo<'a>>::Input) + for<'a> Foo<'a> {}
+
+fn needs_super(_: impl SuperFn) {}
+
+fn main() {
+    needs_super(|_: u32| {});
+}
diff --git a/tests/ui/closures/supertrait-hint-cycle-3.rs b/tests/ui/closures/supertrait-hint-cycle-3.rs
new file mode 100644
index 00000000000..8149474df19
--- /dev/null
+++ b/tests/ui/closures/supertrait-hint-cycle-3.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+
+trait Foo<'a> {
+    type Input;
+}
+
+impl<F: Fn(u32)> Foo<'_> for F {
+    type Input = u32;
+}
+
+fn needs_super<F: for<'a> Fn(<F as Foo<'a>>::Input) + for<'a> Foo<'a>>(_: F) {}
+
+fn main() {
+    needs_super(|_: u32| {});
+}
diff --git a/tests/ui/closures/supertrait-hint-cycle.rs b/tests/ui/closures/supertrait-hint-cycle.rs
new file mode 100644
index 00000000000..dbb06b2ef7a
--- /dev/null
+++ b/tests/ui/closures/supertrait-hint-cycle.rs
@@ -0,0 +1,65 @@
+// edition:2021
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+#![feature(closure_lifetime_binder)]
+
+use std::future::Future;
+
+trait AsyncFn<I, R>: FnMut(I) -> Self::Fut {
+    type Fut: Future<Output = R>;
+}
+
+impl<F, I, R, Fut> AsyncFn<I, R> for F
+where
+    Fut: Future<Output = R>,
+    F: FnMut(I) -> Fut,
+{
+    type Fut = Fut;
+}
+
+async fn call<C, R, F>(mut ctx: C, mut f: F) -> Result<R, ()>
+where
+    F: for<'a> AsyncFn<&'a mut C, Result<R, ()>>,
+{
+    loop {
+        match f(&mut ctx).await {
+            Ok(val) => return Ok(val),
+            Err(_) => continue,
+        }
+    }
+}
+
+trait Cap<'a> {}
+impl<T> Cap<'_> for T {}
+
+fn works(ctx: &mut usize) {
+    let mut inner = 0;
+
+    type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;
+
+    let callback = for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
+        inner += 1;
+        async move {
+            let _c = c;
+            Ok(1usize)
+        }
+    };
+    call(ctx, callback);
+}
+
+fn doesnt_work_but_should(ctx: &mut usize) {
+    let mut inner = 0;
+
+    type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;
+
+    call(ctx, for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
+        inner += 1;
+        async move {
+            let _c = c;
+            Ok(1usize)
+        }
+    });
+}
+
+fn main() {}
diff --git a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs
new file mode 100644
index 00000000000..270bf14c35e
--- /dev/null
+++ b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs
@@ -0,0 +1,17 @@
+// check-pass
+
+pub trait Fn0: Fn(i32) -> Self::Out {
+    type Out;
+}
+
+impl<F: Fn(i32) -> ()> Fn0 for F {
+    type Out = ();
+}
+
+pub fn closure_typer(_: impl Fn0) {}
+
+fn main() {
+    closure_typer(move |x| {
+        let _: i64 = x.into();
+    });
+}
diff --git a/tests/ui/closures/thir-unsafeck-issue-85871.rs b/tests/ui/closures/thir-unsafeck-issue-85871.rs
new file mode 100644
index 00000000000..aea539b74df
--- /dev/null
+++ b/tests/ui/closures/thir-unsafeck-issue-85871.rs
@@ -0,0 +1,20 @@
+// Tests that no ICE occurs when a closure appears inside a node
+// that does not have a body when compiling with
+// compile-flags: -Zthir-unsafeck=yes
+// check-pass
+
+#![allow(dead_code)]
+
+struct Bug {
+    inner: [(); match || 1 {
+        _n => 42, // we may not call the closure here (E0015)
+    }],
+}
+
+enum E {
+    V([(); { let _ = || 1; 42 }]),
+}
+
+type Ty = [(); { let _ = || 1; 42 }];
+
+fn main() {}