about summary refs log tree commit diff
path: root/tests/ui/pattern
diff options
context:
space:
mode:
Diffstat (limited to 'tests/ui/pattern')
-rw-r--r--tests/ui/pattern/auxiliary/declarations-for-tuple-field-count-errors.rs20
-rw-r--r--tests/ui/pattern/bindings-after-at/bind-by-copy.rs47
-rw-r--r--tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.rs37
-rw-r--r--tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr79
-rw-r--r--tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.rs11
-rw-r--r--tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.stderr13
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-move-and-move.rs31
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr112
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs84
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs69
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr145
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs49
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs7
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr17
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.rs80
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr487
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.rs82
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.stderr375
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-both-sides.rs45
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs136
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr445
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs109
-rw-r--r--tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr359
-rw-r--r--tests/ui/pattern/bindings-after-at/box-patterns.rs35
-rw-r--r--tests/ui/pattern/bindings-after-at/copy-and-move-mixed.rs14
-rw-r--r--tests/ui/pattern/bindings-after-at/copy-and-move-mixed.stderr17
-rw-r--r--tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs48
-rw-r--r--tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr68
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs12
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs11
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr25
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs11
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr15
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-patterns.rs14
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs33
-rw-r--r--tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr26
-rw-r--r--tests/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs43
-rw-r--r--tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs54
-rw-r--r--tests/ui/pattern/bindings-after-at/or-patterns.rs38
-rw-r--r--tests/ui/pattern/bindings-after-at/pat-at-same-name-both.rs29
-rw-r--r--tests/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr64
-rw-r--r--tests/ui/pattern/bindings-after-at/slice-patterns.rs39
-rw-r--r--tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs16
-rw-r--r--tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr43
-rw-r--r--tests/ui/pattern/for-loop-bad-item.rs20
-rw-r--r--tests/ui/pattern/for-loop-bad-item.stderr32
-rw-r--r--tests/ui/pattern/ignore-all-the-things.rs44
-rw-r--r--tests/ui/pattern/integer-range-binding.rs8
-rw-r--r--tests/ui/pattern/issue-10392.rs30
-rw-r--r--tests/ui/pattern/issue-11577.rs18
-rw-r--r--tests/ui/pattern/issue-12582.rs21
-rw-r--r--tests/ui/pattern/issue-14221.rs21
-rw-r--r--tests/ui/pattern/issue-14221.stderr32
-rw-r--r--tests/ui/pattern/issue-15080.rs22
-rw-r--r--tests/ui/pattern/issue-17718-patterns.rs12
-rw-r--r--tests/ui/pattern/issue-17718-patterns.stderr21
-rw-r--r--tests/ui/pattern/issue-22546.rs52
-rw-r--r--tests/ui/pattern/issue-27320.rs15
-rw-r--r--tests/ui/pattern/issue-52240.rs16
-rw-r--r--tests/ui/pattern/issue-52240.stderr9
-rw-r--r--tests/ui/pattern/issue-6449.rs44
-rw-r--r--tests/ui/pattern/issue-66270-pat-struct-parser-recovery.rs14
-rw-r--r--tests/ui/pattern/issue-66270-pat-struct-parser-recovery.stderr10
-rw-r--r--tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs21
-rw-r--r--tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr33
-rw-r--r--tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs42
-rw-r--r--tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr41
-rw-r--r--tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs26
-rw-r--r--tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr15
-rw-r--r--tests/ui/pattern/issue-72565.rs8
-rw-r--r--tests/ui/pattern/issue-72565.stderr8
-rw-r--r--tests/ui/pattern/issue-72574-1.rs10
-rw-r--r--tests/ui/pattern/issue-72574-1.stderr34
-rw-r--r--tests/ui/pattern/issue-72574-2.rs12
-rw-r--r--tests/ui/pattern/issue-72574-2.stderr37
-rw-r--r--tests/ui/pattern/issue-74539.rs15
-rw-r--r--tests/ui/pattern/issue-74539.stderr37
-rw-r--r--tests/ui/pattern/issue-74702.rs7
-rw-r--r--tests/ui/pattern/issue-74702.stderr34
-rw-r--r--tests/ui/pattern/issue-74954.rs7
-rw-r--r--tests/ui/pattern/issue-80186-mut-binding-help-suggestion.rs9
-rw-r--r--tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr10
-rw-r--r--tests/ui/pattern/issue-8351-1.rs16
-rw-r--r--tests/ui/pattern/issue-8351-2.rs16
-rw-r--r--tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs28
-rw-r--r--tests/ui/pattern/issue-88074-pat-range-type-inference-err.stderr19
-rw-r--r--tests/ui/pattern/issue-88074-pat-range-type-inference.rs16
-rw-r--r--tests/ui/pattern/issue-92074-macro-ice.rs36
-rw-r--r--tests/ui/pattern/issue-92074-macro-ice.stderr35
-rw-r--r--tests/ui/pattern/issue-95878.rs12
-rw-r--r--tests/ui/pattern/issue-95878.stderr8
-rw-r--r--tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs29
-rw-r--r--tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs48
-rw-r--r--tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr216
-rw-r--r--tests/ui/pattern/move-ref-patterns/by-move-sub-pat-unreachable.rs12
-rw-r--r--tests/ui/pattern/move-ref-patterns/issue-53840.rs20
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.rs120
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr404
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs28
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.rs32
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.stderr63
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed12
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs12
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr17
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs10
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr17
-rw-r--r--tests/ui/pattern/move-ref-patterns/move-ref-patterns-dynamic-semantics.rs79
-rw-r--r--tests/ui/pattern/non-constant-in-const-path.rs18
-rw-r--r--tests/ui/pattern/non-constant-in-const-path.stderr28
-rw-r--r--tests/ui/pattern/non-structural-match-types.rs14
-rw-r--r--tests/ui/pattern/non-structural-match-types.stderr14
-rw-r--r--tests/ui/pattern/pat-shadow-in-nested-binding.rs6
-rw-r--r--tests/ui/pattern/pat-shadow-in-nested-binding.stderr12
-rw-r--r--tests/ui/pattern/pat-struct-field-expr-has-type.rs9
-rw-r--r--tests/ui/pattern/pat-struct-field-expr-has-type.stderr14
-rw-r--r--tests/ui/pattern/pat-tuple-bad-type.rs15
-rw-r--r--tests/ui/pattern/pat-tuple-bad-type.stderr26
-rw-r--r--tests/ui/pattern/pat-tuple-field-count-cross.rs57
-rw-r--r--tests/ui/pattern/pat-tuple-field-count-cross.stderr531
-rw-r--r--tests/ui/pattern/pat-tuple-overfield.rs74
-rw-r--r--tests/ui/pattern/pat-tuple-overfield.stderr312
-rw-r--r--tests/ui/pattern/pat-tuple-underfield.rs67
-rw-r--r--tests/ui/pattern/pat-tuple-underfield.stderr167
-rw-r--r--tests/ui/pattern/pat-type-err-formal-param.rs8
-rw-r--r--tests/ui/pattern/pat-type-err-formal-param.stderr11
-rw-r--r--tests/ui/pattern/pat-type-err-let-stmt.rs16
-rw-r--r--tests/ui/pattern/pat-type-err-let-stmt.stderr51
-rw-r--r--tests/ui/pattern/patkind-litrange-no-expr.rs24
-rw-r--r--tests/ui/pattern/patkind-litrange-no-expr.stderr14
-rw-r--r--tests/ui/pattern/pattern-binding-disambiguation.rs57
-rw-r--r--tests/ui/pattern/pattern-binding-disambiguation.stderr63
-rw-r--r--tests/ui/pattern/pattern-error-continue.rs35
-rw-r--r--tests/ui/pattern/pattern-error-continue.stderr62
-rw-r--r--tests/ui/pattern/pattern-ident-path-generics.rs6
-rw-r--r--tests/ui/pattern/pattern-ident-path-generics.stderr14
-rw-r--r--tests/ui/pattern/pattern-tyvar-2.rs6
-rw-r--r--tests/ui/pattern/pattern-tyvar-2.stderr11
-rw-r--r--tests/ui/pattern/pattern-tyvar.rs12
-rw-r--r--tests/ui/pattern/pattern-tyvar.stderr14
-rw-r--r--tests/ui/pattern/rest-pat-semantic-disallowed.rs83
-rw-r--r--tests/ui/pattern/rest-pat-semantic-disallowed.stderr201
-rw-r--r--tests/ui/pattern/rest-pat-syntactic.rs73
-rw-r--r--tests/ui/pattern/rest-pat-syntactic.stderr24
-rw-r--r--tests/ui/pattern/size-and-align.rs20
-rw-r--r--tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.fixed10
-rw-r--r--tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.rs9
-rw-r--r--tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.stderr21
-rw-r--r--tests/ui/pattern/usefulness/always-inhabited-union-ref.rs32
-rw-r--r--tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr37
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/empty.rs10
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/hidden.rs14
-rw-r--r--tests/ui/pattern/usefulness/auxiliary/unstable.rs23
-rw-r--r--tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs18
-rw-r--r--tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr8
-rw-r--r--tests/ui/pattern/usefulness/const-pat-ice.rs11
-rw-r--r--tests/ui/pattern/usefulness/const-private-fields.rs30
-rw-r--r--tests/ui/pattern/usefulness/consts-opaque.rs145
-rw-r--r--tests/ui/pattern/usefulness/consts-opaque.stderr202
-rw-r--r--tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs16
-rw-r--r--tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr34
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-fields.rs26
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-fields.stderr59
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs43
-rw-r--r--tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr102
-rw-r--r--tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr303
-rw-r--r--tests/ui/pattern/usefulness/empty-match.normal.stderr303
-rw-r--r--tests/ui/pattern/usefulness/empty-match.rs95
-rw-r--r--tests/ui/pattern/usefulness/floats.rs19
-rw-r--r--tests/ui/pattern/usefulness/floats.stderr28
-rw-r--r--tests/ui/pattern/usefulness/guards.rs22
-rw-r--r--tests/ui/pattern/usefulness/guards.stderr16
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs101
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr149
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs59
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr89
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr17
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr170
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs50
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs18
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr33
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/reachability.rs113
-rw-r--r--tests/ui/pattern/usefulness/integer-ranges/reachability.stderr154
-rw-r--r--tests/ui/pattern/usefulness/irrefutable-let-patterns.rs11
-rw-r--r--tests/ui/pattern/usefulness/irrefutable-unit.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-12116.rs21
-rw-r--r--tests/ui/pattern/usefulness/issue-12116.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-12369.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-12369.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-13727.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-13727.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-15129.rs17
-rw-r--r--tests/ui/pattern/usefulness/issue-15129.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-2111.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-2111.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-b.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-b.stderr14
-rw-r--r--tests/ui/pattern/usefulness/issue-30240-rpass.rs14
-rw-r--r--tests/ui/pattern/usefulness/issue-30240.rs10
-rw-r--r--tests/ui/pattern/usefulness/issue-30240.stderr29
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-1.rs3
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-1.stderr17
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-2.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-3096-2.stderr17
-rw-r--r--tests/ui/pattern/usefulness/issue-31221.rs34
-rw-r--r--tests/ui/pattern/usefulness/issue-31221.stderr32
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.rs10
-rw-r--r--tests/ui/pattern/usefulness/issue-31561.stderr31
-rw-r--r--tests/ui/pattern/usefulness/issue-35609.rs43
-rw-r--r--tests/ui/pattern/usefulness/issue-35609.stderr119
-rw-r--r--tests/ui/pattern/usefulness/issue-3601.rs34
-rw-r--r--tests/ui/pattern/usefulness/issue-3601.stderr18
-rw-r--r--tests/ui/pattern/usefulness/issue-39362.rs18
-rw-r--r--tests/ui/pattern/usefulness/issue-39362.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-40221.rs16
-rw-r--r--tests/ui/pattern/usefulness/issue-40221.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-4321.rs8
-rw-r--r--tests/ui/pattern/usefulness/issue-4321.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-50900.rs19
-rw-r--r--tests/ui/pattern/usefulness/issue-50900.stderr21
-rw-r--r--tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs11
-rw-r--r--tests/ui/pattern/usefulness/issue-56379.rs14
-rw-r--r--tests/ui/pattern/usefulness/issue-56379.stderr27
-rw-r--r--tests/ui/pattern/usefulness/issue-57472.rs35
-rw-r--r--tests/ui/pattern/usefulness/issue-57472.stderr20
-rw-r--r--tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs15
-rw-r--r--tests/ui/pattern/usefulness/issue-66501.rs12
-rw-r--r--tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs22
-rw-r--r--tests/ui/pattern/usefulness/issue-72377.rs17
-rw-r--r--tests/ui/pattern/usefulness/issue-72377.stderr16
-rw-r--r--tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs56
-rw-r--r--tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs12
-rw-r--r--tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr23
-rw-r--r--tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs25
-rw-r--r--tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs27
-rw-r--r--tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs6
-rw-r--r--tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr9
-rw-r--r--tests/ui/pattern/usefulness/issue-88747.rs14
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics-2.rs62
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics-2.stderr54
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics.rs69
-rw-r--r--tests/ui/pattern/usefulness/match-arm-statics.stderr26
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs13
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr29
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns.rs55
-rw-r--r--tests/ui/pattern/usefulness/match-byte-array-patterns.stderr56
-rw-r--r--tests/ui/pattern/usefulness/match-non-exhaustive.rs4
-rw-r--r--tests/ui/pattern/usefulness/match-non-exhaustive.stderr27
-rw-r--r--tests/ui/pattern/usefulness/match-privately-empty.rs21
-rw-r--r--tests/ui/pattern/usefulness/match-privately-empty.stderr21
-rw-r--r--tests/ui/pattern/usefulness/match-ref-ice.rs16
-rw-r--r--tests/ui/pattern/usefulness/match-ref-ice.stderr14
-rw-r--r--tests/ui/pattern/usefulness/match-slice-patterns.rs12
-rw-r--r--tests/ui/pattern/usefulness/match-slice-patterns.stderr16
-rw-r--r--tests/ui/pattern/usefulness/match-vec-fixed.rs18
-rw-r--r--tests/ui/pattern/usefulness/match-vec-fixed.stderr20
-rw-r--r--tests/ui/pattern/usefulness/match-vec-unreachable.rs29
-rw-r--r--tests/ui/pattern/usefulness/match-vec-unreachable.stderr26
-rw-r--r--tests/ui/pattern/usefulness/nested-exhaustive-match.rs14
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs105
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr198
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs19
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr34
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match.rs63
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-match.stderr121
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs89
-rw-r--r--tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr129
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.rs7
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-errors.stderr25
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs5
-rw-r--r--tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr11
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-2.rs31
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-2.stderr32
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-3.rs31
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const-3.stderr32
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const.rs54
-rw-r--r--tests/ui/pattern/usefulness/slice-pattern-const.stderr62
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs129
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr263
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs26
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-reachability.rs25
-rw-r--r--tests/ui/pattern/usefulness/slice-patterns-reachability.stderr44
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-fields.rs16
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-fields.stderr29
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-patterns.rs18
-rw-r--r--tests/ui/pattern/usefulness/stable-gated-patterns.stderr42
-rw-r--r--tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs12
-rw-r--r--tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr23
-rw-r--r--tests/ui/pattern/usefulness/struct-pattern-match-useless.rs15
-rw-r--r--tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr16
-rw-r--r--tests/ui/pattern/usefulness/top-level-alternation.rs57
-rw-r--r--tests/ui/pattern/usefulness/top-level-alternation.stderr74
-rw-r--r--tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs9
-rw-r--r--tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr21
-rw-r--r--tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs36
-rw-r--r--tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr29
-rw-r--r--tests/ui/pattern/usefulness/uninhabited.rs143
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-fields.rs18
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-fields.stderr33
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-patterns.rs22
-rw-r--r--tests/ui/pattern/usefulness/unstable-gated-patterns.stderr24
300 files changed, 14295 insertions, 0 deletions
diff --git a/tests/ui/pattern/auxiliary/declarations-for-tuple-field-count-errors.rs b/tests/ui/pattern/auxiliary/declarations-for-tuple-field-count-errors.rs
new file mode 100644
index 00000000000..f7373c45396
--- /dev/null
+++ b/tests/ui/pattern/auxiliary/declarations-for-tuple-field-count-errors.rs
@@ -0,0 +1,20 @@
+pub struct Z0;
+pub struct Z1();
+
+pub struct S(pub u8, pub u8, pub u8);
+pub struct M(
+    pub u8,
+    pub u8,
+    pub u8,
+);
+
+pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+
+pub enum E2 {
+    S(u8, u8, u8),
+    M(
+        u8,
+        u8,
+        u8,
+    ),
+}
diff --git a/tests/ui/pattern/bindings-after-at/bind-by-copy.rs b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs
new file mode 100644
index 00000000000..2b349f0ed5f
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/bind-by-copy.rs
@@ -0,0 +1,47 @@
+// run-pass
+
+// Test copy
+
+struct A { a: i32, b: i32 }
+struct B { a: i32, b: C }
+struct D { a: i32, d: C }
+#[derive(Copy,Clone)]
+struct C { c: i32 }
+
+pub fn main() {
+    match (A {a: 10, b: 20}) {
+        x@A {a, b: 20} => { assert!(x.a == 10); assert!(a == 10); }
+        A {b: _b, ..} => { panic!(); }
+    }
+
+    let mut x@B {b, ..} = B {a: 10, b: C {c: 20}};
+    assert_eq!(x.a, 10);
+    x.b.c = 30;
+    assert_eq!(b.c, 20);
+    let mut y@D {d, ..} = D {a: 10, d: C {c: 20}};
+    assert_eq!(y.a, 10);
+    y.d.c = 30;
+    assert_eq!(d.c, 20);
+
+    let some_b = Some(B { a: 10, b: C { c: 20 } });
+
+    // in irrefutable pattern
+    if let Some(x @ B { b, .. }) = some_b {
+        assert_eq!(x.b.c, 20);
+        assert_eq!(b.c, 20);
+    } else {
+        unreachable!();
+    }
+
+    let some_b = Some(B { a: 10, b: C { c: 20 } });
+
+    if let Some(x @ B { b: mut b @ C { c }, .. }) = some_b {
+        assert_eq!(x.b.c, 20);
+        assert_eq!(b.c, 20);
+        b.c = 30;
+        assert_eq!(b.c, 30);
+        assert_eq!(c, 20);
+    } else {
+        unreachable!();
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.rs b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.rs
new file mode 100644
index 00000000000..9d1f08d6eea
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.rs
@@ -0,0 +1,37 @@
+// This test is taken directly from #16053.
+// It checks that you cannot use an AND-pattern (`binding @ pat`)
+// where one side is by-ref and the other is by-move.
+
+struct X {
+    x: (),
+}
+
+fn main() {
+    let x = Some(X { x: () });
+    match x {
+        Some(ref _y @ _z) => {} //~ ERROR cannot move out of value because it is borrowed
+        //~| ERROR borrow of moved value
+        None => panic!(),
+    }
+
+    let x = Some(X { x: () });
+    match x {
+        Some(_z @ ref _y) => {}
+        //~^ ERROR borrow of moved value
+        None => panic!(),
+    }
+
+    let mut x = Some(X { x: () });
+    match x {
+        Some(ref mut _y @ _z) => {} //~ ERROR cannot move out of value because it is borrowed
+        //~| ERROR borrow of moved value
+        None => panic!(),
+    }
+
+    let mut x = Some(X { x: () });
+    match x {
+        Some(_z @ ref mut _y) => {}
+        //~^ ERROR borrow of moved value
+        None => panic!(),
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr
new file mode 100644
index 00000000000..c8b45fd24d9
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr
@@ -0,0 +1,79 @@
+error: cannot move out of value because it is borrowed
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:12:14
+   |
+LL |         Some(ref _y @ _z) => {}
+   |              ------^^^--
+   |              |        |
+   |              |        value moved into `_z` here
+   |              value borrowed, by `_y`, here
+
+error: borrow of moved value
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:19:14
+   |
+LL |         Some(_z @ ref _y) => {}
+   |              --^^^------
+   |              |    |
+   |              |    value borrowed here after move
+   |              value moved into `_z` here
+   |              move occurs because `_z` has type `X` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         Some(ref _z @ ref _y) => {}
+   |              +++
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:26:14
+   |
+LL |         Some(ref mut _y @ _z) => {}
+   |              ----------^^^--
+   |              |            |
+   |              |            value moved into `_z` here
+   |              value borrowed, by `_y`, here
+
+error: borrow of moved value
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:33:14
+   |
+LL |         Some(_z @ ref mut _y) => {}
+   |              --^^^----------
+   |              |    |
+   |              |    value borrowed here after move
+   |              value moved into `_z` here
+   |              move occurs because `_z` has type `X` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         Some(ref _z @ ref mut _y) => {}
+   |              +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:12:14
+   |
+LL |         Some(ref _y @ _z) => {}
+   |              ^^^^^^   -- value moved here
+   |              |
+   |              value borrowed here after move
+   |
+   = note: move occurs because value has type `X`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         Some(ref _y @ ref _z) => {}
+   |                       +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:26:14
+   |
+LL |         Some(ref mut _y @ _z) => {}
+   |              ^^^^^^^^^^   -- value moved here
+   |              |
+   |              value borrowed here after move
+   |
+   = note: move occurs because value has type `X`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         Some(ref mut _y @ ref _z) => {}
+   |                           +++
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.rs b/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.rs
new file mode 100644
index 00000000000..1816a74a0ba
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.rs
@@ -0,0 +1,11 @@
+// See issue #12534.
+
+fn main() {}
+
+struct A(Box<u8>);
+
+fn f(a @ A(u): A) -> Box<u8> {
+    //~^ ERROR use of partially moved value
+    drop(a);
+    u
+}
diff --git a/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.stderr b/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.stderr
new file mode 100644
index 00000000000..a481ca46833
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/bind-by-move-no-subbindings-fun-param.stderr
@@ -0,0 +1,13 @@
+error[E0382]: use of partially moved value
+  --> $DIR/bind-by-move-no-subbindings-fun-param.rs:7:6
+   |
+LL | fn f(a @ A(u): A) -> Box<u8> {
+   |      ^     - value partially moved here
+   |      |
+   |      value used here after partial move
+   |
+   = note: partial move occurs because value has type `Box<u8>`, 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/pattern/bindings-after-at/borrowck-move-and-move.rs b/tests/ui/pattern/bindings-after-at/borrowck-move-and-move.rs
new file mode 100644
index 00000000000..a61d6821524
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-move-and-move.rs
@@ -0,0 +1,31 @@
+// Test that moving on both sides of an `@` pattern is not allowed.
+
+fn main() {
+    struct U; // Not copy!
+
+    // Prevent promotion:
+    fn u() -> U {
+        U
+    }
+
+    let a @ b = U; //~ ERROR use of moved value
+
+    let a @ (b, c) = (U, U); //~ ERROR use of partially moved value
+
+    let a @ (b, c) = (u(), u()); //~ ERROR use of partially moved value
+
+    match Ok(U) {
+        a @ Ok(b) | a @ Err(b) => {} //~ ERROR use of moved value
+                                     //~^ ERROR use of moved value
+    }
+
+    fn fun(a @ b: U) {} //~ ERROR use of moved value
+
+    match [u(), u(), u(), u()] {
+        xs @ [a, .., b] => {} //~ ERROR use of partially moved value
+    }
+
+    match [u(), u(), u(), u()] {
+        xs @ [_, ys @ .., _] => {} //~ ERROR use of partially moved value
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr b/tests/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr
new file mode 100644
index 00000000000..32489715112
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-move-and-move.stderr
@@ -0,0 +1,112 @@
+error[E0382]: use of moved value
+  --> $DIR/borrowck-move-and-move.rs:11:9
+   |
+LL |     let a @ b = U;
+   |         ^   -   - move occurs because value has type `U`, which does not implement the `Copy` trait
+   |         |   |
+   |         |   value moved here
+   |         value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ ref b = U;
+   |         +++     +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-move-and-move.rs:13:9
+   |
+LL |     let a @ (b, c) = (U, U);
+   |         ^       - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (b, ref c) = (U, U);
+   |         +++         +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-move-and-move.rs:15:9
+   |
+LL |     let a @ (b, c) = (u(), u());
+   |         ^       - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (b, ref c) = (u(), u());
+   |         +++         +++
+
+error[E0382]: use of moved value
+  --> $DIR/borrowck-move-and-move.rs:18:16
+   |
+LL |     match Ok(U) {
+   |           ----- move occurs because value has type `Result<U, U>`, which does not implement the `Copy` trait
+LL |         a @ Ok(b) | a @ Err(b) => {}
+   |         -      ^ value used here after move
+   |         |
+   |         value moved here
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Ok(b) | a @ Err(b) => {}
+   |         +++
+
+error[E0382]: use of moved value
+  --> $DIR/borrowck-move-and-move.rs:18:29
+   |
+LL |     match Ok(U) {
+   |           ----- move occurs because value has type `Result<U, U>`, which does not implement the `Copy` trait
+LL |         a @ Ok(b) | a @ Err(b) => {}
+   |                     -       ^ value used here after move
+   |                     |
+   |                     value moved here
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         a @ Ok(b) | ref a @ Err(b) => {}
+   |                     +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-move-and-move.rs:25:9
+   |
+LL |         xs @ [a, .., b] => {}
+   |         ^^           - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref xs @ [a, .., ref b] => {}
+   |         +++              +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-move-and-move.rs:29:9
+   |
+LL |         xs @ [_, ys @ .., _] => {}
+   |         ^^       -- value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref xs @ [_, ref ys @ .., _] => {}
+   |         +++          +++
+
+error[E0382]: use of moved value
+  --> $DIR/borrowck-move-and-move.rs:22:12
+   |
+LL |     fn fun(a @ b: U) {}
+   |            ^   - value moved here
+   |            |
+   |            value used here after move
+   |            move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs
new file mode 100644
index 00000000000..fbdefd9d36c
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box-pass.rs
@@ -0,0 +1,84 @@
+// check-pass
+
+// Test `@` patterns combined with `box` patterns.
+
+#![feature(box_patterns)]
+
+#[derive(Copy, Clone)]
+struct C;
+
+fn c() -> C { C }
+
+struct NC;
+
+fn nc() -> NC { NC }
+
+fn main() {
+    let ref a @ box b = Box::new(C); // OK; the type is `Copy`.
+    drop(b);
+    drop(b);
+    drop(a);
+
+    let ref a @ box b = Box::new(c()); // OK; the type is `Copy`.
+    drop(b);
+    drop(b);
+    drop(a);
+
+    fn f3(ref a @ box b: Box<C>) { // OK; the type is `Copy`.
+        drop(b);
+        drop(b);
+        drop(a);
+    }
+    match Box::new(c()) {
+        ref a @ box b => { // OK; the type is `Copy`.
+            drop(b);
+            drop(b);
+            drop(a);
+        }
+    }
+
+    let ref a @ box ref b = Box::new(NC); // OK.
+    drop(a);
+    drop(b);
+
+    fn f4(ref a @ box ref b: Box<NC>) { // OK.
+        drop(a);
+        drop(b)
+    }
+
+    match Box::new(nc()) {
+        ref a @ box ref b => { // OK.
+            drop(a);
+            drop(b);
+        }
+    }
+
+    match Box::new([Ok(c()), Err(nc()), Ok(c())]) {
+        box [Ok(a), ref xs @ .., Err(ref b)] => {
+            let _: C = a;
+            let _: &[Result<C, NC>; 1] = xs;
+            let _: &NC = b;
+        }
+        _ => {}
+    }
+
+    match [Ok(Box::new(c())), Err(Box::new(nc())), Ok(Box::new(c())), Ok(Box::new(c()))] {
+        [Ok(box a), ref xs @ .., Err(box ref b), Err(box ref c)] => {
+            let _: C = a;
+            let _: &[Result<Box<C>, Box<NC>>; 1] = xs;
+            let _: &NC = b;
+            let _: &NC = c;
+        }
+        _ => {}
+    }
+
+    match Box::new([Ok(c()), Err(nc()), Ok(c())]) {
+        box [Ok(a), ref xs @ .., Err(b)] => {}
+        _ => {}
+    }
+
+    match [Ok(Box::new(c())), Err(Box::new(nc())), Ok(Box::new(c())), Ok(Box::new(c()))] {
+        [Ok(box ref a), ref xs @ .., Err(box b), Err(box ref mut c)] => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs
new file mode 100644
index 00000000000..45aa65e67a9
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.rs
@@ -0,0 +1,69 @@
+// Test `@` patterns combined with `box` patterns.
+
+#![feature(box_patterns)]
+
+#[derive(Copy, Clone)]
+struct C;
+
+fn c() -> C {
+    C
+}
+
+struct NC;
+
+fn nc() -> NC {
+    NC
+}
+
+fn main() {
+    let a @ box &b = Box::new(&C);
+
+    let a @ box b = Box::new(C);
+
+    fn f1(a @ box &b: Box<&C>) {}
+
+    fn f2(a @ box b: Box<C>) {}
+
+    match Box::new(C) {
+        a @ box b => {}
+    }
+
+    let ref a @ box b = Box::new(NC); //~ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+
+    let ref a @ box ref mut b = Box::new(nc());
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    let ref a @ box ref mut b = Box::new(NC);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    let ref a @ box ref mut b = Box::new(NC);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = NC;
+    let ref a @ box ref mut b = Box::new(NC);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = NC;
+    drop(a);
+
+    let ref mut a @ box ref b = Box::new(NC);
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    *a = Box::new(NC);
+    drop(b);
+
+    fn f5(ref mut a @ box ref b: Box<NC>) {
+        //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+        *a = Box::new(NC);
+        drop(b);
+    }
+
+    match Box::new(nc()) {
+        ref mut a @ box ref b => {
+            //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            *a = Box::new(NC);
+            drop(b);
+        }
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr
new file mode 100644
index 00000000000..f27df32ccfa
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-at-and-box.stderr
@@ -0,0 +1,145 @@
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-at-and-box.rs:31:9
+   |
+LL |     let ref a @ box b = Box::new(NC);
+   |         -----^^^^^^^-
+   |         |           |
+   |         |           value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:34:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(nc());
+   |         -----^^^^^^^---------
+   |         |           |
+   |         |           mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:36:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(NC);
+   |         -----^^^^^^^---------
+   |         |           |
+   |         |           mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:38:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(NC);
+   |         -----^^^^^^^---------
+   |         |           |
+   |         |           mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:42:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(NC);
+   |         -----^^^^^^^---------
+   |         |           |
+   |         |           mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-at-and-box.rs:48:9
+   |
+LL |     let ref mut a @ box ref b = Box::new(NC);
+   |         ---------^^^^^^^-----
+   |         |               |
+   |         |               immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-at-and-box.rs:62:9
+   |
+LL |         ref mut a @ box ref b => {
+   |         ---------^^^^^^^-----
+   |         |               |
+   |         |               immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-at-and-box.rs:54:11
+   |
+LL |     fn f5(ref mut a @ box ref b: Box<NC>) {
+   |           ---------^^^^^^^-----
+   |           |               |
+   |           |               immutable borrow, by `b`, occurs here
+   |           mutable borrow, by `a`, occurs here
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-at-and-box.rs:31:9
+   |
+LL |     let ref a @ box b = Box::new(NC);
+   |         ^^^^^       - value moved here
+   |         |
+   |         value borrowed here after move
+   |
+   = note: move occurs because value has type `NC`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ box ref b = Box::new(NC);
+   |                     +++
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-at-and-box.rs:38:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(NC);
+   |         ^^^^^       --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |     *b = NC;
+   |     ------- mutable borrow later used here
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-at-and-box.rs:42:9
+   |
+LL |     let ref a @ box ref mut b = Box::new(NC);
+   |         ^^^^^       --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |     *b = NC;
+   |     ------- mutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:48:9
+   |
+LL |     let ref mut a @ box ref b = Box::new(NC);
+   |         ^^^^^^^^^       ----- immutable borrow occurs here
+   |         |
+   |         mutable borrow occurs here
+...
+LL |     drop(b);
+   |          - immutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:62:9
+   |
+LL |         ref mut a @ box ref b => {
+   |         ^^^^^^^^^       ----- immutable borrow occurs here
+   |         |
+   |         mutable borrow occurs here
+...
+LL |             drop(b);
+   |                  - immutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-at-and-box.rs:54:11
+   |
+LL |     fn f5(ref mut a @ box ref b: Box<NC>) {
+   |           ^^^^^^^^^       ----- immutable borrow occurs here
+   |           |
+   |           mutable borrow occurs here
+...
+LL |         drop(b);
+   |              - immutable borrow later used here
+
+error: aborting due to 14 previous errors
+
+Some errors have detailed explanations: E0382, E0502.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs
new file mode 100644
index 00000000000..0108861cfce
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-copy-bindings-in-at.rs
@@ -0,0 +1,49 @@
+// check-pass
+
+// Test `Copy` bindings in the rhs of `@` patterns.
+
+#[derive(Copy, Clone)]
+struct C;
+
+fn mk_c() -> C { C }
+
+#[derive(Copy, Clone)]
+struct P<A, B>(A, B);
+
+enum E<A, B> { L(A), R(B) }
+
+fn main() {
+    let a @ b @ c @ d = C;
+    let a @ (b, c) = (C, mk_c());
+    let a @ P(b, P(c, d)) = P(mk_c(), P(C, C));
+    let a @ [b, c] = [C, C];
+    let a @ [b, .., c] = [C, mk_c(), C];
+    let a @ [b, mid @ .., c] = [C, mk_c(), C];
+    let a @ &(b, c) = &(C, C);
+    let a @ &(b, &P(c, d)) = &(mk_c(), &P(C, C));
+
+    fn foo(a @ [b, mid @ .., c]: [C; 3]) {}
+
+    use self::E::*;
+    match L(C) {
+        L(a) | R(a) => {
+            let a: C = a;
+            drop(a);
+            drop(a);
+        }
+    }
+    match R(&L(&mk_c())) {
+        L(L(&a)) | L(R(&a)) | R(L(&a)) | R(R(&a)) => {
+            let a: C = a;
+            drop(a);
+            drop(a);
+        }
+    }
+
+    match Ok(mk_c()) {
+        Ok(ref a @ b) | Err(b @ ref a) => {
+            let _: &C = a;
+            let _: C = b;
+        }
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs
new file mode 100644
index 00000000000..82f16fca65f
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.rs
@@ -0,0 +1,7 @@
+// Test that `by_move_binding @ pat_with_by_ref_bindings` is prevented even with promotion.
+// Currently this logic exists in THIR match checking as opposed to borrowck.
+
+fn main() {
+    struct U;
+    let a @ ref b = U; //~ ERROR borrow of moved value
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr
new file mode 100644
index 00000000000..d6474f1b49f
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr
@@ -0,0 +1,17 @@
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse-promotion.rs:6:9
+   |
+LL |     let a @ ref b = U;
+   |         -^^^-----
+   |         |   |
+   |         |   value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ ref b = U;
+   |         +++
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.rs
new file mode 100644
index 00000000000..06dc6e1c4da
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.rs
@@ -0,0 +1,80 @@
+// Test that `by_move_binding @ pat_with_by_ref_bindings` is prevented.
+
+fn main() {
+    struct U;
+
+    // Prevent promotion.
+    fn u() -> U {
+        U
+    }
+
+    fn f1(a @ ref b: U) {}
+    //~^ ERROR borrow of moved value
+
+    fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+    //~^ ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR use of partially moved value
+    fn f3(a @ [ref mut b, ref c]: [U; 2]) {}
+    //~^ ERROR borrow of moved value
+
+    let a @ ref b = U;
+    //~^ ERROR borrow of moved value
+    let a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+    //~^ ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR use of partially moved value
+    let a @ [ref mut b, ref c] = [U, U];
+    //~^ ERROR borrow of moved value
+    let a @ ref b = u();
+    //~^ ERROR borrow of moved value
+    let a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+    //~^ ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    //~| ERROR use of partially moved value
+    let a @ [ref mut b, ref c] = [u(), u()];
+    //~^ ERROR borrow of moved value
+
+    match Some(U) {
+        a @ Some(ref b) => {}
+        //~^ ERROR borrow of moved value
+        None => {}
+    }
+    match Some((U, U)) {
+        a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+        //~^ ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        //~| ERROR use of moved value
+        None => {}
+    }
+    match Some([U, U]) {
+        mut a @ Some([ref b, ref mut c]) => {}
+        //~^ ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        None => {}
+    }
+    match Some(u()) {
+        a @ Some(ref b) => {}
+        //~^ ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        None => {}
+    }
+    match Some((u(), u())) {
+        a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+        //~^ ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        //~| ERROR use of moved value
+        None => {}
+    }
+    match Some([u(), u()]) {
+        mut a @ Some([ref b, ref mut c]) => {}
+        //~^ ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        None => {}
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr
new file mode 100644
index 00000000000..389e86e6464
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr
@@ -0,0 +1,487 @@
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:22:9
+   |
+LL |     let a @ ref b = U;
+   |         -^^^-----
+   |         |   |
+   |         |   value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ ref b = U;
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:24:9
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+   |         -^^^^^^^^^^^^---------^^^^^^-----^
+   |         |            |              |
+   |         |            |              value borrowed here after move
+   |         |            value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:24:14
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+   |              -----^^^---------
+   |              |       |
+   |              |       value borrowed here after move
+   |              value moved into `b` here
+   |              move occurs because `b` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let a @ (ref mut b @ ref mut c, d @ ref e) = (U, U);
+   |              +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:24:33
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+   |                                 -^^^-----
+   |                                 |   |
+   |                                 |   value borrowed here after move
+   |                                 value moved into `d` here
+   |                                 move occurs because `d` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let a @ (mut b @ ref mut c, ref d @ ref e) = (U, U);
+   |                                 +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:29:9
+   |
+LL |     let a @ [ref mut b, ref c] = [U, U];
+   |         -^^^^---------^^-----^
+   |         |    |          |
+   |         |    |          value borrowed here after move
+   |         |    value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ [ref mut b, ref c] = [U, U];
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:31:9
+   |
+LL |     let a @ ref b = u();
+   |         -^^^-----
+   |         |   |
+   |         |   value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ ref b = u();
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:33:9
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+   |         -^^^^^^^^^^^^---------^^^^^^-----^
+   |         |            |              |
+   |         |            |              value borrowed here after move
+   |         |            value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:33:14
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+   |              -----^^^---------
+   |              |       |
+   |              |       value borrowed here after move
+   |              value moved into `b` here
+   |              move occurs because `b` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let a @ (ref mut b @ ref mut c, d @ ref e) = (u(), u());
+   |              +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:33:33
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+   |                                 -^^^-----
+   |                                 |   |
+   |                                 |   value borrowed here after move
+   |                                 value moved into `d` here
+   |                                 move occurs because `d` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let a @ (mut b @ ref mut c, ref d @ ref e) = (u(), u());
+   |                                 +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:38:9
+   |
+LL |     let a @ [ref mut b, ref c] = [u(), u()];
+   |         -^^^^---------^^-----^
+   |         |    |          |
+   |         |    |          value borrowed here after move
+   |         |    value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ [ref mut b, ref c] = [u(), u()];
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:42:9
+   |
+LL |         a @ Some(ref b) => {}
+   |         -^^^^^^^^-----^
+   |         |        |
+   |         |        value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<U>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some(ref b) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:47:9
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         -^^^^^^^^^^^^^^^^^---------^^^^^^-----^^
+   |         |                 |              |
+   |         |                 |              value borrowed here after move
+   |         |                 value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:47:19
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |                   -----^^^---------
+   |                   |       |
+   |                   |       value borrowed here after move
+   |                   value moved into `b` here
+   |                   move occurs because `b` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         a @ Some((ref mut b @ ref mut c, d @ ref e)) => {}
+   |                   +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:47:38
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |                                      -^^^-----
+   |                                      |   |
+   |                                      |   value borrowed here after move
+   |                                      value moved into `d` here
+   |                                      move occurs because `d` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         a @ Some((mut b @ ref mut c, ref d @ ref e)) => {}
+   |                                      +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:55:9
+   |
+LL |         mut a @ Some([ref b, ref mut c]) => {}
+   |         -----^^^^^^^^^-----^^---------^^
+   |         |             |      |
+   |         |             |      value borrowed here after move
+   |         |             value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref mut a @ Some([ref b, ref mut c]) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:9
+   |
+LL |         a @ Some(ref b) => {}
+   |         -^^^^^^^^-----^
+   |         |        |
+   |         |        value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<U>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some(ref b) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:67:9
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         -^^^^^^^^^^^^^^^^^---------^^^^^^-----^^
+   |         |                 |              |
+   |         |                 |              value borrowed here after move
+   |         |                 value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:67:19
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |                   -----^^^---------
+   |                   |       |
+   |                   |       value borrowed here after move
+   |                   value moved into `b` here
+   |                   move occurs because `b` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         a @ Some((ref mut b @ ref mut c, d @ ref e)) => {}
+   |                   +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:67:38
+   |
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |                                      -^^^-----
+   |                                      |   |
+   |                                      |   value borrowed here after move
+   |                                      value moved into `d` here
+   |                                      move occurs because `d` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         a @ Some((mut b @ ref mut c, ref d @ ref e)) => {}
+   |                                      +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:75:9
+   |
+LL |         mut a @ Some([ref b, ref mut c]) => {}
+   |         -----^^^^^^^^^-----^^---------^^
+   |         |             |      |
+   |         |             |      value borrowed here after move
+   |         |             value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref mut a @ Some([ref b, ref mut c]) => {}
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:11:11
+   |
+LL |     fn f1(a @ ref b: U) {}
+   |           -^^^-----
+   |           |   |
+   |           |   value borrowed here after move
+   |           value moved into `a` here
+   |           move occurs because `a` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     fn f1(ref a @ ref b: U) {}
+   |           +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:14:11
+   |
+LL |     fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+   |           -----^^^^^^^^-----^^^^^^^^^^-----^
+   |           |            |              |
+   |           |            |              value borrowed here after move
+   |           |            value borrowed here after move
+   |           value moved into `a` here
+   |           move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     fn f2(ref mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+   |           +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:14:20
+   |
+LL |     fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+   |                    -^^^-----
+   |                    |   |
+   |                    |   value borrowed here after move
+   |                    value moved into `b` here
+   |                    move occurs because `b` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     fn f2(mut a @ (ref b @ ref c, mut d @ ref e): (U, U)) {}
+   |                    +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:14:31
+   |
+LL |     fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+   |                               -----^^^-----
+   |                               |       |
+   |                               |       value borrowed here after move
+   |                               value moved into `d` here
+   |                               move occurs because `d` has type `U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     fn f2(mut a @ (b @ ref c, ref mut d @ ref e): (U, U)) {}
+   |                               +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:19:11
+   |
+LL |     fn f3(a @ [ref mut b, ref c]: [U; 2]) {}
+   |           -^^^^---------^^-----^
+   |           |    |          |
+   |           |    |          value borrowed here after move
+   |           |    value borrowed here after move
+   |           value moved into `a` here
+   |           move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     fn f3(ref a @ [ref mut b, ref c]: [U; 2]) {}
+   |           +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:24:9
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (U, U);
+   |         ^                       - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (mut b @ ref mut c, ref d @ ref e) = (U, U);
+   |         +++                         +++
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:33:9
+   |
+LL |     let a @ (mut b @ ref mut c, d @ ref e) = (u(), u());
+   |         ^                       - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (mut b @ ref mut c, ref d @ ref e) = (u(), u());
+   |         +++                         +++
+
+error[E0382]: use of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:47:38
+   |
+LL |     match Some((U, U)) {
+   |           ------------ move occurs because value has type `Option<(U, U)>`, which does not implement the `Copy` trait
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         - value moved here           ^ value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:55:30
+   |
+LL |     match Some([U, U]) {
+   |           ------------ move occurs because value has type `Option<[U; 2]>`, which does not implement the `Copy` trait
+LL |         mut a @ Some([ref b, ref mut c]) => {}
+   |         -----                ^^^^^^^^^ value borrowed here after move
+   |         |
+   |         value moved here
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:61:18
+   |
+LL |     match Some(u()) {
+   |           --------- move occurs because value has type `Option<U>`, which does not implement the `Copy` trait
+LL |         a @ Some(ref b) => {}
+   |         -        ^^^^^ value borrowed here after move
+   |         |
+   |         value moved here
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some(ref b) => {}
+   |         +++
+
+error[E0382]: use of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:67:38
+   |
+LL |     match Some((u(), u())) {
+   |           ---------------- move occurs because value has type `Option<(U, U)>`, which does not implement the `Copy` trait
+LL |         a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         - value moved here           ^ value used here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((mut b @ ref mut c, d @ ref e)) => {}
+   |         +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:75:30
+   |
+LL |     match Some([u(), u()]) {
+   |           ---------------- move occurs because value has type `Option<[U; 2]>`, which does not implement the `Copy` trait
+LL |         mut a @ Some([ref b, ref mut c]) => {}
+   |         -----                ^^^^^^^^^ value borrowed here after move
+   |         |
+   |         value moved here
+
+error[E0382]: use of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref-inverse.rs:14:11
+   |
+LL |     fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {}
+   |           ^^^^^               ----- value partially moved here
+   |           |
+   |           value used here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error: aborting due to 33 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.rs
new file mode 100644
index 00000000000..0b0a7801049
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.rs
@@ -0,0 +1,82 @@
+// Test that `ref mut? @ pat_with_by_move_bindings` is prevented.
+
+fn main() {
+    struct U;
+
+    // Prevent promotion.
+    fn u() -> U {
+        U
+    }
+
+    fn f1(ref a @ b: U) {}
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+    fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    fn f3(ref mut a @ [b, mut c]: [U; 2]) {}
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of partially moved value
+
+    let ref a @ b = U;
+    //~^ ERROR cannot move out of value because it is borrowed
+    let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    let ref mut a @ [b, mut c] = [U, U];
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of partially moved value
+    let ref a @ b = u();
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+    let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+    //~| ERROR borrow of moved value
+    let ref mut a @ [b, mut c] = [u(), u()];
+    //~^ ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of partially moved value
+
+    match Some(U) {
+        ref a @ Some(b) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        None => {}
+    }
+    match Some((U, U)) {
+        ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        //~| ERROR cannot move out of value because it is borrowed
+        //~| ERROR cannot move out of value because it is borrowed
+        None => {}
+    }
+    match Some([U, U]) {
+        ref mut a @ Some([b, mut c]) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        None => {}
+    }
+    match Some(u()) {
+        ref a @ Some(b) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        None => {}
+    }
+    match Some((u(), u())) {
+        ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        //~| ERROR cannot move out of value because it is borrowed
+        //~| ERROR cannot move out of value because it is borrowed
+        //~| ERROR borrow of moved value
+        //~| ERROR borrow of moved value
+        None => {}
+    }
+    match Some([u(), u()]) {
+        ref mut a @ Some([b, mut c]) => {}
+        //~^ ERROR cannot move out of value because it is borrowed
+        None => {}
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.stderr
new file mode 100644
index 00000000000..770bb89530c
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref.stderr
@@ -0,0 +1,375 @@
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:24:9
+   |
+LL |     let ref a @ b = U;
+   |         -----^^^-
+   |         |       |
+   |         |       value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:26:9
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
+   |         -----^^^^^^^^^^^^-----^^^^^^^^^^-^
+   |         |                |              |
+   |         |                |              value moved into `e` here
+   |         |                value moved into `c` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:26:18
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
+   |                  -----^^^-----
+   |                  |       |
+   |                  |       value moved into `c` here
+   |                  value borrowed, by `b`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:26:33
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
+   |                                 -----^^^-
+   |                                 |       |
+   |                                 |       value moved into `e` here
+   |                                 value borrowed, by `d`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:30:9
+   |
+LL |     let ref mut a @ [b, mut c] = [U, U];
+   |         ---------^^^^-^^-----^
+   |         |            |  |
+   |         |            |  value moved into `c` here
+   |         |            value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:33:9
+   |
+LL |     let ref a @ b = u();
+   |         -----^^^-
+   |         |       |
+   |         |       value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:36:9
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+   |         -----^^^^^^^^^^^^-----^^^^^^^^^^-^
+   |         |                |              |
+   |         |                |              value moved into `e` here
+   |         |                value moved into `c` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:36:18
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+   |                  -----^^^-----
+   |                  |       |
+   |                  |       value moved into `c` here
+   |                  value borrowed, by `b`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:36:33
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+   |                                 -----^^^-
+   |                                 |       |
+   |                                 |       value moved into `e` here
+   |                                 value borrowed, by `d`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:42:9
+   |
+LL |     let ref mut a @ [b, mut c] = [u(), u()];
+   |         ---------^^^^-^^-----^
+   |         |            |  |
+   |         |            |  value moved into `c` here
+   |         |            value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:47:9
+   |
+LL |         ref a @ Some(b) => {}
+   |         -----^^^^^^^^-^
+   |         |            |
+   |         |            value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:52:9
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |         -----^^^^^^^^^^^^^^^^^-----^^^^^^^^^^-^^
+   |         |                     |              |
+   |         |                     |              value moved into `e` here
+   |         |                     value moved into `c` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:52:23
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                       -----^^^-----
+   |                       |       |
+   |                       |       value moved into `c` here
+   |                       value borrowed, by `b`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:52:38
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                                      -----^^^-
+   |                                      |       |
+   |                                      |       value moved into `e` here
+   |                                      value borrowed, by `d`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:59:9
+   |
+LL |         ref mut a @ Some([b, mut c]) => {}
+   |         ---------^^^^^^^^^-^^-----^^
+   |         |                 |  |
+   |         |                 |  value moved into `c` here
+   |         |                 value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:64:9
+   |
+LL |         ref a @ Some(b) => {}
+   |         -----^^^^^^^^-^
+   |         |            |
+   |         |            value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:69:9
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |         -----^^^^^^^^^^^^^^^^^-----^^^^^^^^^^-^^
+   |         |                     |              |
+   |         |                     |              value moved into `e` here
+   |         |                     value moved into `c` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:69:23
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                       -----^^^-----
+   |                       |       |
+   |                       |       value moved into `c` here
+   |                       value borrowed, by `b`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:69:38
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                                      -----^^^-
+   |                                      |       |
+   |                                      |       value moved into `e` here
+   |                                      value borrowed, by `d`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:78:9
+   |
+LL |         ref mut a @ Some([b, mut c]) => {}
+   |         ---------^^^^^^^^^-^^-----^^
+   |         |                 |  |
+   |         |                 |  value moved into `c` here
+   |         |                 value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:11:11
+   |
+LL |     fn f1(ref a @ b: U) {}
+   |           -----^^^-
+   |           |       |
+   |           |       value moved into `b` here
+   |           value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:14:11
+   |
+LL |     fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+   |           -----^^^^^^^^^^^^-----^^^^^^^^^^-^
+   |           |                |              |
+   |           |                |              value moved into `e` here
+   |           |                value moved into `c` here
+   |           value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:14:20
+   |
+LL |     fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+   |                    -----^^^-----
+   |                    |       |
+   |                    |       value moved into `c` here
+   |                    value borrowed, by `b`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:14:35
+   |
+LL |     fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+   |                                   -----^^^-
+   |                                   |       |
+   |                                   |       value moved into `e` here
+   |                                   value borrowed, by `d`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:20:11
+   |
+LL |     fn f3(ref mut a @ [b, mut c]: [U; 2]) {}
+   |           ---------^^^^-^^-----^
+   |           |            |  |
+   |           |            |  value moved into `c` here
+   |           |            value moved into `b` here
+   |           value borrowed, by `a`, here
+
+error[E0382]: borrow of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:30:9
+   |
+LL |     let ref mut a @ [b, mut c] = [U, U];
+   |         ^^^^^^^^^       ----- value partially moved here
+   |         |
+   |         value borrowed here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref mut a @ [b, ref mut c] = [U, U];
+   |                         +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:33:9
+   |
+LL |     let ref a @ b = u();
+   |         ^^^^^   -   --- move occurs because value has type `U`, which does not implement the `Copy` trait
+   |         |       |
+   |         |       value moved here
+   |         value borrowed here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ ref b = u();
+   |                 +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:36:18
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+   |                  ^^^^^   ----- value moved here
+   |                  |
+   |                  value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (ref b @ ref mut c, ref d @ e) = (u(), u());
+   |                          +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:36:33
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
+   |                                 ^^^^^   - value moved here
+   |                                 |
+   |                                 value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (ref b @ mut c, ref d @ ref e) = (u(), u());
+   |                                         +++
+
+error[E0382]: borrow of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:42:9
+   |
+LL |     let ref mut a @ [b, mut c] = [u(), u()];
+   |         ^^^^^^^^^       ----- value partially moved here
+   |         |
+   |         value borrowed here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref mut a @ [b, ref mut c] = [u(), u()];
+   |                         +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:69:23
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                       ^^^^^   ----- value moved here
+   |                       |
+   |                       value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((ref b @ ref mut c, ref d @ e)) => {}
+   |                               +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:69:38
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ e)) => {}
+   |                                      ^^^^^   - value moved here
+   |                                      |
+   |                                      value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         ref a @ Some((ref b @ mut c, ref d @ ref e)) => {}
+   |                                              +++
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:11:11
+   |
+LL |     fn f1(ref a @ b: U) {}
+   |           ^^^^^   - value moved here
+   |           |
+   |           value borrowed here after move
+   |           move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:14:20
+   |
+LL |     fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+   |                    ^^^^^   ----- value moved here
+   |                    |
+   |                    value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:14:35
+   |
+LL |     fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
+   |                                   ^^^^^   - value moved here
+   |                                   |
+   |                                   value borrowed here after move
+   |
+   = note: move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error[E0382]: borrow of partially moved value
+  --> $DIR/borrowck-pat-by-move-and-ref.rs:20:11
+   |
+LL |     fn f3(ref mut a @ [b, mut c]: [U; 2]) {}
+   |           ^^^^^^^^^       ----- value partially moved here
+   |           |
+   |           value borrowed here after partial move
+   |
+   = note: partial move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error: aborting due to 36 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-both-sides.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-both-sides.rs
new file mode 100644
index 00000000000..df213f688c2
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-both-sides.rs
@@ -0,0 +1,45 @@
+// check-pass
+
+// Test that `ref` patterns may be used on both sides
+// of an `@` pattern according to NLL borrowck.
+
+fn main() {
+    struct U; // Not copy!
+
+    // Promotion:
+    let ref a @ ref b = U;
+    let _: &U = a;
+    let _: &U = b;
+
+    // Prevent promotion:
+    fn u() -> U { U }
+
+    let ref a @ ref b = u();
+    let _: &U = a;
+    let _: &U = b;
+
+    let ref a @ (ref b, [ref c, ref d]) = (u(), [u(), u()]);
+    let _: &(U, [U; 2]) = a;
+    let _: &U = b;
+    let _: &U = c;
+    let _: &U = d;
+
+    fn f1(ref a @ (ref b, [ref c, ref mid @ .., ref d]): (U, [U; 4])) {}
+
+    let a @ (b, [c, d]) = &(u(), [u(), u()]);
+    let _: &(U, [U; 2]) = a;
+    let _: &U = b;
+    let _: &U = c;
+    let _: &U = d;
+
+    let ref a @ &ref b = &u();
+    let _: &&U = a;
+    let _: &U = b;
+
+    match Ok(u()) {
+        ref a @ Ok(ref b) | ref a @ Err(ref b) => {
+            let _: &Result<U, U> = a;
+            let _: &U = b;
+        }
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs
new file mode 100644
index 00000000000..6bc0d346c11
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.rs
@@ -0,0 +1,136 @@
+enum Option<T> {
+    None,
+    Some(T),
+}
+
+fn main() {
+    match &mut Some(1) {
+        ref mut z @ &mut Some(ref a) => {
+        //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+            **z = None;
+            println!("{}", *a);
+        }
+        _ => ()
+    }
+
+    struct U;
+
+    // Prevent promotion:
+    fn u() -> U { U }
+
+    fn f1(ref a @ ref mut b: U) {}
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    fn f2(ref mut a @ ref b: U) {}
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    fn f3(ref a @ [ref b, ref mut mid @ .., ref c]: [U; 4]) {}
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    fn f4_also_moved(ref a @ ref mut b @ c: U) {}
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+
+    let ref mut a @ (ref b @ ref mut c) = u(); // sub-in-sub
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+
+    let ref a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    let ref mut a @ ref b = U;
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    let ref a @ (ref mut b, ref mut c) = (U, U);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    let ref mut a @ (ref b, ref c) = (U, U);
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+
+    let ref mut a @ ref b = u();
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    *a = u();
+    drop(b);
+    let ref a @ ref mut b = u();
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = u();
+    drop(a);
+
+    let ref mut a @ ref b = U;
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *a = U;
+    drop(b);
+    let ref a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    *b = U;
+    drop(a);
+
+    match Ok(U) {
+        ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) => {
+            //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+            //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+            *a = Err(U);
+            drop(b);
+        }
+    }
+
+    match Ok(U) {
+        ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
+            //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+            *b = U;
+            drop(a);
+        }
+    }
+
+    match Ok(U) {
+        ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
+        //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+        //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+        //~| ERROR cannot assign to `*b`, as it is immutable for the pattern guard
+        _ => {}
+    }
+    match Ok(U) {
+        ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
+        //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot assign to `*a`, as it is immutable for the pattern guard
+        _ => {}
+    }
+    match Ok(U) {
+        ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+        //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+        //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+        //~| ERROR cannot move out of `b` in pattern guard
+        //~| ERROR cannot move out of `b` in pattern guard
+        _ => {}
+    }
+    match Ok(U) {
+        ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+        //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+        //~| ERROR cannot move out of `a` in pattern guard
+        //~| ERROR cannot move out of `a` in pattern guard
+        _ => {}
+    }
+
+    let ref a @ (ref mut b, ref mut c) = (U, U);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = U;
+    *c = U;
+
+    let ref a @ (ref mut b, ref mut c) = (U, U);
+    //~^ ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    //~| ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = U;
+    drop(a);
+
+    let ref a @ (ref mut b, ref mut c) = (U, U);
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+    *b = U; //~| ERROR cannot borrow value as mutable because it is also borrowed as immutable
+    *c = U;
+    drop(a);
+    let ref mut a @ (ref b, ref c) = (U, U);
+    //~^ ERROR cannot borrow value as immutable because it is also borrowed as mutable
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
new file mode 100644
index 00000000000..8546b4bb477
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-and-ref.stderr
@@ -0,0 +1,445 @@
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:8:9
+   |
+LL |         ref mut z @ &mut Some(ref a) => {
+   |         ---------^^^^^^^^^^^^^-----^
+   |         |                     |
+   |         |                     immutable borrow, by `a`, occurs here
+   |         mutable borrow, by `z`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:33:9
+   |
+LL |     let ref mut a @ (ref b @ ref mut c) = u(); // sub-in-sub
+   |         ---------^^^^-----------------^
+   |         |            |       |
+   |         |            |       another mutable borrow, by `c`, occurs here
+   |         |            also borrowed as immutable, by `b`, here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:33:22
+   |
+LL |     let ref mut a @ (ref b @ ref mut c) = u(); // sub-in-sub
+   |                      -----^^^---------
+   |                      |       |
+   |                      |       mutable borrow, by `c`, occurs here
+   |                      immutable borrow, by `b`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:37:9
+   |
+LL |     let ref a @ ref mut b = U;
+   |         -----^^^---------
+   |         |       |
+   |         |       mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:39:9
+   |
+LL |     let ref mut a @ ref b = U;
+   |         ---------^^^-----
+   |         |           |
+   |         |           immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:41:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         -----^^^^---------^^---------^
+   |         |        |          |
+   |         |        |          mutable borrow, by `c`, occurs here
+   |         |        mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:43:9
+   |
+LL |     let ref mut a @ (ref b, ref c) = (U, U);
+   |         ---------^^^^-----^^-----^
+   |         |            |      |
+   |         |            |      immutable borrow, by `c`, occurs here
+   |         |            immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:46:9
+   |
+LL |     let ref mut a @ ref b = u();
+   |         ---------^^^-----
+   |         |           |
+   |         |           immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:51:9
+   |
+LL |     let ref a @ ref mut b = u();
+   |         -----^^^---------
+   |         |       |
+   |         |       mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:57:9
+   |
+LL |     let ref mut a @ ref b = U;
+   |         ---------^^^-----
+   |         |           |
+   |         |           immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:61:9
+   |
+LL |     let ref a @ ref mut b = U;
+   |         -----^^^---------
+   |         |       |
+   |         |       mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:67:9
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) => {
+   |         ---------^^^^^^-----^
+   |         |              |
+   |         |              immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:67:33
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) => {
+   |                                 ---------^^^^^^^-----^
+   |                                 |               |
+   |                                 |               immutable borrow, by `b`, occurs here
+   |                                 mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:9
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
+   |         -----^^^^^^---------^
+   |         |          |
+   |         |          mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:33
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
+   |                                 -----^^^^^^^---------^
+   |                                 |           |
+   |                                 |           mutable borrow, by `b`, occurs here
+   |                                 immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:87:9
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
+   |         -----^^^^^^---------^
+   |         |          |
+   |         |          mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:87:33
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
+   |                                 -----^^^^^^^---------^
+   |                                 |           |
+   |                                 |           mutable borrow, by `b`, occurs here
+   |                                 immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:94:9
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
+   |         ---------^^^^^^-----^
+   |         |              |
+   |         |              immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:94:33
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
+   |                                 ---------^^^^^^^-----^
+   |                                 |               |
+   |                                 |               immutable borrow, by `b`, occurs here
+   |                                 mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:9
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+   |         -----^^^^^^---------^
+   |         |          |
+   |         |          mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:33
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+   |                                 -----^^^^^^^---------^
+   |                                 |           |
+   |                                 |           mutable borrow, by `b`, occurs here
+   |                                 immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:9
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+   |         ---------^^^^^^-----^
+   |         |              |
+   |         |              immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:33
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+   |                                 ---------^^^^^^^-----^
+   |                                 |               |
+   |                                 |               immutable borrow, by `b`, occurs here
+   |                                 mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:117:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         -----^^^^---------^^---------^
+   |         |        |          |
+   |         |        |          mutable borrow, by `c`, occurs here
+   |         |        mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:123:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         -----^^^^---------^^---------^
+   |         |        |          |
+   |         |        |          mutable borrow, by `c`, occurs here
+   |         |        mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:129:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         -----^^^^---------^^---------^
+   |         |        |          |
+   |         |        |          mutable borrow, by `c`, occurs here
+   |         |        mutable borrow, by `b`, occurs here
+   |         immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:134:9
+   |
+LL |     let ref mut a @ (ref b, ref c) = (U, U);
+   |         ---------^^^^-----^^-----^
+   |         |            |      |
+   |         |            |      immutable borrow, by `c`, occurs here
+   |         |            immutable borrow, by `b`, occurs here
+   |         mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:22:11
+   |
+LL |     fn f1(ref a @ ref mut b: U) {}
+   |           -----^^^---------
+   |           |       |
+   |           |       mutable borrow, by `b`, occurs here
+   |           immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:24:11
+   |
+LL |     fn f2(ref mut a @ ref b: U) {}
+   |           ---------^^^-----
+   |           |           |
+   |           |           immutable borrow, by `b`, occurs here
+   |           mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:26:11
+   |
+LL |     fn f3(ref a @ [ref b, ref mut mid @ .., ref c]: [U; 4]) {}
+   |           -----^^^^^^^^^^^----------------^^^^^^^^
+   |           |               |
+   |           |               mutable borrow, by `mid`, occurs here
+   |           immutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:28:22
+   |
+LL |     fn f4_also_moved(ref a @ ref mut b @ c: U) {}
+   |                      -----^^^-------------
+   |                      |       |           |
+   |                      |       |           also moved into `c` here
+   |                      |       mutable borrow, by `b`, occurs here
+   |                      immutable borrow, by `a`, occurs here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:28:30
+   |
+LL |     fn f4_also_moved(ref a @ ref mut b @ c: U) {}
+   |                              ---------^^^-
+   |                              |           |
+   |                              |           value moved into `c` here
+   |                              value borrowed, by `b`, here
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:8:31
+   |
+LL |         ref mut z @ &mut Some(ref a) => {
+   |         ---------             ^^^^^ immutable borrow occurs here
+   |         |
+   |         mutable borrow occurs here
+...
+LL |             **z = None;
+   |             ---------- mutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:46:9
+   |
+LL |     let ref mut a @ ref b = u();
+   |         ^^^^^^^^^   ----- immutable borrow occurs here
+   |         |
+   |         mutable borrow occurs here
+...
+LL |     drop(b);
+   |          - immutable borrow later used here
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:51:9
+   |
+LL |     let ref a @ ref mut b = u();
+   |         ^^^^^   --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |     *b = u();
+   |     -------- mutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:20
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
+   |         -----      ^^^^^^^^^ mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |             drop(a);
+   |                  - immutable borrow later used here
+
+error[E0502]: cannot borrow value as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:45
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
+   |                                 -----       ^^^^^^^^^ mutable borrow occurs here
+   |                                 |
+   |                                 immutable borrow occurs here
+...
+LL |             drop(a);
+   |                  - immutable borrow later used here
+
+error[E0594]: cannot assign to `*b`, as it is immutable for the pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:87:61
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
+   |                                                             ^^^^^^ cannot assign
+   |
+   = note: variables bound in patterns are immutable until the end of the pattern guard
+
+error[E0594]: cannot assign to `*a`, as it is immutable for the pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:94:61
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
+   |                                                             ^^^^^^^^^^^ cannot assign
+   |
+   = note: variables bound in patterns are immutable until the end of the pattern guard
+
+error[E0507]: cannot move out of `b` in pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:66
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+   |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
+   |
+   = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
+error[E0507]: cannot move out of `b` in pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:66
+   |
+LL |         ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
+   |                                                                  ^ move occurs because `b` has type `&mut U`, which does not implement the `Copy` trait
+   |
+   = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
+error[E0507]: cannot move out of `a` in pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:66
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+   |                                                                  ^ move occurs because `a` has type `&mut Result<U, U>`, which does not implement the `Copy` trait
+   |
+   = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
+error[E0507]: cannot move out of `a` in pattern guard
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:66
+   |
+LL |         ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
+   |                                                                  ^ move occurs because `a` has type `&mut Result<U, U>`, which does not implement the `Copy` trait
+   |
+   = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:117:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         ^^^^^    --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |     *b = U;
+   |     ------ mutable borrow later used here
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:123:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         ^^^^^    --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+...
+LL |     *b = U;
+   |     ------ mutable borrow later used here
+
+error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:129:9
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         ^^^^^    --------- mutable borrow occurs here
+   |         |
+   |         immutable borrow occurs here
+LL |
+LL |     *b = U;
+   |     ------ mutable borrow later used here
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-and-ref.rs:28:30
+   |
+LL |     fn f4_also_moved(ref a @ ref mut b @ c: U) {}
+   |                      -----   ^^^^^^^^^   - value moved here
+   |                      |       |
+   |                      |       value borrowed here after move
+   |                      move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error: aborting due to 47 previous errors
+
+Some errors have detailed explanations: E0382, E0502, E0507, E0594.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs
new file mode 100644
index 00000000000..99739c7bce0
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.rs
@@ -0,0 +1,109 @@
+// Test that `ref mut x @ ref mut y` and varieties of that are not allowed.
+
+fn main() {
+    struct U;
+
+    fn u() -> U { U }
+
+    fn f1(ref mut a @ ref mut b: U) {}
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    fn f2(ref mut a @ ref mut b: U) {}
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    fn f3(
+        ref mut a @ [
+        //~^ ERROR cannot borrow value as mutable more than once at a time
+            [ref b @ .., _],
+            [_, ref mut mid @ ..],
+            ..,
+            [..],
+        ] : [[U; 4]; 5]
+    ) {}
+    fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    //~| ERROR cannot move out of value because it is borrowed
+    //~| ERROR borrow of moved value
+
+    let ref mut a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    drop(a);
+    let ref mut a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    //~| ERROR cannot borrow value as mutable more than once at a time
+    drop(b);
+    let ref mut a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+
+    let ref mut a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    *a = U;
+    let ref mut a @ ref mut b = U;
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+    //~| ERROR cannot borrow value as mutable more than once at a time
+    *b = U;
+
+    let ref mut a @ (
+    //~^ ERROR cannot borrow value as mutable more than once at a time
+        ref mut b,
+        [
+            ref mut c,
+            ref mut d,
+            ref e,
+        ]
+    ) = (U, [U, U, U]);
+
+    let ref mut a @ (
+        //~^ ERROR cannot borrow value as mutable more than once at a time
+            ref mut b,
+            [
+                ref mut c,
+                ref mut d,
+                ref e,
+            ]
+        ) = (u(), [u(), u(), u()]);
+
+    let a @ (ref mut b, ref mut c) = (U, U);
+    //~^ ERROR borrow of moved value
+    let mut val = (U, [U, U]);
+    let a @ (b, [c, d]) = &mut val; // Same as ^--
+    //~^ ERROR borrow of moved value
+
+    let a @ &mut ref mut b = &mut U;
+    //~^ ERROR borrow of moved value
+    let a @ &mut (ref mut b, ref mut c) = &mut (U, U);
+    //~^ ERROR borrow of moved value
+
+    match Ok(U) {
+        ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+            //~^ ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+        }
+    }
+    match Ok(U) {
+        ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+            //~^ ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            *b = U;
+        }
+    }
+    match Ok(U) {
+        ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+            //~^ ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            *a = Err(U);
+
+            // FIXME: The binding name value used above makes for problematic diagnostics.
+            // Resolve that somehow...
+        }
+    }
+    match Ok(U) {
+        ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+            //~^ ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            //~| ERROR cannot borrow value as mutable more than once at a time
+            drop(a);
+        }
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr
new file mode 100644
index 00000000000..ad4ce7952ca
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr
@@ -0,0 +1,359 @@
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:26:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ---------^^^---------
+   |         |           |
+   |         |           another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:29:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ---------^^^---------
+   |         |           |
+   |         |           another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:33:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ---------^^^---------
+   |         |           |
+   |         |           another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:36:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ---------^^^---------
+   |         |           |
+   |         |           another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:39:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ---------^^^---------
+   |         |           |
+   |         |           another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:44:9
+   |
+LL |       let ref mut a @ (
+   |           ^--------
+   |           |
+   |  _________first mutable borrow, by `a`, occurs here
+   | |
+LL | |
+LL | |         ref mut b,
+   | |         --------- another mutable borrow, by `b`, occurs here
+LL | |         [
+LL | |             ref mut c,
+   | |             --------- another mutable borrow, by `c`, occurs here
+LL | |             ref mut d,
+   | |             --------- another mutable borrow, by `d`, occurs here
+LL | |             ref e,
+   | |             ----- also borrowed as immutable, by `e`, here
+LL | |         ]
+LL | |     ) = (U, [U, U, U]);
+   | |_____^
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:54:9
+   |
+LL |       let ref mut a @ (
+   |           ^--------
+   |           |
+   |  _________first mutable borrow, by `a`, occurs here
+   | |
+LL | |
+LL | |             ref mut b,
+   | |             --------- another mutable borrow, by `b`, occurs here
+LL | |             [
+LL | |                 ref mut c,
+   | |                 --------- another mutable borrow, by `c`, occurs here
+LL | |                 ref mut d,
+   | |                 --------- another mutable borrow, by `d`, occurs here
+LL | |                 ref e,
+   | |                 ----- also borrowed as immutable, by `e`, here
+LL | |             ]
+LL | |         ) = (u(), [u(), u(), u()]);
+   | |_________^
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:64:9
+   |
+LL |     let a @ (ref mut b, ref mut c) = (U, U);
+   |         -^^^^---------^^---------^
+   |         |    |          |
+   |         |    |          value borrowed here after move
+   |         |    value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (ref mut b, ref mut c) = (U, U);
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:67:9
+   |
+LL |     let a @ (b, [c, d]) = &mut val; // Same as ^--
+   |         -^^^^-^^^-^^-^^
+   |         |    |   |  |
+   |         |    |   |  value borrowed here after move
+   |         |    |   value borrowed here after move
+   |         |    value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `&mut (U, [U; 2])` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ (b, [c, d]) = &mut val; // Same as ^--
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:70:9
+   |
+LL |     let a @ &mut ref mut b = &mut U;
+   |         -^^^^^^^^---------
+   |         |        |
+   |         |        value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `&mut U` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ &mut ref mut b = &mut U;
+   |         +++
+
+error: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:72:9
+   |
+LL |     let a @ &mut (ref mut b, ref mut c) = &mut (U, U);
+   |         -^^^^^^^^^---------^^---------^
+   |         |         |          |
+   |         |         |          value borrowed here after move
+   |         |         value borrowed here after move
+   |         value moved into `a` here
+   |         move occurs because `a` has type `&mut (U, U)` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ &mut (ref mut b, ref mut c) = &mut (U, U);
+   |         +++
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:76:9
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------^^^^^^---------^
+   |         |              |
+   |         |              another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:76:37
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------^^^^^^^---------^
+   |                                     |               |
+   |                                     |               another mutable borrow, by `b`, occurs here
+   |                                     first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:82:9
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------^^^^^^---------^
+   |         |              |
+   |         |              another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:82:37
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------^^^^^^^---------^
+   |                                     |               |
+   |                                     |               another mutable borrow, by `b`, occurs here
+   |                                     first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:89:9
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------^^^^^^---------^
+   |         |              |
+   |         |              another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:89:37
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------^^^^^^^---------^
+   |                                     |               |
+   |                                     |               another mutable borrow, by `b`, occurs here
+   |                                     first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:101:9
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------^^^^^^---------^
+   |         |              |
+   |         |              another mutable borrow, by `b`, occurs here
+   |         first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:101:37
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------^^^^^^^---------^
+   |                                     |               |
+   |                                     |               another mutable borrow, by `b`, occurs here
+   |                                     first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:8:11
+   |
+LL |     fn f1(ref mut a @ ref mut b: U) {}
+   |           ---------^^^---------
+   |           |           |
+   |           |           another mutable borrow, by `b`, occurs here
+   |           first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:10:11
+   |
+LL |     fn f2(ref mut a @ ref mut b: U) {}
+   |           ---------^^^---------
+   |           |           |
+   |           |           another mutable borrow, by `b`, occurs here
+   |           first mutable borrow, by `a`, occurs here
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:13:9
+   |
+LL |           ref mut a @ [
+   |           ^--------
+   |           |
+   |  _________first mutable borrow, by `a`, occurs here
+   | |
+LL | |
+LL | |             [ref b @ .., _],
+   | |              ---------- also borrowed as immutable, by `b`, here
+LL | |             [_, ref mut mid @ ..],
+   | |                 ---------------- another mutable borrow, by `mid`, occurs here
+LL | |             ..,
+LL | |             [..],
+LL | |         ] : [[U; 4]; 5]
+   | |_________^
+
+error: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:21:22
+   |
+LL |     fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
+   |                      ---------^^^-------------
+   |                      |           |           |
+   |                      |           |           also moved into `c` here
+   |                      |           another mutable borrow, by `b`, occurs here
+   |                      first mutable borrow, by `a`, occurs here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:21:34
+   |
+LL |     fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
+   |                                  ---------^^^-
+   |                                  |           |
+   |                                  |           value moved into `c` here
+   |                                  value borrowed, by `b`, here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:29:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ^^^^^^^^^   --------- first mutable borrow occurs here
+   |         |
+   |         second mutable borrow occurs here
+...
+LL |     drop(b);
+   |          - first borrow later used here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:39:9
+   |
+LL |     let ref mut a @ ref mut b = U;
+   |         ^^^^^^^^^   --------- first mutable borrow occurs here
+   |         |
+   |         second mutable borrow occurs here
+...
+LL |     *b = U;
+   |     ------ first borrow later used here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:89:24
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------      ^^^^^^^^^ second mutable borrow occurs here
+   |         |
+   |         first mutable borrow occurs here
+...
+LL |             *a = Err(U);
+   |             ----------- first borrow later used here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:89:53
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------       ^^^^^^^^^ second mutable borrow occurs here
+   |                                     |
+   |                                     first mutable borrow occurs here
+...
+LL |             *a = Err(U);
+   |             ----------- first borrow later used here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:101:24
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |         ---------      ^^^^^^^^^ second mutable borrow occurs here
+   |         |
+   |         first mutable borrow occurs here
+...
+LL |             drop(a);
+   |                  - first borrow later used here
+
+error[E0499]: cannot borrow value as mutable more than once at a time
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:101:53
+   |
+LL |         ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
+   |                                     ---------       ^^^^^^^^^ second mutable borrow occurs here
+   |                                     |
+   |                                     first mutable borrow occurs here
+...
+LL |             drop(a);
+   |                  - first borrow later used here
+
+error[E0382]: borrow of moved value
+  --> $DIR/borrowck-pat-ref-mut-twice.rs:21:34
+   |
+LL |     fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
+   |                      ---------   ^^^^^^^^^   - value moved here
+   |                      |           |
+   |                      |           value borrowed here after move
+   |                      move occurs because value has type `U`, which does not implement the `Copy` trait
+
+error: aborting due to 31 previous errors
+
+Some errors have detailed explanations: E0382, E0499.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/box-patterns.rs b/tests/ui/pattern/bindings-after-at/box-patterns.rs
new file mode 100644
index 00000000000..9db37253c53
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/box-patterns.rs
@@ -0,0 +1,35 @@
+// Test bindings-after-at with box-patterns
+
+// run-pass
+
+#![feature(box_patterns)]
+
+#[derive(Debug, PartialEq)]
+enum MatchArm {
+    Arm(usize),
+    Wild,
+}
+
+fn test(x: Option<Box<i32>>) -> MatchArm {
+    match x {
+        ref bar @ Some(box n) if n > 0 => {
+            // bar is a &Option<Box<i32>>
+            assert_eq!(bar, &x);
+
+            MatchArm::Arm(0)
+        },
+        Some(ref bar @ box n) if n < 0 => {
+            // bar is a &Box<i32> here
+            assert_eq!(**bar, n);
+
+            MatchArm::Arm(1)
+        },
+        _ => MatchArm::Wild,
+    }
+}
+
+fn main() {
+    assert_eq!(test(Some(Box::new(2))), MatchArm::Arm(0));
+    assert_eq!(test(Some(Box::new(-1))), MatchArm::Arm(1));
+    assert_eq!(test(Some(Box::new(0))), MatchArm::Wild);
+}
diff --git a/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.rs b/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.rs
new file mode 100644
index 00000000000..1e2c2968c4b
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.rs
@@ -0,0 +1,14 @@
+// Test that mixing `Copy` and non-`Copy` types in `@` patterns is forbidden.
+
+#[derive(Copy, Clone)]
+struct C;
+
+struct NC<A, B>(A, B);
+
+fn main() {
+    // this compiles
+    let a @ NC(b, c) = NC(C, C);
+
+    let a @ NC(b, c @ NC(d, e)) = NC(C, NC(C, C));
+    //~^ ERROR use of partially moved value
+}
diff --git a/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.stderr b/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.stderr
new file mode 100644
index 00000000000..e0e623fa544
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/copy-and-move-mixed.stderr
@@ -0,0 +1,17 @@
+error[E0382]: use of partially moved value
+  --> $DIR/copy-and-move-mixed.rs:12:9
+   |
+LL |     let a @ NC(b, c @ NC(d, e)) = NC(C, NC(C, C));
+   |         ^         - value partially moved here
+   |         |
+   |         value used here after partial move
+   |
+   = note: partial move occurs because value has type `NC<C, C>`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref a @ NC(b, ref c @ NC(d, e)) = NC(C, NC(C, C));
+   |         +++           +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs
new file mode 100644
index 00000000000..dfd4d02854d
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.rs
@@ -0,0 +1,48 @@
+// Ensures the independence of each side in `binding @ subpat`
+// determine their binding modes independently of each other.
+//
+// That is, `binding` does not influence `subpat`.
+// This is important because we might want to allow `p1 @ p2`,
+// where both `p1` and `p2` are syntactically unrestricted patterns.
+// If `binding` is allowed to influence `subpat`,
+// this would create problems for the generalization aforementioned.
+
+
+fn main() {
+    struct NotCopy;
+
+    fn f1(a @ b: &NotCopy) { // OK
+        let _: &NotCopy = a;
+    }
+    fn f2(ref a @ b: &NotCopy) {
+        let _: &&NotCopy = a; // Ok
+    }
+
+    let a @ b = &NotCopy; // OK
+    let _: &NotCopy = a;
+    let ref a @ b = &NotCopy; // OK
+    let _: &&NotCopy = a;
+
+    let ref a @ b = NotCopy; //~ ERROR cannot move out of value because it is borrowed
+    let _a: &NotCopy = a;
+    let _b: NotCopy = b;
+    let ref mut a @ b = NotCopy; //~ ERROR cannot move out of value because it is borrowed
+    //~^ ERROR borrow of moved value
+    let _a: &NotCopy = a;
+    let _b: NotCopy = b;
+    match Ok(NotCopy) {
+        Ok(ref a @ b) | Err(b @ ref a) => {
+            //~^ ERROR cannot move out of value because it is borrowed
+            //~| ERROR borrow of moved value
+            let _a: &NotCopy = a;
+            let _b: NotCopy = b;
+        }
+    }
+    match NotCopy {
+        ref a @ b => {
+            //~^ ERROR cannot move out of value because it is borrowed
+            let _a: &NotCopy = a;
+            let _b: NotCopy = b;
+        }
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr
new file mode 100644
index 00000000000..638bdd6db76
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr
@@ -0,0 +1,68 @@
+error: cannot move out of value because it is borrowed
+  --> $DIR/default-binding-modes-both-sides-independent.rs:26:9
+   |
+LL |     let ref a @ b = NotCopy;
+   |         -----^^^-
+   |         |       |
+   |         |       value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/default-binding-modes-both-sides-independent.rs:29:9
+   |
+LL |     let ref mut a @ b = NotCopy;
+   |         ---------^^^-
+   |         |           |
+   |         |           value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/default-binding-modes-both-sides-independent.rs:34:12
+   |
+LL |         Ok(ref a @ b) | Err(b @ ref a) => {
+   |            -----^^^-
+   |            |       |
+   |            |       value moved into `b` here
+   |            value borrowed, by `a`, here
+
+error: borrow of moved value
+  --> $DIR/default-binding-modes-both-sides-independent.rs:34:29
+   |
+LL |         Ok(ref a @ b) | Err(b @ ref a) => {
+   |                             -^^^-----
+   |                             |   |
+   |                             |   value borrowed here after move
+   |                             value moved into `b` here
+   |                             move occurs because `b` has type `NotCopy` which does not implement the `Copy` trait
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |         Ok(ref a @ b) | Err(ref b @ ref a) => {
+   |                             +++
+
+error: cannot move out of value because it is borrowed
+  --> $DIR/default-binding-modes-both-sides-independent.rs:42:9
+   |
+LL |         ref a @ b => {
+   |         -----^^^-
+   |         |       |
+   |         |       value moved into `b` here
+   |         value borrowed, by `a`, here
+
+error[E0382]: borrow of moved value
+  --> $DIR/default-binding-modes-both-sides-independent.rs:29:9
+   |
+LL |     let ref mut a @ b = NotCopy;
+   |         ^^^^^^^^^   -   ------- move occurs because value has type `NotCopy`, which does not implement the `Copy` trait
+   |         |           |
+   |         |           value moved here
+   |         value borrowed here after move
+   |
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let ref mut a @ ref b = NotCopy;
+   |                     +++
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs b/tests/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs
new file mode 100644
index 00000000000..fe7d1eba1d9
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-binding-mode-lint.rs
@@ -0,0 +1,12 @@
+// check-pass
+
+#![deny(unused_mut)]
+
+fn main() {
+    let mut is_mut @ not_mut = 42;
+    &mut is_mut;
+    &not_mut;
+    let not_mut @ mut is_mut = 42;
+    &mut is_mut;
+    &not_mut;
+}
diff --git a/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs b/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs
new file mode 100644
index 00000000000..e7d99534d6a
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let mut is_mut @ not_mut = 42;
+    &mut is_mut;
+    &mut not_mut;
+    //~^ ERROR cannot borrow
+
+    let not_mut @ mut is_mut = 42;
+    &mut is_mut;
+    &mut not_mut;
+    //~^ ERROR cannot borrow
+}
diff --git a/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr b/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr
new file mode 100644
index 00000000000..54118dc3677
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-binding-modes-mut.stderr
@@ -0,0 +1,25 @@
+error[E0596]: cannot borrow `not_mut` as mutable, as it is not declared as mutable
+  --> $DIR/nested-binding-modes-mut.rs:4:5
+   |
+LL |     &mut not_mut;
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut is_mut @ mut not_mut = 42;
+   |                      +++
+
+error[E0596]: cannot borrow `not_mut` as mutable, as it is not declared as mutable
+  --> $DIR/nested-binding-modes-mut.rs:9:5
+   |
+LL |     &mut not_mut;
+   |     ^^^^^^^^^^^^ cannot borrow as mutable
+   |
+help: consider changing this to be mutable
+   |
+LL |     let mut not_mut @ mut is_mut = 42;
+   |         +++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs b/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs
new file mode 100644
index 00000000000..adfb0387f46
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let ref is_ref @ is_val = 42;
+    *is_ref;
+    *is_val;
+    //~^ ERROR cannot be dereferenced
+
+    let is_val @ ref is_ref = 42;
+    *is_ref;
+    *is_val;
+    //~^ ERROR cannot be dereferenced
+}
diff --git a/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr b/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr
new file mode 100644
index 00000000000..b378fe356ce
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-binding-modes-ref.stderr
@@ -0,0 +1,15 @@
+error[E0614]: type `{integer}` cannot be dereferenced
+  --> $DIR/nested-binding-modes-ref.rs:4:5
+   |
+LL |     *is_val;
+   |     ^^^^^^^
+
+error[E0614]: type `{integer}` cannot be dereferenced
+  --> $DIR/nested-binding-modes-ref.rs:9:5
+   |
+LL |     *is_val;
+   |     ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0614`.
diff --git a/tests/ui/pattern/bindings-after-at/nested-patterns.rs b/tests/ui/pattern/bindings-after-at/nested-patterns.rs
new file mode 100644
index 00000000000..f06563d56cb
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-patterns.rs
@@ -0,0 +1,14 @@
+// run-pass
+
+
+struct A { a: u8, b: u8 }
+
+pub fn main() {
+    match (A { a: 10, b: 20 }) {
+        ref x @ A { ref a, b: 20 } => {
+            assert_eq!(x.a, 10);
+            assert_eq!(*a, 10);
+        }
+        A { b: ref _b, .. } => panic!(),
+    }
+}
diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs
new file mode 100644
index 00000000000..a709e34b501
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.rs
@@ -0,0 +1,33 @@
+// Here we check that type ascription is syntactically invalid when
+// not in the top position of an ascribing `let` binding or function parameter.
+
+
+// This has no effect.
+// We include it to demonstrate that this is the case:
+#![feature(type_ascription)]
+
+fn main() {}
+
+fn _ok() {
+    let _a @ _b: u8 = 0; // OK.
+    fn _f(_a @ _b: u8) {} // OK.
+}
+
+#[cfg(FALSE)]
+fn case_1() {
+    let a: u8 @ b = 0;
+    //~^ ERROR expected one of `!`
+}
+
+#[cfg(FALSE)]
+fn case_2() {
+    let a @ (b: u8);
+    //~^ ERROR expected one of `!`
+    //~| ERROR expected one of `)`
+}
+
+#[cfg(FALSE)]
+fn case_3() {
+    let a: T1 @ Outer(b: T2);
+    //~^ ERROR expected one of `!`
+}
diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr
new file mode 100644
index 00000000000..27660ae406e
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr
@@ -0,0 +1,26 @@
+error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@`
+  --> $DIR/nested-type-ascription-syntactically-invalid.rs:18:15
+   |
+LL |     let a: u8 @ b = 0;
+   |               ^ expected one of 7 possible tokens
+
+error: expected one of `)`, `,`, `@`, or `|`, found `:`
+  --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15
+   |
+LL |     let a @ (b: u8);
+   |               ^ expected one of `)`, `,`, `@`, or `|`
+
+error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `)`
+  --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:19
+   |
+LL |     let a @ (b: u8);
+   |                   ^ expected one of 7 possible tokens
+
+error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found `@`
+  --> $DIR/nested-type-ascription-syntactically-invalid.rs:31:15
+   |
+LL |     let a: T1 @ Outer(b: T2);
+   |               ^ expected one of 7 possible tokens
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs b/tests/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs
new file mode 100644
index 00000000000..383e377a5eb
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/or-patterns-box-patterns.rs
@@ -0,0 +1,43 @@
+// Test bindings-after-at with or-patterns and box-patterns
+
+// run-pass
+
+#![feature(box_patterns)]
+
+#[derive(Debug, PartialEq)]
+enum MatchArm {
+    Arm(usize),
+    Wild,
+}
+
+#[derive(Debug, PartialEq)]
+enum Test {
+    Foo,
+    Bar,
+    Baz,
+    Qux,
+}
+
+fn test(foo: Option<Box<Test>>) -> MatchArm {
+    match foo {
+        ref bar @ Some(box Test::Foo | box Test::Bar) => {
+            assert_eq!(bar, &foo);
+
+            MatchArm::Arm(0)
+        },
+        Some(ref bar @ box Test::Baz | ref bar @ box Test::Qux) => {
+            assert!(**bar == Test::Baz || **bar == Test::Qux);
+
+            MatchArm::Arm(1)
+        },
+        _ => MatchArm::Wild,
+    }
+}
+
+fn main() {
+    assert_eq!(test(Some(Box::new(Test::Foo))), MatchArm::Arm(0));
+    assert_eq!(test(Some(Box::new(Test::Bar))), MatchArm::Arm(0));
+    assert_eq!(test(Some(Box::new(Test::Baz))), MatchArm::Arm(1));
+    assert_eq!(test(Some(Box::new(Test::Qux))), MatchArm::Arm(1));
+    assert_eq!(test(None), MatchArm::Wild);
+}
diff --git a/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs b/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs
new file mode 100644
index 00000000000..d315f7ee3b6
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs
@@ -0,0 +1,54 @@
+// Test bindings-after-at with or-patterns and slice-patterns
+
+// run-pass
+
+
+#[derive(Debug, PartialEq)]
+enum MatchArm {
+    Arm(usize),
+    Wild,
+}
+
+#[derive(Debug, PartialEq)]
+enum Test {
+    Foo,
+    Bar,
+    Baz,
+    Qux,
+}
+
+fn test(foo: &[Option<Test>]) -> MatchArm {
+    match foo {
+        bar @ [Some(Test::Foo), .., Some(Test::Qux | Test::Foo)] => {
+            assert_eq!(bar, foo);
+
+            MatchArm::Arm(0)
+        },
+        [.., bar @ Some(Test::Bar | Test::Qux), _] => {
+            assert!(bar == &Some(Test::Bar) || bar == &Some(Test::Qux));
+
+            MatchArm::Arm(1)
+        },
+        _ => MatchArm::Wild,
+    }
+}
+
+fn main() {
+    let foo = vec![
+        Some(Test::Foo),
+        Some(Test::Bar),
+        Some(Test::Baz),
+        Some(Test::Qux),
+    ];
+
+    // path 1a
+    assert_eq!(test(&foo), MatchArm::Arm(0));
+    // path 1b
+    assert_eq!(test(&[Some(Test::Foo), Some(Test::Bar), Some(Test::Foo)]), MatchArm::Arm(0));
+    // path 2a
+    assert_eq!(test(&foo[..3]), MatchArm::Arm(1));
+    // path 2b
+    assert_eq!(test(&[Some(Test::Bar), Some(Test::Qux), Some(Test::Baz)]), MatchArm::Arm(1));
+    // path 3
+    assert_eq!(test(&foo[1..2]), MatchArm::Wild);
+}
diff --git a/tests/ui/pattern/bindings-after-at/or-patterns.rs b/tests/ui/pattern/bindings-after-at/or-patterns.rs
new file mode 100644
index 00000000000..fcc36148999
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/or-patterns.rs
@@ -0,0 +1,38 @@
+// Test bindings-after-at with or-patterns
+
+// run-pass
+
+
+#[derive(Debug, PartialEq)]
+enum MatchArm {
+    Arm(usize),
+    Wild,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq)]
+enum Test {
+    Foo,
+    Bar,
+    Baz,
+    Qux,
+}
+
+fn test(foo: Option<Test>) -> MatchArm {
+    match foo {
+        bar @ Some(Test::Foo | Test::Bar) => {
+            assert!(bar == Some(Test::Foo) || bar == Some(Test::Bar));
+
+            MatchArm::Arm(0)
+        },
+        Some(_) => MatchArm::Arm(1),
+        _ => MatchArm::Wild,
+    }
+}
+
+fn main() {
+    assert_eq!(test(Some(Test::Foo)), MatchArm::Arm(0));
+    assert_eq!(test(Some(Test::Bar)), MatchArm::Arm(0));
+    assert_eq!(test(Some(Test::Baz)), MatchArm::Arm(1));
+    assert_eq!(test(Some(Test::Qux)), MatchArm::Arm(1));
+    assert_eq!(test(None), MatchArm::Wild);
+}
diff --git a/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.rs b/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.rs
new file mode 100644
index 00000000000..f167a3952ee
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.rs
@@ -0,0 +1,29 @@
+// Test that `binding @ subpat` acts as a product context with respect to duplicate binding names.
+// The code that is tested here lives in resolve (see `resolve_pattern_inner`).
+
+
+fn main() {
+    fn f(a @ a @ a: ()) {}
+    //~^ ERROR identifier `a` is bound more than once in this parameter list
+    //~| ERROR identifier `a` is bound more than once in this parameter list
+
+    match Ok(0) {
+        Ok(a @ b @ a)
+        //~^ ERROR identifier `a` is bound more than once in the same pattern
+        | Err(a @ b @ a)
+        //~^ ERROR identifier `a` is bound more than once in the same pattern
+        => {}
+    }
+
+    let a @ a @ a = ();
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+    let ref a @ ref a = ();
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    let ref mut a @ ref mut a = ();
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+
+    let a @ (Ok(a) | Err(a)) = Ok(());
+    //~^ ERROR identifier `a` is bound more than once in the same pattern
+    //~| ERROR identifier `a` is bound more than once in the same pattern
+}
diff --git a/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr b/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr
new file mode 100644
index 00000000000..a165549f6b4
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr
@@ -0,0 +1,64 @@
+error[E0415]: identifier `a` is bound more than once in this parameter list
+  --> $DIR/pat-at-same-name-both.rs:6:14
+   |
+LL |     fn f(a @ a @ a: ()) {}
+   |              ^ used as parameter more than once
+
+error[E0415]: identifier `a` is bound more than once in this parameter list
+  --> $DIR/pat-at-same-name-both.rs:6:18
+   |
+LL |     fn f(a @ a @ a: ()) {}
+   |                  ^ used as parameter more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:11:20
+   |
+LL |         Ok(a @ b @ a)
+   |                    ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:13:23
+   |
+LL |         | Err(a @ b @ a)
+   |                       ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:18:13
+   |
+LL |     let a @ a @ a = ();
+   |             ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:18:17
+   |
+LL |     let a @ a @ a = ();
+   |                 ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:21:21
+   |
+LL |     let ref a @ ref a = ();
+   |                     ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:23:29
+   |
+LL |     let ref mut a @ ref mut a = ();
+   |                             ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:26:17
+   |
+LL |     let a @ (Ok(a) | Err(a)) = Ok(());
+   |                 ^ used in a pattern more than once
+
+error[E0416]: identifier `a` is bound more than once in the same pattern
+  --> $DIR/pat-at-same-name-both.rs:26:26
+   |
+LL |     let a @ (Ok(a) | Err(a)) = Ok(());
+   |                          ^ used in a pattern more than once
+
+error: aborting due to 10 previous errors
+
+Some errors have detailed explanations: E0415, E0416.
+For more information about an error, try `rustc --explain E0415`.
diff --git a/tests/ui/pattern/bindings-after-at/slice-patterns.rs b/tests/ui/pattern/bindings-after-at/slice-patterns.rs
new file mode 100644
index 00000000000..4f4c96e450b
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/slice-patterns.rs
@@ -0,0 +1,39 @@
+// Test bindings-after-at with slice-patterns
+
+// run-pass
+
+
+#[derive(Debug, PartialEq)]
+enum MatchArm {
+    Arm(usize),
+    Wild,
+}
+
+fn test(foo: &[i32]) -> MatchArm {
+    match foo {
+        [bar @ .., n] if n == &5 => {
+            for i in bar {
+                assert!(i < &5);
+            }
+
+            MatchArm::Arm(0)
+        },
+        bar @ [x0, .., xn] => {
+            assert_eq!(x0, &1);
+            assert_eq!(x0, &1);
+            assert_eq!(xn, &4);
+            assert_eq!(bar, &[1, 2, 3, 4]);
+
+            MatchArm::Arm(1)
+        },
+        _ => MatchArm::Wild,
+    }
+}
+
+fn main() {
+    let foo = vec![1, 2, 3, 4, 5];
+
+    assert_eq!(test(&foo), MatchArm::Arm(0));
+    assert_eq!(test(&foo[..4]), MatchArm::Arm(1));
+    assert_eq!(test(&foo[0..1]), MatchArm::Wild);
+}
diff --git a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs
new file mode 100644
index 00000000000..50ac0ef2783
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.rs
@@ -0,0 +1,16 @@
+// Here we check that `_ @ sub` is syntactically invalid
+// and comes with a nice actionable suggestion.
+
+fn main() {}
+
+#[cfg(FALSE)]
+fn wild_before_at_is_bad_syntax() {
+    let _ @ a = 0;
+    //~^ ERROR pattern on wrong side of `@`
+    let _ @ ref a = 0;
+    //~^ ERROR pattern on wrong side of `@`
+    let _ @ ref mut a = 0;
+    //~^ ERROR pattern on wrong side of `@`
+    let _ @ (a, .., b) = (0, 1, 2, 3);
+    //~^ ERROR left-hand side of `@` must be a binding
+}
diff --git a/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr
new file mode 100644
index 00000000000..2f45415844d
--- /dev/null
+++ b/tests/ui/pattern/bindings-after-at/wild-before-at-syntactically-rejected.stderr
@@ -0,0 +1,43 @@
+error: pattern on wrong side of `@`
+  --> $DIR/wild-before-at-syntactically-rejected.rs:8:9
+   |
+LL |     let _ @ a = 0;
+   |         -^^^-
+   |         |   |
+   |         |   binding on the right, should be on the left
+   |         pattern on the left, should be on the right
+   |         help: switch the order: `a @ _`
+
+error: pattern on wrong side of `@`
+  --> $DIR/wild-before-at-syntactically-rejected.rs:10:9
+   |
+LL |     let _ @ ref a = 0;
+   |         -^^^-----
+   |         |   |
+   |         |   binding on the right, should be on the left
+   |         pattern on the left, should be on the right
+   |         help: switch the order: `ref a @ _`
+
+error: pattern on wrong side of `@`
+  --> $DIR/wild-before-at-syntactically-rejected.rs:12:9
+   |
+LL |     let _ @ ref mut a = 0;
+   |         -^^^---------
+   |         |   |
+   |         |   binding on the right, should be on the left
+   |         pattern on the left, should be on the right
+   |         help: switch the order: `ref mut a @ _`
+
+error: left-hand side of `@` must be a binding
+  --> $DIR/wild-before-at-syntactically-rejected.rs:14:9
+   |
+LL |     let _ @ (a, .., b) = (0, 1, 2, 3);
+   |         -^^^----------
+   |         |   |
+   |         |   also a pattern
+   |         interpreted as a pattern, not a binding
+   |
+   = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/for-loop-bad-item.rs b/tests/ui/pattern/for-loop-bad-item.rs
new file mode 100644
index 00000000000..9a56a399b9b
--- /dev/null
+++ b/tests/ui/pattern/for-loop-bad-item.rs
@@ -0,0 +1,20 @@
+struct Qux(i32);
+
+fn bad() {
+    let mut map = std::collections::HashMap::new();
+    map.insert(('a', 'b'), ('c', 'd'));
+
+    for ((_, _), (&mut c, _)) in &mut map {
+    //~^ ERROR mismatched types
+        if c == 'e' {}
+    }
+}
+
+fn bad2() {
+    for Some(Qux(_)) | None in [Some(""), None] {
+    //~^ ERROR mismatched types
+        todo!();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/for-loop-bad-item.stderr b/tests/ui/pattern/for-loop-bad-item.stderr
new file mode 100644
index 00000000000..f064a25a9c9
--- /dev/null
+++ b/tests/ui/pattern/for-loop-bad-item.stderr
@@ -0,0 +1,32 @@
+error[E0308]: mismatched types
+  --> $DIR/for-loop-bad-item.rs:7:19
+   |
+LL |     for ((_, _), (&mut c, _)) in &mut map {
+   |                   ^^^^^^         -------- this is an iterator with items of type `(&(char, char), &mut (char, char))`
+   |                   |
+   |                   expected `char`, found `&mut _`
+   |
+   = note:           expected type `char`
+           found mutable reference `&mut _`
+note: to declare a mutable binding use: `mut c`
+  --> $DIR/for-loop-bad-item.rs:7:19
+   |
+LL |     for ((_, _), (&mut c, _)) in &mut map {
+   |                   ^^^^^^
+help: consider removing `&mut` from the pattern
+   |
+LL -     for ((_, _), (&mut c, _)) in &mut map {
+LL +     for ((_, _), (c, _)) in &mut map {
+   |
+
+error[E0308]: mismatched types
+  --> $DIR/for-loop-bad-item.rs:14:14
+   |
+LL |     for Some(Qux(_)) | None in [Some(""), None] {
+   |              ^^^^^^            ---------------- this is an iterator with items of type `Option<&str>`
+   |              |
+   |              expected `str`, found struct `Qux`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/ignore-all-the-things.rs b/tests/ui/pattern/ignore-all-the-things.rs
new file mode 100644
index 00000000000..5980e1a857f
--- /dev/null
+++ b/tests/ui/pattern/ignore-all-the-things.rs
@@ -0,0 +1,44 @@
+// run-pass
+
+#![allow(non_shorthand_field_patterns)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+
+struct Foo(isize, isize, isize, isize);
+struct Bar{a: isize, b: isize, c: isize, d: isize}
+
+pub fn main() {
+    let Foo(..) = Foo(5, 5, 5, 5);
+    let Foo(..) = Foo(5, 5, 5, 5);
+    let Bar{..} = Bar{a: 5, b: 5, c: 5, d: 5};
+    let (..) = (5, 5, 5, 5);
+    let Foo(a, b, ..) = Foo(5, 5, 5, 5);
+    let Foo(.., d) = Foo(5, 5, 5, 5);
+    let (a, b, ..) = (5, 5, 5, 5);
+    let (.., c, d) = (5, 5, 5, 5);
+    let Bar{b: b, ..} = Bar{a: 5, b: 5, c: 5, d: 5};
+    match [5, 5, 5, 5] {
+        [..] => { }
+    }
+    match [5, 5, 5, 5] {
+        [a, ..] => { }
+    }
+    match [5, 5, 5, 5] {
+        [.., b] => { }
+    }
+    match [5, 5, 5, 5] {
+        [a, .., b] => { }
+    }
+    match [5, 5, 5] {
+        [..] => { }
+    }
+    match [5, 5, 5] {
+        [a, ..] => { }
+    }
+    match [5, 5, 5] {
+        [.., a] => { }
+    }
+    match [5, 5, 5] {
+        [a, .., b] => { }
+    }
+}
diff --git a/tests/ui/pattern/integer-range-binding.rs b/tests/ui/pattern/integer-range-binding.rs
new file mode 100644
index 00000000000..ff065882d96
--- /dev/null
+++ b/tests/ui/pattern/integer-range-binding.rs
@@ -0,0 +1,8 @@
+// run-pass
+
+fn main() {
+    let -2147483648..=2147483647 = 1;
+    let 0..=255 = 0u8;
+    let -128..=127 = 0i8;
+    let '\u{0000}'..='\u{10FFFF}' = 'v';
+}
diff --git a/tests/ui/pattern/issue-10392.rs b/tests/ui/pattern/issue-10392.rs
new file mode 100644
index 00000000000..926fa94800e
--- /dev/null
+++ b/tests/ui/pattern/issue-10392.rs
@@ -0,0 +1,30 @@
+// run-pass
+#![allow(unused_variables)]
+
+struct A { foo: isize }
+struct B { a: isize, b: isize, c: isize }
+
+fn mka() -> A { panic!() }
+fn mkb() -> B { panic!() }
+
+fn test() {
+    let A { foo, } = mka();
+    let A {
+        foo,
+    } = mka();
+
+    let B { a, b, c, } = mkb();
+
+    match mka() {
+        A { foo: _foo, } => {}
+    }
+
+    match Some(mka()) {
+        Some(A { foo: _foo, }) => {}
+        None => {}
+    }
+}
+
+pub fn main() {
+    if false { test() }
+}
diff --git a/tests/ui/pattern/issue-11577.rs b/tests/ui/pattern/issue-11577.rs
new file mode 100644
index 00000000000..70177c5ed0d
--- /dev/null
+++ b/tests/ui/pattern/issue-11577.rs
@@ -0,0 +1,18 @@
+// run-pass
+// Destructuring struct variants would ICE where regular structs wouldn't
+
+enum Foo {
+    VBar { num: isize }
+}
+
+struct SBar { num: isize }
+
+pub fn main() {
+    let vbar = Foo::VBar { num: 1 };
+    let Foo::VBar { num } = vbar;
+    assert_eq!(num, 1);
+
+    let sbar = SBar { num: 2 };
+    let SBar { num } = sbar;
+    assert_eq!(num, 2);
+}
diff --git a/tests/ui/pattern/issue-12582.rs b/tests/ui/pattern/issue-12582.rs
new file mode 100644
index 00000000000..f3366704e63
--- /dev/null
+++ b/tests/ui/pattern/issue-12582.rs
@@ -0,0 +1,21 @@
+// run-pass
+
+pub fn main() {
+    let x = 1;
+    let y = 2;
+
+    assert_eq!(3, match (x, y) {
+        (1, 1) => 1,
+        (2, 2) => 2,
+        (1..=2, 2) => 3,
+        _ => 4,
+    });
+
+    // nested tuple
+    assert_eq!(3, match ((x, y),) {
+        ((1, 1),) => 1,
+        ((2, 2),) => 2,
+        ((1..=2, 2),) => 3,
+        _ => 4,
+    });
+}
diff --git a/tests/ui/pattern/issue-14221.rs b/tests/ui/pattern/issue-14221.rs
new file mode 100644
index 00000000000..282c4111369
--- /dev/null
+++ b/tests/ui/pattern/issue-14221.rs
@@ -0,0 +1,21 @@
+#![deny(unreachable_patterns)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+
+pub enum E {
+    A,
+    B,
+}
+
+pub mod b {
+    pub fn key(e: ::E) -> &'static str {
+        match e {
+            A => "A",
+//~^ WARN pattern binding `A` is named the same as one of the variants of the type `E`
+            B => "B", //~ ERROR: unreachable pattern
+//~^ WARN pattern binding `B` is named the same as one of the variants of the type `E`
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/issue-14221.stderr b/tests/ui/pattern/issue-14221.stderr
new file mode 100644
index 00000000000..fc8ae1ed7b5
--- /dev/null
+++ b/tests/ui/pattern/issue-14221.stderr
@@ -0,0 +1,32 @@
+warning[E0170]: pattern binding `A` is named the same as one of the variants of the type `E`
+  --> $DIR/issue-14221.rs:13:13
+   |
+LL |             A => "A",
+   |             ^ help: to match on the variant, qualify the path: `E::A`
+   |
+   = note: `#[warn(bindings_with_variant_name)]` on by default
+
+warning[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
+  --> $DIR/issue-14221.rs:15:13
+   |
+LL |             B => "B",
+   |             ^ help: to match on the variant, qualify the path: `E::B`
+
+error: unreachable pattern
+  --> $DIR/issue-14221.rs:15:13
+   |
+LL |             A => "A",
+   |             - matches any value
+LL |
+LL |             B => "B",
+   |             ^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/issue-14221.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 2 warnings emitted
+
+For more information about this error, try `rustc --explain E0170`.
diff --git a/tests/ui/pattern/issue-15080.rs b/tests/ui/pattern/issue-15080.rs
new file mode 100644
index 00000000000..4dd6981d448
--- /dev/null
+++ b/tests/ui/pattern/issue-15080.rs
@@ -0,0 +1,22 @@
+// run-pass
+
+fn main() {
+    let mut x: &[_] = &[1, 2, 3, 4];
+
+    let mut result = vec![];
+    loop {
+        x = match *x {
+            [1, n, 3, ref rest @ ..] => {
+                result.push(n);
+                rest
+            }
+            [n, ref rest @ ..] => {
+                result.push(n);
+                rest
+            }
+            [] =>
+                break
+        }
+    }
+    assert_eq!(result, [2, 4]);
+}
diff --git a/tests/ui/pattern/issue-17718-patterns.rs b/tests/ui/pattern/issue-17718-patterns.rs
new file mode 100644
index 00000000000..2ca0f67f80e
--- /dev/null
+++ b/tests/ui/pattern/issue-17718-patterns.rs
@@ -0,0 +1,12 @@
+static A1: usize = 1;
+static mut A2: usize = 1;
+const A3: usize = 1;
+
+fn main() {
+    match 1 {
+        A1 => {} //~ ERROR: match bindings cannot shadow statics
+        A2 => {} //~ ERROR: match bindings cannot shadow statics
+        A3 => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/issue-17718-patterns.stderr b/tests/ui/pattern/issue-17718-patterns.stderr
new file mode 100644
index 00000000000..109091c2af0
--- /dev/null
+++ b/tests/ui/pattern/issue-17718-patterns.stderr
@@ -0,0 +1,21 @@
+error[E0530]: match bindings cannot shadow statics
+  --> $DIR/issue-17718-patterns.rs:7:9
+   |
+LL | static A1: usize = 1;
+   | --------------------- the static `A1` is defined here
+...
+LL |         A1 => {}
+   |         ^^ cannot be named the same as a static
+
+error[E0530]: match bindings cannot shadow statics
+  --> $DIR/issue-17718-patterns.rs:8:9
+   |
+LL | static mut A2: usize = 1;
+   | ------------------------- the static `A2` is defined here
+...
+LL |         A2 => {}
+   |         ^^ cannot be named the same as a static
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0530`.
diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs
new file mode 100644
index 00000000000..c26e457f9e4
--- /dev/null
+++ b/tests/ui/pattern/issue-22546.rs
@@ -0,0 +1,52 @@
+// run-pass
+#![allow(unused_variables)]
+// Parsing patterns with paths with type parameters (issue #22544)
+
+use std::default::Default;
+
+#[derive(Default)]
+pub struct Foo<T>(T, T);
+
+impl<T: ::std::fmt::Display> Foo<T> {
+    fn foo(&self) {
+        match *self {
+            Foo::<T>(ref x, ref y) => println!("Goodbye, World! {} {}", x, y)
+        }
+    }
+}
+
+trait Tr {
+    type U;
+}
+
+impl<T> Tr for Foo<T> {
+    type U = T;
+}
+
+struct Wrapper<T> {
+    value: T
+}
+
+fn main() {
+    let Foo::<i32>(a, b) = Default::default();
+
+    let f = Foo(2,3);
+    f.foo();
+
+    let w = Wrapper { value: Foo(10u8, 11u8) };
+    match w {
+        Wrapper::<Foo<u8>> { value: Foo(10, 11) } => {},
+        ::Wrapper::<<Foo<_> as Tr>::U> { value: Foo::<u8>(11, 16) } => { panic!() },
+        _ => { panic!() }
+    }
+
+    if let None::<u8> = Some(8) {
+        panic!();
+    }
+    if let None::<u8> { .. } = Some(8) {
+        panic!();
+    }
+    if let Option::None::<u8> { .. } = Some(8) {
+        panic!();
+    }
+}
diff --git a/tests/ui/pattern/issue-27320.rs b/tests/ui/pattern/issue-27320.rs
new file mode 100644
index 00000000000..d1aa56b915b
--- /dev/null
+++ b/tests/ui/pattern/issue-27320.rs
@@ -0,0 +1,15 @@
+// run-pass
+#![allow(unused_variables)]
+#![allow(dead_code)]
+
+macro_rules! piece(
+    ($piece:pat) => ($piece);
+);
+
+enum Piece {A, B}
+
+fn main() {
+    match Piece::A {
+        piece!(pt@ Piece::A) | piece!(pt@ Piece::B) => {}
+    }
+}
diff --git a/tests/ui/pattern/issue-52240.rs b/tests/ui/pattern/issue-52240.rs
new file mode 100644
index 00000000000..5def557789f
--- /dev/null
+++ b/tests/ui/pattern/issue-52240.rs
@@ -0,0 +1,16 @@
+// issue-52240: Can turn immutable into mut with `ref mut`
+
+enum Foo {
+    Bar(i32),
+}
+
+fn main() {
+    let arr = vec!(Foo::Bar(0));
+    if let (Some(Foo::Bar(ref mut val)), _) = (&arr.get(0), 0) {
+        //~^ ERROR cannot borrow data in a `&` reference as mutable
+        *val = 9001;
+    }
+    match arr[0] {
+        Foo::Bar(ref s) => println!("{}", s)
+    }
+}
diff --git a/tests/ui/pattern/issue-52240.stderr b/tests/ui/pattern/issue-52240.stderr
new file mode 100644
index 00000000000..69b663b17d3
--- /dev/null
+++ b/tests/ui/pattern/issue-52240.stderr
@@ -0,0 +1,9 @@
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/issue-52240.rs:9:27
+   |
+LL |     if let (Some(Foo::Bar(ref mut val)), _) = (&arr.get(0), 0) {
+   |                           ^^^^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/pattern/issue-6449.rs b/tests/ui/pattern/issue-6449.rs
new file mode 100644
index 00000000000..bfd4c123208
--- /dev/null
+++ b/tests/ui/pattern/issue-6449.rs
@@ -0,0 +1,44 @@
+// run-pass
+#![allow(dead_code)]
+
+enum Foo {
+    Bar(isize),
+    Baz,
+}
+
+enum Other {
+    Other1(Foo),
+    Other2(Foo, Foo),
+}
+
+fn main() {
+    match Foo::Baz {
+        ::Foo::Bar(3) => panic!(),
+        ::Foo::Bar(_) if false => panic!(),
+        ::Foo::Bar(..) if false => panic!(),
+        ::Foo::Bar(_n) => panic!(),
+        ::Foo::Baz => {}
+    }
+    match Foo::Bar(3) {
+        ::Foo::Bar(3) => {}
+        ::Foo::Bar(_) if false => panic!(),
+        ::Foo::Bar(..) if false => panic!(),
+        ::Foo::Bar(_n) => panic!(),
+        ::Foo::Baz => panic!(),
+    }
+    match Foo::Bar(4) {
+        ::Foo::Bar(3) => panic!(),
+        ::Foo::Bar(_) if false => panic!(),
+        ::Foo::Bar(..) if false => panic!(),
+        ::Foo::Bar(n) => assert_eq!(n, 4),
+        ::Foo::Baz => panic!(),
+    }
+
+    match Other::Other1(Foo::Baz) {
+        ::Other::Other1(::Foo::Baz) => {}
+        ::Other::Other1(::Foo::Bar(_)) => {}
+        ::Other::Other2(::Foo::Baz, ::Foo::Bar(_)) => {}
+        ::Other::Other2(::Foo::Bar(..), ::Foo::Baz) => {}
+        ::Other::Other2(..) => {}
+    }
+}
diff --git a/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.rs b/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.rs
new file mode 100644
index 00000000000..48a8e04829a
--- /dev/null
+++ b/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.rs
@@ -0,0 +1,14 @@
+// Regression test for #66270, fixed by #66246
+
+struct Bug {
+    incorrect_field: 0,
+    //~^ ERROR expected type
+}
+
+struct Empty {}
+
+fn main() {
+    let Bug {
+        any_field: Empty {},
+    } = Bug {};
+}
diff --git a/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.stderr b/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.stderr
new file mode 100644
index 00000000000..f40642f300c
--- /dev/null
+++ b/tests/ui/pattern/issue-66270-pat-struct-parser-recovery.stderr
@@ -0,0 +1,10 @@
+error: expected type, found `0`
+  --> $DIR/issue-66270-pat-struct-parser-recovery.rs:4:22
+   |
+LL | struct Bug {
+   |        --- while parsing this struct
+LL |     incorrect_field: 0,
+   |                      ^ expected type
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs b/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs
new file mode 100644
index 00000000000..ae28c140375
--- /dev/null
+++ b/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs
@@ -0,0 +1,21 @@
+// Regression test for #67037.
+//
+// In type checking patterns, E0023 occurs when the tuple pattern and the expected
+// tuple pattern have different number of fields. For example, as below, `P()`,
+// the tuple struct pattern, has 0 fields, but requires 1 field.
+//
+// In emitting E0023, we try to see if this is a case of e.g., `Some(a, b, c)` but where
+// the scrutinee was of type `Some((a, b, c))`, and suggest that parentheses be added.
+//
+// However, we did not account for the expected type being different than the tuple pattern type.
+// This caused an issue when the tuple pattern type (`P<T>`) was generic.
+// Specifically, we tried deriving the 0th field's type using the `substs` of the expected type.
+// When attempting to substitute `T`, there was no such substitution, so "out of range" occurred.
+
+struct U {} // 0 type parameters offered
+struct P<T>(T); // 1 type parameter wanted
+
+fn main() {
+    let P() = U {}; //~ ERROR mismatched types
+    //~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 1 field
+}
diff --git a/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr b/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr
new file mode 100644
index 00000000000..75a231f6b4b
--- /dev/null
+++ b/tests/ui/pattern/issue-67037-pat-tup-scrut-ty-diff-less-fields.stderr
@@ -0,0 +1,33 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
+   |
+LL |     let P() = U {};
+   |         ^^^   ---- this expression has type `U`
+   |         |
+   |         expected struct `U`, found struct `P`
+   |
+   = note: expected struct `U`
+              found struct `P<_>`
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 1 field
+  --> $DIR/issue-67037-pat-tup-scrut-ty-diff-less-fields.rs:19:9
+   |
+LL | struct P<T>(T); // 1 type parameter wanted
+   |             - tuple struct has 1 field
+...
+LL |     let P() = U {};
+   |         ^^^ expected 1 field, found 0
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |     let P(_) = U {};
+   |           +
+help: use `..` to ignore all fields
+   |
+LL |     let P(..) = U {};
+   |           ++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0023, E0308.
+For more information about an error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs b/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs
new file mode 100644
index 00000000000..6fd5768a5a2
--- /dev/null
+++ b/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.rs
@@ -0,0 +1,42 @@
+// Test for issue #67776: binding named the same as enum variant
+// should report a warning even when matching against a reference type
+
+// check-pass
+
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+
+enum Foo {
+    Bar,
+    Baz,
+}
+
+
+fn fn1(e: Foo) {
+    match e {
+        Bar => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+        Baz => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+    }
+}
+
+fn fn2(e: &Foo) {
+    match e {
+        Bar => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+        Baz => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+    }
+}
+
+fn fn3(e: &mut &&mut Foo) {
+    match e {
+        Bar => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+        Baz => {},
+        //~^ WARNING named the same as one of the variants of the type `Foo`
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr b/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr
new file mode 100644
index 00000000000..6f3613b63c9
--- /dev/null
+++ b/tests/ui/pattern/issue-67776-match-same-name-enum-variant-refs.stderr
@@ -0,0 +1,41 @@
+warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:17:9
+   |
+LL |         Bar => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
+   |
+   = note: `#[warn(bindings_with_variant_name)]` on by default
+
+warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:19:9
+   |
+LL |         Baz => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
+
+warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:26:9
+   |
+LL |         Bar => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
+
+warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:28:9
+   |
+LL |         Baz => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
+
+warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:35:9
+   |
+LL |         Bar => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
+
+warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+  --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:37:9
+   |
+LL |         Baz => {},
+   |         ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
+
+warning: 6 warnings emitted
+
+For more information about this error, try `rustc --explain E0170`.
diff --git a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs
new file mode 100644
index 00000000000..95ead6b5d4a
--- /dev/null
+++ b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.rs
@@ -0,0 +1,26 @@
+pub enum EFoo {
+    A,
+}
+
+pub trait Foo {
+    const X: EFoo;
+}
+
+struct Abc;
+
+impl Foo for Abc {
+    const X: EFoo = EFoo::A;
+}
+
+struct Def;
+impl Foo for Def {
+    const X: EFoo = EFoo::A;
+}
+
+pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+    //~^ ERROR associated consts cannot be referenced in patterns
+    let A::X = arg;
+    //~^ ERROR associated consts cannot be referenced in patterns
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
new file mode 100644
index 00000000000..54ecc24981f
--- /dev/null
+++ b/tests/ui/pattern/issue-68393-let-pat-assoc-constant.stderr
@@ -0,0 +1,15 @@
+error[E0158]: associated consts cannot be referenced in patterns
+  --> $DIR/issue-68393-let-pat-assoc-constant.rs:20:40
+   |
+LL | pub fn test<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
+   |                                        ^^^^
+
+error[E0158]: associated consts cannot be referenced in patterns
+  --> $DIR/issue-68393-let-pat-assoc-constant.rs:22:9
+   |
+LL |     let A::X = arg;
+   |         ^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0158`.
diff --git a/tests/ui/pattern/issue-72565.rs b/tests/ui/pattern/issue-72565.rs
new file mode 100644
index 00000000000..1e262fd5067
--- /dev/null
+++ b/tests/ui/pattern/issue-72565.rs
@@ -0,0 +1,8 @@
+const F: &'static dyn PartialEq<u32> = &7u32;
+
+fn main() {
+    let a: &dyn PartialEq<u32> = &7u32;
+    match a {
+        F => panic!(), //~ ERROR: `&dyn PartialEq<u32>` cannot be used in patterns
+    }
+}
diff --git a/tests/ui/pattern/issue-72565.stderr b/tests/ui/pattern/issue-72565.stderr
new file mode 100644
index 00000000000..2f82616b277
--- /dev/null
+++ b/tests/ui/pattern/issue-72565.stderr
@@ -0,0 +1,8 @@
+error: `&dyn PartialEq<u32>` cannot be used in patterns
+  --> $DIR/issue-72565.rs:6:9
+   |
+LL |         F => panic!(),
+   |         ^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/issue-72574-1.rs b/tests/ui/pattern/issue-72574-1.rs
new file mode 100644
index 00000000000..1b80a21793a
--- /dev/null
+++ b/tests/ui/pattern/issue-72574-1.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let x = (1, 2, 3);
+    match x {
+        (_a, _x @ ..) => {}
+        _ => {}
+    }
+}
+//~^^^^ ERROR `_x @` is not allowed in a tuple
+//~| ERROR: `..` patterns are not allowed here
+//~| ERROR: mismatched types
diff --git a/tests/ui/pattern/issue-72574-1.stderr b/tests/ui/pattern/issue-72574-1.stderr
new file mode 100644
index 00000000000..653869a237d
--- /dev/null
+++ b/tests/ui/pattern/issue-72574-1.stderr
@@ -0,0 +1,34 @@
+error: `_x @` is not allowed in a tuple
+  --> $DIR/issue-72574-1.rs:4:14
+   |
+LL |         (_a, _x @ ..) => {}
+   |              ^^^^^^^ this is only allowed in slice patterns
+   |
+   = help: remove this and bind each tuple field independently
+help: if you don't need to use the contents of _x, discard the tuple's remaining fields
+   |
+LL |         (_a, ..) => {}
+   |              ~~
+
+error: `..` patterns are not allowed here
+  --> $DIR/issue-72574-1.rs:4:19
+   |
+LL |         (_a, _x @ ..) => {}
+   |                   ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0308]: mismatched types
+  --> $DIR/issue-72574-1.rs:4:9
+   |
+LL |     match x {
+   |           - this expression has type `({integer}, {integer}, {integer})`
+LL |         (_a, _x @ ..) => {}
+   |         ^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements
+   |
+   = note: expected tuple `({integer}, {integer}, {integer})`
+              found tuple `(_, _)`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/issue-72574-2.rs b/tests/ui/pattern/issue-72574-2.rs
new file mode 100644
index 00000000000..0ad2db848b2
--- /dev/null
+++ b/tests/ui/pattern/issue-72574-2.rs
@@ -0,0 +1,12 @@
+struct Binder(i32, i32, i32);
+
+fn main() {
+    let x = Binder(1, 2, 3);
+    match x {
+        Binder(_a, _x @ ..) => {}
+        _ => {}
+    }
+}
+//~^^^^ ERROR `_x @` is not allowed in a tuple struct
+//~| ERROR: `..` patterns are not allowed here
+//~| ERROR: this pattern has 2 fields, but the corresponding tuple struct has 3 fields
diff --git a/tests/ui/pattern/issue-72574-2.stderr b/tests/ui/pattern/issue-72574-2.stderr
new file mode 100644
index 00000000000..05650f05cbf
--- /dev/null
+++ b/tests/ui/pattern/issue-72574-2.stderr
@@ -0,0 +1,37 @@
+error: `_x @` is not allowed in a tuple struct
+  --> $DIR/issue-72574-2.rs:6:20
+   |
+LL |         Binder(_a, _x @ ..) => {}
+   |                    ^^^^^^^ this is only allowed in slice patterns
+   |
+   = help: remove this and bind each tuple field independently
+help: if you don't need to use the contents of _x, discard the tuple's remaining fields
+   |
+LL |         Binder(_a, ..) => {}
+   |                    ~~
+
+error: `..` patterns are not allowed here
+  --> $DIR/issue-72574-2.rs:6:25
+   |
+LL |         Binder(_a, _x @ ..) => {}
+   |                         ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/issue-72574-2.rs:6:16
+   |
+LL | struct Binder(i32, i32, i32);
+   |               ---  ---  --- tuple struct has 3 fields
+...
+LL |         Binder(_a, _x @ ..) => {}
+   |                ^^  ^^^^^^^ expected 3 fields, found 2
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         Binder(_a, _x @ .., _) => {}
+   |                           +++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/issue-74539.rs b/tests/ui/pattern/issue-74539.rs
new file mode 100644
index 00000000000..0b25f87ec53
--- /dev/null
+++ b/tests/ui/pattern/issue-74539.rs
@@ -0,0 +1,15 @@
+enum E {
+    A(u8, u8),
+}
+
+fn main() {
+    let e = E::A(2, 3);
+    match e {
+        E::A(x @ ..) => {
+            //~^ ERROR: `x @` is not allowed in a tuple struct
+            //~| ERROR: `..` patterns are not allowed here
+            //~| ERROR: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+            x
+        }
+    };
+}
diff --git a/tests/ui/pattern/issue-74539.stderr b/tests/ui/pattern/issue-74539.stderr
new file mode 100644
index 00000000000..7443946c013
--- /dev/null
+++ b/tests/ui/pattern/issue-74539.stderr
@@ -0,0 +1,37 @@
+error: `x @` is not allowed in a tuple struct
+  --> $DIR/issue-74539.rs:8:14
+   |
+LL |         E::A(x @ ..) => {
+   |              ^^^^^^ this is only allowed in slice patterns
+   |
+   = help: remove this and bind each tuple field independently
+help: if you don't need to use the contents of x, discard the tuple's remaining fields
+   |
+LL |         E::A(..) => {
+   |              ~~
+
+error: `..` patterns are not allowed here
+  --> $DIR/issue-74539.rs:8:18
+   |
+LL |         E::A(x @ ..) => {
+   |                  ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/issue-74539.rs:8:14
+   |
+LL |     A(u8, u8),
+   |       --  -- tuple variant has 2 fields
+...
+LL |         E::A(x @ ..) => {
+   |              ^^^^^^ expected 2 fields, found 1
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E::A(x @ .., _) => {
+   |                    +++
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/issue-74702.rs b/tests/ui/pattern/issue-74702.rs
new file mode 100644
index 00000000000..0aeb3b217a2
--- /dev/null
+++ b/tests/ui/pattern/issue-74702.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let (foo @ ..,) = (0, 0);
+    //~^ ERROR: `foo @` is not allowed in a tuple
+    //~| ERROR: `..` patterns are not allowed here
+    //~| ERROR: mismatched types
+    dbg!(foo);
+}
diff --git a/tests/ui/pattern/issue-74702.stderr b/tests/ui/pattern/issue-74702.stderr
new file mode 100644
index 00000000000..f2e2c8f021b
--- /dev/null
+++ b/tests/ui/pattern/issue-74702.stderr
@@ -0,0 +1,34 @@
+error: `foo @` is not allowed in a tuple
+  --> $DIR/issue-74702.rs:2:10
+   |
+LL |     let (foo @ ..,) = (0, 0);
+   |          ^^^^^^^^ this is only allowed in slice patterns
+   |
+   = help: remove this and bind each tuple field independently
+help: if you don't need to use the contents of foo, discard the tuple's remaining fields
+   |
+LL |     let (..,) = (0, 0);
+   |          ~~
+
+error: `..` patterns are not allowed here
+  --> $DIR/issue-74702.rs:2:16
+   |
+LL |     let (foo @ ..,) = (0, 0);
+   |                ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0308]: mismatched types
+  --> $DIR/issue-74702.rs:2:9
+   |
+LL |     let (foo @ ..,) = (0, 0);
+   |         ^^^^^^^^^^^   ------ this expression has type `({integer}, {integer})`
+   |         |
+   |         expected a tuple with 2 elements, found one with 1 element
+   |
+   = note: expected tuple `({integer}, {integer})`
+              found tuple `(_,)`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/issue-74954.rs b/tests/ui/pattern/issue-74954.rs
new file mode 100644
index 00000000000..269ec3c7abe
--- /dev/null
+++ b/tests/ui/pattern/issue-74954.rs
@@ -0,0 +1,7 @@
+// check-pass
+
+fn main() {
+    if let Some([b'@', filename @ ..]) = Some(b"@abc123") {
+        println!("filename {:?}", filename);
+    }
+}
diff --git a/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.rs b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.rs
new file mode 100644
index 00000000000..a5e9b1db546
--- /dev/null
+++ b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.rs
@@ -0,0 +1,9 @@
+// Regression test for correct pretty-printing of an AST representing `&(mut x)` in help
+// suggestion diagnostic.
+
+fn main() {
+    let mut &x = &0;
+    //~^ ERROR `mut` must be attached to each individual binding
+    //~| HELP add `mut` to each binding
+    //~| SUGGESTION &(mut x)
+}
diff --git a/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr
new file mode 100644
index 00000000000..75b6c163b2c
--- /dev/null
+++ b/tests/ui/pattern/issue-80186-mut-binding-help-suggestion.stderr
@@ -0,0 +1,10 @@
+error: `mut` must be attached to each individual binding
+  --> $DIR/issue-80186-mut-binding-help-suggestion.rs:5:9
+   |
+LL |     let mut &x = &0;
+   |         ^^^^^^ help: add `mut` to each binding: `&(mut x)`
+   |
+   = note: `mut` may be followed by `variable` and `variable @ pattern`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/issue-8351-1.rs b/tests/ui/pattern/issue-8351-1.rs
new file mode 100644
index 00000000000..139f027cb90
--- /dev/null
+++ b/tests/ui/pattern/issue-8351-1.rs
@@ -0,0 +1,16 @@
+// run-pass
+#![allow(dead_code)]
+
+enum E {
+    Foo{f: isize},
+    Bar,
+}
+
+pub fn main() {
+    let e = E::Foo{f: 0};
+    match e {
+        E::Foo{f: 1} => panic!(),
+        E::Foo{..} => (),
+        _ => panic!(),
+    }
+}
diff --git a/tests/ui/pattern/issue-8351-2.rs b/tests/ui/pattern/issue-8351-2.rs
new file mode 100644
index 00000000000..bc66cbb77c0
--- /dev/null
+++ b/tests/ui/pattern/issue-8351-2.rs
@@ -0,0 +1,16 @@
+// run-pass
+#![allow(dead_code)]
+
+enum E {
+    Foo{f: isize, b: bool},
+    Bar,
+}
+
+pub fn main() {
+    let e = E::Foo{f: 0, b: false};
+    match e {
+        E::Foo{f: 1, b: true} => panic!(),
+        E::Foo{b: false, f: 0} => (),
+        _ => panic!(),
+    }
+}
diff --git a/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs b/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs
new file mode 100644
index 00000000000..16df272df6b
--- /dev/null
+++ b/tests/ui/pattern/issue-88074-pat-range-type-inference-err.rs
@@ -0,0 +1,28 @@
+trait Zero {
+    const ZERO: Self;
+}
+
+impl Zero for String {
+    const ZERO: Self = String::new();
+}
+
+fn foo() {
+     match String::new() {
+        Zero::ZERO ..= Zero::ZERO => {},
+        //~^ ERROR only `char` and numeric types are allowed in range patterns
+        _ => {},
+    }
+}
+
+fn bar() {
+    match Zero::ZERO {
+        Zero::ZERO ..= Zero::ZERO => {},
+        //~^ ERROR type annotations needed [E0282]
+        _ => {},
+    }
+}
+
+fn main() {
+    foo();
+    bar();
+}
diff --git a/tests/ui/pattern/issue-88074-pat-range-type-inference-err.stderr b/tests/ui/pattern/issue-88074-pat-range-type-inference-err.stderr
new file mode 100644
index 00000000000..8e528f8c1db
--- /dev/null
+++ b/tests/ui/pattern/issue-88074-pat-range-type-inference-err.stderr
@@ -0,0 +1,19 @@
+error[E0029]: only `char` and numeric types are allowed in range patterns
+  --> $DIR/issue-88074-pat-range-type-inference-err.rs:11:9
+   |
+LL |         Zero::ZERO ..= Zero::ZERO => {},
+   |         ----------^^^^^----------
+   |         |              |
+   |         |              this is of type `String` but it should be `char` or numeric
+   |         this is of type `String` but it should be `char` or numeric
+
+error[E0282]: type annotations needed
+  --> $DIR/issue-88074-pat-range-type-inference-err.rs:19:9
+   |
+LL |         Zero::ZERO ..= Zero::ZERO => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0029, E0282.
+For more information about an error, try `rustc --explain E0029`.
diff --git a/tests/ui/pattern/issue-88074-pat-range-type-inference.rs b/tests/ui/pattern/issue-88074-pat-range-type-inference.rs
new file mode 100644
index 00000000000..27db7d8c7ab
--- /dev/null
+++ b/tests/ui/pattern/issue-88074-pat-range-type-inference.rs
@@ -0,0 +1,16 @@
+// check-pass
+
+trait Zero {
+    const ZERO: Self;
+}
+
+impl Zero for i32 {
+    const ZERO: Self = 0;
+}
+
+fn main() {
+    match 1 {
+        Zero::ZERO ..= 1 => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/pattern/issue-92074-macro-ice.rs b/tests/ui/pattern/issue-92074-macro-ice.rs
new file mode 100644
index 00000000000..039d3b31444
--- /dev/null
+++ b/tests/ui/pattern/issue-92074-macro-ice.rs
@@ -0,0 +1,36 @@
+pub enum En {
+    A(Vec<u8>)
+}
+
+fn get_usize() -> usize {
+    0
+}
+
+macro_rules! force_expr {
+    ($e:expr) => { $e }
+}
+
+macro_rules! force_pat {
+    ($a:expr, $b:expr) => { $a..=$b }
+}
+
+macro_rules! make_vec {
+    () => { force_expr!(Vec::new()) } //~ ERROR arbitrary expressions aren't allowed
+}
+
+macro_rules! make_pat {
+    () => { force_pat!(get_usize(), get_usize()) }
+    //~^ ERROR arbitrary expressions aren't allowed
+    //~| ERROR arbitrary expressions aren't allowed
+}
+
+#[allow(unreachable_code)]
+fn f() -> Result<(), impl core::fmt::Debug> {
+    let x: En = loop {};
+
+    assert!(matches!(x, En::A(make_vec!())));
+    assert!(matches!(5, make_pat!()));
+    Ok::<(), &'static str>(())
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/issue-92074-macro-ice.stderr b/tests/ui/pattern/issue-92074-macro-ice.stderr
new file mode 100644
index 00000000000..b340afff010
--- /dev/null
+++ b/tests/ui/pattern/issue-92074-macro-ice.stderr
@@ -0,0 +1,35 @@
+error: arbitrary expressions aren't allowed in patterns
+  --> $DIR/issue-92074-macro-ice.rs:18:25
+   |
+LL |     () => { force_expr!(Vec::new()) }
+   |                         ^^^^^^^^^^
+...
+LL |     assert!(matches!(x, En::A(make_vec!())));
+   |                               ----------- in this macro invocation
+   |
+   = note: this error originates in the macro `make_vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: arbitrary expressions aren't allowed in patterns
+  --> $DIR/issue-92074-macro-ice.rs:22:24
+   |
+LL |     () => { force_pat!(get_usize(), get_usize()) }
+   |                        ^^^^^^^^^^^
+...
+LL |     assert!(matches!(5, make_pat!()));
+   |                         ----------- in this macro invocation
+   |
+   = note: this error originates in the macro `make_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: arbitrary expressions aren't allowed in patterns
+  --> $DIR/issue-92074-macro-ice.rs:22:37
+   |
+LL |     () => { force_pat!(get_usize(), get_usize()) }
+   |                                     ^^^^^^^^^^^
+...
+LL |     assert!(matches!(5, make_pat!()));
+   |                         ----------- in this macro invocation
+   |
+   = note: this error originates in the macro `make_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/issue-95878.rs b/tests/ui/pattern/issue-95878.rs
new file mode 100644
index 00000000000..f59814468b2
--- /dev/null
+++ b/tests/ui/pattern/issue-95878.rs
@@ -0,0 +1,12 @@
+struct Foo<'a>(&'a ());
+
+impl<'a> Foo<'a> {
+    fn spam(&mut self, baz: &mut Vec<u32>) {
+        match 15 {
+            ref Self => (),
+            //~^ ERROR expected identifier, found keyword `Self`
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/issue-95878.stderr b/tests/ui/pattern/issue-95878.stderr
new file mode 100644
index 00000000000..e0eea06e0a3
--- /dev/null
+++ b/tests/ui/pattern/issue-95878.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found keyword `Self`
+  --> $DIR/issue-95878.rs:6:17
+   |
+LL |             ref Self => (),
+   |                 ^^^^ expected identifier, found keyword
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs
new file mode 100644
index 00000000000..5445696fdff
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern-pass.rs
@@ -0,0 +1,29 @@
+// check-pass
+
+fn main() {}
+
+struct U;
+
+fn slice() {
+    let mut arr = [U, U, U, U, U, U, U, U];
+    let [ref _x0, _x1, _, mut _x3, .., ref _x6, _x7] = arr;
+    _x3 = U;
+    let [ref mut _x0, _, ref _x2, _, _x4, ref mut _x5, _x6, _] = arr;
+    *_x5 = U;
+    let [_, _, _x2, _, _, _x5, _, _] = arr;
+    *_x0 = U;
+    let [ref _x0, ..] = arr;
+    let [_x0, ..] = arr;
+}
+
+fn tuple() {
+    let mut tup = (U, U, U, U, U);
+    let (ref _x0, mut _x1, ref _x2, ..) = tup;
+    _x1 = U;
+    let (ref mut _x0, _, _, ref _x3, _x4) = tup;
+    let (_, _, _, _x3, _) = tup;
+    *_x0 = U;
+    drop(_x2);
+    drop(tup.2);
+    let (_x0, _, _, ..) = tup;
+}
diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs
new file mode 100644
index 00000000000..a6144c9497d
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.rs
@@ -0,0 +1,48 @@
+fn main() {}
+
+struct U;
+
+fn slice() {
+    let mut arr = [U, U, U, U, U];
+    let hold_all = &arr;
+    let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr; //~ ERROR cannot move out of `arr[..]`
+    _x1 = U; //~ ERROR cannot assign twice to immutable variable `_x1`
+    drop(hold_all);
+    let [_x0, ..] = arr; //~ ERROR cannot move out of `arr[..]`
+    drop(_x0_hold);
+    let [_, _, ref mut _x2, _x3, mut _x4] = arr;
+    //~^ ERROR cannot borrow `arr[..]` as mutable
+    //~| ERROR cannot move out of `arr[..]` because it is borrowed
+    //~| ERROR cannot move out of `arr[..]` because it is borrowed
+    drop(xs_hold);
+}
+
+fn tuple() {
+    let mut tup = (U, U, U, U);
+    let (ref _x0, _x1, ref _x2, ..) = tup;
+    _x1 = U; //~ ERROR cannot assign twice to immutable variable
+    let _x0_hold = &mut tup.0; //~ ERROR cannot borrow `tup.0` as mutable because it is also
+    let (ref mut _x0_hold, ..) = tup; //~ ERROR cannot borrow `tup.0` as mutable because it is also
+    *_x0 = U; //~ ERROR cannot assign to `*_x0`, which is behind a `&` reference
+    *_x2 = U; //~ ERROR cannot assign to `*_x2`, which is behind a `&` reference
+    drop(tup.1); //~ ERROR use of moved value: `tup.1`
+    let _x1_hold = &tup.1; //~ ERROR borrow of moved value: `tup.1`
+    let (.., ref mut _x3) = tup;
+    let _x3_hold = &tup.3; //~ ERROR cannot borrow `tup.3` as immutable
+    let _x3_hold = &mut tup.3; //~ ERROR cannot borrow `tup.3` as mutable more
+    let (.., ref mut _x4_hold) = tup; //~ ERROR cannot borrow `tup.3` as mutable more
+    let (.., ref _x4_hold) = tup; //~ ERROR cannot borrow `tup.3` as immutable
+    drop(_x3);
+}
+
+fn closure() {
+    let mut tup = (U, U, U);
+    let c1 = || {
+        let (ref _x0, _x1, _) = tup;
+    };
+    let c2 = || {
+        //~^ ERROR use of moved value
+        let (ref mut _x0, _, _x2) = tup;
+    };
+    drop(c1);
+}
diff --git a/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr
new file mode 100644
index 00000000000..1b93267b397
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/borrowck-move-ref-pattern.stderr
@@ -0,0 +1,216 @@
+error[E0505]: cannot move out of `arr[..]` because it is borrowed
+  --> $DIR/borrowck-move-ref-pattern.rs:8:24
+   |
+LL |     let hold_all = &arr;
+   |                    ---- borrow of `arr` occurs here
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |                        ^^^ move out of `arr[..]` occurs here
+LL |     _x1 = U;
+LL |     drop(hold_all);
+   |          -------- borrow later used here
+
+error[E0384]: cannot assign twice to immutable variable `_x1`
+  --> $DIR/borrowck-move-ref-pattern.rs:9:5
+   |
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |                        ---
+   |                        |
+   |                        first assignment to `_x1`
+   |                        help: consider making this binding mutable: `mut _x1`
+LL |     _x1 = U;
+   |     ^^^^^^^ cannot assign twice to immutable variable
+
+error[E0505]: cannot move out of `arr[..]` because it is borrowed
+  --> $DIR/borrowck-move-ref-pattern.rs:11:10
+   |
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |          ------------ borrow of `arr[..]` occurs here
+...
+LL |     let [_x0, ..] = arr;
+   |          ^^^ move out of `arr[..]` occurs here
+LL |     drop(_x0_hold);
+   |          -------- borrow later used here
+
+error[E0502]: cannot borrow `arr[..]` as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-move-ref-pattern.rs:13:16
+   |
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |                             ----------- immutable borrow occurs here
+...
+LL |     let [_, _, ref mut _x2, _x3, mut _x4] = arr;
+   |                ^^^^^^^^^^^ mutable borrow occurs here
+...
+LL |     drop(xs_hold);
+   |          ------- immutable borrow later used here
+
+error[E0505]: cannot move out of `arr[..]` because it is borrowed
+  --> $DIR/borrowck-move-ref-pattern.rs:13:29
+   |
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |                             ----------- borrow of `arr[..]` occurs here
+...
+LL |     let [_, _, ref mut _x2, _x3, mut _x4] = arr;
+   |                             ^^^ move out of `arr[..]` occurs here
+...
+LL |     drop(xs_hold);
+   |          ------- borrow later used here
+
+error[E0505]: cannot move out of `arr[..]` because it is borrowed
+  --> $DIR/borrowck-move-ref-pattern.rs:13:34
+   |
+LL |     let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
+   |                             ----------- borrow of `arr[..]` occurs here
+...
+LL |     let [_, _, ref mut _x2, _x3, mut _x4] = arr;
+   |                                  ^^^^^^^ move out of `arr[..]` occurs here
+...
+LL |     drop(xs_hold);
+   |          ------- borrow later used here
+
+error[E0384]: cannot assign twice to immutable variable `_x1`
+  --> $DIR/borrowck-move-ref-pattern.rs:23:5
+   |
+LL |     let (ref _x0, _x1, ref _x2, ..) = tup;
+   |                   ---
+   |                   |
+   |                   first assignment to `_x1`
+   |                   help: consider making this binding mutable: `mut _x1`
+LL |     _x1 = U;
+   |     ^^^^^^^ cannot assign twice to immutable variable
+
+error[E0502]: cannot borrow `tup.0` as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-move-ref-pattern.rs:24:20
+   |
+LL |     let (ref _x0, _x1, ref _x2, ..) = tup;
+   |          ------- immutable borrow occurs here
+LL |     _x1 = U;
+LL |     let _x0_hold = &mut tup.0;
+   |                    ^^^^^^^^^^ mutable borrow occurs here
+LL |     let (ref mut _x0_hold, ..) = tup;
+LL |     *_x0 = U;
+   |     -------- immutable borrow later used here
+
+error[E0502]: cannot borrow `tup.0` as mutable because it is also borrowed as immutable
+  --> $DIR/borrowck-move-ref-pattern.rs:25:10
+   |
+LL |     let (ref _x0, _x1, ref _x2, ..) = tup;
+   |          ------- immutable borrow occurs here
+...
+LL |     let (ref mut _x0_hold, ..) = tup;
+   |          ^^^^^^^^^^^^^^^^ mutable borrow occurs here
+LL |     *_x0 = U;
+   |     -------- immutable borrow later used here
+
+error[E0594]: cannot assign to `*_x0`, which is behind a `&` reference
+  --> $DIR/borrowck-move-ref-pattern.rs:26:5
+   |
+LL |     *_x0 = U;
+   |     ^^^^^^^^ `_x0` is a `&` reference, so the data it refers to cannot be written
+   |
+help: consider changing this to be a mutable reference
+   |
+LL |     let (ref mut _x0, _x1, ref _x2, ..) = tup;
+   |          ~~~~~~~~~~~
+
+error[E0594]: cannot assign to `*_x2`, which is behind a `&` reference
+  --> $DIR/borrowck-move-ref-pattern.rs:27:5
+   |
+LL |     *_x2 = U;
+   |     ^^^^^^^^ `_x2` is a `&` reference, so the data it refers to cannot be written
+   |
+help: consider changing this to be a mutable reference
+   |
+LL |     let (ref _x0, _x1, ref mut _x2, ..) = tup;
+   |                        ~~~~~~~~~~~
+
+error[E0382]: use of moved value: `tup.1`
+  --> $DIR/borrowck-move-ref-pattern.rs:28:10
+   |
+LL |     let (ref _x0, _x1, ref _x2, ..) = tup;
+   |                   --- value moved here
+...
+LL |     drop(tup.1);
+   |          ^^^^^ value used here after move
+   |
+   = note: move occurs because `tup.1` has type `U`, which does not implement the `Copy` trait
+help: borrow this binding in the pattern to avoid moving the value
+   |
+LL |     let (ref _x0, ref _x1, ref _x2, ..) = tup;
+   |                   +++
+
+error[E0382]: borrow of moved value: `tup.1`
+  --> $DIR/borrowck-move-ref-pattern.rs:29:20
+   |
+LL |     drop(tup.1);
+   |          ----- value moved here
+LL |     let _x1_hold = &tup.1;
+   |                    ^^^^^^ value borrowed here after move
+   |
+   = note: move occurs because `tup.1` has type `U`, which does not implement the `Copy` trait
+
+error[E0502]: cannot borrow `tup.3` as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-move-ref-pattern.rs:31:20
+   |
+LL |     let (.., ref mut _x3) = tup;
+   |              ----------- mutable borrow occurs here
+LL |     let _x3_hold = &tup.3;
+   |                    ^^^^^^ immutable borrow occurs here
+...
+LL |     drop(_x3);
+   |          --- mutable borrow later used here
+
+error[E0499]: cannot borrow `tup.3` as mutable more than once at a time
+  --> $DIR/borrowck-move-ref-pattern.rs:32:20
+   |
+LL |     let (.., ref mut _x3) = tup;
+   |              ----------- first mutable borrow occurs here
+LL |     let _x3_hold = &tup.3;
+LL |     let _x3_hold = &mut tup.3;
+   |                    ^^^^^^^^^^ second mutable borrow occurs here
+...
+LL |     drop(_x3);
+   |          --- first borrow later used here
+
+error[E0499]: cannot borrow `tup.3` as mutable more than once at a time
+  --> $DIR/borrowck-move-ref-pattern.rs:33:14
+   |
+LL |     let (.., ref mut _x3) = tup;
+   |              ----------- first mutable borrow occurs here
+...
+LL |     let (.., ref mut _x4_hold) = tup;
+   |              ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
+LL |     let (.., ref _x4_hold) = tup;
+LL |     drop(_x3);
+   |          --- first borrow later used here
+
+error[E0502]: cannot borrow `tup.3` as immutable because it is also borrowed as mutable
+  --> $DIR/borrowck-move-ref-pattern.rs:34:14
+   |
+LL |     let (.., ref mut _x3) = tup;
+   |              ----------- mutable borrow occurs here
+...
+LL |     let (.., ref _x4_hold) = tup;
+   |              ^^^^^^^^^^^^ immutable borrow occurs here
+LL |     drop(_x3);
+   |          --- mutable borrow later used here
+
+error[E0382]: use of moved value: `tup`
+  --> $DIR/borrowck-move-ref-pattern.rs:43:14
+   |
+LL |     let mut tup = (U, U, U);
+   |         ------- move occurs because `tup` has type `(U, U, U)`, which does not implement the `Copy` trait
+LL |     let c1 = || {
+   |              -- value moved into closure here
+LL |         let (ref _x0, _x1, _) = tup;
+   |                                 --- variable moved due to use in closure
+LL |     };
+LL |     let c2 = || {
+   |              ^^ value used here after move
+LL |
+LL |         let (ref mut _x0, _, _x2) = tup;
+   |                                     --- use occurs due to use in closure
+
+error: aborting due to 18 previous errors
+
+Some errors have detailed explanations: E0382, E0384, E0499, E0502, E0505, E0594.
+For more information about an error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/move-ref-patterns/by-move-sub-pat-unreachable.rs b/tests/ui/pattern/move-ref-patterns/by-move-sub-pat-unreachable.rs
new file mode 100644
index 00000000000..ff7b625a68e
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/by-move-sub-pat-unreachable.rs
@@ -0,0 +1,12 @@
+// When conflicts between by-move bindings in `by_move_1 @ has_by_move` patterns
+// happen and that code is unreachable according to borrowck, we accept this code.
+// In particular, we want to ensure here that an ICE does not happen, which it did originally.
+
+// check-pass
+
+fn main() {
+    return;
+
+    struct S;
+    let a @ (b, c) = (S, S);
+}
diff --git a/tests/ui/pattern/move-ref-patterns/issue-53840.rs b/tests/ui/pattern/move-ref-patterns/issue-53840.rs
new file mode 100644
index 00000000000..80effc497ed
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/issue-53840.rs
@@ -0,0 +1,20 @@
+// check-pass
+
+enum E {
+    Foo(String, String, String),
+}
+
+struct Bar {
+    a: String,
+    b: String,
+}
+
+fn main() {
+    let bar = Bar { a: "1".to_string(), b: "2".to_string() };
+    match E::Foo("".into(), "".into(), "".into()) {
+        E::Foo(a, b, ref c) => {}
+    }
+    match bar {
+        Bar { a, ref b } => {}
+    }
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.rs
new file mode 100644
index 00000000000..ebb1683af7d
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.rs
@@ -0,0 +1,120 @@
+fn main() {
+    struct S; // Not `Copy`.
+
+    let mut tup0 = (S, S);
+    let mut tup1 = (S, S, S);
+    let tup2 = (S, S);
+    let tup3 = (S, S, S);
+    let tup4 = (S, S);
+    let mut arr0 = [S, S, S];
+    let mut arr1 = [S, S, S, S, S];
+    let arr2 = [S, S, S];
+    let arr3 = [S, S, S, S, S];
+
+    // The `mov` bindings require that we capture the scrutinees by-move.
+    let mut closure = || {
+        // Tuples...
+        let (ref mut borrow, mov) = tup0;
+        let (mov, _, ref mut borrow) = tup1;
+        let (ref borrow, mov) = tup2;
+        let (mov, _, ref borrow) = tup3;
+        let (ref borrow, mov) = tup4;
+        // Arrays...
+        let [mov @ .., ref borrow] = arr0;
+        let [_, ref mut borrow @ .., _, mov] = arr1;
+        let [mov @ .., ref borrow] = arr2;
+        let [_, ref borrow @ .., _, mov] = arr3;
+    };
+
+    // Now we try to borrow and move the captures, which should result in errors...
+    // ...for tuples:
+    drop(&tup0); //~ ERROR borrow of moved value: `tup0`
+    drop(&tup1); //~ ERROR borrow of moved value: `tup1`
+    drop(&tup2); //~ ERROR borrow of moved value: `tup2`
+    drop(&tup3); //~ ERROR borrow of moved value: `tup3`
+    // Ostensibly this should compile.
+    // However, because closures don't capture individual fields, which is changed in RFC 2229,
+    // this won't compile because the entire product is moved into the closure.
+    // The same applies to the array patterns below.
+    drop(&tup4.0); //~ ERROR borrow of moved value: `tup4`
+    // ...for arrays:
+    drop(&arr0); //~ ERROR borrow of moved value: `arr0`
+    let [_, mov1, mov2, mov3, _] = &arr1; //~ ERROR borrow of moved value: `arr1`
+    drop(&arr2); //~ ERROR borrow of moved value: `arr2`
+    let [_, mov1, mov2, mov3, _] = &arr3; //~ ERROR borrow of moved value: `arr3`
+
+    // Let's redo ^--- with a `match` + sum type:
+    macro_rules! m {
+        ($p:pat = $s:expr) => {
+            match $s {
+                Some($p) => {}
+                _ => {}
+            }
+        };
+    }
+    let mut tup0: Option<(S, S)> = None;
+    let mut tup1: Option<(S, S, S)> = None;
+    let tup2: Option<(S, S)> = None;
+    let tup3: Option<(S, S, S)> = None;
+    let tup4: Option<(S, S)> = None;
+    let mut arr0: Option<[S; 3]> = None;
+    let mut arr1: Option<[S; 5]> = None;
+    let arr2: Option<[S; 3]> = None;
+    let arr3: Option<[S; 5]> = None;
+    let mut closure = || {
+        m!((ref mut borrow, mov) = tup0);
+        m!((mov, _, ref mut borrow) = tup1);
+        m!((ref borrow, mov) = tup2);
+        m!((mov, _, ref borrow) = tup3);
+        m!((ref borrow, mov) = tup4);
+        m!([mov @ .., ref borrow] = arr0);
+        m!([_, ref mut borrow @ .., _, mov] = arr1);
+        m!([mov @ .., ref borrow] = arr2);
+        m!([_, ref borrow @ .., _, mov] = arr3);
+    };
+    drop(&tup0); //~ ERROR borrow of moved value: `tup0`
+    drop(&tup1); //~ ERROR borrow of moved value: `tup1`
+    drop(&tup2); //~ ERROR borrow of moved value: `tup2`
+    drop(&tup3); //~ ERROR borrow of moved value: `tup3`
+    m!((ref x, _) = &tup4); //~ ERROR borrow of moved value: `tup4`
+    drop(&arr0); //~ ERROR borrow of moved value: `arr0`
+    m!([_, mov1, mov2, mov3, _] = &arr1); //~ ERROR borrow of moved value: `arr1`
+    drop(&arr2); //~ ERROR borrow of moved value: `arr2`
+    m!([_, mov1, mov2, mov3, _] = &arr3); //~ ERROR borrow of moved value: `arr3`
+
+    // Let's redo ^--- with `if let` (which may diverge from `match` in the future):
+    macro_rules! m {
+        ($p:pat = $s:expr) => {
+            if let Some($p) = $s {}
+        };
+    }
+    let mut tup0: Option<(S, S)> = None;
+    let mut tup1: Option<(S, S, S)> = None;
+    let tup2: Option<(S, S)> = None;
+    let tup3: Option<(S, S, S)> = None;
+    let tup4: Option<(S, S)> = None;
+    let mut arr0: Option<[S; 3]> = None;
+    let mut arr1: Option<[S; 5]> = None;
+    let arr2: Option<[S; 3]> = None;
+    let arr3: Option<[S; 5]> = None;
+    let mut closure = || {
+        m!((ref mut borrow, mov) = tup0);
+        m!((mov, _, ref mut borrow) = tup1);
+        m!((ref borrow, mov) = tup2);
+        m!((mov, _, ref borrow) = tup3);
+        m!((ref borrow, mov) = tup4);
+        m!([mov @ .., ref borrow] = arr0);
+        m!([_, ref mut borrow @ .., _, mov] = arr1);
+        m!([mov @ .., ref borrow] = arr2);
+        m!([_, ref borrow @ .., _, mov] = arr3);
+    };
+    drop(&tup0); //~ ERROR borrow of moved value: `tup0`
+    drop(&tup1); //~ ERROR borrow of moved value: `tup1`
+    drop(&tup2); //~ ERROR borrow of moved value: `tup2`
+    drop(&tup3); //~ ERROR borrow of moved value: `tup3`
+    m!((ref x, _) = &tup4); //~ ERROR borrow of moved value: `tup4`
+    drop(&arr0); //~ ERROR borrow of moved value: `arr0`
+    m!([_, mov1, mov2, mov3, _] = &arr1); //~ ERROR borrow of moved value: `arr1`
+    drop(&arr2); //~ ERROR borrow of moved value: `arr2`
+    m!([_, mov1, mov2, mov3, _] = &arr3); //~ ERROR borrow of moved value: `arr3`
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr
new file mode 100644
index 00000000000..f19fed08917
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-inside.stderr
@@ -0,0 +1,404 @@
+error[E0382]: borrow of moved value: `tup0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:31:10
+   |
+LL |     let mut tup0 = (S, S);
+   |         -------- move occurs because `tup0` has type `(S, S)`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+LL |         // Tuples...
+LL |         let (ref mut borrow, mov) = tup0;
+   |                                     ---- variable moved due to use in closure
+...
+LL |     drop(&tup0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:32:10
+   |
+LL |     let mut tup1 = (S, S, S);
+   |         -------- move occurs because `tup1` has type `(S, S, S)`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let (mov, _, ref mut borrow) = tup1;
+   |                                        ---- variable moved due to use in closure
+...
+LL |     drop(&tup1);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:33:10
+   |
+LL |     let tup2 = (S, S);
+   |         ---- move occurs because `tup2` has type `(S, S)`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let (ref borrow, mov) = tup2;
+   |                                 ---- variable moved due to use in closure
+...
+LL |     drop(&tup2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:34:10
+   |
+LL |     let tup3 = (S, S, S);
+   |         ---- move occurs because `tup3` has type `(S, S, S)`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let (mov, _, ref borrow) = tup3;
+   |                                    ---- variable moved due to use in closure
+...
+LL |     drop(&tup3);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup4`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:39:10
+   |
+LL |     let tup4 = (S, S);
+   |         ---- move occurs because `tup4` has type `(S, S)`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let (ref borrow, mov) = tup4;
+   |                                 ---- variable moved due to use in closure
+...
+LL |     drop(&tup4.0);
+   |          ^^^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:41:10
+   |
+LL |     let mut arr0 = [S, S, S];
+   |         -------- move occurs because `arr0` has type `[S; 3]`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let [mov @ .., ref borrow] = arr0;
+   |                                      ---- variable moved due to use in closure
+...
+LL |     drop(&arr0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:42:36
+   |
+LL |     let mut arr1 = [S, S, S, S, S];
+   |         -------- move occurs because `arr1` has type `[S; 5]`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let [_, ref mut borrow @ .., _, mov] = arr1;
+   |                                                ---- variable moved due to use in closure
+...
+LL |     let [_, mov1, mov2, mov3, _] = &arr1;
+   |                                    ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:43:10
+   |
+LL |     let arr2 = [S, S, S];
+   |         ---- move occurs because `arr2` has type `[S; 3]`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let [mov @ .., ref borrow] = arr2;
+   |                                      ---- variable moved due to use in closure
+...
+LL |     drop(&arr2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:44:36
+   |
+LL |     let arr3 = [S, S, S, S, S];
+   |         ---- move occurs because `arr3` has type `[S; 5]`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         let [_, ref borrow @ .., _, mov] = arr3;
+   |                                            ---- variable moved due to use in closure
+...
+LL |     let [_, mov1, mov2, mov3, _] = &arr3;
+   |                                    ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:75:10
+   |
+LL |     let mut tup0: Option<(S, S)> = None;
+   |         -------- move occurs because `tup0` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+LL |         m!((ref mut borrow, mov) = tup0);
+   |                                    ---- variable moved due to use in closure
+...
+LL |     drop(&tup0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:76:10
+   |
+LL |     let mut tup1: Option<(S, S, S)> = None;
+   |         -------- move occurs because `tup1` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+LL |         m!((ref mut borrow, mov) = tup0);
+LL |         m!((mov, _, ref mut borrow) = tup1);
+   |                                       ---- variable moved due to use in closure
+...
+LL |     drop(&tup1);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:77:10
+   |
+LL |     let tup2: Option<(S, S)> = None;
+   |         ---- move occurs because `tup2` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((ref borrow, mov) = tup2);
+   |                                ---- variable moved due to use in closure
+...
+LL |     drop(&tup2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:78:10
+   |
+LL |     let tup3: Option<(S, S, S)> = None;
+   |         ---- move occurs because `tup3` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((mov, _, ref borrow) = tup3);
+   |                                   ---- variable moved due to use in closure
+...
+LL |     drop(&tup3);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup4`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:79:21
+   |
+LL |     let tup4: Option<(S, S)> = None;
+   |         ---- move occurs because `tup4` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((ref borrow, mov) = tup4);
+   |                                ---- variable moved due to use in closure
+...
+LL |     m!((ref x, _) = &tup4);
+   |                     ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:80:10
+   |
+LL |     let mut arr0: Option<[S; 3]> = None;
+   |         -------- move occurs because `arr0` has type `Option<[S; 3]>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([mov @ .., ref borrow] = arr0);
+   |                                     ---- variable moved due to use in closure
+...
+LL |     drop(&arr0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:81:35
+   |
+LL |     let mut arr1: Option<[S; 5]> = None;
+   |         -------- move occurs because `arr1` has type `Option<[S; 5]>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([_, ref mut borrow @ .., _, mov] = arr1);
+   |                                               ---- variable moved due to use in closure
+...
+LL |     m!([_, mov1, mov2, mov3, _] = &arr1);
+   |                                   ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:82:10
+   |
+LL |     let arr2: Option<[S; 3]> = None;
+   |         ---- move occurs because `arr2` has type `Option<[S; 3]>`, which does not implement the `Copy` trait
+LL |     let arr3: Option<[S; 5]> = None;
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([mov @ .., ref borrow] = arr2);
+   |                                     ---- variable moved due to use in closure
+...
+LL |     drop(&arr2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:83:35
+   |
+LL |     let arr3: Option<[S; 5]> = None;
+   |         ---- move occurs because `arr3` has type `Option<[S; 5]>`, which does not implement the `Copy` trait
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([_, ref borrow @ .., _, mov] = arr3);
+   |                                           ---- variable moved due to use in closure
+...
+LL |     m!([_, mov1, mov2, mov3, _] = &arr3);
+   |                                   ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:111:10
+   |
+LL |     let mut tup0: Option<(S, S)> = None;
+   |         -------- move occurs because `tup0` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+LL |         m!((ref mut borrow, mov) = tup0);
+   |                                    ---- variable moved due to use in closure
+...
+LL |     drop(&tup0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:112:10
+   |
+LL |     let mut tup1: Option<(S, S, S)> = None;
+   |         -------- move occurs because `tup1` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+LL |         m!((ref mut borrow, mov) = tup0);
+LL |         m!((mov, _, ref mut borrow) = tup1);
+   |                                       ---- variable moved due to use in closure
+...
+LL |     drop(&tup1);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:113:10
+   |
+LL |     let tup2: Option<(S, S)> = None;
+   |         ---- move occurs because `tup2` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((ref borrow, mov) = tup2);
+   |                                ---- variable moved due to use in closure
+...
+LL |     drop(&tup2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:114:10
+   |
+LL |     let tup3: Option<(S, S, S)> = None;
+   |         ---- move occurs because `tup3` has type `Option<(S, S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((mov, _, ref borrow) = tup3);
+   |                                   ---- variable moved due to use in closure
+...
+LL |     drop(&tup3);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `tup4`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:115:21
+   |
+LL |     let tup4: Option<(S, S)> = None;
+   |         ---- move occurs because `tup4` has type `Option<(S, S)>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!((ref borrow, mov) = tup4);
+   |                                ---- variable moved due to use in closure
+...
+LL |     m!((ref x, _) = &tup4);
+   |                     ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr0`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:116:10
+   |
+LL |     let mut arr0: Option<[S; 3]> = None;
+   |         -------- move occurs because `arr0` has type `Option<[S; 3]>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([mov @ .., ref borrow] = arr0);
+   |                                     ---- variable moved due to use in closure
+...
+LL |     drop(&arr0);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr1`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:117:35
+   |
+LL |     let mut arr1: Option<[S; 5]> = None;
+   |         -------- move occurs because `arr1` has type `Option<[S; 5]>`, which does not implement the `Copy` trait
+...
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([_, ref mut borrow @ .., _, mov] = arr1);
+   |                                               ---- variable moved due to use in closure
+...
+LL |     m!([_, mov1, mov2, mov3, _] = &arr1);
+   |                                   ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr2`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:118:10
+   |
+LL |     let arr2: Option<[S; 3]> = None;
+   |         ---- move occurs because `arr2` has type `Option<[S; 3]>`, which does not implement the `Copy` trait
+LL |     let arr3: Option<[S; 5]> = None;
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([mov @ .., ref borrow] = arr2);
+   |                                     ---- variable moved due to use in closure
+...
+LL |     drop(&arr2);
+   |          ^^^^^ value borrowed here after move
+
+error[E0382]: borrow of moved value: `arr3`
+  --> $DIR/move-ref-patterns-closure-captures-inside.rs:119:35
+   |
+LL |     let arr3: Option<[S; 5]> = None;
+   |         ---- move occurs because `arr3` has type `Option<[S; 5]>`, which does not implement the `Copy` trait
+LL |     let mut closure = || {
+   |                       -- value moved into closure here
+...
+LL |         m!([_, ref borrow @ .., _, mov] = arr3);
+   |                                           ---- variable moved due to use in closure
+...
+LL |     m!([_, mov1, mov2, mov3, _] = &arr3);
+   |                                   ^^^^^ value borrowed here after move
+
+error: aborting due to 27 previous errors
+
+For more information about this error, try `rustc --explain E0382`.
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs
new file mode 100644
index 00000000000..583f70f41aa
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures-pass.rs
@@ -0,0 +1,28 @@
+// check-pass
+
+fn main() {
+    struct U;
+    fn accept_fn_once(_: impl FnOnce()) {}
+    fn accept_fn_mut(_: impl FnMut()) {}
+    fn accept_fn(_: impl Fn()) {}
+
+    let mut tup = (U, U, U);
+    let (ref _x0, _x1, ref mut _x2) = tup;
+    let c1 = || {
+        drop::<&U>(_x0);
+        drop::<U>(_x1);
+        drop::<&mut U>(_x2);
+    };
+    accept_fn_once(c1);
+
+    let c2 = || {
+        drop::<&U>(_x0);
+        drop::<&mut U>(_x2);
+    };
+    accept_fn_mut(c2);
+
+    let c3 = || {
+        drop::<&U>(_x0);
+    };
+    accept_fn(c3);
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.rs
new file mode 100644
index 00000000000..cd619cc41eb
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.rs
@@ -0,0 +1,32 @@
+fn main() {
+    struct U;
+    fn accept_fn_once(_: &impl FnOnce()) {}
+    fn accept_fn_mut(_: &impl FnMut()) {}
+    fn accept_fn(_: &impl Fn()) {}
+
+    let mut tup = (U, U, U);
+    let (ref _x0, _x1, ref mut _x2) = tup;
+    let c1 = || {
+        //~^ ERROR expected a closure that implements the `FnMut`
+        //~| ERROR expected a closure that implements the `Fn`
+        drop::<&U>(_x0);
+        drop::<U>(_x1);
+        drop::<&mut U>(_x2);
+    };
+    accept_fn_once(&c1);
+    accept_fn_mut(&c1);
+    accept_fn(&c1);
+
+    let c2 = || {
+        //~^ ERROR expected a closure that implements the `Fn`
+        drop::<&U>(_x0);
+        drop::<&mut U>(_x2);
+    };
+    accept_fn_mut(&c2);
+    accept_fn(&c2);
+
+    let c3 = || {
+        drop::<&U>(_x0);
+    };
+    accept_fn(&c3);
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.stderr
new file mode 100644
index 00000000000..eba65a61803
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-closure-captures.stderr
@@ -0,0 +1,63 @@
+error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
+  --> $DIR/move-ref-patterns-closure-captures.rs:9:14
+   |
+LL |     let c1 = || {
+   |              ^^ this closure implements `FnOnce`, not `FnMut`
+...
+LL |         drop::<U>(_x1);
+   |                   --- closure is `FnOnce` because it moves the variable `_x1` out of its environment
+...
+LL |     accept_fn_mut(&c1);
+   |     ------------- --- the requirement to implement `FnMut` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `accept_fn_mut`
+  --> $DIR/move-ref-patterns-closure-captures.rs:4:31
+   |
+LL |     fn accept_fn_mut(_: &impl FnMut()) {}
+   |                               ^^^^^^^ required by this bound in `accept_fn_mut`
+
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
+  --> $DIR/move-ref-patterns-closure-captures.rs:9:14
+   |
+LL |     let c1 = || {
+   |              ^^ this closure implements `FnOnce`, not `Fn`
+...
+LL |         drop::<U>(_x1);
+   |                   --- closure is `FnOnce` because it moves the variable `_x1` out of its environment
+...
+LL |     accept_fn(&c1);
+   |     --------- --- the requirement to implement `Fn` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `accept_fn`
+  --> $DIR/move-ref-patterns-closure-captures.rs:5:27
+   |
+LL |     fn accept_fn(_: &impl Fn()) {}
+   |                           ^^^^ required by this bound in `accept_fn`
+
+error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
+  --> $DIR/move-ref-patterns-closure-captures.rs:20:14
+   |
+LL |     let c2 = || {
+   |              ^^ this closure implements `FnMut`, not `Fn`
+...
+LL |         drop::<&mut U>(_x2);
+   |                        --- closure is `FnMut` because it mutates the variable `_x2` here
+...
+LL |     accept_fn(&c2);
+   |     --------- --- the requirement to implement `Fn` derives from here
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `accept_fn`
+  --> $DIR/move-ref-patterns-closure-captures.rs:5:27
+   |
+LL |     fn accept_fn(_: &impl Fn()) {}
+   |                           ^^^^ required by this bound in `accept_fn`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0525`.
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed
new file mode 100644
index 00000000000..5f04fc83d37
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.fixed
@@ -0,0 +1,12 @@
+// run-rustfix
+#![allow(unused_variables)]
+fn main() {
+    struct U;
+
+    // A tuple is a "non-reference pattern".
+    // A `mut` binding pattern resets the binding mode to by-value.
+
+    let mut p = (U, U);
+    let (a, ref mut b) = &mut p;
+    //~^ ERROR cannot move out of a mutable reference
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs
new file mode 100644
index 00000000000..5dc1ae2feb5
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.rs
@@ -0,0 +1,12 @@
+// run-rustfix
+#![allow(unused_variables)]
+fn main() {
+    struct U;
+
+    // A tuple is a "non-reference pattern".
+    // A `mut` binding pattern resets the binding mode to by-value.
+
+    let mut p = (U, U);
+    let (a, mut b) = &mut p;
+    //~^ ERROR cannot move out of a mutable reference
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr
new file mode 100644
index 00000000000..d3ab533e35e
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes-fixable.stderr
@@ -0,0 +1,17 @@
+error[E0507]: cannot move out of a mutable reference
+  --> $DIR/move-ref-patterns-default-binding-modes-fixable.rs:10:22
+   |
+LL |     let (a, mut b) = &mut p;
+   |             -----    ^^^^^^
+   |             |
+   |             data moved here
+   |             move occurs because `b` has type `U`, which does not implement the `Copy` trait
+   |
+help: consider borrowing the pattern binding
+   |
+LL |     let (a, ref mut b) = &mut p;
+   |             +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs
new file mode 100644
index 00000000000..6c913c24513
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.rs
@@ -0,0 +1,10 @@
+fn main() {
+    struct U;
+
+    // A tuple is a "non-reference pattern".
+    // A `mut` binding pattern resets the binding mode to by-value.
+
+    let p = (U, U);
+    let (a, mut b) = &p;
+    //~^ ERROR cannot move out of a shared reference
+}
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr
new file mode 100644
index 00000000000..65030b62250
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-default-binding-modes.stderr
@@ -0,0 +1,17 @@
+error[E0507]: cannot move out of a shared reference
+  --> $DIR/move-ref-patterns-default-binding-modes.rs:8:22
+   |
+LL |     let (a, mut b) = &p;
+   |             -----    ^^
+   |             |
+   |             data moved here
+   |             move occurs because `b` has type `U`, which does not implement the `Copy` trait
+   |
+help: consider borrowing the pattern binding
+   |
+LL |     let (a, ref mut b) = &p;
+   |             +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0507`.
diff --git a/tests/ui/pattern/move-ref-patterns/move-ref-patterns-dynamic-semantics.rs b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-dynamic-semantics.rs
new file mode 100644
index 00000000000..1d6d9acead1
--- /dev/null
+++ b/tests/ui/pattern/move-ref-patterns/move-ref-patterns-dynamic-semantics.rs
@@ -0,0 +1,79 @@
+// run-pass
+
+// This test checks the dynamic semantics and drop order of pattern matching
+// where a product pattern has both a by-move and by-ref binding.
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+struct X {
+    x: Box<usize>,
+    d: DropOrderListPtr,
+}
+
+type DropOrderListPtr = Rc<RefCell<Vec<usize>>>;
+
+impl Drop for X {
+    fn drop(&mut self) {
+        self.d.borrow_mut().push(*self.x);
+    }
+}
+
+enum DoubleOption<T, U> {
+    Some2(T, U),
+    _None2,
+}
+
+fn main() {
+    let d: DropOrderListPtr = <_>::default();
+    {
+        let mk = |v| X { x: Box::new(v), d: d.clone() };
+        let check = |a1: &X, a2, b1: &X, b2| {
+            assert_eq!(*a1.x, a2);
+            assert_eq!(*b1.x, b2);
+        };
+
+        let x = DoubleOption::Some2(mk(1), mk(2));
+        match x {
+            DoubleOption::Some2(ref a, b) => check(a, 1, &b, 2),
+            DoubleOption::_None2 => panic!(),
+        }
+        let x = DoubleOption::Some2(mk(3), mk(4));
+        match x {
+            DoubleOption::Some2(a, ref b) => check(&a, 3, b, 4),
+            DoubleOption::_None2 => panic!(),
+        }
+        match DoubleOption::Some2(mk(5), mk(6)) {
+            DoubleOption::Some2(ref a, b) => check(a, 5, &b, 6),
+            DoubleOption::_None2 => panic!(),
+        }
+        match DoubleOption::Some2(mk(7), mk(8)) {
+            DoubleOption::Some2(a, ref b) => check(&a, 7, b, 8),
+            DoubleOption::_None2 => panic!(),
+        }
+        {
+            let (a, ref b) = (mk(9), mk(10));
+            let (ref c, d) = (mk(11), mk(12));
+            check(&a, 9, b, 10);
+            check(c, 11, &d, 12);
+        }
+        fn fun([a, ref mut b, ref xs @ .., ref c, d]: [X; 6]) {
+            assert_eq!(*a.x, 13);
+            assert_eq!(*b.x, 14);
+            assert_eq!(&[*xs[0].x, *xs[1].x], &[15, 16]);
+            assert_eq!(*c.x, 17);
+            assert_eq!(*d.x, 18);
+        }
+        fun([mk(13), mk(14), mk(15), mk(16), mk(17), mk(18)]);
+
+        let lam = |(a, ref b, c, ref mut d): (X, X, X, X)| {
+            assert_eq!(*a.x, 19);
+            assert_eq!(*b.x, 20);
+            assert_eq!(*c.x, 21);
+            assert_eq!(*d.x, 22);
+        };
+        lam((mk(19), mk(20), mk(21), mk(22)));
+    }
+    let expected = [2, 3, 6, 5, 7, 8, 12, 11, 9, 10, 18, 13, 14, 15, 16, 17, 21, 19, 20, 22, 4, 1];
+    assert_eq!(&*d.borrow(), &expected);
+}
diff --git a/tests/ui/pattern/non-constant-in-const-path.rs b/tests/ui/pattern/non-constant-in-const-path.rs
new file mode 100644
index 00000000000..3918485bacb
--- /dev/null
+++ b/tests/ui/pattern/non-constant-in-const-path.rs
@@ -0,0 +1,18 @@
+// Checks if we emit `PatternError`s correctly.
+// This is also a regression test for #27895 and #68394.
+
+static FOO: u8 = 10;
+
+fn main() {
+    let x = 0;
+    let 0u8..=x = 0;
+    //~^ ERROR: runtime values cannot be referenced in patterns
+    let 0u8..=FOO = 0;
+    //~^ ERROR: statics cannot be referenced in patterns
+    match 1 {
+        0 ..= x => {}
+        //~^ ERROR: runtime values cannot be referenced in patterns
+        0 ..= FOO => {}
+        //~^ ERROR: statics cannot be referenced in patterns
+    };
+}
diff --git a/tests/ui/pattern/non-constant-in-const-path.stderr b/tests/ui/pattern/non-constant-in-const-path.stderr
new file mode 100644
index 00000000000..53c3974f780
--- /dev/null
+++ b/tests/ui/pattern/non-constant-in-const-path.stderr
@@ -0,0 +1,28 @@
+error[E0080]: runtime values cannot be referenced in patterns
+  --> $DIR/non-constant-in-const-path.rs:8:15
+   |
+LL |     let 0u8..=x = 0;
+   |               ^
+
+error[E0158]: statics cannot be referenced in patterns
+  --> $DIR/non-constant-in-const-path.rs:10:15
+   |
+LL |     let 0u8..=FOO = 0;
+   |               ^^^
+
+error[E0080]: runtime values cannot be referenced in patterns
+  --> $DIR/non-constant-in-const-path.rs:13:15
+   |
+LL |         0 ..= x => {}
+   |               ^
+
+error[E0158]: statics cannot be referenced in patterns
+  --> $DIR/non-constant-in-const-path.rs:15:15
+   |
+LL |         0 ..= FOO => {}
+   |               ^^^
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0080, E0158.
+For more information about an error, try `rustc --explain E0080`.
diff --git a/tests/ui/pattern/non-structural-match-types.rs b/tests/ui/pattern/non-structural-match-types.rs
new file mode 100644
index 00000000000..5c331547366
--- /dev/null
+++ b/tests/ui/pattern/non-structural-match-types.rs
@@ -0,0 +1,14 @@
+// edition:2021
+#![allow(incomplete_features)]
+#![allow(unreachable_code)]
+#![feature(const_async_blocks)]
+#![feature(inline_const_pat)]
+
+fn main() {
+    match loop {} {
+        const { || {} } => {}, //~ ERROR cannot be used in patterns
+    }
+    match loop {} {
+        const { async {} } => {}, //~ ERROR cannot be used in patterns
+    }
+}
diff --git a/tests/ui/pattern/non-structural-match-types.stderr b/tests/ui/pattern/non-structural-match-types.stderr
new file mode 100644
index 00000000000..dea7c4695cc
--- /dev/null
+++ b/tests/ui/pattern/non-structural-match-types.stderr
@@ -0,0 +1,14 @@
+error: `[closure@$DIR/non-structural-match-types.rs:9:17: 9:19]` cannot be used in patterns
+  --> $DIR/non-structural-match-types.rs:9:9
+   |
+LL |         const { || {} } => {},
+   |         ^^^^^^^^^^^^^^^
+
+error: `[async block@$DIR/non-structural-match-types.rs:12:17: 12:25]` cannot be used in patterns
+  --> $DIR/non-structural-match-types.rs:12:9
+   |
+LL |         const { async {} } => {},
+   |         ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/pat-shadow-in-nested-binding.rs b/tests/ui/pattern/pat-shadow-in-nested-binding.rs
new file mode 100644
index 00000000000..7badbb6b95c
--- /dev/null
+++ b/tests/ui/pattern/pat-shadow-in-nested-binding.rs
@@ -0,0 +1,6 @@
+#[allow(non_camel_case_types)]
+struct foo(usize);
+
+fn main() {
+    let (foo, _) = (2, 3); //~ ERROR let bindings cannot shadow tuple structs
+}
diff --git a/tests/ui/pattern/pat-shadow-in-nested-binding.stderr b/tests/ui/pattern/pat-shadow-in-nested-binding.stderr
new file mode 100644
index 00000000000..0c5824be95d
--- /dev/null
+++ b/tests/ui/pattern/pat-shadow-in-nested-binding.stderr
@@ -0,0 +1,12 @@
+error[E0530]: let bindings cannot shadow tuple structs
+  --> $DIR/pat-shadow-in-nested-binding.rs:5:10
+   |
+LL | struct foo(usize);
+   | ------------------ the tuple struct `foo` is defined here
+...
+LL |     let (foo, _) = (2, 3);
+   |          ^^^ cannot be named the same as a tuple struct
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0530`.
diff --git a/tests/ui/pattern/pat-struct-field-expr-has-type.rs b/tests/ui/pattern/pat-struct-field-expr-has-type.rs
new file mode 100644
index 00000000000..1d18214de7f
--- /dev/null
+++ b/tests/ui/pattern/pat-struct-field-expr-has-type.rs
@@ -0,0 +1,9 @@
+struct S {
+    f: u8,
+}
+
+fn main() {
+    match (S { f: 42 }) {
+        S { f: Ok(_) } => {} //~ ERROR mismatched types
+    }
+}
diff --git a/tests/ui/pattern/pat-struct-field-expr-has-type.stderr b/tests/ui/pattern/pat-struct-field-expr-has-type.stderr
new file mode 100644
index 00000000000..3a61d4293b0
--- /dev/null
+++ b/tests/ui/pattern/pat-struct-field-expr-has-type.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-struct-field-expr-has-type.rs:7:16
+   |
+LL |     match (S { f: 42 }) {
+   |           ------------- this expression has type `S`
+LL |         S { f: Ok(_) } => {}
+   |                ^^^^^ expected `u8`, found enum `Result`
+   |
+   = note: expected type `u8`
+              found enum `Result<_, _>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/pat-tuple-bad-type.rs b/tests/ui/pattern/pat-tuple-bad-type.rs
new file mode 100644
index 00000000000..98481167a59
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-bad-type.rs
@@ -0,0 +1,15 @@
+fn main() {
+    let x; //~ ERROR type annotations needed
+
+    match x {
+        (..) => {}
+        _ => {}
+    }
+
+    match 0u8 {
+        (..) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+
+    x = 10;
+}
diff --git a/tests/ui/pattern/pat-tuple-bad-type.stderr b/tests/ui/pattern/pat-tuple-bad-type.stderr
new file mode 100644
index 00000000000..da369d33397
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-bad-type.stderr
@@ -0,0 +1,26 @@
+error[E0282]: type annotations needed
+  --> $DIR/pat-tuple-bad-type.rs:2:9
+   |
+LL |     let x;
+   |         ^
+...
+LL |         (..) => {}
+   |         ---- type must be known at this point
+   |
+help: consider giving `x` an explicit type
+   |
+LL |     let x: /* Type */;
+   |          ++++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/pat-tuple-bad-type.rs:10:9
+   |
+LL |     match 0u8 {
+   |           --- this expression has type `u8`
+LL |         (..) => {}
+   |         ^^^^ expected `u8`, found `()`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0308.
+For more information about an error, try `rustc --explain E0282`.
diff --git a/tests/ui/pattern/pat-tuple-field-count-cross.rs b/tests/ui/pattern/pat-tuple-field-count-cross.rs
new file mode 100644
index 00000000000..b63da4e154f
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-field-count-cross.rs
@@ -0,0 +1,57 @@
+// aux-build:declarations-for-tuple-field-count-errors.rs
+
+extern crate declarations_for_tuple_field_count_errors;
+
+use declarations_for_tuple_field_count_errors::*;
+
+fn main() {
+    match Z0 {
+        Z0() => {} //~ ERROR expected tuple struct or tuple variant, found unit struct `Z0`
+        Z0(x) => {} //~ ERROR expected tuple struct or tuple variant, found unit struct `Z0`
+    }
+    match Z1() {
+        Z1 => {} //~ ERROR match bindings cannot shadow tuple structs
+        Z1(x) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple struct has 0 fields
+    }
+
+    match S(1, 2, 3) {
+        S() => {} //~ ERROR this pattern has 0 fields, but the corresponding tuple struct has 3 fields
+        S(1) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple struct has 3 fields
+        S(xyz, abc) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple struct has 3 fields
+        S(1, 2, 3, 4) => {} //~ ERROR this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+    }
+    match M(1, 2, 3) {
+        M() => {} //~ ERROR this pattern has 0 fields, but the corresponding tuple struct has 3 fields
+        M(1) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple struct has 3 fields
+        M(xyz, abc) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple struct has 3 fields
+        M(1, 2, 3, 4) => {} //~ ERROR this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+    }
+
+    match E1::Z0 {
+        E1::Z0() => {} //~ ERROR expected tuple struct or tuple variant, found unit variant `E1::Z0`
+        E1::Z0(x) => {} //~ ERROR expected tuple struct or tuple variant, found unit variant `E1::Z0`
+    }
+    match E1::Z1() {
+        E1::Z1 => {} //~ ERROR expected unit struct, unit variant or constant, found tuple variant `E1::Z1`
+        E1::Z1(x) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple variant has 0 fields
+    }
+    match E1::S(1, 2, 3) {
+        E1::S() => {} //~ ERROR this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+        E1::S(1) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple variant has 3 fields
+        E1::S(xyz, abc) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+        E1::S(1, 2, 3, 4) => {} //~ ERROR this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+    }
+
+    match E2::S(1, 2, 3) {
+        E2::S() => {} //~ ERROR this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+        E2::S(1) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple variant has 3 fields
+        E2::S(xyz, abc) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+        E2::S(1, 2, 3, 4) => {} //~ ERROR this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+    }
+    match E2::M(1, 2, 3) {
+        E2::M() => {} //~ ERROR this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+        E2::M(1) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple variant has 3 fields
+        E2::M(xyz, abc) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+        E2::M(1, 2, 3, 4) => {} //~ ERROR this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+    }
+}
diff --git a/tests/ui/pattern/pat-tuple-field-count-cross.stderr b/tests/ui/pattern/pat-tuple-field-count-cross.stderr
new file mode 100644
index 00000000000..d9295746158
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-field-count-cross.stderr
@@ -0,0 +1,531 @@
+error[E0530]: match bindings cannot shadow tuple structs
+  --> $DIR/pat-tuple-field-count-cross.rs:13:9
+   |
+LL | use declarations_for_tuple_field_count_errors::*;
+   |     -------------------------------------------- the tuple struct `Z1` is imported here
+...
+LL |         Z1 => {}
+   |         ^^
+   |         |
+   |         cannot be named the same as a tuple struct
+   |         help: try specify the pattern arguments: `Z1(..)`
+
+error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0`
+  --> $DIR/pat-tuple-field-count-cross.rs:9:9
+   |
+LL |         Z0() => {}
+   |         ^^^^
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:1:1
+   |
+LL | pub struct Z0;
+   | ------------- `Z0` defined here
+LL | pub struct Z1();
+   | ------------- similarly named tuple struct `Z1` defined here
+   |
+help: use this syntax instead
+   |
+LL |         Z0 => {}
+   |         ~~
+help: a tuple struct with a similar name exists
+   |
+LL |         Z1() => {}
+   |         ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0`
+  --> $DIR/pat-tuple-field-count-cross.rs:10:9
+   |
+LL |         Z0(x) => {}
+   |         ^^^^^
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:1:1
+   |
+LL | pub struct Z0;
+   | ------------- `Z0` defined here
+LL | pub struct Z1();
+   | ------------- similarly named tuple struct `Z1` defined here
+   |
+help: use this syntax instead
+   |
+LL |         Z0 => {}
+   |         ~~
+help: a tuple struct with a similar name exists
+   |
+LL |         Z1(x) => {}
+   |         ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0`
+  --> $DIR/pat-tuple-field-count-cross.rs:31:9
+   |
+LL |         E1::Z0() => {}
+   |         ^^^^^^^^
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:15
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |               --  -- similarly named tuple variant `Z1` defined here
+   |               |
+   |               `E1::Z0` defined here
+   |
+help: use this syntax instead
+   |
+LL |         E1::Z0 => {}
+   |         ~~~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         E1::Z1() => {}
+   |             ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0`
+  --> $DIR/pat-tuple-field-count-cross.rs:32:9
+   |
+LL |         E1::Z0(x) => {}
+   |         ^^^^^^^^^
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:15
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |               --  -- similarly named tuple variant `Z1` defined here
+   |               |
+   |               `E1::Z0` defined here
+   |
+help: use this syntax instead
+   |
+LL |         E1::Z0 => {}
+   |         ~~~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         E1::Z1(x) => {}
+   |             ~~
+
+error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E1::Z1`
+  --> $DIR/pat-tuple-field-count-cross.rs:35:9
+   |
+LL |         E1::Z1 => {}
+   |         ^^^^^^
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:19
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |               --  -- `E1::Z1` defined here
+   |               |
+   |               similarly named unit variant `Z0` defined here
+   |
+help: use the tuple variant pattern syntax instead
+   |
+LL |         E1::Z1(/* fields */) => {}
+   |         ~~~~~~~~~~~~~~~~~~~~
+help: a unit variant with a similar name exists
+   |
+LL |         E1::Z0 => {}
+   |             ~~
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 0 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:14:12
+   |
+LL |         Z1(x) => {}
+   |            ^ expected 0 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:2:1
+   |
+LL | pub struct Z1();
+   | ------------- tuple struct has 0 fields
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:18:9
+   |
+LL |         S() => {}
+   |         ^^^ expected 3 fields, found 0
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:4:14
+   |
+LL | pub struct S(pub u8, pub u8, pub u8);
+   |              ------  ------  ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(_, _, _) => {}
+   |           +++++++
+help: use `..` to ignore all fields
+   |
+LL |         S(..) => {}
+   |           ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:19:11
+   |
+LL |         S(1) => {}
+   |           ^ expected 3 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:4:14
+   |
+LL | pub struct S(pub u8, pub u8, pub u8);
+   |              ------  ------  ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(1, _, _) => {}
+   |            ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         S(1, ..) => {}
+   |            ++++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:20:11
+   |
+LL |         S(xyz, abc) => {}
+   |           ^^^  ^^^ expected 3 fields, found 2
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:4:14
+   |
+LL | pub struct S(pub u8, pub u8, pub u8);
+   |              ------  ------  ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(xyz, abc, _) => {}
+   |                   +++
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:21:11
+   |
+LL |         S(1, 2, 3, 4) => {}
+   |           ^  ^  ^  ^ expected 3 fields, found 4
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:4:14
+   |
+LL | pub struct S(pub u8, pub u8, pub u8);
+   |              ------  ------  ------ tuple struct has 3 fields
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:24:9
+   |
+LL |         M() => {}
+   |         ^^^ expected 3 fields, found 0
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:5:12
+   |
+LL | pub struct M(
+   |            - tuple struct defined here
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         M(_, _, _) => {}
+   |           +++++++
+help: use `..` to ignore all fields
+   |
+LL |         M(..) => {}
+   |           ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:25:11
+   |
+LL |         M(1) => {}
+   |           ^ expected 3 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:5:12
+   |
+LL | pub struct M(
+   |            - tuple struct defined here
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         M(1, _, _) => {}
+   |            ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         M(1, ..) => {}
+   |            ++++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:26:11
+   |
+LL |         M(xyz, abc) => {}
+   |           ^^^  ^^^ expected 3 fields, found 2
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:5:12
+   |
+LL | pub struct M(
+   |            - tuple struct defined here
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------ tuple struct has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         M(xyz, abc, _) => {}
+   |                   +++
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:27:11
+   |
+LL |         M(1, 2, 3, 4) => {}
+   |           ^  ^  ^  ^ expected 3 fields, found 4
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:5:12
+   |
+LL | pub struct M(
+   |            - tuple struct defined here
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------
+LL |     pub u8,
+   |     ------ tuple struct has 3 fields
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 0 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:36:16
+   |
+LL |         E1::Z1(x) => {}
+   |                ^ expected 0 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:19
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |                   -- tuple variant has 0 fields
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:39:9
+   |
+LL |         E1::S() => {}
+   |         ^^^^^^^ expected 3 fields, found 0
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:27
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |                           --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E1::S(_, _, _) => {}
+   |               +++++++
+help: use `..` to ignore all fields
+   |
+LL |         E1::S(..) => {}
+   |               ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:40:15
+   |
+LL |         E1::S(1) => {}
+   |               ^ expected 3 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:27
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |                           --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E1::S(1, _, _) => {}
+   |                ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         E1::S(1, ..) => {}
+   |                ++++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:41:15
+   |
+LL |         E1::S(xyz, abc) => {}
+   |               ^^^  ^^^ expected 3 fields, found 2
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:27
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |                           --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E1::S(xyz, abc, _) => {}
+   |                       +++
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:42:15
+   |
+LL |         E1::S(1, 2, 3, 4) => {}
+   |               ^  ^  ^  ^ expected 3 fields, found 4
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:11:27
+   |
+LL | pub enum E1 { Z0, Z1(), S(u8, u8, u8) }
+   |                           --  --  -- tuple variant has 3 fields
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:46:9
+   |
+LL |         E2::S() => {}
+   |         ^^^^^^^ expected 3 fields, found 0
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:14:7
+   |
+LL |     S(u8, u8, u8),
+   |       --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::S(_, _, _) => {}
+   |               +++++++
+help: use `..` to ignore all fields
+   |
+LL |         E2::S(..) => {}
+   |               ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:47:15
+   |
+LL |         E2::S(1) => {}
+   |               ^ expected 3 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:14:7
+   |
+LL |     S(u8, u8, u8),
+   |       --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::S(1, _, _) => {}
+   |                ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         E2::S(1, ..) => {}
+   |                ++++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:48:15
+   |
+LL |         E2::S(xyz, abc) => {}
+   |               ^^^  ^^^ expected 3 fields, found 2
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:14:7
+   |
+LL |     S(u8, u8, u8),
+   |       --  --  -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::S(xyz, abc, _) => {}
+   |                       +++
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:49:15
+   |
+LL |         E2::S(1, 2, 3, 4) => {}
+   |               ^  ^  ^  ^ expected 3 fields, found 4
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:14:7
+   |
+LL |     S(u8, u8, u8),
+   |       --  --  -- tuple variant has 3 fields
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:52:9
+   |
+LL |         E2::M() => {}
+   |         ^^^^^^^ expected 3 fields, found 0
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:15:5
+   |
+LL |     M(
+   |     - tuple variant defined here
+LL |         u8,
+   |         --
+LL |         u8,
+   |         --
+LL |         u8,
+   |         -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::M(_, _, _) => {}
+   |               +++++++
+help: use `..` to ignore all fields
+   |
+LL |         E2::M(..) => {}
+   |               ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:53:15
+   |
+LL |         E2::M(1) => {}
+   |               ^ expected 3 fields, found 1
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:15:5
+   |
+LL |     M(
+   |     - tuple variant defined here
+LL |         u8,
+   |         --
+LL |         u8,
+   |         --
+LL |         u8,
+   |         -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::M(1, _, _) => {}
+   |                ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         E2::M(1, ..) => {}
+   |                ++++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:54:15
+   |
+LL |         E2::M(xyz, abc) => {}
+   |               ^^^  ^^^ expected 3 fields, found 2
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:15:5
+   |
+LL |     M(
+   |     - tuple variant defined here
+LL |         u8,
+   |         --
+LL |         u8,
+   |         --
+LL |         u8,
+   |         -- tuple variant has 3 fields
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E2::M(xyz, abc, _) => {}
+   |                       +++
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple variant has 3 fields
+  --> $DIR/pat-tuple-field-count-cross.rs:55:15
+   |
+LL |         E2::M(1, 2, 3, 4) => {}
+   |               ^  ^  ^  ^ expected 3 fields, found 4
+   |
+  ::: $DIR/auxiliary/declarations-for-tuple-field-count-errors.rs:15:5
+   |
+LL |     M(
+   |     - tuple variant defined here
+LL |         u8,
+   |         --
+LL |         u8,
+   |         --
+LL |         u8,
+   |         -- tuple variant has 3 fields
+
+error: aborting due to 28 previous errors
+
+Some errors have detailed explanations: E0023, E0530, E0532.
+For more information about an error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/pat-tuple-overfield.rs b/tests/ui/pattern/pat-tuple-overfield.rs
new file mode 100644
index 00000000000..c863c657514
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-overfield.rs
@@ -0,0 +1,74 @@
+struct S(u8, u8, u8);
+struct M(
+    u8,
+    u8,
+    u8,
+    u8,
+    u8,
+);
+
+struct Z0;
+struct Z1();
+enum E1 {
+    Z0,
+    Z1(),
+}
+
+fn main() {
+    match (1, 2, 3) {
+        (1, 2, 3, 4) => {} //~ ERROR mismatched types
+        (1, 2, .., 3, 4) => {} //~ ERROR mismatched types
+        _ => {}
+    }
+    match S(1, 2, 3) {
+        S(1, 2, 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+        S(1, 2, .., 3, 4) => {}
+        //~^ ERROR this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+        _ => {}
+    }
+    match M(1, 2, 3, 4, 5) {
+        M(1, 2, 3, 4, 5, 6) => {}
+        //~^ ERROR this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+        M(1,
+          2,
+          3,
+          4,
+          5,
+          6) => {}
+        //~^ ERROR this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+        M(
+            1,
+            2,
+            3,
+            4,
+            5,
+            6,
+        ) => {}
+        //~^^ ERROR this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+    }
+    match Z0 {
+        Z0 => {}
+        Z0() => {} //~ ERROR expected tuple struct or tuple variant, found unit struct `Z0`
+        Z0(_) => {} //~ ERROR expected tuple struct or tuple variant, found unit struct `Z0`
+        Z0(_, _) => {} //~ ERROR expected tuple struct or tuple variant, found unit struct `Z0`
+    }
+    match Z1() {
+        Z1 => {} //~ ERROR match bindings cannot shadow tuple structs
+        Z1() => {}
+        Z1(_) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple struct has 0 fields
+        Z1(_, _) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple struct has 0 fields
+    }
+    match E1::Z0 {
+        E1::Z0 => {}
+        E1::Z0() => {} //~ ERROR expected tuple struct or tuple variant, found unit variant `E1::Z0`
+        E1::Z0(_) => {} //~ ERROR expected tuple struct or tuple variant, found unit variant `E1::Z0`
+        E1::Z0(_, _) => {} //~ ERROR expected tuple struct or tuple variant, found unit variant `E1::Z0`
+    }
+    match E1::Z1() {
+        E1::Z1 => {} //~ ERROR expected unit struct, unit variant or constant, found tuple variant `E1::Z1`
+        E1::Z1() => {}
+        E1::Z1(_) => {} //~ ERROR this pattern has 1 field, but the corresponding tuple variant has 0 fields
+        E1::Z1(_, _) => {} //~ ERROR this pattern has 2 fields, but the corresponding tuple variant has 0 fields
+    }
+}
diff --git a/tests/ui/pattern/pat-tuple-overfield.stderr b/tests/ui/pattern/pat-tuple-overfield.stderr
new file mode 100644
index 00000000000..54d89e03101
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-overfield.stderr
@@ -0,0 +1,312 @@
+error[E0530]: match bindings cannot shadow tuple structs
+  --> $DIR/pat-tuple-overfield.rs:57:9
+   |
+LL | struct Z1();
+   | ------------ the tuple struct `Z1` is defined here
+...
+LL |         Z1 => {}
+   |         ^^
+   |         |
+   |         cannot be named the same as a tuple struct
+   |         help: try specify the pattern arguments: `Z1(..)`
+
+error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0`
+  --> $DIR/pat-tuple-overfield.rs:52:9
+   |
+LL | struct Z0;
+   | ---------- `Z0` defined here
+LL | struct Z1();
+   | ------------ similarly named tuple struct `Z1` defined here
+...
+LL |         Z0() => {}
+   |         ^^^^
+   |
+help: use this syntax instead
+   |
+LL |         Z0 => {}
+   |         ~~
+help: a tuple struct with a similar name exists
+   |
+LL |         Z1() => {}
+   |         ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0`
+  --> $DIR/pat-tuple-overfield.rs:53:9
+   |
+LL | struct Z0;
+   | ---------- `Z0` defined here
+LL | struct Z1();
+   | ------------ similarly named tuple struct `Z1` defined here
+...
+LL |         Z0(_) => {}
+   |         ^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         Z0 => {}
+   |         ~~
+help: a tuple struct with a similar name exists
+   |
+LL |         Z1(_) => {}
+   |         ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit struct `Z0`
+  --> $DIR/pat-tuple-overfield.rs:54:9
+   |
+LL | struct Z0;
+   | ---------- `Z0` defined here
+LL | struct Z1();
+   | ------------ similarly named tuple struct `Z1` defined here
+...
+LL |         Z0(_, _) => {}
+   |         ^^^^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         Z0 => {}
+   |         ~~
+help: a tuple struct with a similar name exists
+   |
+LL |         Z1(_, _) => {}
+   |         ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0`
+  --> $DIR/pat-tuple-overfield.rs:64:9
+   |
+LL |     Z0,
+   |     -- `E1::Z0` defined here
+LL |     Z1(),
+   |     ---- similarly named tuple variant `Z1` defined here
+...
+LL |         E1::Z0() => {}
+   |         ^^^^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         E1::Z0 => {}
+   |         ~~~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         E1::Z1() => {}
+   |             ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0`
+  --> $DIR/pat-tuple-overfield.rs:65:9
+   |
+LL |     Z0,
+   |     -- `E1::Z0` defined here
+LL |     Z1(),
+   |     ---- similarly named tuple variant `Z1` defined here
+...
+LL |         E1::Z0(_) => {}
+   |         ^^^^^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         E1::Z0 => {}
+   |         ~~~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         E1::Z1(_) => {}
+   |             ~~
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `E1::Z0`
+  --> $DIR/pat-tuple-overfield.rs:66:9
+   |
+LL |     Z0,
+   |     -- `E1::Z0` defined here
+LL |     Z1(),
+   |     ---- similarly named tuple variant `Z1` defined here
+...
+LL |         E1::Z0(_, _) => {}
+   |         ^^^^^^^^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         E1::Z0 => {}
+   |         ~~~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         E1::Z1(_, _) => {}
+   |             ~~
+
+error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E1::Z1`
+  --> $DIR/pat-tuple-overfield.rs:69:9
+   |
+LL |     Z0,
+   |     -- similarly named unit variant `Z0` defined here
+LL |     Z1(),
+   |     ---- `E1::Z1` defined here
+...
+LL |         E1::Z1 => {}
+   |         ^^^^^^
+   |
+help: use the tuple variant pattern syntax instead
+   |
+LL |         E1::Z1() => {}
+   |         ~~~~~~~~
+help: a unit variant with a similar name exists
+   |
+LL |         E1::Z0 => {}
+   |             ~~
+
+error[E0308]: mismatched types
+  --> $DIR/pat-tuple-overfield.rs:19:9
+   |
+LL |     match (1, 2, 3) {
+   |           --------- this expression has type `({integer}, {integer}, {integer})`
+LL |         (1, 2, 3, 4) => {}
+   |         ^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 4 elements
+   |
+   = note: expected tuple `({integer}, {integer}, {integer})`
+              found tuple `(_, _, _, _)`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-tuple-overfield.rs:20:9
+   |
+LL |     match (1, 2, 3) {
+   |           --------- this expression has type `({integer}, {integer}, {integer})`
+LL |         (1, 2, 3, 4) => {}
+LL |         (1, 2, .., 3, 4) => {}
+   |         ^^^^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 4 elements
+   |
+   = note: expected tuple `({integer}, {integer}, {integer})`
+              found tuple `(_, _, _, _)`
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-overfield.rs:24:11
+   |
+LL | struct S(u8, u8, u8);
+   |          --  --  -- tuple struct has 3 fields
+...
+LL |         S(1, 2, 3, 4) => {}
+   |           ^  ^  ^  ^ expected 3 fields, found 4
+
+error[E0023]: this pattern has 4 fields, but the corresponding tuple struct has 3 fields
+  --> $DIR/pat-tuple-overfield.rs:26:11
+   |
+LL | struct S(u8, u8, u8);
+   |          --  --  -- tuple struct has 3 fields
+...
+LL |         S(1, 2, .., 3, 4) => {}
+   |           ^  ^      ^  ^ expected 3 fields, found 4
+
+error[E0023]: this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+  --> $DIR/pat-tuple-overfield.rs:31:11
+   |
+LL | struct M(
+   |        - tuple struct defined here
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     -- tuple struct has 5 fields
+...
+LL |         M(1, 2, 3, 4, 5, 6) => {}
+   |           ^  ^  ^  ^  ^  ^ expected 5 fields, found 6
+
+error[E0023]: this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+  --> $DIR/pat-tuple-overfield.rs:33:11
+   |
+LL | struct M(
+   |        - tuple struct defined here
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     -- tuple struct has 5 fields
+...
+LL |         M(1,
+   |         - ^
+LL |           2,
+   |           ^
+LL |           3,
+   |           ^
+LL |           4,
+   |           ^
+LL |           5,
+   |           ^
+LL |           6) => {}
+   |           ^ expected 5 fields, found 6
+
+error[E0023]: this pattern has 6 fields, but the corresponding tuple struct has 5 fields
+  --> $DIR/pat-tuple-overfield.rs:41:13
+   |
+LL | struct M(
+   |        - tuple struct defined here
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     --
+LL |     u8,
+   |     -- tuple struct has 5 fields
+...
+LL |         M(
+   |         -
+LL |             1,
+   |             ^
+LL |             2,
+   |             ^
+LL |             3,
+   |             ^
+LL |             4,
+   |             ^
+LL |             5,
+   |             ^
+LL |             6,
+   |             ^ expected 5 fields, found 6
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 0 fields
+  --> $DIR/pat-tuple-overfield.rs:59:12
+   |
+LL | struct Z1();
+   | --------- tuple struct has 0 fields
+...
+LL |         Z1(_) => {}
+   |            ^ expected 0 fields, found 1
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 0 fields
+  --> $DIR/pat-tuple-overfield.rs:60:12
+   |
+LL | struct Z1();
+   | --------- tuple struct has 0 fields
+...
+LL |         Z1(_, _) => {}
+   |            ^  ^ expected 0 fields, found 2
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 0 fields
+  --> $DIR/pat-tuple-overfield.rs:71:16
+   |
+LL |     Z1(),
+   |     -- tuple variant has 0 fields
+...
+LL |         E1::Z1(_) => {}
+   |                ^ expected 0 fields, found 1
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple variant has 0 fields
+  --> $DIR/pat-tuple-overfield.rs:72:16
+   |
+LL |     Z1(),
+   |     -- tuple variant has 0 fields
+...
+LL |         E1::Z1(_, _) => {}
+   |                ^  ^ expected 0 fields, found 2
+
+error: aborting due to 19 previous errors
+
+Some errors have detailed explanations: E0023, E0308, E0530, E0532.
+For more information about an error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/pat-tuple-underfield.rs b/tests/ui/pattern/pat-tuple-underfield.rs
new file mode 100644
index 00000000000..dac60e3fab2
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-underfield.rs
@@ -0,0 +1,67 @@
+struct S(i32, f32);
+enum E {
+    S(i32, f32),
+}
+struct Point4(i32, i32, i32, i32);
+
+fn main() {
+    match S(0, 1.0) {
+        S(x) => {}
+        //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+    }
+    match S(0, 1.0) {
+        S(_) => {}
+        //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+    }
+    match S(0, 1.0) {
+        S() => {}
+        //~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+
+        // Test non-standard formatting
+        S () => {}
+        //~^ ERROR this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+    }
+
+    match E::S(0, 1.0) {
+        E::S(x) => {}
+        //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+    }
+    match E::S(0, 1.0) {
+        E::S(_) => {}
+        //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+    }
+    match E::S(0, 1.0) {
+        E::S() => {}
+        //~^ ERROR this pattern has 0 fields, but the corresponding tuple variant has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+
+        // Test non-standard formatting
+        E::S () => {}
+        //~^ ERROR this pattern has 0 fields, but the corresponding tuple variant has 2 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore all fields
+    }
+    match E::S(0, 1.0) {
+        E::S => {}
+        //~^ ERROR expected unit struct, unit variant or constant, found tuple variant `E::S`
+        //~| HELP use the tuple variant pattern syntax instead
+    }
+
+    match Point4(0, 1, 2, 3) {
+        Point4(   a   ,     _    ) => {}
+        //~^ ERROR this pattern has 2 fields, but the corresponding tuple struct has 4 fields
+        //~| HELP use `_` to explicitly ignore each field
+        //~| HELP use `..` to ignore the rest of the fields
+    }
+}
diff --git a/tests/ui/pattern/pat-tuple-underfield.stderr b/tests/ui/pattern/pat-tuple-underfield.stderr
new file mode 100644
index 00000000000..e75f9b38da5
--- /dev/null
+++ b/tests/ui/pattern/pat-tuple-underfield.stderr
@@ -0,0 +1,167 @@
+error[E0532]: expected unit struct, unit variant or constant, found tuple variant `E::S`
+  --> $DIR/pat-tuple-underfield.rs:56:9
+   |
+LL |     S(i32, f32),
+   |     ----------- `E::S` defined here
+...
+LL |         E::S => {}
+   |         ^^^^ help: use the tuple variant pattern syntax instead: `E::S(_, _)`
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:9:11
+   |
+LL | struct S(i32, f32);
+   |          ---  --- tuple struct has 2 fields
+...
+LL |         S(x) => {}
+   |           ^ expected 2 fields, found 1
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(x, _) => {}
+   |            +++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:14:11
+   |
+LL | struct S(i32, f32);
+   |          ---  --- tuple struct has 2 fields
+...
+LL |         S(_) => {}
+   |           ^ expected 2 fields, found 1
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(_, _) => {}
+   |            +++
+help: use `..` to ignore all fields
+   |
+LL |         S(..) => {}
+   |           ~~
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:20:9
+   |
+LL | struct S(i32, f32);
+   |          ---  --- tuple struct has 2 fields
+...
+LL |         S() => {}
+   |         ^^^ expected 2 fields, found 0
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S(_, _) => {}
+   |           ++++
+help: use `..` to ignore all fields
+   |
+LL |         S(..) => {}
+   |           ++
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:26:9
+   |
+LL | struct S(i32, f32);
+   |          ---  --- tuple struct has 2 fields
+...
+LL |         S () => {}
+   |         ^^^^ expected 2 fields, found 0
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         S (_, _) => {}
+   |            ++++
+help: use `..` to ignore all fields
+   |
+LL |         S (..) => {}
+   |            ++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:33:14
+   |
+LL |     S(i32, f32),
+   |       ---  --- tuple variant has 2 fields
+...
+LL |         E::S(x) => {}
+   |              ^ expected 2 fields, found 1
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E::S(x, _) => {}
+   |               +++
+
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:38:14
+   |
+LL |     S(i32, f32),
+   |       ---  --- tuple variant has 2 fields
+...
+LL |         E::S(_) => {}
+   |              ^ expected 2 fields, found 1
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E::S(_, _) => {}
+   |               +++
+help: use `..` to ignore all fields
+   |
+LL |         E::S(..) => {}
+   |              ~~
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:44:9
+   |
+LL |     S(i32, f32),
+   |       ---  --- tuple variant has 2 fields
+...
+LL |         E::S() => {}
+   |         ^^^^^^ expected 2 fields, found 0
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E::S(_, _) => {}
+   |              ++++
+help: use `..` to ignore all fields
+   |
+LL |         E::S(..) => {}
+   |              ++
+
+error[E0023]: this pattern has 0 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/pat-tuple-underfield.rs:50:9
+   |
+LL |     S(i32, f32),
+   |       ---  --- tuple variant has 2 fields
+...
+LL |         E::S () => {}
+   |         ^^^^^^^ expected 2 fields, found 0
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         E::S (_, _) => {}
+   |               ++++
+help: use `..` to ignore all fields
+   |
+LL |         E::S (..) => {}
+   |               ++
+
+error[E0023]: this pattern has 2 fields, but the corresponding tuple struct has 4 fields
+  --> $DIR/pat-tuple-underfield.rs:62:19
+   |
+LL | struct Point4(i32, i32, i32, i32);
+   |               ---  ---  ---  --- tuple struct has 4 fields
+...
+LL |         Point4(   a   ,     _    ) => {}
+   |                   ^         ^ expected 4 fields, found 2
+   |
+help: use `_` to explicitly ignore each field
+   |
+LL |         Point4(   a   ,     _    , _, _) => {}
+   |                                  ++++++
+help: use `..` to ignore the rest of the fields
+   |
+LL |         Point4(   a, ..) => {}
+   |                    ~~~~
+
+error: aborting due to 10 previous errors
+
+Some errors have detailed explanations: E0023, E0532.
+For more information about an error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/pat-type-err-formal-param.rs b/tests/ui/pattern/pat-type-err-formal-param.rs
new file mode 100644
index 00000000000..54336b34923
--- /dev/null
+++ b/tests/ui/pattern/pat-type-err-formal-param.rs
@@ -0,0 +1,8 @@
+// Test the `.span_label(..)` to the type when there's a
+// type error in a pattern due to a the formal parameter.
+
+fn main() {}
+
+struct Tuple(u8);
+
+fn foo(Tuple(_): String) {} //~ ERROR mismatched types
diff --git a/tests/ui/pattern/pat-type-err-formal-param.stderr b/tests/ui/pattern/pat-type-err-formal-param.stderr
new file mode 100644
index 00000000000..206713a4bfc
--- /dev/null
+++ b/tests/ui/pattern/pat-type-err-formal-param.stderr
@@ -0,0 +1,11 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-formal-param.rs:8:8
+   |
+LL | fn foo(Tuple(_): String) {}
+   |        ^^^^^^^^  ------ expected due to this
+   |        |
+   |        expected struct `String`, found struct `Tuple`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/pat-type-err-let-stmt.rs b/tests/ui/pattern/pat-type-err-let-stmt.rs
new file mode 100644
index 00000000000..6e9850b655c
--- /dev/null
+++ b/tests/ui/pattern/pat-type-err-let-stmt.rs
@@ -0,0 +1,16 @@
+// Test the `.span_label` to the type / scrutinee
+// when there's a type error in checking a pattern.
+
+fn main() {
+    // We want to point at the `Option<u8>`.
+    let Ok(0): Option<u8> = 42u8;
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
+
+    // We want to point at the `Option<u8>`.
+    let Ok(0): Option<u8>;
+    //~^ ERROR mismatched types
+
+    // We want to point at the scrutinee.
+    let Ok(0) = 42u8; //~ ERROR mismatched types
+}
diff --git a/tests/ui/pattern/pat-type-err-let-stmt.stderr b/tests/ui/pattern/pat-type-err-let-stmt.stderr
new file mode 100644
index 00000000000..090bd67117e
--- /dev/null
+++ b/tests/ui/pattern/pat-type-err-let-stmt.stderr
@@ -0,0 +1,51 @@
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:6:29
+   |
+LL |     let Ok(0): Option<u8> = 42u8;
+   |                ----------   ^^^^ expected enum `Option`, found `u8`
+   |                |
+   |                expected due to this
+   |
+   = note: expected enum `Option<u8>`
+              found type `u8`
+help: try wrapping the expression in `Some`
+   |
+LL |     let Ok(0): Option<u8> = Some(42u8);
+   |                             +++++    +
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:6:9
+   |
+LL |     let Ok(0): Option<u8> = 42u8;
+   |         ^^^^^  ---------- expected due to this
+   |         |
+   |         expected enum `Option`, found enum `Result`
+   |
+   = note: expected enum `Option<u8>`
+              found enum `Result<_, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:11:9
+   |
+LL |     let Ok(0): Option<u8>;
+   |         ^^^^^  ---------- expected due to this
+   |         |
+   |         expected enum `Option`, found enum `Result`
+   |
+   = note: expected enum `Option<u8>`
+              found enum `Result<_, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/pat-type-err-let-stmt.rs:15:9
+   |
+LL |     let Ok(0) = 42u8;
+   |         ^^^^^   ---- this expression has type `u8`
+   |         |
+   |         expected `u8`, found enum `Result`
+   |
+   = note: expected type `u8`
+              found enum `Result<_, _>`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/patkind-litrange-no-expr.rs b/tests/ui/pattern/patkind-litrange-no-expr.rs
new file mode 100644
index 00000000000..7ef541cb585
--- /dev/null
+++ b/tests/ui/pattern/patkind-litrange-no-expr.rs
@@ -0,0 +1,24 @@
+macro_rules! enum_number {
+    ($name:ident { $($variant:ident = $value:expr, )* }) => {
+        enum $name {
+            $($variant = $value,)*
+        }
+
+        fn foo(value: i32) -> Option<$name> {
+            match value {
+                $( $value => Some($name::$variant), )* // PatKind::Lit
+                $( $value ..= 42 => Some($name::$variant), )* // PatKind::Range
+                _ => None
+            }
+        }
+    }
+}
+
+enum_number!(Change {
+    Pos = 1,
+    Neg = -1,
+    Arith = 1 + 1, //~ ERROR arbitrary expressions aren't allowed in patterns
+                   //~| ERROR arbitrary expressions aren't allowed in patterns
+});
+
+fn main() {}
diff --git a/tests/ui/pattern/patkind-litrange-no-expr.stderr b/tests/ui/pattern/patkind-litrange-no-expr.stderr
new file mode 100644
index 00000000000..eb1ee7e4567
--- /dev/null
+++ b/tests/ui/pattern/patkind-litrange-no-expr.stderr
@@ -0,0 +1,14 @@
+error: arbitrary expressions aren't allowed in patterns
+  --> $DIR/patkind-litrange-no-expr.rs:20:13
+   |
+LL |     Arith = 1 + 1,
+   |             ^^^^^
+
+error: arbitrary expressions aren't allowed in patterns
+  --> $DIR/patkind-litrange-no-expr.rs:20:13
+   |
+LL |     Arith = 1 + 1,
+   |             ^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/pattern-binding-disambiguation.rs b/tests/ui/pattern/pattern-binding-disambiguation.rs
new file mode 100644
index 00000000000..ce1d8c6c047
--- /dev/null
+++ b/tests/ui/pattern/pattern-binding-disambiguation.rs
@@ -0,0 +1,57 @@
+struct UnitStruct;
+struct TupleStruct();
+struct BracedStruct{}
+
+enum E {
+    UnitVariant,
+    TupleVariant(),
+    BracedVariant{},
+}
+use E::*;
+
+const CONST: () = ();
+static STATIC: () = ();
+
+fn function() {}
+
+fn main() {
+    let doesnt_matter = 0;
+
+    match UnitStruct {
+        UnitStruct => {} // OK, `UnitStruct` is a unit struct pattern
+    }
+    match doesnt_matter {
+        TupleStruct => {} //~ ERROR match bindings cannot shadow tuple structs
+    }
+    match doesnt_matter {
+        BracedStruct => {} // OK, `BracedStruct` is a fresh binding
+    }
+    match UnitVariant {
+        UnitVariant => {} // OK, `UnitVariant` is a unit variant pattern
+    }
+    match doesnt_matter {
+        TupleVariant => {} //~ ERROR match bindings cannot shadow tuple variants
+    }
+    match doesnt_matter {
+        BracedVariant => {} // OK, `BracedVariant` is a fresh binding
+    }
+    match CONST {
+        CONST => {} // OK, `CONST` is a const pattern
+    }
+    match doesnt_matter {
+        STATIC => {} //~ ERROR match bindings cannot shadow statics
+    }
+    match doesnt_matter {
+        function => {} // OK, `function` is a fresh binding
+    }
+
+    let UnitStruct = UnitStruct; // OK, `UnitStruct` is a unit struct pattern
+    let TupleStruct = doesnt_matter; //~ ERROR let bindings cannot shadow tuple structs
+    let BracedStruct = doesnt_matter; // OK, `BracedStruct` is a fresh binding
+    let UnitVariant = UnitVariant; // OK, `UnitVariant` is a unit variant pattern
+    let TupleVariant = doesnt_matter; //~ ERROR let bindings cannot shadow tuple variants
+    let BracedVariant = doesnt_matter; // OK, `BracedVariant` is a fresh binding
+    let CONST = CONST; // OK, `CONST` is a const pattern
+    let STATIC = doesnt_matter; //~ ERROR let bindings cannot shadow statics
+    let function = doesnt_matter; // OK, `function` is a fresh binding
+}
diff --git a/tests/ui/pattern/pattern-binding-disambiguation.stderr b/tests/ui/pattern/pattern-binding-disambiguation.stderr
new file mode 100644
index 00000000000..d54467b3c0c
--- /dev/null
+++ b/tests/ui/pattern/pattern-binding-disambiguation.stderr
@@ -0,0 +1,63 @@
+error[E0530]: match bindings cannot shadow tuple structs
+  --> $DIR/pattern-binding-disambiguation.rs:24:9
+   |
+LL | struct TupleStruct();
+   | --------------------- the tuple struct `TupleStruct` is defined here
+...
+LL |         TupleStruct => {}
+   |         ^^^^^^^^^^^
+   |         |
+   |         cannot be named the same as a tuple struct
+   |         help: try specify the pattern arguments: `TupleStruct(..)`
+
+error[E0530]: match bindings cannot shadow tuple variants
+  --> $DIR/pattern-binding-disambiguation.rs:33:9
+   |
+LL | use E::*;
+   |     ---- the tuple variant `TupleVariant` is imported here
+...
+LL |         TupleVariant => {}
+   |         ^^^^^^^^^^^^
+   |         |
+   |         cannot be named the same as a tuple variant
+   |         help: try specify the pattern arguments: `TupleVariant(..)`
+
+error[E0530]: match bindings cannot shadow statics
+  --> $DIR/pattern-binding-disambiguation.rs:42:9
+   |
+LL | static STATIC: () = ();
+   | ----------------------- the static `STATIC` is defined here
+...
+LL |         STATIC => {}
+   |         ^^^^^^ cannot be named the same as a static
+
+error[E0530]: let bindings cannot shadow tuple structs
+  --> $DIR/pattern-binding-disambiguation.rs:49:9
+   |
+LL | struct TupleStruct();
+   | --------------------- the tuple struct `TupleStruct` is defined here
+...
+LL |     let TupleStruct = doesnt_matter;
+   |         ^^^^^^^^^^^ cannot be named the same as a tuple struct
+
+error[E0530]: let bindings cannot shadow tuple variants
+  --> $DIR/pattern-binding-disambiguation.rs:52:9
+   |
+LL | use E::*;
+   |     ---- the tuple variant `TupleVariant` is imported here
+...
+LL |     let TupleVariant = doesnt_matter;
+   |         ^^^^^^^^^^^^ cannot be named the same as a tuple variant
+
+error[E0530]: let bindings cannot shadow statics
+  --> $DIR/pattern-binding-disambiguation.rs:55:9
+   |
+LL | static STATIC: () = ();
+   | ----------------------- the static `STATIC` is defined here
+...
+LL |     let STATIC = doesnt_matter;
+   |         ^^^^^^ cannot be named the same as a static
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0530`.
diff --git a/tests/ui/pattern/pattern-error-continue.rs b/tests/ui/pattern/pattern-error-continue.rs
new file mode 100644
index 00000000000..0702a9986fc
--- /dev/null
+++ b/tests/ui/pattern/pattern-error-continue.rs
@@ -0,0 +1,35 @@
+// Test that certain pattern-match type errors are non-fatal
+
+enum A {
+    B(isize, isize),
+    C(isize, isize, isize),
+    D
+}
+
+struct S {
+    a: isize
+}
+
+fn f(_c: char) {}
+
+fn main() {
+    match A::B(1, 2) {
+        A::B(_, _, _) => (), //~ ERROR this pattern has 3 fields, but
+        A::D(_) => (), //~ ERROR expected tuple struct or tuple variant, found unit variant `A::D`
+        _ => ()
+    }
+    match 'c' {
+        S { .. } => (),
+        //~^ ERROR mismatched types
+        //~| expected `char`, found struct `S`
+
+        _ => ()
+    }
+    f(true);
+    //~^ ERROR mismatched types
+    //~| expected `char`, found `bool`
+
+    match () {
+        E::V => {} //~ ERROR failed to resolve: use of undeclared type `E`
+    }
+}
diff --git a/tests/ui/pattern/pattern-error-continue.stderr b/tests/ui/pattern/pattern-error-continue.stderr
new file mode 100644
index 00000000000..4c2eff63ab5
--- /dev/null
+++ b/tests/ui/pattern/pattern-error-continue.stderr
@@ -0,0 +1,62 @@
+error[E0433]: failed to resolve: use of undeclared type `E`
+  --> $DIR/pattern-error-continue.rs:33:9
+   |
+LL |         E::V => {}
+   |         ^ use of undeclared type `E`
+
+error[E0532]: expected tuple struct or tuple variant, found unit variant `A::D`
+  --> $DIR/pattern-error-continue.rs:18:9
+   |
+LL |     B(isize, isize),
+   |     --------------- similarly named tuple variant `B` defined here
+LL |     C(isize, isize, isize),
+LL |     D
+   |     - `A::D` defined here
+...
+LL |         A::D(_) => (),
+   |         ^^^^^^^
+   |
+help: use this syntax instead
+   |
+LL |         A::D => (),
+   |         ~~~~
+help: a tuple variant with a similar name exists
+   |
+LL |         A::B(_) => (),
+   |            ~
+
+error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/pattern-error-continue.rs:17:14
+   |
+LL |     B(isize, isize),
+   |       -----  ----- tuple variant has 2 fields
+...
+LL |         A::B(_, _, _) => (),
+   |              ^  ^  ^ expected 2 fields, found 3
+
+error[E0308]: mismatched types
+  --> $DIR/pattern-error-continue.rs:22:9
+   |
+LL |     match 'c' {
+   |           --- this expression has type `char`
+LL |         S { .. } => (),
+   |         ^^^^^^^^ expected `char`, found struct `S`
+
+error[E0308]: mismatched types
+  --> $DIR/pattern-error-continue.rs:28:7
+   |
+LL |     f(true);
+   |     - ^^^^ expected `char`, found `bool`
+   |     |
+   |     arguments to this function are incorrect
+   |
+note: function defined here
+  --> $DIR/pattern-error-continue.rs:13:4
+   |
+LL | fn f(_c: char) {}
+   |    ^ --------
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0023, E0308, E0433, E0532.
+For more information about an error, try `rustc --explain E0023`.
diff --git a/tests/ui/pattern/pattern-ident-path-generics.rs b/tests/ui/pattern/pattern-ident-path-generics.rs
new file mode 100644
index 00000000000..48c02623f28
--- /dev/null
+++ b/tests/ui/pattern/pattern-ident-path-generics.rs
@@ -0,0 +1,6 @@
+fn main() {
+    match Some("foo") {
+        None::<isize> => {}   //~ ERROR mismatched types
+        Some(_) => {}
+    }
+}
diff --git a/tests/ui/pattern/pattern-ident-path-generics.stderr b/tests/ui/pattern/pattern-ident-path-generics.stderr
new file mode 100644
index 00000000000..01b082bd35b
--- /dev/null
+++ b/tests/ui/pattern/pattern-ident-path-generics.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/pattern-ident-path-generics.rs:3:9
+   |
+LL |     match Some("foo") {
+   |           ----------- this expression has type `Option<&str>`
+LL |         None::<isize> => {}
+   |         ^^^^^^^^^^^^^ expected `&str`, found `isize`
+   |
+   = note: expected enum `Option<&str>`
+              found enum `Option<isize>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/pattern-tyvar-2.rs b/tests/ui/pattern/pattern-tyvar-2.rs
new file mode 100644
index 00000000000..7647c766ef9
--- /dev/null
+++ b/tests/ui/pattern/pattern-tyvar-2.rs
@@ -0,0 +1,6 @@
+enum Bar { T1((), Option<Vec<isize>>), T2, }
+
+fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; } _ => { panic!(); } } }
+//~^ ERROR cannot multiply `Vec<isize>` by `{integer}`
+
+fn main() { }
diff --git a/tests/ui/pattern/pattern-tyvar-2.stderr b/tests/ui/pattern/pattern-tyvar-2.stderr
new file mode 100644
index 00000000000..121817e7056
--- /dev/null
+++ b/tests/ui/pattern/pattern-tyvar-2.stderr
@@ -0,0 +1,11 @@
+error[E0369]: cannot multiply `Vec<isize>` by `{integer}`
+  --> $DIR/pattern-tyvar-2.rs:3:71
+   |
+LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3; } _ => { panic!(); } } }
+   |                                                                     - ^ - {integer}
+   |                                                                     |
+   |                                                                     Vec<isize>
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0369`.
diff --git a/tests/ui/pattern/pattern-tyvar.rs b/tests/ui/pattern/pattern-tyvar.rs
new file mode 100644
index 00000000000..e2cbf05193d
--- /dev/null
+++ b/tests/ui/pattern/pattern-tyvar.rs
@@ -0,0 +1,12 @@
+enum Bar { T1((), Option<Vec<isize>>), T2 }
+
+fn foo(t: Bar) {
+    match t {
+      Bar::T1(_, Some::<isize>(x)) => { //~ ERROR mismatched types
+        println!("{}", x);
+      }
+      _ => { panic!(); }
+    }
+}
+
+fn main() { }
diff --git a/tests/ui/pattern/pattern-tyvar.stderr b/tests/ui/pattern/pattern-tyvar.stderr
new file mode 100644
index 00000000000..f1e2a9d72ce
--- /dev/null
+++ b/tests/ui/pattern/pattern-tyvar.stderr
@@ -0,0 +1,14 @@
+error[E0308]: mismatched types
+  --> $DIR/pattern-tyvar.rs:5:18
+   |
+LL |     match t {
+   |           - this expression has type `Bar`
+LL |       Bar::T1(_, Some::<isize>(x)) => {
+   |                  ^^^^^^^^^^^^^^^^ expected struct `Vec`, found `isize`
+   |
+   = note: expected enum `Option<Vec<isize>>`
+              found enum `Option<isize>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/pattern/rest-pat-semantic-disallowed.rs b/tests/ui/pattern/rest-pat-semantic-disallowed.rs
new file mode 100644
index 00000000000..156285e0f9f
--- /dev/null
+++ b/tests/ui/pattern/rest-pat-semantic-disallowed.rs
@@ -0,0 +1,83 @@
+// Here we test that rest patterns, i.e. `..`, are not allowed
+// outside of slice (+ ident patterns within those), tuple,
+// and tuple struct patterns and that duplicates are caught in these contexts.
+
+#![feature(box_patterns)]
+
+fn main() {}
+
+macro_rules! mk_pat {
+    () => { .. } //~ ERROR `..` patterns are not allowed here
+}
+
+fn rest_patterns() {
+    let mk_pat!();
+
+    // Top level:
+    fn foo(..: u8) {} //~ ERROR `..` patterns are not allowed here
+    let ..;  //~ ERROR `..` patterns are not allowed here
+
+    // Box patterns:
+    let box ..;  //~ ERROR `..` patterns are not allowed here
+
+    // In or-patterns:
+    match 1 {
+        1 | .. => {} //~ ERROR `..` patterns are not allowed here
+    }
+
+    // Ref patterns:
+    let &..; //~ ERROR `..` patterns are not allowed here
+    let &mut ..; //~ ERROR `..` patterns are not allowed here
+
+    // Ident patterns:
+    let x @ ..; //~ ERROR `..` patterns are not allowed here
+    //~^ ERROR type annotations needed
+    let ref x @ ..; //~ ERROR `..` patterns are not allowed here
+    let ref mut x @ ..; //~ ERROR `..` patterns are not allowed here
+
+    // Tuple:
+    let (..): (u8,); // OK.
+    let (..,): (u8,); // OK.
+    let (
+        ..,
+        .., //~ ERROR `..` can only be used once per tuple pattern
+        .. //~ ERROR `..` can only be used once per tuple pattern
+    ): (u8, u8, u8);
+    let (
+        ..,
+        x,
+        .. //~ ERROR `..` can only be used once per tuple pattern
+    ): (u8, u8, u8);
+
+    struct A(u8, u8, u8);
+
+    // Tuple struct (same idea as for tuple patterns):
+    let A(..); // OK.
+    let A(..,); // OK.
+    let A(
+        ..,
+        .., //~ ERROR `..` can only be used once per tuple struct pattern
+        .. //~ ERROR `..` can only be used once per tuple struct pattern
+    );
+    let A(
+        ..,
+        x,
+        .. //~ ERROR `..` can only be used once per tuple struct pattern
+    );
+
+    // Array/Slice:
+    let [..]: &[u8]; // OK.
+    let [..,]: &[u8]; // OK.
+    let [
+        ..,
+        .., //~ ERROR `..` can only be used once per slice pattern
+        .. //~ ERROR `..` can only be used once per slice pattern
+    ]: &[u8];
+    let [
+        ..,
+        ref x @ .., //~ ERROR `..` can only be used once per slice pattern
+        ref mut y @ .., //~ ERROR `..` can only be used once per slice pattern
+        (ref z @ ..), //~ ERROR `..` patterns are not allowed here
+        .. //~ ERROR `..` can only be used once per slice pattern
+    ]: &[u8];
+}
diff --git a/tests/ui/pattern/rest-pat-semantic-disallowed.stderr b/tests/ui/pattern/rest-pat-semantic-disallowed.stderr
new file mode 100644
index 00000000000..beba7def96f
--- /dev/null
+++ b/tests/ui/pattern/rest-pat-semantic-disallowed.stderr
@@ -0,0 +1,201 @@
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:10:13
+   |
+LL |     () => { .. }
+   |             ^^
+...
+LL |     let mk_pat!();
+   |         --------- in this macro invocation
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+   = note: this error originates in the macro `mk_pat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:18:9
+   |
+LL |     let ..;
+   |         ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:21:13
+   |
+LL |     let box ..;
+   |             ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:25:13
+   |
+LL |         1 | .. => {}
+   |             ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:29:10
+   |
+LL |     let &..;
+   |          ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:30:14
+   |
+LL |     let &mut ..;
+   |              ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:33:13
+   |
+LL |     let x @ ..;
+   |             ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:35:17
+   |
+LL |     let ref x @ ..;
+   |                 ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:36:21
+   |
+LL |     let ref mut x @ ..;
+   |                     ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` can only be used once per tuple pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:43:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+   |         ^^ can only be used once per tuple pattern
+
+error: `..` can only be used once per tuple pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:44:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+LL |         ..
+   |         ^^ can only be used once per tuple pattern
+
+error: `..` can only be used once per tuple pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:49:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         x,
+LL |         ..
+   |         ^^ can only be used once per tuple pattern
+
+error: `..` can only be used once per tuple struct pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:59:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+   |         ^^ can only be used once per tuple struct pattern
+
+error: `..` can only be used once per tuple struct pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:60:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+LL |         ..
+   |         ^^ can only be used once per tuple struct pattern
+
+error: `..` can only be used once per tuple struct pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:65:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         x,
+LL |         ..
+   |         ^^ can only be used once per tuple struct pattern
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:73:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+   |         ^^ can only be used once per slice pattern
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:74:9
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ..,
+LL |         ..
+   |         ^^ can only be used once per slice pattern
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:78:17
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ref x @ ..,
+   |                 ^^ can only be used once per slice pattern
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:79:21
+   |
+LL |         ..,
+   |         -- previously used here
+LL |         ref x @ ..,
+LL |         ref mut y @ ..,
+   |                     ^^ can only be used once per slice pattern
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:80:18
+   |
+LL |         (ref z @ ..),
+   |                  ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/rest-pat-semantic-disallowed.rs:81:9
+   |
+LL |         ..,
+   |         -- previously used here
+...
+LL |         ..
+   |         ^^ can only be used once per slice pattern
+
+error: `..` patterns are not allowed here
+  --> $DIR/rest-pat-semantic-disallowed.rs:17:12
+   |
+LL |     fn foo(..: u8) {}
+   |            ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0282]: type annotations needed
+  --> $DIR/rest-pat-semantic-disallowed.rs:33:9
+   |
+LL |     let x @ ..;
+   |         ^^^^^^
+   |
+help: consider giving this pattern a type
+   |
+LL |     let x @ ..: /* Type */;
+   |               ++++++++++++
+
+error: aborting due to 23 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/pattern/rest-pat-syntactic.rs b/tests/ui/pattern/rest-pat-syntactic.rs
new file mode 100644
index 00000000000..4da5a2db767
--- /dev/null
+++ b/tests/ui/pattern/rest-pat-syntactic.rs
@@ -0,0 +1,73 @@
+// Here we test that `..` is allowed in all pattern locations *syntactically*.
+// The semantic test is in `rest-pat-semantic-disallowed.rs`.
+
+// check-pass
+
+fn main() {}
+
+macro_rules! accept_pat {
+    ($p:pat) => {}
+}
+
+accept_pat!(..);
+
+#[cfg(FALSE)]
+fn rest_patterns() {
+    // Top level:
+    fn foo(..: u8) {}
+    let ..;
+
+    // Box patterns:
+    let box ..;
+    //~^ WARN box pattern syntax is experimental
+    //~| WARN unstable syntax
+
+    // In or-patterns:
+    match x {
+        .. | .. => {}
+    }
+
+    // Ref patterns:
+    let &..;
+    let &mut ..;
+
+    // Ident patterns:
+    let x @ ..;
+    let ref x @ ..;
+    let ref mut x @ ..;
+
+    // Tuple:
+    let (..); // This is interpreted as a tuple pattern, not a parenthesis one.
+    let (..,); // Allowing trailing comma.
+    let (.., .., ..); // Duplicates also.
+    let (.., P, ..); // Including with things in between.
+
+    // Tuple struct (same idea as for tuple patterns):
+    let A(..);
+    let A(..,);
+    let A(.., .., ..);
+    let A(.., P, ..);
+
+    // Array/Slice (like with tuple patterns):
+    let [..];
+    let [..,];
+    let [.., .., ..];
+    let [.., P, ..];
+
+    // Random walk to guard against special casing:
+    match x {
+        .. |
+        [
+            (
+                box .., //~ WARN box pattern syntax is experimental
+                &(..),
+                &mut ..,
+                x @ ..
+            ),
+            ref x @ ..,
+        ] |
+        ref mut x @ ..
+        => {}
+    }
+    //~| WARN unstable syntax
+}
diff --git a/tests/ui/pattern/rest-pat-syntactic.stderr b/tests/ui/pattern/rest-pat-syntactic.stderr
new file mode 100644
index 00000000000..37019b7d5ba
--- /dev/null
+++ b/tests/ui/pattern/rest-pat-syntactic.stderr
@@ -0,0 +1,24 @@
+warning: box pattern syntax is experimental
+  --> $DIR/rest-pat-syntactic.rs:21:9
+   |
+LL |     let box ..;
+   |         ^^^^^^
+   |
+   = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
+   = help: add `#![feature(box_patterns)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: box pattern syntax is experimental
+  --> $DIR/rest-pat-syntactic.rs:62:17
+   |
+LL |                 box ..,
+   |                 ^^^^^^
+   |
+   = note: see issue #29641 <https://github.com/rust-lang/rust/issues/29641> for more information
+   = help: add `#![feature(box_patterns)]` to the crate attributes to enable
+   = warning: unstable syntax can change at any point in the future, causing a hard error!
+   = note: for more information, see issue #65860 <https://github.com/rust-lang/rust/issues/65860>
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/pattern/size-and-align.rs b/tests/ui/pattern/size-and-align.rs
new file mode 100644
index 00000000000..a32b5de7292
--- /dev/null
+++ b/tests/ui/pattern/size-and-align.rs
@@ -0,0 +1,20 @@
+// run-pass
+
+#![allow(non_camel_case_types)]
+enum clam<T> { a(T, isize), b, }
+
+fn uhoh<T>(v: Vec<clam<T>> ) {
+    match v[1] {
+      clam::a::<T>(ref _t, ref u) => {
+          println!("incorrect");
+          println!("{}", u);
+          panic!();
+      }
+      clam::b::<T> => { println!("correct"); }
+    }
+}
+
+pub fn main() {
+    let v: Vec<clam<isize>> = vec![clam::b::<isize>, clam::b::<isize>, clam::a::<isize>(42, 17)];
+    uhoh::<isize>(v);
+}
diff --git a/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.fixed b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.fixed
new file mode 100644
index 00000000000..b28dce88105
--- /dev/null
+++ b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+
+fn main() {
+    match Some(1) { //~ ERROR non-exhaustive patterns: `None` not covered
+        Some(1) => {}
+        // hello
+        Some(_) => {}
+        None => todo!()
+    }
+}
diff --git a/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.rs b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.rs
new file mode 100644
index 00000000000..42493a63271
--- /dev/null
+++ b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.rs
@@ -0,0 +1,9 @@
+// run-rustfix
+
+fn main() {
+    match Some(1) { //~ ERROR non-exhaustive patterns: `None` not covered
+        Some(1) => {}
+        // hello
+        Some(_) => {}
+    }
+}
diff --git a/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.stderr b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.stderr
new file mode 100644
index 00000000000..2a016048f2f
--- /dev/null
+++ b/tests/ui/pattern/suggest-adding-appropriate-missing-pattern-excluding-comments.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `None` not covered
+  --> $DIR/suggest-adding-appropriate-missing-pattern-excluding-comments.rs:4:11
+   |
+LL |     match Some(1) {
+   |           ^^^^^^^ pattern `None` not covered
+   |
+note: `Option<i32>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<i32>`
+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 ~         Some(_) => {}
+LL +         None => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs
new file mode 100644
index 00000000000..7d1cac8a442
--- /dev/null
+++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.rs
@@ -0,0 +1,32 @@
+// The precise semantics of inhabitedness with respect to unions and references is currently
+// undecided. This test file currently checks a conservative choice.
+
+#![feature(exhaustive_patterns)]
+#![feature(never_type)]
+
+#![allow(dead_code)]
+#![allow(unreachable_code)]
+
+pub union Foo {
+    foo: !,
+}
+
+fn uninhab_ref() -> &'static ! {
+    unimplemented!()
+}
+
+fn uninhab_union() -> Foo {
+    unimplemented!()
+}
+
+fn match_on_uninhab() {
+    match uninhab_ref() {
+        //~^ ERROR non-exhaustive patterns: type `&!` is non-empty
+    }
+
+    match uninhab_union() {
+        //~^ ERROR non-exhaustive patterns: type `Foo` is non-empty
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr
new file mode 100644
index 00000000000..cd5c283f9fd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/always-inhabited-union-ref.stderr
@@ -0,0 +1,37 @@
+error[E0004]: non-exhaustive patterns: type `&!` is non-empty
+  --> $DIR/always-inhabited-union-ref.rs:23:11
+   |
+LL |     match uninhab_ref() {
+   |           ^^^^^^^^^^^^^
+   |
+   = note: the matched value is of type `&!`
+   = note: references are always considered inhabited
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match uninhab_ref() {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error[E0004]: non-exhaustive patterns: type `Foo` is non-empty
+  --> $DIR/always-inhabited-union-ref.rs:27:11
+   |
+LL |     match uninhab_union() {
+   |           ^^^^^^^^^^^^^^^
+   |
+note: `Foo` defined here
+  --> $DIR/always-inhabited-union-ref.rs:10:11
+   |
+LL | pub union Foo {
+   |           ^^^
+   = note: the matched value is of type `Foo`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match uninhab_union() {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/auxiliary/empty.rs b/tests/ui/pattern/usefulness/auxiliary/empty.rs
new file mode 100644
index 00000000000..29a03c9e8b5
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/empty.rs
@@ -0,0 +1,10 @@
+#![crate_type = "rlib"]
+pub enum EmptyForeignEnum {}
+
+pub struct VisiblyUninhabitedForeignStruct {
+    pub field: EmptyForeignEnum,
+}
+
+pub struct SecretlyUninhabitedForeignStruct {
+    _priv: EmptyForeignEnum,
+}
diff --git a/tests/ui/pattern/usefulness/auxiliary/hidden.rs b/tests/ui/pattern/usefulness/auxiliary/hidden.rs
new file mode 100644
index 00000000000..364514ba1d3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/hidden.rs
@@ -0,0 +1,14 @@
+pub enum HiddenEnum {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
+
+#[derive(Default)]
+pub struct HiddenStruct {
+    pub one: u8,
+    pub two: bool,
+    #[doc(hidden)]
+    pub hide: usize,
+}
diff --git a/tests/ui/pattern/usefulness/auxiliary/unstable.rs b/tests/ui/pattern/usefulness/auxiliary/unstable.rs
new file mode 100644
index 00000000000..a06b3a6e4e5
--- /dev/null
+++ b/tests/ui/pattern/usefulness/auxiliary/unstable.rs
@@ -0,0 +1,23 @@
+#![feature(staged_api)]
+#![stable(feature = "stable_test_feature", since = "1.0.0")]
+
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+pub enum UnstableEnum {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    Stable2,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    Unstable,
+}
+
+#[derive(Default)]
+#[stable(feature = "stable_test_feature", since = "1.0.0")]
+pub struct UnstableStruct {
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    pub stable: bool,
+    #[stable(feature = "stable_test_feature", since = "1.0.0")]
+    pub stable2: usize,
+    #[unstable(feature = "unstable_test_feature", issue = "none")]
+    pub unstable: u8,
+}
diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs
new file mode 100644
index 00000000000..02599d7c05b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.rs
@@ -0,0 +1,18 @@
+#![allow(warnings)]
+
+struct MyType;
+
+impl PartialEq<usize> for MyType {
+    fn eq(&self, y: &usize) -> bool {
+        true
+    }
+}
+
+const CONSTANT: &&MyType = &&MyType;
+
+fn main() {
+    if let CONSTANT = &&MyType {
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        println!("did match!");
+    }
+}
diff --git a/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr
new file mode 100644
index 00000000000..358421cd6d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-partial_eq-fallback-ice.stderr
@@ -0,0 +1,8 @@
+error: to use a constant of type `MyType` in a pattern, `MyType` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/const-partial_eq-fallback-ice.rs:14:12
+   |
+LL |     if let CONSTANT = &&MyType {
+   |            ^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/const-pat-ice.rs b/tests/ui/pattern/usefulness/const-pat-ice.rs
new file mode 100644
index 00000000000..abfacf3936b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-pat-ice.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+const FOO: &&&u32 = &&&42;
+
+fn main() {
+    match unimplemented!() {
+        &&&42 => {},
+        FOO => {},
+        _ => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/const-private-fields.rs b/tests/ui/pattern/usefulness/const-private-fields.rs
new file mode 100644
index 00000000000..06c832ca46a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/const-private-fields.rs
@@ -0,0 +1,30 @@
+// check-pass
+//
+// Check that we don't ignore private fields in usefulness checking
+#![deny(unreachable_patterns)]
+
+mod inner {
+    #[derive(PartialEq, Eq)]
+    pub struct PrivateField {
+        pub x: bool,
+        y: bool,
+    }
+
+    pub const FOO: PrivateField = PrivateField { x: true, y: true };
+    pub const BAR: PrivateField = PrivateField { x: true, y: false };
+}
+use inner::*;
+
+fn main() {
+    match FOO {
+        FOO => {}
+        BAR => {}
+        _ => {}
+    }
+
+    match FOO {
+        FOO => {}
+        PrivateField { x: true, .. } => {}
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/consts-opaque.rs b/tests/ui/pattern/usefulness/consts-opaque.rs
new file mode 100644
index 00000000000..ca4fcd85bb6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/consts-opaque.rs
@@ -0,0 +1,145 @@
+// This file tests the exhaustiveness algorithm on opaque constants. Most of the examples give
+// unnecessary warnings because const_to_pat.rs converts a constant pattern to a wildcard when the
+// constant is not allowed as a pattern. This is an edge case so we may not care to fix it.
+// See also https://github.com/rust-lang/rust/issues/78057
+
+#![deny(unreachable_patterns)]
+
+#[derive(PartialEq)]
+struct Foo(i32);
+impl Eq for Foo {}
+const FOO: Foo = Foo(42);
+const FOO_REF: &Foo = &Foo(42);
+const FOO_REF_REF: &&Foo = &&Foo(42);
+
+#[derive(PartialEq)]
+struct Bar;
+impl Eq for Bar {}
+const BAR: Bar = Bar;
+
+#[derive(PartialEq)]
+enum Baz {
+    Baz1,
+    Baz2
+}
+impl Eq for Baz {}
+const BAZ: Baz = Baz::Baz1;
+
+fn main() {
+    match FOO {
+        FOO => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    match FOO_REF {
+        FOO_REF => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Foo(_) => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    // This used to cause an ICE (https://github.com/rust-lang/rust/issues/78071)
+    match FOO_REF_REF {
+        FOO_REF_REF => {}
+        //~^ WARNING must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| WARNING this was previously accepted by the compiler but is being phased out
+        Foo(_) => {}
+    }
+
+    match BAR {
+        Bar => {}
+        BAR => {} // should not be emitting unreachable warning
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAR {
+        BAR => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Bar => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAR {
+        BAR => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        BAR => {} // should not be emitting unreachable warning
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        //~| ERROR unreachable pattern
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Baz::Baz1 => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        Baz::Baz1 => {}
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        _ => {}
+        //~^ ERROR unreachable pattern
+    }
+
+    match BAZ {
+        BAZ => {}
+        //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
+        Baz::Baz2 => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+        _ => {} // should not be emitting unreachable warning
+        //~^ ERROR unreachable pattern
+    }
+
+    type Quux = fn(usize, usize) -> usize;
+    fn quux(a: usize, b: usize) -> usize { a + b }
+    const QUUX: Quux = quux;
+
+    match QUUX {
+        QUUX => {}
+        QUUX => {}
+        _ => {}
+    }
+
+    #[derive(PartialEq, Eq)]
+    struct Wrap<T>(T);
+    const WRAPQUUX: Wrap<Quux> = Wrap(quux);
+
+    match WRAPQUUX {
+        WRAPQUUX => {}
+        WRAPQUUX => {}
+        Wrap(_) => {}
+    }
+
+    match WRAPQUUX {
+        Wrap(_) => {}
+        WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
+        //~^ ERROR unreachable pattern
+    }
+
+    #[derive(PartialEq, Eq)]
+    enum WhoKnows<T> {
+        Yay(T),
+        Nope,
+    };
+    const WHOKNOWSQUUX: WhoKnows<Quux> = WhoKnows::Yay(quux);
+
+    match WHOKNOWSQUUX {
+        WHOKNOWSQUUX => {}
+        WhoKnows::Yay(_) => {}
+        WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
+        //~^ ERROR unreachable pattern
+        WhoKnows::Nope => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/consts-opaque.stderr b/tests/ui/pattern/usefulness/consts-opaque.stderr
new file mode 100644
index 00000000000..35396751abe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/consts-opaque.stderr
@@ -0,0 +1,202 @@
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:30:9
+   |
+LL |         FOO => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:32:9
+   |
+LL |         FOO => {}
+   |         --- matches any value
+LL |
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/consts-opaque.rs:6:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:37:9
+   |
+LL |         FOO_REF => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:39:9
+   |
+LL |         FOO_REF => {}
+   |         ------- matches any value
+LL |
+LL |         Foo(_) => {} // should not be emitting unreachable warning
+   |         ^^^^^^ unreachable pattern
+
+warning: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:45:9
+   |
+LL |         FOO_REF_REF => {}
+   |         ^^^^^^^^^^^
+   |
+   = 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 #62411 <https://github.com/rust-lang/rust/issues/62411>
+   = note: `#[warn(indirect_structural_match)]` on by default
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:53:9
+   |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:53:9
+   |
+LL |         Bar => {}
+   |         --- matches any value
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:56:9
+   |
+LL |         Bar => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:61:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:63:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
+LL |         Bar => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:65:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:70:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: to use a constant of type `Bar` in a pattern, `Bar` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:72:9
+   |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:72:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+LL |
+LL |         BAR => {} // should not be emitting unreachable warning
+   |         ^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:75:9
+   |
+LL |         BAR => {}
+   |         --- matches any value
+...
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:80:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:82:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         Baz::Baz1 => {} // should not be emitting unreachable warning
+   |         ^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:84:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+...
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:90:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:92:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         _ => {}
+   |         ^ unreachable pattern
+
+error: to use a constant of type `Baz` in a pattern, `Baz` must be annotated with `#[derive(PartialEq, Eq)]`
+  --> $DIR/consts-opaque.rs:97:9
+   |
+LL |         BAZ => {}
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:99:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+LL |
+LL |         Baz::Baz2 => {} // should not be emitting unreachable warning
+   |         ^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:101:9
+   |
+LL |         BAZ => {}
+   |         --- matches any value
+...
+LL |         _ => {} // should not be emitting unreachable warning
+   |         ^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:127:9
+   |
+LL |         Wrap(_) => {}
+   |         ------- matches any value
+LL |         WRAPQUUX => {} // detected unreachable because we do inspect the `Wrap` layer
+   |         ^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/consts-opaque.rs:141:9
+   |
+LL |         WHOKNOWSQUUX => {} // detected unreachable because we do inspect the `WhoKnows` layer
+   |         ^^^^^^^^^^^^
+
+error: aborting due to 24 previous errors; 1 warning emitted
+
diff --git a/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs
new file mode 100644
index 00000000000..c85af7f3bcc
--- /dev/null
+++ b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.rs
@@ -0,0 +1,16 @@
+#![feature(if_let_guard)]
+
+#![deny(irrefutable_let_patterns)]
+
+fn main() {
+    if let _ = 5 {} //~ ERROR irrefutable `if let` pattern
+
+    while let _ = 5 { //~ ERROR irrefutable `while let` pattern
+        break;
+    }
+
+    match 5 {
+        _ if let _ = 2 => {} //~ ERROR irrefutable `if let` guard pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
new file mode 100644
index 00000000000..cdb6b5c7a49
--- /dev/null
+++ b/tests/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
@@ -0,0 +1,34 @@
+error: irrefutable `if let` pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:6:8
+   |
+LL |     if let _ = 5 {}
+   |        ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `if let` is useless
+   = help: consider replacing the `if let` with a `let`
+note: the lint level is defined here
+  --> $DIR/deny-irrefutable-let-patterns.rs:3:9
+   |
+LL | #![deny(irrefutable_let_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: irrefutable `while let` pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:8:11
+   |
+LL |     while let _ = 5 {
+   |           ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the loop will never exit
+   = help: consider instead using a `loop { ... }` with a `let` inside it
+
+error: irrefutable `if let` guard pattern
+  --> $DIR/deny-irrefutable-let-patterns.rs:13:18
+   |
+LL |         _ if let _ = 2 => {}
+   |                  ^
+   |
+   = note: this pattern will always match, so the guard is useless
+   = help: consider removing the guard and adding a `let` inside the match arm
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.rs b/tests/ui/pattern/usefulness/doc-hidden-fields.rs
new file mode 100644
index 00000000000..4163b87dc85
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-fields.rs
@@ -0,0 +1,26 @@
+// aux-build:hidden.rs
+
+extern crate hidden;
+
+use hidden::HiddenStruct;
+
+struct InCrate {
+    a: usize,
+    b: bool,
+    #[doc(hidden)]
+    im_hidden: u8
+}
+
+fn main() {
+    let HiddenStruct { one, two } = HiddenStruct::default();
+    //~^ pattern requires `..` due to inaccessible fields
+
+    let HiddenStruct { one } = HiddenStruct::default();
+    //~^ pattern does not mention field `two` and inaccessible fields
+
+    let HiddenStruct { one, hide } = HiddenStruct::default();
+    //~^ pattern does not mention field `two`
+
+    let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 };
+    //~^ pattern does not mention field `im_hidden`
+}
diff --git a/tests/ui/pattern/usefulness/doc-hidden-fields.stderr b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr
new file mode 100644
index 00000000000..f277bfbc884
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-fields.stderr
@@ -0,0 +1,59 @@
+error: pattern requires `..` due to inaccessible fields
+  --> $DIR/doc-hidden-fields.rs:15:9
+   |
+LL |     let HiddenStruct { one, two } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: ignore the inaccessible and unused fields
+   |
+LL |     let HiddenStruct { one, two, .. } = HiddenStruct::default();
+   |                                ++++
+
+error[E0027]: pattern does not mention field `two` and inaccessible fields
+  --> $DIR/doc-hidden-fields.rs:18:9
+   |
+LL |     let HiddenStruct { one } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^ missing field `two` and inaccessible fields
+   |
+help: include the missing field in the pattern and ignore the inaccessible fields
+   |
+LL |     let HiddenStruct { one, two, .. } = HiddenStruct::default();
+   |                           ~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let HiddenStruct { one, .. } = HiddenStruct::default();
+   |                           ~~~~~~
+
+error[E0027]: pattern does not mention field `two`
+  --> $DIR/doc-hidden-fields.rs:21:9
+   |
+LL |     let HiddenStruct { one, hide } = HiddenStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `two`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let HiddenStruct { one, hide, two } = HiddenStruct::default();
+   |                                 ~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let HiddenStruct { one, hide, .. } = HiddenStruct::default();
+   |                                 ~~~~~~
+
+error[E0027]: pattern does not mention field `im_hidden`
+  --> $DIR/doc-hidden-fields.rs:24:9
+   |
+LL |     let InCrate { a, b } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |         ^^^^^^^^^^^^^^^^ missing field `im_hidden`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let InCrate { a, b, im_hidden } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |                       ~~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let InCrate { a, b, .. } = InCrate { a: 0, b: false, im_hidden: 0 };
+   |                       ~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
new file mode 100644
index 00000000000..5d4181a30f0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs
@@ -0,0 +1,43 @@
+// aux-build:hidden.rs
+
+extern crate hidden;
+
+use hidden::HiddenEnum;
+
+enum InCrate {
+    A,
+    B,
+    #[doc(hidden)]
+    C,
+}
+
+fn main() {
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+        HiddenEnum::B => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+        HiddenEnum::C => {}
+    }
+    //~^^^^ non-exhaustive patterns: `HiddenEnum::B` not covered
+
+    match HiddenEnum::A {
+        HiddenEnum::A => {}
+    }
+    //~^^^ non-exhaustive patterns: `HiddenEnum::B` and `_` not covered
+
+    match None {
+        None => {}
+        Some(HiddenEnum::A) => {}
+    }
+    //~^^^^ non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered
+
+    match InCrate::A {
+        InCrate::A => {}
+        InCrate::B => {}
+    }
+    //~^^^^ non-exhaustive patterns: `InCrate::C` not covered
+}
diff --git a/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
new file mode 100644
index 00000000000..17e1a2304a1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.stderr
@@ -0,0 +1,102 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:15:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ pattern `_` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:1:1
+   |
+LL | pub enum HiddenEnum {
+   | ^^^^^^^^^^^^^^^^^^^
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::B => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `HiddenEnum::B` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:21:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ pattern `HiddenEnum::B` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:3:5
+   |
+LL | pub enum HiddenEnum {
+   | -------------------
+LL |     A,
+LL |     B,
+   |     ^ not covered
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::C => {}
+LL +         HiddenEnum::B => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `HiddenEnum::B` and `_` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:27:11
+   |
+LL |     match HiddenEnum::A {
+   |           ^^^^^^^^^^^^^ patterns `HiddenEnum::B` and `_` not covered
+   |
+note: `HiddenEnum` defined here
+  --> $DIR/auxiliary/hidden.rs:3:5
+   |
+LL | pub enum HiddenEnum {
+   | -------------------
+LL |     A,
+LL |     B,
+   |     ^ not covered
+   = note: the matched value is of type `HiddenEnum`
+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 ~         HiddenEnum::A => {}
+LL +         HiddenEnum::B | _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(HiddenEnum::B)` and `Some(_)` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:32:11
+   |
+LL |     match None {
+   |           ^^^^ patterns `Some(HiddenEnum::B)` and `Some(_)` not covered
+   |
+note: `Option<HiddenEnum>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<HiddenEnum>`
+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 ~         Some(HiddenEnum::A) => {}
+LL +         Some(HiddenEnum::B) | Some(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `InCrate::C` not covered
+  --> $DIR/doc-hidden-non-exhaustive.rs:38:11
+   |
+LL |     match InCrate::A {
+   |           ^^^^^^^^^^ pattern `InCrate::C` not covered
+   |
+note: `InCrate` defined here
+  --> $DIR/doc-hidden-non-exhaustive.rs:11:5
+   |
+LL | enum InCrate {
+   |      -------
+...
+LL |     C,
+   |     ^ not covered
+   = note: the matched value is of type `InCrate`
+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 ~         InCrate::B => {}
+LL +         InCrate::C => todo!()
+   |
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
new file mode 100644
index 00000000000..5e12bc1d22f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.exhaustive_patterns.stderr
@@ -0,0 +1,303 @@
+error: unreachable pattern
+  --> $DIR/empty-match.rs:37:9
+   |
+LL |         _ => {},
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-match.rs:8:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:40:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:47:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:50:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:57:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:60:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+  --> $DIR/empty-match.rs:78:20
+   |
+LL |     match_no_arms!(0u8);
+   |                    ^^^
+   |
+   = 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
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
+  --> $DIR/empty-match.rs:79:20
+   |
+LL |     match_no_arms!(NonEmptyStruct1);
+   |                    ^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+LL | struct NonEmptyStruct1;
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
+  --> $DIR/empty-match.rs:80:20
+   |
+LL |     match_no_arms!(NonEmptyStruct2(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+LL | struct NonEmptyStruct2(bool);
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
+  --> $DIR/empty-match.rs:81:20
+   |
+LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+LL | union NonEmptyUnion1 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
+  --> $DIR/empty-match.rs:82:20
+   |
+LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+LL | union NonEmptyUnion2 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:83:20
+   |
+LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+LL | enum NonEmptyEnum1 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:84:20
+   |
+LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+LL | enum NonEmptyEnum2 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+LL |     Bar,
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+  --> $DIR/empty-match.rs:85:20
+   |
+LL |     match_no_arms!(NonEmptyEnum5::V1);
+   |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |
+note: `NonEmptyEnum5` defined here
+  --> $DIR/empty-match.rs:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyEnum5`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/empty-match.rs:87:24
+   |
+LL |     match_guarded_arm!(0u8);
+   |                        ^^^ pattern `_` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
+  --> $DIR/empty-match.rs:88:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct1);
+   |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+LL | struct NonEmptyStruct1;
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct1`
+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 ~             _ if false => {}
+LL +             NonEmptyStruct1 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+  --> $DIR/empty-match.rs:89:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct2(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+LL | struct NonEmptyStruct2(bool);
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct2`
+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 ~             _ if false => {}
+LL +             NonEmptyStruct2(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+  --> $DIR/empty-match.rs:90:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+LL | union NonEmptyUnion1 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion1`
+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 ~             _ if false => {}
+LL +             NonEmptyUnion1 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+  --> $DIR/empty-match.rs:91:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+LL | union NonEmptyUnion2 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion2`
+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 ~             _ if false => {}
+LL +             NonEmptyUnion2 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:92:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+LL | enum NonEmptyEnum1 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum1`
+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 ~             _ if false => {}
+LL +             NonEmptyEnum1::Foo(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:93:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+LL | enum NonEmptyEnum2 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+LL |     Bar,
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum2`
+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 ~             _ if false => {}
+LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+  --> $DIR/empty-match.rs:94:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum5::V1);
+   |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |
+note: `NonEmptyEnum5` defined here
+  --> $DIR/empty-match.rs:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyEnum5`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error: aborting due to 22 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.normal.stderr b/tests/ui/pattern/usefulness/empty-match.normal.stderr
new file mode 100644
index 00000000000..5e12bc1d22f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.normal.stderr
@@ -0,0 +1,303 @@
+error: unreachable pattern
+  --> $DIR/empty-match.rs:37:9
+   |
+LL |         _ => {},
+   |         ^
+   |
+note: the lint level is defined here
+  --> $DIR/empty-match.rs:8:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:40:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:47:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:50:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:57:9
+   |
+LL |         _ => {},
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/empty-match.rs:60:9
+   |
+LL |         _ if false => {},
+   |         ^
+
+error[E0004]: non-exhaustive patterns: type `u8` is non-empty
+  --> $DIR/empty-match.rs:78:20
+   |
+LL |     match_no_arms!(0u8);
+   |                    ^^^
+   |
+   = 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
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct1` is non-empty
+  --> $DIR/empty-match.rs:79:20
+   |
+LL |     match_no_arms!(NonEmptyStruct1);
+   |                    ^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+LL | struct NonEmptyStruct1;
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyStruct2` is non-empty
+  --> $DIR/empty-match.rs:80:20
+   |
+LL |     match_no_arms!(NonEmptyStruct2(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+LL | struct NonEmptyStruct2(bool);
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty
+  --> $DIR/empty-match.rs:81:20
+   |
+LL |     match_no_arms!((NonEmptyUnion1 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+LL | union NonEmptyUnion1 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty
+  --> $DIR/empty-match.rs:82:20
+   |
+LL |     match_no_arms!((NonEmptyUnion2 { foo: () }));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+LL | union NonEmptyUnion2 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:83:20
+   |
+LL |     match_no_arms!(NonEmptyEnum1::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+LL | enum NonEmptyEnum1 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum1`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:84:20
+   |
+LL |     match_no_arms!(NonEmptyEnum2::Foo(true));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+LL | enum NonEmptyEnum2 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+LL |     Bar,
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum2`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+  --> $DIR/empty-match.rs:85:20
+   |
+LL |     match_no_arms!(NonEmptyEnum5::V1);
+   |                    ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |
+note: `NonEmptyEnum5` defined here
+  --> $DIR/empty-match.rs:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyEnum5`
+   = help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or multiple match arms
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/empty-match.rs:87:24
+   |
+LL |     match_guarded_arm!(0u8);
+   |                        ^^^ pattern `_` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct1` not covered
+  --> $DIR/empty-match.rs:88:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct1);
+   |                        ^^^^^^^^^^^^^^^ pattern `NonEmptyStruct1` not covered
+   |
+note: `NonEmptyStruct1` defined here
+  --> $DIR/empty-match.rs:14:8
+   |
+LL | struct NonEmptyStruct1;
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct1`
+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 ~             _ if false => {}
+LL +             NonEmptyStruct1 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyStruct2(_)` not covered
+  --> $DIR/empty-match.rs:89:24
+   |
+LL |     match_guarded_arm!(NonEmptyStruct2(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct2(_)` not covered
+   |
+note: `NonEmptyStruct2` defined here
+  --> $DIR/empty-match.rs:15:8
+   |
+LL | struct NonEmptyStruct2(bool);
+   |        ^^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyStruct2`
+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 ~             _ if false => {}
+LL +             NonEmptyStruct2(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
+  --> $DIR/empty-match.rs:90:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion1 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
+   |
+note: `NonEmptyUnion1` defined here
+  --> $DIR/empty-match.rs:16:7
+   |
+LL | union NonEmptyUnion1 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion1`
+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 ~             _ if false => {}
+LL +             NonEmptyUnion1 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
+  --> $DIR/empty-match.rs:91:24
+   |
+LL |     match_guarded_arm!((NonEmptyUnion2 { foo: () }));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
+   |
+note: `NonEmptyUnion2` defined here
+  --> $DIR/empty-match.rs:19:7
+   |
+LL | union NonEmptyUnion2 {
+   |       ^^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyUnion2`
+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 ~             _ if false => {}
+LL +             NonEmptyUnion2 { .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum1::Foo(_)` not covered
+  --> $DIR/empty-match.rs:92:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum1::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyEnum1::Foo(_)` not covered
+   |
+note: `NonEmptyEnum1` defined here
+  --> $DIR/empty-match.rs:24:5
+   |
+LL | enum NonEmptyEnum1 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum1`
+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 ~             _ if false => {}
+LL +             NonEmptyEnum1::Foo(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+  --> $DIR/empty-match.rs:93:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum2::Foo(true));
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+   |
+note: `NonEmptyEnum2` defined here
+  --> $DIR/empty-match.rs:27:5
+   |
+LL | enum NonEmptyEnum2 {
+   |      -------------
+LL |     Foo(bool),
+   |     ^^^ not covered
+LL |     Bar,
+   |     ^^^ not covered
+   = note: the matched value is of type `NonEmptyEnum2`
+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 ~             _ if false => {}
+LL +             NonEmptyEnum2::Foo(_) | NonEmptyEnum2::Bar => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+  --> $DIR/empty-match.rs:94:24
+   |
+LL |     match_guarded_arm!(NonEmptyEnum5::V1);
+   |                        ^^^^^^^^^^^^^^^^^ patterns `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+   |
+note: `NonEmptyEnum5` defined here
+  --> $DIR/empty-match.rs:30:6
+   |
+LL | enum NonEmptyEnum5 {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `NonEmptyEnum5`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~             _ if false => {}
+LL +             _ => todo!()
+   |
+
+error: aborting due to 22 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/empty-match.rs b/tests/ui/pattern/usefulness/empty-match.rs
new file mode 100644
index 00000000000..9cdc0413ba1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/empty-match.rs
@@ -0,0 +1,95 @@
+// aux-build:empty.rs
+// revisions: normal exhaustive_patterns
+//
+// This tests a match with no arms on various types.
+#![feature(never_type)]
+#![feature(never_type_fallback)]
+#![cfg_attr(exhaustive_patterns, feature(exhaustive_patterns))]
+#![deny(unreachable_patterns)]
+
+extern crate empty;
+
+enum EmptyEnum {}
+
+struct NonEmptyStruct1;
+struct NonEmptyStruct2(bool);
+union NonEmptyUnion1 {
+    foo: (),
+}
+union NonEmptyUnion2 {
+    foo: (),
+    bar: (),
+}
+enum NonEmptyEnum1 {
+    Foo(bool),
+}
+enum NonEmptyEnum2 {
+    Foo(bool),
+    Bar,
+}
+enum NonEmptyEnum5 {
+    V1, V2, V3, V4, V5,
+}
+
+fn empty_enum(x: EmptyEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn empty_foreign_enum(x: empty::EmptyForeignEnum) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+fn never(x: !) {
+    match x {} // ok
+    match x {
+        _ => {}, //~ ERROR unreachable pattern
+    }
+    match x {
+        _ if false => {}, //~ ERROR unreachable pattern
+    }
+}
+
+macro_rules! match_no_arms {
+    ($e:expr) => {
+        match $e {}
+    };
+}
+macro_rules! match_guarded_arm {
+    ($e:expr) => {
+        match $e {
+            _ if false => {}
+        }
+    };
+}
+
+fn main() {
+    match_no_arms!(0u8); //~ ERROR type `u8` is non-empty
+    match_no_arms!(NonEmptyStruct1); //~ ERROR type `NonEmptyStruct1` is non-empty
+    match_no_arms!(NonEmptyStruct2(true)); //~ ERROR type `NonEmptyStruct2` is non-empty
+    match_no_arms!((NonEmptyUnion1 { foo: () })); //~ ERROR type `NonEmptyUnion1` is non-empty
+    match_no_arms!((NonEmptyUnion2 { foo: () })); //~ ERROR type `NonEmptyUnion2` is non-empty
+    match_no_arms!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_no_arms!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_no_arms!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+
+    match_guarded_arm!(0u8); //~ ERROR `_` not covered
+    match_guarded_arm!(NonEmptyStruct1); //~ ERROR `NonEmptyStruct1` not covered
+    match_guarded_arm!(NonEmptyStruct2(true)); //~ ERROR `NonEmptyStruct2(_)` not covered
+    match_guarded_arm!((NonEmptyUnion1 { foo: () })); //~ ERROR `NonEmptyUnion1 { .. }` not covered
+    match_guarded_arm!((NonEmptyUnion2 { foo: () })); //~ ERROR `NonEmptyUnion2 { .. }` not covered
+    match_guarded_arm!(NonEmptyEnum1::Foo(true)); //~ ERROR `NonEmptyEnum1::Foo(_)` not covered
+    match_guarded_arm!(NonEmptyEnum2::Foo(true)); //~ ERROR `NonEmptyEnum2::Foo(_)` and `NonEmptyEnum2::Bar` not covered
+    match_guarded_arm!(NonEmptyEnum5::V1); //~ ERROR `NonEmptyEnum5::V1`, `NonEmptyEnum5::V2`, `NonEmptyEnum5::V3` and 2 more not covered
+}
diff --git a/tests/ui/pattern/usefulness/floats.rs b/tests/ui/pattern/usefulness/floats.rs
new file mode 100644
index 00000000000..095f5ac9a89
--- /dev/null
+++ b/tests/ui/pattern/usefulness/floats.rs
@@ -0,0 +1,19 @@
+#![allow(illegal_floating_point_literal_pattern)]
+#![deny(unreachable_patterns)]
+
+fn main() {
+    match 0.0 {
+      0.0..=1.0 => {}
+      _ => {} // ok
+    }
+
+    match 0.0 { //~ ERROR non-exhaustive patterns
+      0.0..=1.0 => {}
+    }
+
+    match 1.0f64 {
+      0.01f64 ..= 6.5f64 => {}
+      0.02f64 => {} //~ ERROR unreachable pattern
+      _ => {}
+    };
+}
diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr
new file mode 100644
index 00000000000..c926e50b358
--- /dev/null
+++ b/tests/ui/pattern/usefulness/floats.stderr
@@ -0,0 +1,28 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/floats.rs:10:11
+   |
+LL |     match 0.0 {
+   |           ^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `f64`
+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 ~       0.0..=1.0 => {}
+LL +       _ => todo!()
+   |
+
+error: unreachable pattern
+  --> $DIR/floats.rs:16:7
+   |
+LL |       0.02f64 => {}
+   |       ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/floats.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/guards.rs b/tests/ui/pattern/usefulness/guards.rs
new file mode 100644
index 00000000000..b15440cf608
--- /dev/null
+++ b/tests/ui/pattern/usefulness/guards.rs
@@ -0,0 +1,22 @@
+#![feature(exclusive_range_pattern)]
+#![deny(unreachable_patterns)]
+
+enum Q { R(Option<usize>) }
+
+pub fn main() {
+    match Q::R(None) {
+        Q::R(S) if S.is_some() => {}
+        _ => {}
+    }
+
+    match 0u8 { //~ ERROR non-exhaustive patterns
+        0 .. 128 => {}
+        128 ..= 255 if true => {}
+    }
+
+    match 0u8 {
+        0 .. 128 => {}
+        128 ..= 255 if false => {}
+        128 ..= 255 => {} // ok, because previous arm was guarded
+    }
+}
diff --git a/tests/ui/pattern/usefulness/guards.stderr b/tests/ui/pattern/usefulness/guards.stderr
new file mode 100644
index 00000000000..0c1563c160c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/guards.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `128_u8..=u8::MAX` not covered
+  --> $DIR/guards.rs:12:11
+   |
+LL |     match 0u8 {
+   |           ^^^ pattern `128_u8..=u8::MAX` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL ~         128 ..= 255 if true => {}
+LL +         128_u8..=u8::MAX => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs
new file mode 100644
index 00000000000..0f5f49c4ca4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.rs
@@ -0,0 +1,101 @@
+#![feature(exclusive_range_pattern)]
+#![allow(overlapping_range_endpoints)]
+#![deny(unreachable_patterns)]
+
+macro_rules! m {
+    ($s:expr, $($t:tt)+) => {
+        match $s { $($t)+ => {} }
+    }
+}
+
+macro_rules! test_int {
+    ($s:expr, $min:path, $max:path) => {
+        m!($s, $min..=$max);
+        m!($s, $min..5 | 5..=$max);
+        m!($s, $min..=4 | 5..=$max);
+        m!($s, $min..$max | $max);
+        m!(($s, true), ($min..5, true) | (5..=$max, true) | ($min..=$max, false));
+    }
+}
+
+fn main() {
+    test_int!(0u8, u8::MIN, u8::MAX);
+    test_int!(0u16, u16::MIN, u16::MAX);
+    test_int!(0u32, u32::MIN, u32::MAX);
+    test_int!(0u64, u64::MIN, u64::MAX);
+    test_int!(0u128, u128::MIN, u128::MAX);
+
+    test_int!(0i8, i8::MIN, i8::MAX);
+    test_int!(0i16, i16::MIN, i16::MAX);
+    test_int!(0i32, i32::MIN, i32::MAX);
+    test_int!(0i64, i64::MIN, i64::MAX);
+    test_int!(0i128, i128::MIN, i128::MAX);
+
+    m!('a', '\u{0}'..=char::MAX);
+    m!('a', '\u{0}'..='\u{10_FFFF}');
+    // We can get away with just covering the following two ranges, which correspond to all valid
+    // Unicode Scalar Values.
+    m!('a', '\u{0}'..='\u{D7FF}' | '\u{E000}'..=char::MAX);
+    m!('a', '\u{0}'..'\u{D7FF}' | '\u{D7FF}' | '\u{E000}'..=char::MAX);
+
+    let 0..=255 = 0u8;
+    let -128..=127 = 0i8;
+    let -2147483648..=2147483647 = 0i32;
+    let '\u{0000}'..='\u{10FFFF}' = 'v';
+
+    // Almost exhaustive
+    m!(0u8, 0..255); //~ ERROR non-exhaustive patterns
+    m!(0u8, 0..=254); //~ ERROR non-exhaustive patterns
+    m!(0u8, 1..=255); //~ ERROR non-exhaustive patterns
+    m!(0u8, 0..42 | 43..=255); //~ ERROR non-exhaustive patterns
+    m!(0i8, -128..127); //~ ERROR non-exhaustive patterns
+    m!(0i8, -128..=126); //~ ERROR non-exhaustive patterns
+    m!(0i8, -127..=127); //~ ERROR non-exhaustive patterns
+    match 0i8 { //~ ERROR non-exhaustive patterns
+        i8::MIN ..= -1 => {}
+        1 ..= i8::MAX => {}
+    }
+    const ALMOST_MAX: u128 = u128::MAX - 1;
+    m!(0u128, 0..=ALMOST_MAX); //~ ERROR non-exhaustive patterns
+    m!(0u128, 0..=4); //~ ERROR non-exhaustive patterns
+    m!(0u128, 1..=u128::MAX); //~ ERROR non-exhaustive patterns
+
+    // More complicatedly (non-)exhaustive
+    match 0u8 {
+        0 ..= 30 => {}
+        20 ..= 70 => {}
+        50 ..= 255 => {}
+    }
+    match (0u8, true) { //~ ERROR non-exhaustive patterns
+        (0 ..= 125, false) => {}
+        (128 ..= 255, false) => {}
+        (0 ..= 255, true) => {}
+    }
+    match (0u8, true) { // ok
+        (0 ..= 125, false) => {}
+        (128 ..= 255, false) => {}
+        (0 ..= 255, true) => {}
+        (125 .. 128, false) => {}
+    }
+    match (true, 0u8) {
+        (true, 0 ..= 255) => {}
+        (false, 0 ..= 125) => {}
+        (false, 128 ..= 255) => {}
+        (false, 125 .. 128) => {}
+    }
+    match Some(0u8) {
+        None => {}
+        Some(0 ..= 125) => {}
+        Some(128 ..= 255) => {}
+        Some(125 .. 128) => {}
+    }
+    const FOO: u8 = 41;
+    const BAR: &u8 = &42;
+    match &0u8 {
+        0..41 => {}
+        &FOO => {}
+        BAR => {}
+        43..=255 => {}
+    }
+
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr
new file mode 100644
index 00000000000..f30ba05dff9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/exhaustiveness.stderr
@@ -0,0 +1,149 @@
+error[E0004]: non-exhaustive patterns: `u8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:47:8
+   |
+LL |     m!(0u8, 0..255);
+   |        ^^^ pattern `u8::MAX` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL |         match $s { $($t)+ => {}, u8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `u8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:48:8
+   |
+LL |     m!(0u8, 0..=254);
+   |        ^^^ pattern `u8::MAX` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL |         match $s { $($t)+ => {}, u8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_u8` not covered
+  --> $DIR/exhaustiveness.rs:49:8
+   |
+LL |     m!(0u8, 1..=255);
+   |        ^^^ pattern `0_u8` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL |         match $s { $($t)+ => {}, 0_u8 => todo!() }
+   |                                +++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `42_u8` not covered
+  --> $DIR/exhaustiveness.rs:50:8
+   |
+LL |     m!(0u8, 0..42 | 43..=255);
+   |        ^^^ pattern `42_u8` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL |         match $s { $($t)+ => {}, 42_u8 => todo!() }
+   |                                ++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:51:8
+   |
+LL |     m!(0i8, -128..127);
+   |        ^^^ pattern `i8::MAX` not covered
+   |
+   = note: the matched value is of type `i8`
+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 |         match $s { $($t)+ => {}, i8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MAX` not covered
+  --> $DIR/exhaustiveness.rs:52:8
+   |
+LL |     m!(0i8, -128..=126);
+   |        ^^^ pattern `i8::MAX` not covered
+   |
+   = note: the matched value is of type `i8`
+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 |         match $s { $($t)+ => {}, i8::MAX => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `i8::MIN` not covered
+  --> $DIR/exhaustiveness.rs:53:8
+   |
+LL |     m!(0i8, -127..=127);
+   |        ^^^ pattern `i8::MIN` not covered
+   |
+   = note: the matched value is of type `i8`
+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 |         match $s { $($t)+ => {}, i8::MIN => todo!() }
+   |                                ++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_i8` not covered
+  --> $DIR/exhaustiveness.rs:54:11
+   |
+LL |     match 0i8 {
+   |           ^^^ pattern `0_i8` not covered
+   |
+   = note: the matched value is of type `i8`
+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 ~         1 ..= i8::MAX => {}
+LL +         0_i8 => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `u128::MAX` not covered
+  --> $DIR/exhaustiveness.rs:59:8
+   |
+LL |     m!(0u128, 0..=ALMOST_MAX);
+   |        ^^^^^ pattern `u128::MAX` not covered
+   |
+   = note: the matched value is of type `u128`
+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 |         match $s { $($t)+ => {}, u128::MAX => todo!() }
+   |                                ++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `5_u128..=u128::MAX` not covered
+  --> $DIR/exhaustiveness.rs:60:8
+   |
+LL |     m!(0u128, 0..=4);
+   |        ^^^^^ pattern `5_u128..=u128::MAX` not covered
+   |
+   = note: the matched value is of type `u128`
+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 |         match $s { $($t)+ => {}, 5_u128..=u128::MAX => todo!() }
+   |                                +++++++++++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `0_u128` not covered
+  --> $DIR/exhaustiveness.rs:61:8
+   |
+LL |     m!(0u128, 1..=u128::MAX);
+   |        ^^^^^ pattern `0_u128` not covered
+   |
+   = note: the matched value is of type `u128`
+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 |         match $s { $($t)+ => {}, 0_u128 => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(126_u8..=127_u8, false)` not covered
+  --> $DIR/exhaustiveness.rs:69:11
+   |
+LL |     match (0u8, true) {
+   |           ^^^^^^^^^^^ pattern `(126_u8..=127_u8, false)` not covered
+   |
+   = note: the matched value is of type `(u8, bool)`
+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 ~         (0 ..= 255, true) => {}
+LL +         (126_u8..=127_u8, false) => todo!()
+   |
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs
new file mode 100644
index 00000000000..5ea92b07081
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs
@@ -0,0 +1,59 @@
+#![feature(exclusive_range_pattern)]
+#![deny(overlapping_range_endpoints)]
+
+macro_rules! m {
+    ($s:expr, $t1:pat, $t2:pat) => {
+        match $s {
+            $t1 => {}
+            $t2 => {}
+            _ => {}
+        }
+    }
+}
+
+fn main() {
+    m!(0u8, 20..=30, 30..=40); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 31..=40);
+    m!(0u8, 20..=30, 29..=40);
+    m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20.. 30, 28..=40);
+    m!(0u8, 20.. 30, 30..=40);
+    m!(0u8, 20..=30, 30..=30);
+    m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 29..=30);
+    m!(0u8, 20..=30, 20..=20);
+    m!(0u8, 20..=30, 20..=21);
+    m!(0u8, 20..=30, 19..=20); //~ ERROR multiple patterns overlap on their endpoints
+    m!(0u8, 20..=30, 20);
+    m!(0u8, 20..=30, 25);
+    m!(0u8, 20..=30, 30);
+    m!(0u8, 20.. 30, 29);
+    m!(0u8, 20, 20..=30);
+    m!(0u8, 25, 20..=30);
+    m!(0u8, 30, 20..=30);
+
+    match 0u8 {
+        0..=10 => {}
+        20..=30 => {}
+        10..=20 => {} //~ ERROR multiple patterns overlap on their endpoints
+        _ => {}
+    }
+    match (0u8, true) {
+        (0..=10, true) => {}
+        (10..20, true) => {} // not detected
+        (10..20, false) => {}
+        _ => {}
+    }
+    match (true, 0u8) {
+        (true, 0..=10) => {}
+        (true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
+        (false, 10..20) => {}
+        _ => {}
+    }
+    match Some(0u8) {
+        Some(0..=10) => {}
+        Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
new file mode 100644
index 00000000000..ea0e8f6e49e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr
@@ -0,0 +1,89 @@
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:15:22
+   |
+LL |     m!(0u8, 20..=30, 30..=40);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+note: the lint level is defined here
+  --> $DIR/overlapping_range_endpoints.rs:2:9
+   |
+LL | #![deny(overlapping_range_endpoints)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:16:22
+   |
+LL |     m!(0u8, 30..=40, 20..=30);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:19:22
+   |
+LL |     m!(0u8, 20.. 30, 29..=40);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `29_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:23:22
+   |
+LL |     m!(0u8, 20..=30, 30..=31);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `30_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:27:22
+   |
+LL |     m!(0u8, 20..=30, 19..=20);
+   |             -------  ^^^^^^^ ... with this range
+   |             |
+   |             this range overlaps on `20_u8`...
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:39:9
+   |
+LL |         0..=10 => {}
+   |         ------ this range overlaps on `10_u8`...
+LL |         20..=30 => {}
+   |         ------- this range overlaps on `20_u8`...
+LL |         10..=20 => {}
+   |         ^^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:50:16
+   |
+LL |         (true, 0..=10) => {}
+   |                ------ this range overlaps on `10_u8`...
+LL |         (true, 10..20) => {}
+   |                ^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: multiple patterns overlap on their endpoints
+  --> $DIR/overlapping_range_endpoints.rs:56:14
+   |
+LL |         Some(0..=10) => {}
+   |              ------ this range overlaps on `10_u8`...
+LL |         Some(10..20) => {}
+   |              ^^^^^^ ... with this range
+   |
+   = note: you likely meant to write mutually exclusive ranges
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr
new file mode 100644
index 00000000000..9f277fa1e18
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.allow.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+  --> $DIR/pointer-sized-int.rs:48:11
+   |
+LL |     match 7usize {}
+   |           ^^^^^^
+   |
+   = note: the matched value is of type `usize`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match 7usize {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
new file mode 100644
index 00000000000..e3eb98ccdcd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr
@@ -0,0 +1,170 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:12:11
+   |
+LL |     match 0usize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 ~         0 ..= usize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:17:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         isize::MIN ..= isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:22:8
+   |
+LL |     m!(0usize, 0..=usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:24:8
+   |
+LL |     m!(0usize, 0..5 | 5..=usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:26:8
+   |
+LL |     m!(0usize, 0..usize::MAX | usize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/pointer-sized-int.rs:28:8
+   |
+LL |     m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+   |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(usize, bool)`
+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 |         match $s { $($t)+ => {}, (_, _) => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:31:8
+   |
+LL |     m!(0isize, isize::MIN..=isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:33:8
+   |
+LL |     m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:35:8
+   |
+LL |     m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+   |        ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 |         match $s { $($t)+ => {}, _ => todo!() }
+   |                                ++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `(_, _)` not covered
+  --> $DIR/pointer-sized-int.rs:37:8
+   |
+LL |     m!((0isize, true), (isize::MIN..5, true)
+   |        ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(isize, bool)`
+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 |         match $s { $($t)+ => {}, (_, _) => todo!() }
+   |                                +++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/pointer-sized-int.rs:41:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         1 ..= isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: type `usize` is non-empty
+  --> $DIR/pointer-sized-int.rs:48:11
+   |
+LL |     match 7usize {}
+   |           ^^^^^^
+   |
+   = note: the matched value is of type `usize`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match 7usize {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to 12 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs
new file mode 100644
index 00000000000..1ed18c26763
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.rs
@@ -0,0 +1,50 @@
+// revisions: allow deny
+#![feature(exclusive_range_pattern)]
+#![cfg_attr(allow, feature(precise_pointer_size_matching))]
+
+macro_rules! m {
+    ($s:expr, $($t:tt)+) => {
+        match $s { $($t)+ => {} }
+    }
+}
+
+fn main() {
+    match 0usize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        0 ..= usize::MAX => {}
+    }
+
+    match 0isize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        isize::MIN ..= isize::MAX => {}
+    }
+
+    m!(0usize, 0..=usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0usize, 0..5 | 5..=usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0usize, 0..usize::MAX | usize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::MAX, false));
+    //[deny]~^ ERROR non-exhaustive patterns
+
+    m!(0isize, isize::MIN..=isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0isize, isize::MIN..5 | 5..=isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!(0isize, isize::MIN..isize::MAX | isize::MAX);
+    //[deny]~^ ERROR non-exhaustive patterns
+    m!((0isize, true), (isize::MIN..5, true)
+        | (5..=isize::MAX, true) | (isize::MIN..=isize::MAX, false));
+    //[deny]~^^ ERROR non-exhaustive patterns
+
+    match 0isize {
+        //[deny]~^ ERROR non-exhaustive patterns
+        isize::MIN ..= -1 => {}
+        0 => {}
+        1 ..= isize::MAX => {}
+    }
+
+    match 7usize {}
+    //~^ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs
new file mode 100644
index 00000000000..a2aa655ca54
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.rs
@@ -0,0 +1,18 @@
+// This tests that the lint message explains the reason for the error.
+fn main() {
+    match 0usize {
+        //~^ ERROR non-exhaustive patterns: `_` not covered
+        //~| NOTE pattern `_` not covered
+        //~| NOTE the matched value is of type `usize`
+        //~| NOTE `usize` does not have a fixed maximum value
+        0..=usize::MAX => {}
+    }
+
+    match 0isize {
+        //~^ ERROR non-exhaustive patterns: `_` not covered
+        //~| NOTE pattern `_` not covered
+        //~| NOTE the matched value is of type `isize`
+        //~| NOTE `isize` does not have a fixed maximum value
+        isize::MIN..=isize::MAX => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr
new file mode 100644
index 00000000000..30492c98206
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/precise_pointer_matching-message.stderr
@@ -0,0 +1,33 @@
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/precise_pointer_matching-message.rs:3:11
+   |
+LL |     match 0usize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `usize`
+   = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
+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 ~         0..=usize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/precise_pointer_matching-message.rs:11:11
+   |
+LL |     match 0isize {
+   |           ^^^^^^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+   = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
+   = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
+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 ~         isize::MIN..=isize::MAX => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.rs b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs
new file mode 100644
index 00000000000..fb4d59b0578
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.rs
@@ -0,0 +1,113 @@
+#![feature(exclusive_range_pattern)]
+#![allow(overlapping_range_endpoints)]
+#![deny(unreachable_patterns)]
+
+macro_rules! m {
+    ($s:expr, $t1:pat, $t2:pat) => {
+        match $s {
+            $t1 => {}
+            $t2 => {}
+            _ => {}
+        }
+    }
+}
+
+fn main() {
+    m!(0u8, 42, 41);
+    m!(0u8, 42, 42); //~ ERROR unreachable pattern
+    m!(0u8, 42, 43);
+
+    m!(0u8, 20..=30, 19);
+    m!(0u8, 20..=30, 20); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 21); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 25); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 29); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 31);
+    m!(0u8, 20..30, 19);
+    m!(0u8, 20..30, 20); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 21); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 25); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 29); //~ ERROR unreachable pattern
+    m!(0u8, 20..30, 30);
+    m!(0u8, 20..30, 31);
+
+    m!(0u8, 20..=30, 20..=30); //~ ERROR unreachable pattern
+    m!(0u8, 20.. 30, 20.. 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20.. 30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 19..=30);
+    m!(0u8, 20..=30, 21..=30); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20..=29); //~ ERROR unreachable pattern
+    m!(0u8, 20..=30, 20..=31);
+    m!('a', 'A'..='z', 'a'..='z'); //~ ERROR unreachable pattern
+
+    match 0u8 {
+        5 => {},
+        6 => {},
+        7 => {},
+        8 => {},
+        5..=8 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10..20 => {},
+        5..15 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10..20 => {},
+        20..30 => {},
+        5..25 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0u8 {
+        0..10 => {},
+        10 => {},
+        11..=23 => {},
+        19..30 => {},
+        5..25 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    match 0usize {
+        0..10 => {},
+        10..20 => {},
+        5..15 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    // Chars between '\u{D7FF}' and '\u{E000}' are invalid even though ranges that contain them are
+    // allowed.
+    match 'a' {
+        _ => {},
+        '\u{D7FF}'..='\u{E000}' => {}, //~ ERROR unreachable pattern
+    }
+    match 'a' {
+        '\u{0}'..='\u{D7FF}' => {},
+        '\u{E000}'..='\u{10_FFFF}' => {},
+        '\u{D7FF}'..='\u{E000}' => {}, // FIXME should be unreachable
+    }
+
+    match (0u8, true) {
+        (0..=255, false) => {}
+        (0..=255, true) => {} // ok
+    }
+    match (true, 0u8) {
+        (false, 0..=255) => {}
+        (true, 0..=255) => {} // ok
+    }
+
+    const FOO: i32 = 42;
+    const BAR: &i32 = &42;
+    match &0 {
+        &42 => {}
+        &FOO => {} //~ ERROR unreachable pattern
+        BAR => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    // Regression test, see https://github.com/rust-lang/rust/pull/66326#issuecomment-552889933
+    match &0 {
+        BAR => {} // ok
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
new file mode 100644
index 00000000000..0ffb0ffd82a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/integer-ranges/reachability.stderr
@@ -0,0 +1,154 @@
+error: unreachable pattern
+  --> $DIR/reachability.rs:17:17
+   |
+LL |     m!(0u8, 42, 42);
+   |                 ^^
+   |
+note: the lint level is defined here
+  --> $DIR/reachability.rs:3:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:21:22
+   |
+LL |     m!(0u8, 20..=30, 20);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:22:22
+   |
+LL |     m!(0u8, 20..=30, 21);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:23:22
+   |
+LL |     m!(0u8, 20..=30, 25);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:24:22
+   |
+LL |     m!(0u8, 20..=30, 29);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:25:22
+   |
+LL |     m!(0u8, 20..=30, 30);
+   |                      ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:28:21
+   |
+LL |     m!(0u8, 20..30, 20);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:29:21
+   |
+LL |     m!(0u8, 20..30, 21);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:30:21
+   |
+LL |     m!(0u8, 20..30, 25);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:31:21
+   |
+LL |     m!(0u8, 20..30, 29);
+   |                     ^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:35:22
+   |
+LL |     m!(0u8, 20..=30, 20..=30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:36:22
+   |
+LL |     m!(0u8, 20.. 30, 20.. 30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:37:22
+   |
+LL |     m!(0u8, 20..=30, 20.. 30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:39:22
+   |
+LL |     m!(0u8, 20..=30, 21..=30);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:40:22
+   |
+LL |     m!(0u8, 20..=30, 20..=29);
+   |                      ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:42:24
+   |
+LL |     m!('a', 'A'..='z', 'a'..='z');
+   |                        ^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:49:9
+   |
+LL |         5..=8 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:55:9
+   |
+LL |         5..15 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:62:9
+   |
+LL |         5..25 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:70:9
+   |
+LL |         5..25 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:76:9
+   |
+LL |         5..15 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:83:9
+   |
+LL |         _ => {},
+   |         - matches any value
+LL |         '\u{D7FF}'..='\u{E000}' => {},
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:104:9
+   |
+LL |         &FOO => {}
+   |         ^^^^
+
+error: unreachable pattern
+  --> $DIR/reachability.rs:105:9
+   |
+LL |         BAR => {}
+   |         ^^^
+
+error: aborting due to 24 previous errors
+
diff --git a/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs b/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs
new file mode 100644
index 00000000000..d400ef0bbd6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/irrefutable-let-patterns.rs
@@ -0,0 +1,11 @@
+// run-pass
+
+#![allow(irrefutable_let_patterns)]
+
+fn main() {
+    if let _ = 5 {}
+
+    while let _ = 5 {
+        break;
+    }
+}
diff --git a/tests/ui/pattern/usefulness/irrefutable-unit.rs b/tests/ui/pattern/usefulness/irrefutable-unit.rs
new file mode 100644
index 00000000000..dd8f03b6dbd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/irrefutable-unit.rs
@@ -0,0 +1,6 @@
+// run-pass
+// pretty-expanded FIXME #23616
+
+pub fn main() {
+    let ((),()) = ((),());
+}
diff --git a/tests/ui/pattern/usefulness/issue-12116.rs b/tests/ui/pattern/usefulness/issue-12116.rs
new file mode 100644
index 00000000000..3cb92a54029
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12116.rs
@@ -0,0 +1,21 @@
+#![feature(box_patterns)]
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![deny(unreachable_patterns)]
+
+
+enum IntList {
+    Cons(isize, Box<IntList>),
+    Nil
+}
+
+fn tail(source_list: &IntList) -> IntList {
+    match source_list {
+        &IntList::Cons(val, box ref next_list) => tail(next_list),
+        &IntList::Cons(val, box IntList::Nil)  => IntList::Cons(val, Box::new(IntList::Nil)),
+        //~^ ERROR unreachable pattern
+        _ => panic!(),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-12116.stderr b/tests/ui/pattern/usefulness/issue-12116.stderr
new file mode 100644
index 00000000000..7f15c4703a3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12116.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-12116.rs:15:9
+   |
+LL |         &IntList::Cons(val, box IntList::Nil)  => IntList::Cons(val, Box::new(IntList::Nil)),
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-12116.rs:4:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-12369.rs b/tests/ui/pattern/usefulness/issue-12369.rs
new file mode 100644
index 00000000000..0481c1fd9e1
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12369.rs
@@ -0,0 +1,11 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let sl = vec![1,2,3];
+    let v: isize = match &*sl {
+        &[] => 0,
+        &[a,b,c] => 3,
+        &[a, ref rest @ ..] => a,
+        &[10,a, ref rest @ ..] => 10 //~ ERROR: unreachable pattern
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-12369.stderr b/tests/ui/pattern/usefulness/issue-12369.stderr
new file mode 100644
index 00000000000..aab2be78c9a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-12369.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-12369.rs:9:9
+   |
+LL |         &[10,a, ref rest @ ..] => 10
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-12369.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-13727.rs b/tests/ui/pattern/usefulness/issue-13727.rs
new file mode 100644
index 00000000000..7fb565ef3bf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-13727.rs
@@ -0,0 +1,15 @@
+#![allow(overflowing_literals)]
+#![deny(unreachable_patterns)]
+
+fn test(val: u8) {
+  match val {
+    256 => print!("0b1110\n"),
+    512 => print!("0b1111\n"),
+    //~^ ERROR: unreachable pattern
+    _   => print!("fail\n"),
+  }
+}
+
+fn main() {
+  test(1);
+}
diff --git a/tests/ui/pattern/usefulness/issue-13727.stderr b/tests/ui/pattern/usefulness/issue-13727.stderr
new file mode 100644
index 00000000000..07ca56a566f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-13727.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-13727.rs:7:5
+   |
+LL |     512 => print!("0b1111\n"),
+   |     ^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-13727.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-15129.rs b/tests/ui/pattern/usefulness/issue-15129.rs
new file mode 100644
index 00000000000..f02e5c0c6f8
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-15129.rs
@@ -0,0 +1,17 @@
+pub enum T {
+    T1(()),
+    T2(()),
+}
+
+pub enum V {
+    V1(isize),
+    V2(bool),
+}
+
+fn main() {
+    match (T::T1(()), V::V2(true)) {
+        //~^ ERROR non-exhaustive patterns: `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+        (T::T1(()), V::V1(i)) => (),
+        (T::T2(()), V::V2(b)) => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-15129.stderr b/tests/ui/pattern/usefulness/issue-15129.stderr
new file mode 100644
index 00000000000..ee8410b7650
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-15129.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+  --> $DIR/issue-15129.rs:12:11
+   |
+LL |     match (T::T1(()), V::V2(true)) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `(T::T1(()), V::V2(_))` and `(T::T2(()), V::V1(_))` not covered
+   |
+   = note: the matched value is of type `(T, V)`
+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 ~         (T::T2(()), V::V2(b)) => (),
+LL ~         (T::T1(()), V::V2(_)) | (T::T2(()), V::V1(_)) => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-2111.rs b/tests/ui/pattern/usefulness/issue-2111.rs
new file mode 100644
index 00000000000..d27beaeffd6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-2111.rs
@@ -0,0 +1,11 @@
+fn foo(a: Option<usize>, b: Option<usize>) {
+    match (a, b) {
+        //~^ ERROR: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+        (Some(a), Some(b)) if a == b => {}
+        (Some(_), None) | (None, Some(_)) => {}
+    }
+}
+
+fn main() {
+    foo(None, None);
+}
diff --git a/tests/ui/pattern/usefulness/issue-2111.stderr b/tests/ui/pattern/usefulness/issue-2111.stderr
new file mode 100644
index 00000000000..01890b73cbd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-2111.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(None, None)` and `(Some(_), Some(_))` not covered
+  --> $DIR/issue-2111.rs:2:11
+   |
+LL |     match (a, b) {
+   |           ^^^^^^ patterns `(None, None)` and `(Some(_), Some(_))` not covered
+   |
+   = note: the matched value is of type `(Option<usize>, Option<usize>)`
+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 ~         (Some(_), None) | (None, Some(_)) => {}
+LL +         (None, None) | (Some(_), Some(_)) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-30240-b.rs b/tests/ui/pattern/usefulness/issue-30240-b.rs
new file mode 100644
index 00000000000..01a6e7d8cb9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-b.rs
@@ -0,0 +1,15 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    match "world" {
+        "hello" => {}
+        _ => {},
+    }
+
+    match "world" {
+        ref _x if false => {}
+        "hello" => {}
+        "hello" => {} //~ ERROR unreachable pattern
+        _ => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240-b.stderr b/tests/ui/pattern/usefulness/issue-30240-b.stderr
new file mode 100644
index 00000000000..59d64bc256b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-b.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/issue-30240-b.rs:12:9
+   |
+LL |         "hello" => {}
+   |         ^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-30240-b.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/issue-30240-rpass.rs b/tests/ui/pattern/usefulness/issue-30240-rpass.rs
new file mode 100644
index 00000000000..ab16614fd30
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240-rpass.rs
@@ -0,0 +1,14 @@
+// run-pass
+fn main() {
+    let &ref a = &[0i32] as &[_];
+    assert_eq!(a, &[0i32] as &[_]);
+
+    let &ref a = "hello";
+    assert_eq!(a, "hello");
+
+    match "foo" {
+        "fool" => unreachable!(),
+        "foo" => {},
+        ref _x => unreachable!()
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240.rs b/tests/ui/pattern/usefulness/issue-30240.rs
new file mode 100644
index 00000000000..a0c0d1626ec
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240.rs
@@ -0,0 +1,10 @@
+fn main() {
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        "hello" => {}
+    }
+
+    match "world" { //~ ERROR non-exhaustive patterns: `&_`
+        ref _x if false => {}
+        "hello" => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-30240.stderr b/tests/ui/pattern/usefulness/issue-30240.stderr
new file mode 100644
index 00000000000..759fdeafe4e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-30240.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/issue-30240.rs:2:11
+   |
+LL |     match "world" {
+   |           ^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&str`
+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 ~         "hello" => {}
+LL +         &_ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&_` not covered
+  --> $DIR/issue-30240.rs:6:11
+   |
+LL |     match "world" {
+   |           ^^^^^^^ pattern `&_` not covered
+   |
+   = note: the matched value is of type `&str`
+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 ~         "hello" => {}
+LL +         &_ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3096-1.rs b/tests/ui/pattern/usefulness/issue-3096-1.rs
new file mode 100644
index 00000000000..edc3b322305
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-1.rs
@@ -0,0 +1,3 @@
+fn main() {
+    match () { } //~ ERROR non-exhaustive
+}
diff --git a/tests/ui/pattern/usefulness/issue-3096-1.stderr b/tests/ui/pattern/usefulness/issue-3096-1.stderr
new file mode 100644
index 00000000000..d8884394f8e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-1.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `()` is non-empty
+  --> $DIR/issue-3096-1.rs:2:11
+   |
+LL |     match () { }
+   |           ^^
+   |
+   = note: the matched value is of type `()`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match () {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3096-2.rs b/tests/ui/pattern/usefulness/issue-3096-2.rs
new file mode 100644
index 00000000000..a26e425809f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-2.rs
@@ -0,0 +1,6 @@
+enum Bottom { }
+
+fn main() {
+    let x = &() as *const () as *const Bottom;
+    match x { } //~ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/issue-3096-2.stderr b/tests/ui/pattern/usefulness/issue-3096-2.stderr
new file mode 100644
index 00000000000..2df8911badc
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3096-2.stderr
@@ -0,0 +1,17 @@
+error[E0004]: non-exhaustive patterns: type `*const Bottom` is non-empty
+  --> $DIR/issue-3096-2.rs:5:11
+   |
+LL |     match x { }
+   |           ^
+   |
+   = note: the matched value is of type `*const Bottom`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match x {
+LL +         _ => todo!(),
+LL ~     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-31221.rs b/tests/ui/pattern/usefulness/issue-31221.rs
new file mode 100644
index 00000000000..e03f1ec5bc2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31221.rs
@@ -0,0 +1,34 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![allow(non_snake_case)]
+#![deny(unreachable_patterns)]
+
+#[derive(Clone, Copy)]
+enum Enum {
+    Var1,
+    Var2,
+}
+
+fn main() {
+    use Enum::*;
+    let s = Var1;
+    match s {
+        Var1 => (),
+        Var3 => (),
+        Var2 => (),
+        //~^ ERROR unreachable pattern
+    };
+    match &s {
+        &Var1 => (),
+        &Var3 => (),
+        &Var2 => (),
+        //~^ ERROR unreachable pattern
+    };
+    let t = (Var1, Var1);
+    match t {
+        (Var1, b) => (),
+        (c, d) => (),
+        anything => ()
+        //~^ ERROR unreachable pattern
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-31221.stderr b/tests/ui/pattern/usefulness/issue-31221.stderr
new file mode 100644
index 00000000000..7d349144456
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31221.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:18:9
+   |
+LL |         Var3 => (),
+   |         ---- matches any value
+LL |         Var2 => (),
+   |         ^^^^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/issue-31221.rs:4:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:24:9
+   |
+LL |         &Var3 => (),
+   |         ----- matches any value
+LL |         &Var2 => (),
+   |         ^^^^^ unreachable pattern
+
+error: unreachable pattern
+  --> $DIR/issue-31221.rs:31:9
+   |
+LL |         (c, d) => (),
+   |         ------ matches any value
+LL |         anything => ()
+   |         ^^^^^^^^ unreachable pattern
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/issue-31561.rs b/tests/ui/pattern/usefulness/issue-31561.rs
new file mode 100644
index 00000000000..5b878851a31
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31561.rs
@@ -0,0 +1,10 @@
+enum Thing {
+    Foo(u8),
+    Bar,
+    Baz
+}
+
+fn main() {
+    let Thing::Foo(y) = Thing::Foo(1);
+    //~^ ERROR refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+}
diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr
new file mode 100644
index 00000000000..20f2f09500a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-31561.stderr
@@ -0,0 +1,31 @@
+error[E0005]: refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered
+  --> $DIR/issue-31561.rs:8:9
+   |
+LL |     let Thing::Foo(y) = Thing::Foo(1);
+   |         ^^^^^^^^^^^^^ patterns `Thing::Bar` and `Thing::Baz` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `Thing` defined here
+  --> $DIR/issue-31561.rs:3:5
+   |
+LL | enum Thing {
+   |      -----
+LL |     Foo(u8),
+LL |     Bar,
+   |     ^^^ not covered
+LL |     Baz
+   |     ^^^ not covered
+   = note: the matched value is of type `Thing`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() };
+   |     ++++++++++                                   ++++++++++++++++++++++
+help: alternatively, you might want to use let else to handle the variants that aren't matched
+   |
+LL |     let Thing::Foo(y) = Thing::Foo(1) else { todo!() };
+   |                                       ++++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/issue-35609.rs b/tests/ui/pattern/usefulness/issue-35609.rs
new file mode 100644
index 00000000000..8ef75e3511e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-35609.rs
@@ -0,0 +1,43 @@
+enum Enum {
+    A, B, C, D, E, F
+}
+use Enum::*;
+
+struct S(Enum, ());
+struct Sd { x: Enum, y: () }
+
+fn main() {
+    match (A, ()) { //~ ERROR non-exhaustive
+        (A, _) => {}
+    }
+
+    match (A, A) { //~ ERROR non-exhaustive
+        (_, A) => {}
+    }
+
+    match ((A, ()), ()) { //~ ERROR non-exhaustive
+        ((A, ()), _) => {}
+    }
+
+    match ((A, ()), A) { //~ ERROR non-exhaustive
+        ((A, ()), _) => {}
+    }
+
+    match ((A, ()), ()) { //~ ERROR non-exhaustive
+        ((A, _), _) => {}
+    }
+
+
+    match S(A, ()) { //~ ERROR non-exhaustive
+        S(A, _) => {}
+    }
+
+    match (Sd { x: A, y: () }) { //~ ERROR non-exhaustive
+        Sd { x: A, y: _ } => {}
+    }
+
+    match Some(A) { //~ ERROR non-exhaustive
+        Some(A) => (),
+        None => ()
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-35609.stderr b/tests/ui/pattern/usefulness/issue-35609.stderr
new file mode 100644
index 00000000000..12113957d63
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-35609.stderr
@@ -0,0 +1,119 @@
+error[E0004]: non-exhaustive patterns: `(Enum::B, _)`, `(Enum::C, _)`, `(Enum::D, _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:10:11
+   |
+LL |     match (A, ()) {
+   |           ^^^^^^^ patterns `(Enum::B, _)`, `(Enum::C, _)`, `(Enum::D, _)` and 2 more not covered
+   |
+   = note: the matched value is of type `(Enum, ())`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         (A, _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `(_, Enum::B)`, `(_, Enum::C)`, `(_, Enum::D)` and 2 more not covered
+  --> $DIR/issue-35609.rs:14:11
+   |
+LL |     match (A, A) {
+   |           ^^^^^^ patterns `(_, Enum::B)`, `(_, Enum::C)`, `(_, Enum::D)` and 2 more not covered
+   |
+   = note: the matched value is of type `(Enum, Enum)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         (_, A) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:18:11
+   |
+LL |     match ((A, ()), ()) {
+   |           ^^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), ())`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         ((A, ()), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:22:11
+   |
+LL |     match ((A, ()), A) {
+   |           ^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), Enum)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         ((A, ()), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:26:11
+   |
+LL |     match ((A, ()), ()) {
+   |           ^^^^^^^^^^^^^ patterns `((Enum::B, _), _)`, `((Enum::C, _), _)`, `((Enum::D, _), _)` and 2 more not covered
+   |
+   = note: the matched value is of type `((Enum, ()), ())`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         ((A, _), _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `S(Enum::B, _)`, `S(Enum::C, _)`, `S(Enum::D, _)` and 2 more not covered
+  --> $DIR/issue-35609.rs:31:11
+   |
+LL |     match S(A, ()) {
+   |           ^^^^^^^^ patterns `S(Enum::B, _)`, `S(Enum::C, _)`, `S(Enum::D, _)` and 2 more not covered
+   |
+note: `S` defined here
+  --> $DIR/issue-35609.rs:6:8
+   |
+LL | struct S(Enum, ());
+   |        ^
+   = note: the matched value is of type `S`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         S(A, _) => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Sd { x: Enum::B, .. }`, `Sd { x: Enum::C, .. }`, `Sd { x: Enum::D, .. }` and 2 more not covered
+  --> $DIR/issue-35609.rs:35:11
+   |
+LL |     match (Sd { x: A, y: () }) {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `Sd { x: Enum::B, .. }`, `Sd { x: Enum::C, .. }`, `Sd { x: Enum::D, .. }` and 2 more not covered
+   |
+note: `Sd` defined here
+  --> $DIR/issue-35609.rs:7:8
+   |
+LL | struct Sd { x: Enum, y: () }
+   |        ^^
+   = note: the matched value is of type `Sd`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         Sd { x: A, y: _ } => {}
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(Enum::B)`, `Some(Enum::C)`, `Some(Enum::D)` and 2 more not covered
+  --> $DIR/issue-35609.rs:39:11
+   |
+LL |     match Some(A) {
+   |           ^^^^^^^ patterns `Some(Enum::B)`, `Some(Enum::C)`, `Some(Enum::D)` and 2 more not covered
+   |
+note: `Option<Enum>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+   = note: the matched value is of type `Option<Enum>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         None => (),
+LL +         _ => todo!()
+   |
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-3601.rs b/tests/ui/pattern/usefulness/issue-3601.rs
new file mode 100644
index 00000000000..6215a23980d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3601.rs
@@ -0,0 +1,34 @@
+#![feature(box_patterns)]
+
+struct HTMLImageData {
+    image: Option<String>
+}
+
+struct ElementData {
+    kind: Box<ElementKind>
+}
+
+enum ElementKind {
+    HTMLImageElement(HTMLImageData)
+}
+
+enum NodeKind {
+    Element(ElementData)
+}
+
+struct NodeData {
+    kind: Box<NodeKind>,
+}
+
+fn main() {
+    let mut id = HTMLImageData { image: None };
+    let ed = ElementData { kind: Box::new(ElementKind::HTMLImageElement(id)) };
+    let n = NodeData { kind: Box::new(NodeKind::Element(ed)) };
+
+    // n.b. span could be better
+    match n.kind {
+        box NodeKind::Element(ed) => match ed.kind { //~ ERROR non-exhaustive patterns
+            box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
+        },
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-3601.stderr b/tests/ui/pattern/usefulness/issue-3601.stderr
new file mode 100644
index 00000000000..59d7bcd4b5e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-3601.stderr
@@ -0,0 +1,18 @@
+error[E0004]: non-exhaustive patterns: `box _` not covered
+  --> $DIR/issue-3601.rs:30:44
+   |
+LL |         box NodeKind::Element(ed) => match ed.kind {
+   |                                            ^^^^^^^ pattern `box _` not covered
+   |
+note: `Box<ElementKind>` defined here
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   = note: the matched value is of type `Box<ElementKind>`
+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 ~             box ElementKind::HTMLImageElement(ref d) if d.image.is_some() => { true }
+LL +             box _ => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-39362.rs b/tests/ui/pattern/usefulness/issue-39362.rs
new file mode 100644
index 00000000000..ea3c8f88e0b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-39362.rs
@@ -0,0 +1,18 @@
+enum Foo {
+    Bar { bar: Bar, id: usize }
+}
+
+enum Bar {
+    A, B, C, D, E, F
+}
+
+fn test(f: Foo) {
+    match f {
+        //~^ ERROR non-exhaustive patterns
+        //~| patterns
+        Foo::Bar { bar: Bar::A, .. } => (),
+        Foo::Bar { bar: Bar::B, .. } => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-39362.stderr b/tests/ui/pattern/usefulness/issue-39362.stderr
new file mode 100644
index 00000000000..b8b17918aef
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-39362.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered
+  --> $DIR/issue-39362.rs:10:11
+   |
+LL |     match f {
+   |           ^ patterns `Foo::Bar { bar: Bar::C, .. }`, `Foo::Bar { bar: Bar::D, .. }`, `Foo::Bar { bar: Bar::E, .. }` and 1 more not covered
+   |
+note: `Foo` defined here
+  --> $DIR/issue-39362.rs:2:5
+   |
+LL | enum Foo {
+   |      ---
+LL |     Bar { bar: Bar, id: usize }
+   |     ^^^ not covered
+   = note: the matched value is of type `Foo`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         Foo::Bar { bar: Bar::B, .. } => (),
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-40221.rs b/tests/ui/pattern/usefulness/issue-40221.rs
new file mode 100644
index 00000000000..e1f7e975b80
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-40221.rs
@@ -0,0 +1,16 @@
+enum P {
+    C(PC),
+}
+
+enum PC {
+    Q,
+    QA,
+}
+
+fn test(proto: P) {
+    match proto { //~ ERROR non-exhaustive patterns
+        P::C(PC::Q) => (),
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-40221.stderr b/tests/ui/pattern/usefulness/issue-40221.stderr
new file mode 100644
index 00000000000..4973e42b054
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-40221.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `P::C(PC::QA)` not covered
+  --> $DIR/issue-40221.rs:11:11
+   |
+LL |     match proto {
+   |           ^^^^^ pattern `P::C(PC::QA)` not covered
+   |
+note: `P` defined here
+  --> $DIR/issue-40221.rs:2:5
+   |
+LL | enum P {
+   |      -
+LL |     C(PC),
+   |     ^ not covered
+   = note: the matched value is of type `P`
+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 ~         P::C(PC::Q) => (),
+LL ~         P::C(PC::QA) => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-4321.rs b/tests/ui/pattern/usefulness/issue-4321.rs
new file mode 100644
index 00000000000..9715f2eba2f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-4321.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let tup = (true, true);
+    println!("foo {:}", match tup { //~ ERROR non-exhaustive patterns: `(true, false)` not covered
+        (false, false) => "foo",
+        (false, true) => "bar",
+        (true, true) => "baz"
+    });
+}
diff --git a/tests/ui/pattern/usefulness/issue-4321.stderr b/tests/ui/pattern/usefulness/issue-4321.stderr
new file mode 100644
index 00000000000..29327317410
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-4321.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(true, false)` not covered
+  --> $DIR/issue-4321.rs:3:31
+   |
+LL |     println!("foo {:}", match tup {
+   |                               ^^^ pattern `(true, false)` not covered
+   |
+   = note: the matched value is of type `(bool, bool)`
+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 ~         (true, true) => "baz",
+LL +         (true, false) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-50900.rs b/tests/ui/pattern/usefulness/issue-50900.rs
new file mode 100644
index 00000000000..9cc760e9a10
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-50900.rs
@@ -0,0 +1,19 @@
+#[derive(PartialEq, Eq)]
+pub struct Tag(pub Context, pub u16);
+
+#[derive(PartialEq, Eq)]
+pub enum Context {
+    Tiff,
+    Exif,
+}
+
+impl Tag {
+    const ExifIFDPointer: Tag = Tag(Context::Tiff, 34665);
+}
+
+fn main() {
+    match Tag::ExifIFDPointer {
+    //~^ ERROR: non-exhaustive patterns: `Tag(Context::Exif, _)` not covered
+        Tag::ExifIFDPointer => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-50900.stderr b/tests/ui/pattern/usefulness/issue-50900.stderr
new file mode 100644
index 00000000000..348246d28aa
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-50900.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Tag(Context::Exif, _)` not covered
+  --> $DIR/issue-50900.rs:15:11
+   |
+LL |     match Tag::ExifIFDPointer {
+   |           ^^^^^^^^^^^^^^^^^^^ pattern `Tag(Context::Exif, _)` not covered
+   |
+note: `Tag` defined here
+  --> $DIR/issue-50900.rs:2:12
+   |
+LL | pub struct Tag(pub Context, pub u16);
+   |            ^^^
+   = note: the matched value is of type `Tag`
+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 ~         Tag::ExifIFDPointer => {}
+LL +         Tag(Context::Exif, _) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs b/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs
new file mode 100644
index 00000000000..5b0482de220
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-53820-slice-pattern-large-array.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+// This used to cause a stack overflow during exhaustiveness checking in the compiler.
+
+fn main() {
+    const LARGE_SIZE: usize = 1024 * 1024;
+    let [..] = [0u8; LARGE_SIZE];
+    match [0u8; LARGE_SIZE] {
+        [..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-56379.rs b/tests/ui/pattern/usefulness/issue-56379.rs
new file mode 100644
index 00000000000..097cf98d012
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-56379.rs
@@ -0,0 +1,14 @@
+enum Foo {
+    A(bool),
+    B(bool),
+    C(bool),
+}
+
+fn main() {
+    match Foo::A(true) {
+        //~^ ERROR non-exhaustive patterns: `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+        Foo::A(true) => {}
+        Foo::B(true) => {}
+        Foo::C(true) => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-56379.stderr b/tests/ui/pattern/usefulness/issue-56379.stderr
new file mode 100644
index 00000000000..6eed6bfae4c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-56379.stderr
@@ -0,0 +1,27 @@
+error[E0004]: non-exhaustive patterns: `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+  --> $DIR/issue-56379.rs:8:11
+   |
+LL |     match Foo::A(true) {
+   |           ^^^^^^^^^^^^ patterns `Foo::A(false)`, `Foo::B(false)` and `Foo::C(false)` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/issue-56379.rs:2:5
+   |
+LL | enum Foo {
+   |      ---
+LL |     A(bool),
+   |     ^ not covered
+LL |     B(bool),
+   |     ^ not covered
+LL |     C(bool),
+   |     ^ not covered
+   = note: the matched value is of type `Foo`
+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 ~         Foo::C(true) => {}
+LL +         Foo::A(false) | Foo::B(false) | Foo::C(false) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-57472.rs b/tests/ui/pattern/usefulness/issue-57472.rs
new file mode 100644
index 00000000000..1131006374c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-57472.rs
@@ -0,0 +1,35 @@
+#![crate_type="lib"]
+#![deny(unreachable_patterns)]
+
+mod test_struct {
+    // Test the exact copy of the minimal example
+    // posted in the issue.
+    pub struct Punned {
+        foo: [u8; 1],
+        bar: [u8; 1],
+    }
+
+    pub fn test(punned: Punned) {
+        match punned {
+            Punned { foo: [_], .. } => println!("foo"),
+            Punned { bar: [_], .. } => println!("bar"),
+            //~^ ERROR unreachable pattern [unreachable_patterns]
+        }
+    }
+}
+
+mod test_union {
+    // Test the same thing using a union.
+    pub union Punned {
+        foo: [u8; 1],
+        bar: [u8; 1],
+    }
+
+    pub fn test(punned: Punned) {
+        match punned {
+            Punned { foo: [_] } => println!("foo"),
+            Punned { bar: [_] } => println!("bar"),
+            //~^ ERROR unreachable pattern [unreachable_patterns]
+        }
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-57472.stderr b/tests/ui/pattern/usefulness/issue-57472.stderr
new file mode 100644
index 00000000000..26efdf6dbaf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-57472.stderr
@@ -0,0 +1,20 @@
+error: unreachable pattern
+  --> $DIR/issue-57472.rs:15:13
+   |
+LL |             Punned { bar: [_], .. } => println!("bar"),
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/issue-57472.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/issue-57472.rs:31:13
+   |
+LL |             Punned { bar: [_] } => println!("bar"),
+   |             ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs b/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs
new file mode 100644
index 00000000000..54dfa889ee3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-65413-constants-and-slices-exhaustiveness.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+#![deny(unreachable_patterns)]
+
+const C0: &'static [u8] = b"\x00";
+
+fn main() {
+    let x: &[u8] = &[0];
+    match x {
+        &[] => {}
+        &[1..=255] => {}
+        C0 => {}
+        &[_, _, ..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-66501.rs b/tests/ui/pattern/usefulness/issue-66501.rs
new file mode 100644
index 00000000000..ffcfd4ad83e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-66501.rs
@@ -0,0 +1,12 @@
+// check-pass
+
+#![allow(unreachable_patterns)]
+
+fn main() {
+    const CONST: &[Option<()>; 1] = &[Some(())];
+    match &[Some(())] {
+        &[None] => {}
+        CONST => {}
+        &[Some(())] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs b/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs
new file mode 100644
index 00000000000..e2ff9ac87ef
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+// In PR 71930, it was discovered that the code to retrieve the inferred type of a match scrutinee
+// was incorrect.
+
+fn f() -> ! {
+    panic!()
+}
+
+fn g() -> usize {
+    match f() { // Should infer type `bool`
+        false => 0,
+        true => 1,
+    }
+}
+
+fn h() -> usize {
+    match f() { // Should infer type `!`
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-72377.rs b/tests/ui/pattern/usefulness/issue-72377.rs
new file mode 100644
index 00000000000..b5ad3075ca7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72377.rs
@@ -0,0 +1,17 @@
+#[derive(PartialEq, Eq)]
+enum X { A, B, C, }
+
+fn main() {
+    let x = X::A;
+    let y = Some(X::A);
+
+    match (x, y) {
+        //~^ ERROR non-exhaustive patterns: `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2
+        //~| more not covered
+        (_, None) => false,
+        (v, Some(w)) if v == w => true,
+        (X::B, Some(X::C)) => false,
+        (X::B, Some(X::A)) => false,
+        (X::A, Some(X::C)) | (X::C, Some(X::A)) => false,
+    };
+}
diff --git a/tests/ui/pattern/usefulness/issue-72377.stderr b/tests/ui/pattern/usefulness/issue-72377.stderr
new file mode 100644
index 00000000000..123dd051d24
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72377.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2 more not covered
+  --> $DIR/issue-72377.rs:8:11
+   |
+LL |     match (x, y) {
+   |           ^^^^^^ patterns `(X::A, Some(X::A))`, `(X::A, Some(X::B))`, `(X::B, Some(X::B))` and 2 more not covered
+   |
+   = note: the matched value is of type `(X, Option<X>)`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         (X::A, Some(X::C)) | (X::C, Some(X::A)) => false,
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs b/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs
new file mode 100644
index 00000000000..058f4196798
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-72476-and-89393-associated-type.rs
@@ -0,0 +1,56 @@
+// check-pass
+
+// From https://github.com/rust-lang/rust/issues/72476
+// and https://github.com/rust-lang/rust/issues/89393
+
+trait Trait {
+    type Projection;
+}
+
+struct A;
+impl Trait for A {
+    type Projection = bool;
+}
+
+struct B;
+impl Trait for B {
+    type Projection = (u32, u32);
+}
+
+struct Next<T: Trait>(T::Projection);
+
+fn foo1(item: Next<A>) {
+    match item {
+        Next(true) => {}
+        Next(false) => {}
+    }
+}
+
+fn foo2(x: <A as Trait>::Projection) {
+    match x {
+        true => {}
+        false => {}
+    }
+}
+
+fn foo3(x: Next<B>) {
+    let Next((_, _)) = x;
+    match x {
+        Next((_, _)) => {}
+    }
+}
+
+fn foo4(x: <B as Trait>::Projection) {
+    let (_, _) = x;
+    match x {
+        (_, _) => {}
+    }
+}
+
+fn foo5<T: Trait>(x: <T as Trait>::Projection) {
+    match x {
+        _ => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs
new file mode 100644
index 00000000000..cbfcf0eafd4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.rs
@@ -0,0 +1,12 @@
+enum A {}
+    //~^ NOTE `A` defined here
+    //~| NOTE
+
+fn f(a: &A) {
+    match a {}
+    //~^ ERROR non-exhaustive patterns: type `&A` is non-empty
+    //~| NOTE the matched value is of type `&A`
+    //~| NOTE references are always considered inhabited
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr
new file mode 100644
index 00000000000..bf05d616d6e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78123-non-exhaustive-reference.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: type `&A` is non-empty
+  --> $DIR/issue-78123-non-exhaustive-reference.rs:6:11
+   |
+LL |     match a {}
+   |           ^
+   |
+note: `A` defined here
+  --> $DIR/issue-78123-non-exhaustive-reference.rs:1:6
+   |
+LL | enum A {}
+   |      ^
+   = note: the matched value is of type `&A`
+   = note: references are always considered inhabited
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+   |
+LL ~     match a {
+LL +         _ => todo!(),
+LL +     }
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs b/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs
new file mode 100644
index 00000000000..2879caf2c4c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs
@@ -0,0 +1,25 @@
+// check-pass
+// From https://github.com/rust-lang/rust/issues/78549
+
+fn main() {
+    match "foo" {
+        "foo" => {},
+        &_ => {},
+    }
+
+    match "foo" {
+        &_ => {},
+        "foo" => {},
+    }
+
+    match ("foo", 0, "bar") {
+        (&_, 0, &_) => {},
+        ("foo", _, "bar") => {},
+        (&_, _, &_) => {},
+    }
+
+    match (&"foo", "bar") {
+        (&"foo", &_) => {},
+        (&&_, &_) => {},
+    }
+}
diff --git a/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs b/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs
new file mode 100644
index 00000000000..aac7d7d5385
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs
@@ -0,0 +1,27 @@
+// check-pass
+#![deny(unreachable_patterns)]
+pub enum TypeCtor {
+    Slice,
+    Array,
+}
+
+pub struct ApplicationTy(TypeCtor);
+
+macro_rules! ty_app {
+    ($ctor:pat) => {
+        ApplicationTy($ctor)
+    };
+}
+
+fn _foo(ty: ApplicationTy) {
+    match ty {
+        ty_app!(TypeCtor::Array) | ty_app!(TypeCtor::Slice) => {}
+    }
+
+    // same as above, with the macro expanded
+    match ty {
+        ApplicationTy(TypeCtor::Array) | ApplicationTy(TypeCtor::Slice) => {}
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
new file mode 100644
index 00000000000..c1bfcc73402
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
@@ -0,0 +1,6 @@
+// This used to ICE in exhaustiveness checking. Explanation here:
+// https://github.com/rust-lang/rust/issues/82772#issuecomment-905946768
+fn main() {
+    let Box { 1: _, .. }: Box<()>; //~ ERROR field `1` of
+    let Box { .. }: Box<()>;
+}
diff --git a/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr
new file mode 100644
index 00000000000..2c8c85bb1e0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-82772-match-box-as-struct.stderr
@@ -0,0 +1,9 @@
+error[E0451]: field `1` of struct `Box` is private
+  --> $DIR/issue-82772-match-box-as-struct.rs:4:15
+   |
+LL |     let Box { 1: _, .. }: Box<()>;
+   |               ^^^^ private field
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0451`.
diff --git a/tests/ui/pattern/usefulness/issue-88747.rs b/tests/ui/pattern/usefulness/issue-88747.rs
new file mode 100644
index 00000000000..948c99f9ce9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/issue-88747.rs
@@ -0,0 +1,14 @@
+// check-pass: this used to be a stack overflow because of recursion in `usefulness.rs`
+
+macro_rules! long_tuple_arg {
+    ([$($t:tt)*]#$($h:tt)*) => {
+        long_tuple_arg!{[$($t)*$($t)*]$($h)*}
+    };
+    ([$([$t:tt $y:tt])*]) => {
+        pub fn _f(($($t,)*): ($($y,)*)) {}
+    }
+}
+
+long_tuple_arg!{[[_ u8]]########## ###}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics-2.rs b/tests/ui/pattern/usefulness/match-arm-statics-2.rs
new file mode 100644
index 00000000000..3c9c16561c0
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics-2.rs
@@ -0,0 +1,62 @@
+use self::Direction::{North, East, South, West};
+
+#[derive(PartialEq, Eq)]
+struct NewBool(bool);
+
+#[derive(PartialEq, Eq)]
+enum Direction {
+    North,
+    East,
+    South,
+    West
+}
+
+const TRUE_TRUE: (bool, bool) = (true, true);
+
+fn nonexhaustive_1() {
+    match (true, false) {
+    //~^ ERROR non-exhaustive patterns: `(true, false)` not covered
+        TRUE_TRUE => (),
+        (false, false) => (),
+        (false, true) => ()
+    }
+}
+
+const NONE: Option<Direction> = None;
+const EAST: Direction = East;
+
+fn nonexhaustive_2() {
+    match Some(Some(North)) {
+    //~^ ERROR non-exhaustive patterns: `Some(Some(Direction::West))` not covered
+        Some(NONE) => (),
+        Some(Some(North)) => (),
+        Some(Some(EAST)) => (),
+        Some(Some(South)) => (),
+        None => ()
+    }
+}
+
+const NEW_FALSE: NewBool = NewBool(false);
+struct Foo {
+    bar: Option<Direction>,
+    baz: NewBool
+}
+
+const STATIC_FOO: Foo = Foo { bar: None, baz: NEW_FALSE };
+
+fn nonexhaustive_3() {
+    match (Foo { bar: Some(North), baz: NewBool(true) }) {
+    //~^ ERROR non-exhaustive patterns: `Foo { bar: Some(Direction::North), baz: NewBool(true) }`
+        Foo { bar: None, baz: NewBool(true) } => (),
+        Foo { bar: _, baz: NEW_FALSE } => (),
+        Foo { bar: Some(West), baz: NewBool(true) } => (),
+        Foo { bar: Some(South), .. } => (),
+        Foo { bar: Some(EAST), .. } => ()
+    }
+}
+
+fn main() {
+    nonexhaustive_1();
+    nonexhaustive_2();
+    nonexhaustive_3();
+}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics-2.stderr b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr
new file mode 100644
index 00000000000..e4dd35a5995
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics-2.stderr
@@ -0,0 +1,54 @@
+error[E0004]: non-exhaustive patterns: `(true, false)` not covered
+  --> $DIR/match-arm-statics-2.rs:17:11
+   |
+LL |     match (true, false) {
+   |           ^^^^^^^^^^^^^ pattern `(true, false)` not covered
+   |
+   = note: the matched value is of type `(bool, bool)`
+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 ~         (false, true) => (),
+LL +         (true, false) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(Some(Direction::West))` not covered
+  --> $DIR/match-arm-statics-2.rs:29:11
+   |
+LL |     match Some(Some(North)) {
+   |           ^^^^^^^^^^^^^^^^^ pattern `Some(Some(Direction::West))` not covered
+   |
+note: `Option<Option<Direction>>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Option<Direction>>`
+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 ~         None => (),
+LL +         Some(Some(Direction::West)) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Foo { bar: Some(Direction::North), baz: NewBool(true) }` not covered
+  --> $DIR/match-arm-statics-2.rs:48:11
+   |
+LL |     match (Foo { bar: Some(North), baz: NewBool(true) }) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { bar: Some(Direction::North), baz: NewBool(true) }` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/match-arm-statics-2.rs:40:8
+   |
+LL | struct Foo {
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo { bar: Some(EAST), .. } => (),
+LL +         Foo { bar: Some(Direction::North), baz: NewBool(true) } => todo!()
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-arm-statics.rs b/tests/ui/pattern/usefulness/match-arm-statics.rs
new file mode 100644
index 00000000000..91db76ebb9f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics.rs
@@ -0,0 +1,69 @@
+#![allow(dead_code)]
+#![deny(unreachable_patterns)]
+
+use self::Direction::{North, East, South, West};
+
+#[derive(PartialEq, Eq)]
+struct NewBool(bool);
+
+#[derive(PartialEq, Eq)]
+enum Direction {
+    North,
+    East,
+    South,
+    West
+}
+
+const TRUE_TRUE: (bool, bool) = (true, true);
+
+fn unreachable_1() {
+    match (true, false) {
+        TRUE_TRUE => (),
+        (false, false) => (),
+        (false, true) => (),
+        (true, false) => (),
+        (true, true) => ()
+        //~^ ERROR unreachable pattern
+    }
+}
+
+const NONE: Option<Direction> = None;
+const EAST: Direction = East;
+
+fn unreachable_2() {
+    match Some(Some(North)) {
+        Some(NONE) => (),
+        Some(Some(North)) => (),
+        Some(Some(EAST)) => (),
+        Some(Some(South)) => (),
+        Some(Some(West)) => (),
+        Some(Some(East)) => (),
+        //~^ ERROR unreachable pattern
+        None => ()
+    }
+}
+
+const NEW_FALSE: NewBool = NewBool(false);
+struct Foo {
+    bar: Option<Direction>,
+    baz: NewBool
+}
+
+fn unreachable_3() {
+    match (Foo { bar: Some(EAST), baz: NewBool(true) }) {
+        Foo { bar: None, baz: NewBool(true) } => (),
+        Foo { bar: _, baz: NEW_FALSE } => (),
+        Foo { bar: Some(West), baz: NewBool(true) } => (),
+        Foo { bar: Some(South), .. } => (),
+        Foo { bar: Some(EAST), .. } => (),
+        Foo { bar: Some(North), baz: NewBool(true) } => (),
+        Foo { bar: Some(EAST), baz: NewBool(false) } => ()
+        //~^ ERROR unreachable pattern
+    }
+}
+
+fn main() {
+    unreachable_1();
+    unreachable_2();
+    unreachable_3();
+}
diff --git a/tests/ui/pattern/usefulness/match-arm-statics.stderr b/tests/ui/pattern/usefulness/match-arm-statics.stderr
new file mode 100644
index 00000000000..a5dffebf699
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-arm-statics.stderr
@@ -0,0 +1,26 @@
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:25:9
+   |
+LL |         (true, true) => ()
+   |         ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-arm-statics.rs:2:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:40:9
+   |
+LL |         Some(Some(East)) => (),
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-arm-statics.rs:60:9
+   |
+LL |         Foo { bar: Some(EAST), baz: NewBool(false) } => ()
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs
new file mode 100644
index 00000000000..33468d03fae
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.rs
@@ -0,0 +1,13 @@
+fn main() {
+    let buf = &[0, 1, 2, 3];
+
+    match buf { //~ ERROR non-exhaustive
+        b"AAAA" => {}
+    }
+
+    let buf: &[u8] = buf;
+
+    match buf { //~ ERROR non-exhaustive
+        b"AAAA" => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
new file mode 100644
index 00000000000..a90f32f7aeb
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns-2.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+  --> $DIR/match-byte-array-patterns-2.rs:4:11
+   |
+LL |     match buf {
+   |           ^^^ patterns `&[0_u8..=64_u8, _, _, _]` and `&[66_u8..=u8::MAX, _, _, _]` not covered
+   |
+   = note: the matched value is of type `&[u8; 4]`
+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 ~         b"AAAA" => {}
+LL +         &[0_u8..=64_u8, _, _, _] | &[66_u8..=u8::MAX, _, _, _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
+  --> $DIR/match-byte-array-patterns-2.rs:10:11
+   |
+LL |     match buf {
+   |           ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 2 more not covered
+   |
+   = 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, or multiple match arms
+   |
+LL ~         b"AAAA" => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns.rs b/tests/ui/pattern/usefulness/match-byte-array-patterns.rs
new file mode 100644
index 00000000000..9b6c8bd5556
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns.rs
@@ -0,0 +1,55 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let buf = &[0, 1, 2, 3];
+
+    match buf {
+        b"AAAA" => {},
+        &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, 0x41, 0x41, 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[_, 0x41, 0x41, 0x41] => {},
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, .., 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    let buf: &[u8] = buf;
+
+    match buf {
+        b"AAAA" => {},
+        &[0x41, 0x41, 0x41, 0x41] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, 0x41, 0x41, 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[_, 0x41, 0x41, 0x41] => {},
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+
+    match buf {
+        &[0x41, .., 0x41] => {}
+        b"AAAA" => {}, //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
new file mode 100644
index 00000000000..0c582be8df8
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-byte-array-patterns.stderr
@@ -0,0 +1,56 @@
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:8:9
+   |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-byte-array-patterns.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:14:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:20:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:26:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:34:9
+   |
+LL |         &[0x41, 0x41, 0x41, 0x41] => {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:40:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:46:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-byte-array-patterns.rs:52:9
+   |
+LL |         b"AAAA" => {},
+   |         ^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.rs b/tests/ui/pattern/usefulness/match-non-exhaustive.rs
new file mode 100644
index 00000000000..3b210a115d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-non-exhaustive.rs
@@ -0,0 +1,4 @@
+fn main() {
+    match 0 { 1 => () } //~ ERROR non-exhaustive patterns
+    match 0 { 0 if false => () } //~ ERROR non-exhaustive patterns
+}
diff --git a/tests/ui/pattern/usefulness/match-non-exhaustive.stderr b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr
new file mode 100644
index 00000000000..08dde523a15
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-non-exhaustive.stderr
@@ -0,0 +1,27 @@
+error[E0004]: non-exhaustive patterns: `i32::MIN..=0_i32` and `2_i32..=i32::MAX` not covered
+  --> $DIR/match-non-exhaustive.rs:2:11
+   |
+LL |     match 0 { 1 => () }
+   |           ^ patterns `i32::MIN..=0_i32` and `2_i32..=i32::MAX` not covered
+   |
+   = note: the matched value is of type `i32`
+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 |     match 0 { 1 => (), i32::MIN..=0_i32 | 2_i32..=i32::MAX => todo!() }
+   |                      ++++++++++++++++++++++++++++++++++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/match-non-exhaustive.rs:3:11
+   |
+LL |     match 0 { 0 if false => () }
+   |           ^ pattern `_` not covered
+   |
+   = note: the matched value is of type `i32`
+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 |     match 0 { 0 if false => (), _ => todo!() }
+   |                               ++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-privately-empty.rs b/tests/ui/pattern/usefulness/match-privately-empty.rs
new file mode 100644
index 00000000000..315eb03d165
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-privately-empty.rs
@@ -0,0 +1,21 @@
+#![feature(never_type)]
+#![feature(exhaustive_patterns)]
+
+mod private {
+    pub struct Private {
+        _bot: !,
+        pub misc: bool,
+    }
+    pub const DATA: Option<Private> = None;
+}
+
+fn main() {
+    match private::DATA {
+    //~^ ERROR non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered
+        None => {}
+        Some(private::Private {
+            misc: false,
+            ..
+        }) => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-privately-empty.stderr b/tests/ui/pattern/usefulness/match-privately-empty.stderr
new file mode 100644
index 00000000000..86f75d15cfd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-privately-empty.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered
+  --> $DIR/match-privately-empty.rs:13:11
+   |
+LL |     match private::DATA {
+   |           ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered
+   |
+note: `Option<Private>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<Private>`
+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 ~         }) => {}
+LL +         Some(Private { misc: true, .. }) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-ref-ice.rs b/tests/ui/pattern/usefulness/match-ref-ice.rs
new file mode 100644
index 00000000000..dee110f96cd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-ref-ice.rs
@@ -0,0 +1,16 @@
+#![deny(unreachable_patterns)]
+
+// The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose
+// arity is always 0, an ICE occurs.
+//
+// Related issue: #23009
+
+fn main() {
+    let homura = [1, 2, 3];
+
+    match homura {
+        [1, ref _madoka, 3] => (),
+        [1, 2, 3] => (), //~ ERROR unreachable pattern
+        [_, _, _] => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-ref-ice.stderr b/tests/ui/pattern/usefulness/match-ref-ice.stderr
new file mode 100644
index 00000000000..fad0940baa4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-ref-ice.stderr
@@ -0,0 +1,14 @@
+error: unreachable pattern
+  --> $DIR/match-ref-ice.rs:13:9
+   |
+LL |         [1, 2, 3] => (),
+   |         ^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-ref-ice.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/match-slice-patterns.rs b/tests/ui/pattern/usefulness/match-slice-patterns.rs
new file mode 100644
index 00000000000..92d74b8c229
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-slice-patterns.rs
@@ -0,0 +1,12 @@
+fn check(list: &[Option<()>]) {
+    match list {
+    //~^ ERROR `&[_, Some(_), .., None, _]` not covered
+        &[] => {},
+        &[_] => {},
+        &[_, _] => {},
+        &[_, None, ..] => {},
+        &[.., Some(_), _] => {},
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/match-slice-patterns.stderr b/tests/ui/pattern/usefulness/match-slice-patterns.stderr
new file mode 100644
index 00000000000..961dd590119
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-slice-patterns.stderr
@@ -0,0 +1,16 @@
+error[E0004]: non-exhaustive patterns: `&[_, Some(_), .., None, _]` not covered
+  --> $DIR/match-slice-patterns.rs:2:11
+   |
+LL |     match list {
+   |           ^^^^ pattern `&[_, Some(_), .., None, _]` not covered
+   |
+   = note: the matched value is of type `&[Option<()>]`
+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 ~         &[.., Some(_), _] => {}
+LL ~         &[_, Some(_), .., None, _] => todo!(),
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/match-vec-fixed.rs b/tests/ui/pattern/usefulness/match-vec-fixed.rs
new file mode 100644
index 00000000000..e611779dec2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-fixed.rs
@@ -0,0 +1,18 @@
+#![deny(unreachable_patterns)]
+
+fn a() {
+    let v = [1, 2, 3];
+    match v {
+        [_, _, _] => {}
+        [_, _, _] => {} //~ ERROR unreachable pattern
+    }
+    match v {
+        [_, 1, _] => {}
+        [_, 1, _] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
+
+fn main() {
+    a();
+}
diff --git a/tests/ui/pattern/usefulness/match-vec-fixed.stderr b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
new file mode 100644
index 00000000000..e388a06cb9a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-fixed.stderr
@@ -0,0 +1,20 @@
+error: unreachable pattern
+  --> $DIR/match-vec-fixed.rs:7:9
+   |
+LL |         [_, _, _] => {}
+   |         ^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-vec-fixed.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-fixed.rs:11:9
+   |
+LL |         [_, 1, _] => {}
+   |         ^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern/usefulness/match-vec-unreachable.rs b/tests/ui/pattern/usefulness/match-vec-unreachable.rs
new file mode 100644
index 00000000000..3342389be6e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-unreachable.rs
@@ -0,0 +1,29 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let x: Vec<(isize, isize)> = Vec::new();
+    let x: &[(isize, isize)] = &x;
+    match *x {
+        [a, (2, 3), _] => (),
+        [(1, 2), (2, 3), b] => (), //~ ERROR unreachable pattern
+        _ => ()
+    }
+
+    let x: Vec<String> = vec!["foo".to_string(),
+                              "bar".to_string(),
+                              "baz".to_string()];
+    let x: &[String] = &x;
+    match *x {
+        [ref a, _, _, ..] => { println!("{}", a); }
+        [_, _, _, _, _] => { } //~ ERROR unreachable pattern
+        _ => { }
+    }
+
+    let x: Vec<char> = vec!['a', 'b', 'c'];
+    let x: &[char] = &x;
+    match *x {
+        ['a', 'b', 'c', ref _tail @ ..] => {}
+        ['a', 'b', 'c'] => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/match-vec-unreachable.stderr b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
new file mode 100644
index 00000000000..672fd92fb5e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/match-vec-unreachable.stderr
@@ -0,0 +1,26 @@
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:8:9
+   |
+LL |         [(1, 2), (2, 3), b] => (),
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/match-vec-unreachable.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:18:9
+   |
+LL |         [_, _, _, _, _] => { }
+   |         ^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/match-vec-unreachable.rs:26:9
+   |
+LL |         ['a', 'b', 'c'] => {}
+   |         ^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern/usefulness/nested-exhaustive-match.rs b/tests/ui/pattern/usefulness/nested-exhaustive-match.rs
new file mode 100644
index 00000000000..8b2294f8432
--- /dev/null
+++ b/tests/ui/pattern/usefulness/nested-exhaustive-match.rs
@@ -0,0 +1,14 @@
+// run-pass
+#![allow(dead_code)]
+// pretty-expanded FIXME #23616
+
+struct Foo { foo: bool, bar: Option<isize>, baz: isize }
+
+pub fn main() {
+    match (Foo{foo: true, bar: Some(10), baz: 20}) {
+      Foo{foo: true, bar: Some(_), ..} => {}
+      Foo{foo: false, bar: None, ..} => {}
+      Foo{foo: true, bar: None, ..} => {}
+      Foo{foo: false, bar: Some(_), ..} => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
new file mode 100644
index 00000000000..af42fc1aeb4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs
@@ -0,0 +1,105 @@
+// Test the "defined here" and "not covered" diagnostic hints.
+// We also make sure that references are peeled off from the scrutinee type
+// so that the diagnostics work better with default binding modes.
+
+#[derive(Clone)]
+enum E {
+    //~^ NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    //~| NOTE
+    A,
+    B,
+    //~^ NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE `E` defined here
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    //~| NOTE  not covered
+    C
+    //~^ not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+    //~| not covered
+}
+
+fn by_val(e: E) {
+    let e1 = e.clone();
+    match e1 { //~ ERROR non-exhaustive patterns: `E::B` and `E::C` not covered
+        //~^ NOTE patterns `E::B` and `E::C` not covered
+        //~| NOTE the matched value is of type `E`
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `E::B` and `E::C` not covered
+    //~^ NOTE patterns `E::B` and `E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `E`
+}
+
+fn by_ref_once(e: &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&E::B` and `&E::C` not covered
+    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    //~| NOTE the matched value is of type `&E`
+        E::A => {}
+    }
+
+    let E::A = e; //~ ERROR refutable pattern in local binding: `&E::B` and `&E::C` not covered
+    //~^ NOTE patterns `&E::B` and `&E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `&E`
+}
+
+fn by_ref_thrice(e: & &mut &E) {
+    match e { //~ ERROR non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
+    //~^ NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE the matched value is of type `&&mut &E`
+        E::A => {}
+    }
+
+    let E::A = e;
+    //~^ ERROR refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+    //~| NOTE the matched value is of type `&&mut &E`
+}
+
+enum Opt {
+    //~^ NOTE
+    //~| NOTE
+    Some(u8),
+    None,
+    //~^ NOTE `Opt` defined here
+    //~| NOTE `Opt` defined here
+    //~| NOTE not covered
+    //~| NOTE not covered
+}
+
+fn ref_pat(e: Opt) {
+    match e {//~ ERROR non-exhaustive patterns: `Opt::None` not covered
+        //~^ NOTE pattern `Opt::None` not covered
+        //~| NOTE the matched value is of type `Opt`
+        Opt::Some(ref _x) => {}
+    }
+
+    let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `Opt::None` not covered
+    //~^ NOTE the matched value is of type `Opt`
+    //~| NOTE pattern `Opt::None` not covered
+    //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with
+    //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
new file mode 100644
index 00000000000..678c9b2ab58
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr
@@ -0,0 +1,198 @@
+error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:38:11
+   |
+LL |     match e1 {
+   |           ^^ patterns `E::B` and `E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `E`
+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 ~         E::A => {}
+LL +         E::B | E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `E::B` and `E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:44:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `E::B` and `E::C` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:52:11
+   |
+LL |     match e {
+   |           ^ patterns `&E::B` and `&E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&E`
+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 ~         E::A => {}
+LL +         &E::B | &E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `&E::B` and `&E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:58:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `&E::B` and `&E::C` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:66:11
+   |
+LL |     match e {
+   |           ^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
+   |
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&&mut &E`
+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 ~         E::A => {}
+LL +         &&mut &E::B | &&mut &E::C => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:72:9
+   |
+LL |     let E::A = e;
+   |         ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `E` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:14:5
+   |
+LL | enum E {
+   |      -
+...
+LL |     B,
+   |     ^ not covered
+...
+LL |     C
+   |     ^ not covered
+   = note: the matched value is of type `&&mut &E`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let E::A = e { todo!() }
+   |     ++              ~~~~~~~~~~~
+
+error[E0004]: non-exhaustive patterns: `Opt::None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:92:11
+   |
+LL |     match e {
+   |           ^ pattern `Opt::None` not covered
+   |
+note: `Opt` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:84:5
+   |
+LL | enum Opt {
+   |      ---
+...
+LL |     None,
+   |     ^^^^ not covered
+   = note: the matched value is of type `Opt`
+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 ~         Opt::Some(ref _x) => {}
+LL +         Opt::None => todo!()
+   |
+
+error[E0005]: refutable pattern in local binding: `Opt::None` not covered
+  --> $DIR/non-exhaustive-defined-here.rs:98:9
+   |
+LL |     let Opt::Some(ref _x) = e;
+   |         ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+note: `Opt` defined here
+  --> $DIR/non-exhaustive-defined-here.rs:84:5
+   |
+LL | enum Opt {
+   |      ---
+...
+LL |     None,
+   |     ^^^^ not covered
+   = note: the matched value is of type `Opt`
+help: you might want to use `if let` to ignore the variant that isn't matched
+   |
+LL |     let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() };
+   |     +++++++++++                           +++++++++++++++++++++++
+help: alternatively, you might want to use let else to handle the variant that isn't matched
+   |
+LL |     let Opt::Some(ref _x) = e else { todo!() };
+   |                               ++++++++++++++++
+
+error: aborting due to 8 previous errors
+
+Some errors have detailed explanations: E0004, E0005.
+For more information about an error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs
new file mode 100644
index 00000000000..69c3c76580a
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.rs
@@ -0,0 +1,19 @@
+enum T { A(U), B }
+enum U { C, D }
+
+fn match_nested_vecs<'a, T>(l1: Option<&'a [T]>, l2: Result<&'a [T], ()>) -> &'static str {
+    match (l1, l2) { //~ ERROR non-exhaustive patterns: `(Some(&[]), Err(_))` not covered
+        (Some(&[]), Ok(&[])) => "Some(empty), Ok(empty)",
+        (Some(&[_, ..]), Ok(_)) | (Some(&[_, ..]), Err(())) => "Some(non-empty), any",
+        (None, Ok(&[])) | (None, Err(())) | (None, Ok(&[_])) => "None, Ok(less than one element)",
+        (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)"
+    }
+}
+
+fn main() {
+    let x = T::A(U::C);
+    match x { //~ ERROR non-exhaustive patterns: `T::A(U::C)` not covered
+        T::A(U::D) => { panic!("hello"); }
+        T::B => { panic!("goodbye"); }
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
new file mode 100644
index 00000000000..44f32742110
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match-nested.stderr
@@ -0,0 +1,34 @@
+error[E0004]: non-exhaustive patterns: `(Some(&[]), Err(_))` not covered
+  --> $DIR/non-exhaustive-match-nested.rs:5:11
+   |
+LL |     match (l1, l2) {
+   |           ^^^^^^^^ pattern `(Some(&[]), Err(_))` not covered
+   |
+   = note: the matched value is of type `(Option<&[T]>, Result<&[T], ()>)`
+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 ~         (None, Ok(&[_, _, ..])) => "None, Ok(at least two elements)",
+LL +         (Some(&[]), Err(_)) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `T::A(U::C)` not covered
+  --> $DIR/non-exhaustive-match-nested.rs:15:11
+   |
+LL |     match x {
+   |           ^ pattern `T::A(U::C)` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match-nested.rs:1:10
+   |
+LL | enum T { A(U), B }
+   |      -   ^ not covered
+   = note: the matched value is of type `T`
+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 ~         T::B => { panic!("goodbye"); }
+LL +         T::A(U::C) => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.rs b/tests/ui/pattern/usefulness/non-exhaustive-match.rs
new file mode 100644
index 00000000000..1cb58b8cebe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match.rs
@@ -0,0 +1,63 @@
+#![allow(illegal_floating_point_literal_pattern)]
+
+enum T { A, B }
+
+fn main() {
+    let x = T::A;
+    match x { T::B => { } } //~ ERROR non-exhaustive patterns: `T::A` not covered
+    match true { //~ ERROR non-exhaustive patterns: `false` not covered
+      true => {}
+    }
+    match Some(10) { //~ ERROR non-exhaustive patterns: `Some(_)` not covered
+      None => {}
+    }
+    match (2, 3, 4) { //~ ERROR non-exhaustive patterns: `(_, _, i32::MIN..=3_i32)`
+                      //  and `(_, _, 5_i32..=i32::MAX)` not covered
+      (_, _, 4) => {}
+    }
+    match (T::A, T::A) { //~ ERROR non-exhaustive patterns: `(T::A, T::A)` and `(T::B, T::B)` not covered
+      (T::A, T::B) => {}
+      (T::B, T::A) => {}
+    }
+    match T::A { //~ ERROR non-exhaustive patterns: `T::B` not covered
+      T::A => {}
+    }
+    // This is exhaustive, though the algorithm got it wrong at one point
+    match (T::A, T::B) {
+      (T::A, _) => {}
+      (_, T::A) => {}
+      (T::B, T::B) => {}
+    }
+    let vec = vec![Some(42), None, Some(21)];
+    let vec: &[Option<isize>] = &vec;
+    match *vec { //~ ERROR non-exhaustive patterns: `[]` not covered
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
+        [None] => {}
+    }
+    let vec = vec![1];
+    let vec: &[isize] = &vec;
+    match *vec {
+        [_, ref tail @ ..] => (),
+        [] => ()
+    }
+    let vec = vec![0.5f32];
+    let vec: &[f32] = &vec;
+    match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _, ..]` not covered
+        [0.1, 0.2, 0.3] => (),
+        [0.1, 0.2] => (),
+        [0.1] => (),
+        [] => ()
+    }
+    let vec = vec![Some(42), None, Some(21)];
+    let vec: &[Option<isize>] = &vec;
+    match *vec {
+        [Some(..), None, ref tail @ ..] => {}
+        [Some(..), Some(..), ref tail @ ..] => {}
+        [None, None, ref tail @ ..] => {}
+        [None, Some(..), ref tail @ ..] => {}
+        [Some(_)] => {}
+        [None] => {}
+        [] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-match.stderr b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
new file mode 100644
index 00000000000..e2260f50bfe
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-match.stderr
@@ -0,0 +1,121 @@
+error[E0004]: non-exhaustive patterns: `T::A` not covered
+  --> $DIR/non-exhaustive-match.rs:7:11
+   |
+LL |     match x { T::B => { } }
+   |           ^ pattern `T::A` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match.rs:3:10
+   |
+LL | enum T { A, B }
+   |      -   ^ not covered
+   = note: the matched value is of type `T`
+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 |     match x { T::B => { }, T::A => todo!() }
+   |                          +++++++++++++++++
+
+error[E0004]: non-exhaustive patterns: `false` not covered
+  --> $DIR/non-exhaustive-match.rs:8:11
+   |
+LL |     match true {
+   |           ^^^^ pattern `false` not covered
+   |
+   = note: the matched value is of type `bool`
+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 ~       true => {}
+LL +       false => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+  --> $DIR/non-exhaustive-match.rs:11:11
+   |
+LL |     match Some(10) {
+   |           ^^^^^^^^ pattern `Some(_)` not covered
+   |
+note: `Option<i32>` defined here
+  --> $SRC_DIR/core/src/option.rs:LL:COL
+  ::: $SRC_DIR/core/src/option.rs:LL:COL
+   |
+   = note: not covered
+   = note: the matched value is of type `Option<i32>`
+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 ~       None => {}
+LL +       Some(_) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered
+  --> $DIR/non-exhaustive-match.rs:14:11
+   |
+LL |     match (2, 3, 4) {
+   |           ^^^^^^^^^ patterns `(_, _, i32::MIN..=3_i32)` and `(_, _, 5_i32..=i32::MAX)` not covered
+   |
+   = note: the matched value is of type `(i32, i32, i32)`
+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 ~       (_, _, 4) => {}
+LL +       (_, _, i32::MIN..=3_i32) | (_, _, 5_i32..=i32::MAX) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `(T::A, T::A)` and `(T::B, T::B)` not covered
+  --> $DIR/non-exhaustive-match.rs:18:11
+   |
+LL |     match (T::A, T::A) {
+   |           ^^^^^^^^^^^^ patterns `(T::A, T::A)` and `(T::B, T::B)` not covered
+   |
+   = note: the matched value is of type `(T, T)`
+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 ~       (T::B, T::A) => {}
+LL +       (T::A, T::A) | (T::B, T::B) => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `T::B` not covered
+  --> $DIR/non-exhaustive-match.rs:22:11
+   |
+LL |     match T::A {
+   |           ^^^^ pattern `T::B` not covered
+   |
+note: `T` defined here
+  --> $DIR/non-exhaustive-match.rs:3:13
+   |
+LL | enum T { A, B }
+   |      -      ^ not covered
+   = note: the matched value is of type `T`
+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 ~       T::A => {}
+LL +       T::B => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[]` not covered
+  --> $DIR/non-exhaustive-match.rs:33:11
+   |
+LL |     match *vec {
+   |           ^^^^ pattern `[]` not covered
+   |
+   = note: the matched value is of type `[Option<isize>]`
+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 ~         [None] => {}
+LL +         [] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered
+  --> $DIR/non-exhaustive-match.rs:46:11
+   |
+LL |     match *vec {
+   |           ^^^^ pattern `[_, _, _, _, ..]` not covered
+   |
+   = note: the matched value is of type `[f32]`
+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 ~         [] => (),
+LL +         [_, _, _, _, ..] => todo!()
+   |
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs
new file mode 100644
index 00000000000..4bd34421922
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs
@@ -0,0 +1,89 @@
+struct Foo {
+    first: bool,
+    second: Option<[usize; 4]>
+}
+
+fn struct_with_a_nested_enum_and_vector() {
+    match (Foo { first: true, second: None }) {
+//~^ ERROR non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+        Foo { first: true, second: None } => (),
+        Foo { first: true, second: Some(_) } => (),
+        Foo { first: false, second: None } => (),
+        Foo { first: false, second: Some([1, 2, 3, 4]) } => ()
+    }
+}
+
+enum Color {
+    Red,
+    Green,
+    CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
+}
+
+fn enum_with_single_missing_variant() {
+    match Color::Red {
+    //~^ ERROR non-exhaustive patterns: `Color::Red` not covered
+        Color::CustomRGBA { .. } => (),
+        Color::Green => ()
+    }
+}
+
+enum Direction {
+    North, East, South, West
+}
+
+fn enum_with_multiple_missing_variants() {
+    match Direction::North {
+    //~^ ERROR non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
+        Direction::North => ()
+    }
+}
+
+enum ExcessiveEnum {
+    First, Second, Third, Fourth, Fifth, Sixth, Seventh, Eighth, Ninth, Tenth, Eleventh, Twelfth
+}
+
+fn enum_with_excessive_missing_variants() {
+    match ExcessiveEnum::First {
+    //~^ ERROR `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+
+        ExcessiveEnum::First => ()
+    }
+}
+
+fn enum_struct_variant() {
+    match Color::Red {
+    //~^ ERROR non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
+        Color::Red => (),
+        Color::Green => (),
+        Color::CustomRGBA { a: false, r: _, g: _, b: 0 } => (),
+        Color::CustomRGBA { a: false, r: _, g: _, b: _ } => ()
+    }
+}
+
+enum Enum {
+    First,
+    Second(bool)
+}
+
+fn vectors_with_nested_enums() {
+    let x: &'static [Enum] = &[Enum::First, Enum::Second(false)];
+    match *x {
+    //~^ ERROR non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
+        [] => (),
+        [_] => (),
+        [Enum::First, _] => (),
+        [Enum::Second(true), Enum::First] => (),
+        [Enum::Second(true), Enum::Second(true)] => (),
+        [Enum::Second(false), _] => (),
+        [_, _, ref tail @ .., _] => ()
+    }
+}
+
+fn missing_nil() {
+    match ((), false) {
+    //~^ ERROR non-exhaustive patterns: `((), false)` not covered
+        ((), true) => ()
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
new file mode 100644
index 00000000000..b8af566de7c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr
@@ -0,0 +1,129 @@
+error[E0004]: non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:7:11
+   |
+LL |     match (Foo { first: true, second: None }) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { first: false, second: Some([_, _, _, _]) }` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:1:8
+   |
+LL | struct Foo {
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo { first: false, second: Some([1, 2, 3, 4]) } => (),
+LL +         Foo { first: false, second: Some([_, _, _, _]) } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Color::Red` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:23:11
+   |
+LL |     match Color::Red {
+   |           ^^^^^^^^^^ pattern `Color::Red` not covered
+   |
+note: `Color` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:17:5
+   |
+LL | enum Color {
+   |      -----
+LL |     Red,
+   |     ^^^ not covered
+   = note: the matched value is of type `Color`
+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 ~         Color::Green => (),
+LL +         Color::Red => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Direction::East`, `Direction::South` and `Direction::West` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:35:11
+   |
+LL |     match Direction::North {
+   |           ^^^^^^^^^^^^^^^^ patterns `Direction::East`, `Direction::South` and `Direction::West` not covered
+   |
+note: `Direction` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:31:12
+   |
+LL | enum Direction {
+   |      ---------
+LL |     North, East, South, West
+   |            ^^^^  ^^^^^  ^^^^ not covered
+   |            |     |
+   |            |     not covered
+   |            not covered
+   = note: the matched value is of type `Direction`
+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 ~         Direction::North => (),
+LL +         Direction::East | Direction::South | Direction::West => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:46:11
+   |
+LL |     match ExcessiveEnum::First {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `ExcessiveEnum::Second`, `ExcessiveEnum::Third`, `ExcessiveEnum::Fourth` and 8 more not covered
+   |
+note: `ExcessiveEnum` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:41:6
+   |
+LL | enum ExcessiveEnum {
+   |      ^^^^^^^^^^^^^
+   = note: the matched value is of type `ExcessiveEnum`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown, or multiple match arms
+   |
+LL ~         ExcessiveEnum::First => (),
+LL +         _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `Color::CustomRGBA { a: true, .. }` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:54:11
+   |
+LL |     match Color::Red {
+   |           ^^^^^^^^^^ pattern `Color::CustomRGBA { a: true, .. }` not covered
+   |
+note: `Color` defined here
+  --> $DIR/non-exhaustive-pattern-witness.rs:19:5
+   |
+LL | enum Color {
+   |      -----
+...
+LL |     CustomRGBA { a: bool, r: u8, g: u8, b: u8 }
+   |     ^^^^^^^^^^ not covered
+   = note: the matched value is of type `Color`
+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 ~         Color::CustomRGBA { a: false, r: _, g: _, b: _ } => (),
+LL +         Color::CustomRGBA { a: true, .. } => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `[Enum::Second(true), Enum::Second(false)]` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:70:11
+   |
+LL |     match *x {
+   |           ^^ pattern `[Enum::Second(true), Enum::Second(false)]` not covered
+   |
+   = note: the matched value is of type `[Enum]`
+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 ~         [_, _, ref tail @ .., _] => (),
+LL +         [Enum::Second(true), Enum::Second(false)] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `((), false)` not covered
+  --> $DIR/non-exhaustive-pattern-witness.rs:83:11
+   |
+LL |     match ((), false) {
+   |           ^^^^^^^^^^^ pattern `((), false)` not covered
+   |
+   = note: the matched value is of type `((), bool)`
+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 ~         ((), true) => (),
+LL +         ((), false) => todo!()
+   |
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
new file mode 100644
index 00000000000..7c9aa51e748
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs
@@ -0,0 +1,7 @@
+fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
+//~^ ERROR refutable pattern in function argument: `(_, _)` not covered
+
+fn main() {
+    let (1, (Some(1), 2..=3)) = (1, (None, 2));
+    //~^ ERROR refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+}
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
new file mode 100644
index 00000000000..d1dacc822e9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr
@@ -0,0 +1,25 @@
+error[E0005]: refutable pattern in function argument: `(_, _)` not covered
+  --> $DIR/refutable-pattern-errors.rs:1:9
+   |
+LL | fn func((1, (Some(1), 2..=3)): (isize, (Option<isize>, isize))) { }
+   |         ^^^^^^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
+   |
+   = note: the matched value is of type `(isize, (Option<isize>, isize))`
+
+error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+  --> $DIR/refutable-pattern-errors.rs:5:9
+   |
+LL |     let (1, (Some(1), 2..=3)) = (1, (None, 2));
+   |         ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered
+   |
+   = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+   = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+   = note: the matched value is of type `(i32, (Option<i32>, i32))`
+help: you might want to use `if let` to ignore the variants that aren't matched
+   |
+LL |     if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { todo!() }
+   |     ++                                            ~~~~~~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
new file mode 100644
index 00000000000..a2d9e1935de
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let f = |3: isize| println!("hello");
+    //~^ ERROR refutable pattern in function argument: `_` not covered
+    f(4);
+}
diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
new file mode 100644
index 00000000000..c9d8cf43f95
--- /dev/null
+++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr
@@ -0,0 +1,11 @@
+error[E0005]: refutable pattern in function argument: `_` not covered
+  --> $DIR/refutable-pattern-in-fn-arg.rs:2:14
+   |
+LL |     let f = |3: isize| println!("hello");
+   |              ^ pattern `_` not covered
+   |
+   = note: the matched value is of type `isize`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0005`.
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-2.rs b/tests/ui/pattern/usefulness/slice-pattern-const-2.rs
new file mode 100644
index 00000000000..4bf8d0fd2d3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-2.rs
@@ -0,0 +1,31 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &[0x00; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[u32] = &[4, 5, 6, 7]; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        MAGIC_TEST => (),
+        [4, 5, 6, 7] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [4, 5, 6, 7] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [u32; 1] = [4];
+    match [99] {
+        [0x00] => (),
+        [4] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
new file mode 100644
index 00000000000..dcad11a38a7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-2.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:9:9
+   |
+LL |         [4, 5, 6, 7] => (),
+   |         ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const-2.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:15:9
+   |
+LL |         [4, 5, 6, 7] => (),
+   |         ^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-2.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-3.rs b/tests/ui/pattern/usefulness/slice-pattern-const-3.rs
new file mode 100644
index 00000000000..2ca8323f002
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-3.rs
@@ -0,0 +1,31 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &["0x00"; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[&str] = &["4", "5", "6", "7"]; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        ["4", "5", "6", "7"] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        MAGIC_TEST => (),
+        ["4", "5", "6", "7"] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        ["0x00", "0x00", "0x00", "0x00"] => (),
+        ["4", "5", "6", "7"] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [&str; 1] = ["boo"];
+    match ["baa"] {
+        ["0x00"] => (),
+        ["boo"] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
new file mode 100644
index 00000000000..b90b3a88a18
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const-3.stderr
@@ -0,0 +1,32 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:9:9
+   |
+LL |         ["4", "5", "6", "7"] => (),
+   |         ^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const-3.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:15:9
+   |
+LL |         ["4", "5", "6", "7"] => (),
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const-3.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const.rs b/tests/ui/pattern/usefulness/slice-pattern-const.rs
new file mode 100644
index 00000000000..89195d5b11e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const.rs
@@ -0,0 +1,54 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s = &[0x00; 4][..]; //Slice of any value
+    const MAGIC_TEST: &[u8] = b"TEST"; //Const slice to pattern match with
+    match s {
+        MAGIC_TEST => (),
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        MAGIC_TEST => (),
+        [84, 69, 83, 84] => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    match s {
+        [0x00, 0x00, 0x00, 0x00] => (),
+        [84, 69, 83, 84] => (),
+        MAGIC_TEST => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const FOO: [u8; 1] = [4];
+    match [99] {
+        [0x00] => (),
+        [4] => (),
+        FOO => (), //~ ERROR unreachable pattern
+        _ => (),
+    }
+    const BAR: &[u8; 1] = &[4];
+    match &[99] {
+        [0x00] => (),
+        [4] => (),
+        BAR => (), //~ ERROR unreachable pattern
+        b"a" => (),
+        _ => (),
+    }
+
+    const BOO: &[u8; 0] = &[];
+    match &[] {
+        [] => (),
+        BOO => (), //~ ERROR unreachable pattern
+        b"" => (), //~ ERROR unreachable pattern
+        _ => (), //~ ERROR unreachable pattern
+    }
+
+    const CONST1: &[bool; 1] = &[true];
+    match &[false] {
+        CONST1 => {}
+        [true] => {} //~ ERROR unreachable pattern
+        [false] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-pattern-const.stderr b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
new file mode 100644
index 00000000000..1fffb9fedbf
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-pattern-const.stderr
@@ -0,0 +1,62 @@
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:9:9
+   |
+LL |         [84, 69, 83, 84] => (),
+   |         ^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-pattern-const.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:15:9
+   |
+LL |         [84, 69, 83, 84] => (),
+   |         ^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:21:9
+   |
+LL |         MAGIC_TEST => (),
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:28:9
+   |
+LL |         FOO => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:35:9
+   |
+LL |         BAR => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:43:9
+   |
+LL |         BOO => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:44:9
+   |
+LL |         b"" => (),
+   |         ^^^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:45:9
+   |
+LL |         _ => (),
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/slice-pattern-const.rs:51:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: aborting due to 9 previous errors
+
diff --git a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs
new file mode 100644
index 00000000000..46e0da5be9b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs
@@ -0,0 +1,129 @@
+fn main() {
+    let s: &[bool] = &[true; 0];
+    let s1: &[bool; 1] = &[false; 1];
+    let s2: &[bool; 2] = &[false; 2];
+    let s3: &[bool; 3] = &[false; 3];
+    let s10: &[bool; 10] = &[false; 10];
+
+    match s2 {
+        //~^ ERROR `&[false, _]` not covered
+        [true, .., true] => {}
+    }
+    match s3 {
+        //~^ ERROR `&[false, ..]` not covered
+        [true, .., true] => {}
+    }
+    match s10 {
+        //~^ ERROR `&[false, ..]` not covered
+        [true, .., true] => {}
+    }
+
+    match s1 {
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s2 {
+        //~^ ERROR `&[false, true]` not covered
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s3 {
+        //~^ ERROR `&[false, .., true]` not covered
+        [true, ..] => {}
+        [.., false] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, .., true]` not covered
+        [] => {}
+        [true, ..] => {}
+        [.., false] => {}
+    }
+
+    match s {
+        //~^ ERROR `&[_, ..]` not covered
+        [] => {}
+    }
+    match s {
+        //~^ ERROR `&[_, _, ..]` not covered
+        [] => {}
+        [_] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, ..]` not covered
+        [] => {}
+        [true, ..] => {}
+    }
+    match s {
+        //~^ ERROR `&[false, _, ..]` not covered
+        [] => {}
+        [_] => {}
+        [true, ..] => {}
+    }
+    match s {
+        //~^ ERROR `&[_, .., false]` not covered
+        [] => {}
+        [_] => {}
+        [.., true] => {}
+    }
+
+    match s {
+        //~^ ERROR `&[_, _, .., true]` not covered
+        [] => {}
+        [_] => {}
+        [_, _] => {}
+        [.., false] => {}
+    }
+    match s {
+        //~^ ERROR `&[true, _, .., _]` not covered
+        [] => {}
+        [_] => {}
+        [_, _] => {}
+        [false, .., false] => {}
+    }
+
+    const CONST: &[bool] = &[true];
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        &[true] => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        CONST => {}
+        &[false] => {}
+    }
+    match s {
+        //~^ ERROR `&[]` and `&[_, _, ..]` not covered
+        &[false] => {}
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[_, _, ..]` not covered
+        &[] => {}
+        CONST => {}
+    }
+    match s {
+        //~^ ERROR `&[false]` not covered
+        &[] => {}
+        CONST => {}
+        &[_, _, ..] => {}
+    }
+    match s {
+        [] => {}
+        [false] => {}
+        CONST => {}
+        [_, _, ..] => {}
+    }
+    const CONST1: &[bool; 1] = &[true];
+    match s1 {
+        //~^ ERROR `&[false]` not covered
+        CONST1 => {}
+    }
+    match s1 {
+        CONST1 => {}
+        [false] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr
new file mode 100644
index 00000000000..5d1e170ae6c
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr
@@ -0,0 +1,263 @@
+error[E0004]: non-exhaustive patterns: `&[false, _]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:8:11
+   |
+LL |     match s2 {
+   |           ^^ pattern `&[false, _]` not covered
+   |
+   = note: the matched value is of type `&[bool; 2]`
+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 ~         [true, .., true] => {}
+LL +         &[false, _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:12:11
+   |
+LL |     match s3 {
+   |           ^^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool; 3]`
+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 ~         [true, .., true] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:16:11
+   |
+LL |     match s10 {
+   |           ^^^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool; 10]`
+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 ~         [true, .., true] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:25:11
+   |
+LL |     match s2 {
+   |           ^^ pattern `&[false, true]` not covered
+   |
+   = note: the matched value is of type `&[bool; 2]`
+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 ~         [.., false] => {}
+LL +         &[false, true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:30:11
+   |
+LL |     match s3 {
+   |           ^^ pattern `&[false, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool; 3]`
+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 ~         [.., false] => {}
+LL +         &[false, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:35:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., false] => {}
+LL +         &[false, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:42:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [] => {}
+LL +         &[_, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:46:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [_] => {}
+LL +         &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:51:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [true, ..] => {}
+LL +         &[false, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:56:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [true, ..] => {}
+LL +         &[false, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:62:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, .., false]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., true] => {}
+LL +         &[_, .., false] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:69:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, .., true]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [.., false] => {}
+LL +         &[_, _, .., true] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:76:11
+   |
+LL |     match s {
+   |           ^ pattern `&[true, _, .., _]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         [false, .., false] => {}
+LL +         &[true, _, .., _] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:85:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         &[true] => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:89:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:93:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         &[false] => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:98:11
+   |
+LL |     match s {
+   |           ^ patterns `&[]` and `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[] | &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:103:11
+   |
+LL |     match s {
+   |           ^ pattern `&[_, _, ..]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         CONST => {}
+LL +         &[_, _, ..] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:108:11
+   |
+LL |     match s {
+   |           ^ pattern `&[false]` not covered
+   |
+   = note: the matched value is of type `&[bool]`
+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 ~         &[_, _, ..] => {}
+LL +         &[false] => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `&[false]` not covered
+  --> $DIR/slice-patterns-exhaustiveness.rs:121:11
+   |
+LL |     match s1 {
+   |           ^^ pattern `&[false]` not covered
+   |
+   = note: the matched value is of type `&[bool; 1]`
+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 ~         CONST1 => {}
+LL +         &[false] => todo!()
+   |
+
+error: aborting due to 20 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs b/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs
new file mode 100644
index 00000000000..cbf64e2c53d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-irrefutable.rs
@@ -0,0 +1,26 @@
+// check-pass
+
+fn main() {
+    let s: &[bool] = &[true; 0];
+    let s0: &[bool; 0] = &[];
+    let s1: &[bool; 1] = &[false; 1];
+    let s2: &[bool; 2] = &[false; 2];
+
+    let [] = s0;
+    let [_] = s1;
+    let [_, _] = s2;
+
+    let [..] = s;
+    let [..] = s0;
+    let [..] = s1;
+    let [..] = s2;
+
+    let [_, ..] = s1;
+    let [.., _] = s1;
+    let [_, ..] = s2;
+    let [.., _] = s2;
+
+    let [_, _, ..] = s2;
+    let [_, .., _] = s2;
+    let [.., _, _] = s2;
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-reachability.rs b/tests/ui/pattern/usefulness/slice-patterns-reachability.rs
new file mode 100644
index 00000000000..7c747b5e0b9
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-reachability.rs
@@ -0,0 +1,25 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let s: &[bool] = &[];
+
+    match s {
+        [true, ..] => {}
+        [true, ..] => {} //~ ERROR unreachable pattern
+        [true] => {} //~ ERROR unreachable pattern
+        [..] => {}
+    }
+    match s {
+        [.., true] => {}
+        [.., true] => {} //~ ERROR unreachable pattern
+        [true] => {} //~ ERROR unreachable pattern
+        [..] => {}
+    }
+    match s {
+        [false, .., true] => {}
+        [false, .., true] => {} //~ ERROR unreachable pattern
+        [false, true] => {} //~ ERROR unreachable pattern
+        [false] => {}
+        [..] => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
new file mode 100644
index 00000000000..607ffb76595
--- /dev/null
+++ b/tests/ui/pattern/usefulness/slice-patterns-reachability.stderr
@@ -0,0 +1,44 @@
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:8:9
+   |
+LL |         [true, ..] => {}
+   |         ^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/slice-patterns-reachability.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:9:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:14:9
+   |
+LL |         [.., true] => {}
+   |         ^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:15:9
+   |
+LL |         [true] => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:20:9
+   |
+LL |         [false, .., true] => {}
+   |         ^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/slice-patterns-reachability.rs:21:9
+   |
+LL |         [false, true] => {}
+   |         ^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.rs b/tests/ui/pattern/usefulness/stable-gated-fields.rs
new file mode 100644
index 00000000000..90f40a8d629
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-fields.rs
@@ -0,0 +1,16 @@
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableStruct;
+
+fn main() {
+    let UnstableStruct { stable } = UnstableStruct::default();
+    //~^ pattern does not mention field `stable2` and inaccessible fields
+
+    let UnstableStruct { stable, stable2 } = UnstableStruct::default();
+    //~^ pattern requires `..` due to inaccessible fields
+
+    // OK: stable field is matched
+    let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+}
diff --git a/tests/ui/pattern/usefulness/stable-gated-fields.stderr b/tests/ui/pattern/usefulness/stable-gated-fields.stderr
new file mode 100644
index 00000000000..cf98c51a2b4
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-fields.stderr
@@ -0,0 +1,29 @@
+error[E0027]: pattern does not mention field `stable2` and inaccessible fields
+  --> $DIR/stable-gated-fields.rs:8:9
+   |
+LL |     let UnstableStruct { stable } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `stable2` and inaccessible fields
+   |
+help: include the missing field in the pattern and ignore the inaccessible fields
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                ~~~~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, .. } = UnstableStruct::default();
+   |                                ~~~~~~
+
+error: pattern requires `..` due to inaccessible fields
+  --> $DIR/stable-gated-fields.rs:11:9
+   |
+LL |     let UnstableStruct { stable, stable2 } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: ignore the inaccessible and unused fields
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                         ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.rs b/tests/ui/pattern/usefulness/stable-gated-patterns.rs
new file mode 100644
index 00000000000..03db01160dd
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-patterns.rs
@@ -0,0 +1,18 @@
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableEnum;
+
+fn main() {
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+    }
+    //~^^^ non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered
+
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `_` not covered
+}
diff --git a/tests/ui/pattern/usefulness/stable-gated-patterns.stderr b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
new file mode 100644
index 00000000000..7b8588a3c73
--- /dev/null
+++ b/tests/ui/pattern/usefulness/stable-gated-patterns.stderr
@@ -0,0 +1,42 @@
+error[E0004]: non-exhaustive patterns: `UnstableEnum::Stable2` and `_` not covered
+  --> $DIR/stable-gated-patterns.rs:8:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ patterns `UnstableEnum::Stable2` and `_` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:9:5
+   |
+LL | pub enum UnstableEnum {
+   | ---------------------
+...
+LL |     Stable2,
+   |     ^^^^^^^ not covered
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable => {}
+LL +         UnstableEnum::Stable2 | _ => todo!()
+   |
+
+error[E0004]: non-exhaustive patterns: `_` not covered
+  --> $DIR/stable-gated-patterns.rs:13:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:5:1
+   |
+LL | pub enum UnstableEnum {
+   | ^^^^^^^^^^^^^^^^^^^^^
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable2 => {}
+LL +         _ => todo!()
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs
new file mode 100644
index 00000000000..b1fc0f5ad3e
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs
@@ -0,0 +1,12 @@
+enum A {
+    B { x: Option<isize> },
+    C
+}
+
+fn main() {
+    let x = A::B { x: Some(3) };
+    match x {   //~ ERROR non-exhaustive patterns
+        A::C => {}
+        A::B { x: None } => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
new file mode 100644
index 00000000000..85c97be29d6
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr
@@ -0,0 +1,23 @@
+error[E0004]: non-exhaustive patterns: `A::B { x: Some(_) }` not covered
+  --> $DIR/struct-like-enum-nonexhaustive.rs:8:11
+   |
+LL |     match x {
+   |           ^ pattern `A::B { x: Some(_) }` not covered
+   |
+note: `A` defined here
+  --> $DIR/struct-like-enum-nonexhaustive.rs:2:5
+   |
+LL | enum A {
+   |      -
+LL |     B { x: Option<isize> },
+   |     ^ not covered
+   = note: the matched value is of type `A`
+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 ~         A::B { x: None } => {}
+LL +         A::B { x: Some(_) } => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs b/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs
new file mode 100644
index 00000000000..93f0a931761
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-pattern-match-useless.rs
@@ -0,0 +1,15 @@
+#![deny(unreachable_patterns)]
+
+struct Foo {
+    x: isize,
+    y: isize,
+}
+
+pub fn main() {
+    let a = Foo { x: 1, y: 2 };
+    match a {
+        Foo { x: _x, y: _y } => (),
+        Foo { .. } => () //~ ERROR unreachable pattern
+    }
+
+}
diff --git a/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr b/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr
new file mode 100644
index 00000000000..fbee33de6f3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/struct-pattern-match-useless.stderr
@@ -0,0 +1,16 @@
+error: unreachable pattern
+  --> $DIR/struct-pattern-match-useless.rs:12:9
+   |
+LL |         Foo { x: _x, y: _y } => (),
+   |         -------------------- matches any value
+LL |         Foo { .. } => ()
+   |         ^^^^^^^^^^ unreachable pattern
+   |
+note: the lint level is defined here
+  --> $DIR/struct-pattern-match-useless.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/pattern/usefulness/top-level-alternation.rs b/tests/ui/pattern/usefulness/top-level-alternation.rs
new file mode 100644
index 00000000000..e8cd12ea4a2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/top-level-alternation.rs
@@ -0,0 +1,57 @@
+#![deny(unreachable_patterns)]
+
+fn main() {
+    while let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+    if let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern
+
+    match 0u8 {
+        0
+            | 0 => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match Some(0u8) {
+        Some(0)
+            | Some(0) => {} //~ ERROR unreachable pattern
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (_, 0) => {}
+        (0, 0) => {} //~ ERROR unreachable pattern
+        (1, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, 1) | (2, 3) => {}
+        (0, 3) => {}
+        (2, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (_, 0) | (_, 1) => {}
+        _ => {}
+    }
+    match (0u8, 0u8) {
+        (0, _) | (1, _) => {}
+        _ => {}
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        _ => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        None | Some(_) => {}
+        Some(_) => {} //~ ERROR unreachable pattern
+        None => {} //~ ERROR unreachable pattern
+    }
+    match Some(0u8) {
+        Some(_) => {}
+        None => {}
+        None | Some(_) => {} //~ ERROR unreachable pattern
+    }
+    match 0u8 {
+        1 | 2 => {},
+        1..=2 => {}, //~ ERROR unreachable pattern
+        _ => {},
+    }
+    let (0 | 0) = 0 else { return }; //~ ERROR unreachable pattern
+}
diff --git a/tests/ui/pattern/usefulness/top-level-alternation.stderr b/tests/ui/pattern/usefulness/top-level-alternation.stderr
new file mode 100644
index 00000000000..17fa951c539
--- /dev/null
+++ b/tests/ui/pattern/usefulness/top-level-alternation.stderr
@@ -0,0 +1,74 @@
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:4:23
+   |
+LL |     while let 0..=2 | 1 = 0 {}
+   |                       ^
+   |
+note: the lint level is defined here
+  --> $DIR/top-level-alternation.rs:1:9
+   |
+LL | #![deny(unreachable_patterns)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:5:20
+   |
+LL |     if let 0..=2 | 1 = 0 {}
+   |                    ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:9:15
+   |
+LL |             | 0 => {}
+   |               ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:14:15
+   |
+LL |             | Some(0) => {}
+   |               ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:19:9
+   |
+LL |         (0, 0) => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:39:9
+   |
+LL |         _ => {}
+   |         ^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:43:9
+   |
+LL |         Some(_) => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:44:9
+   |
+LL |         None => {}
+   |         ^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:49:9
+   |
+LL |         None | Some(_) => {}
+   |         ^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:53:9
+   |
+LL |         1..=2 => {},
+   |         ^^^^^
+
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:56:14
+   |
+LL |     let (0 | 0) = 0 else { return };
+   |              ^
+
+error: aborting due to 11 previous errors
+
diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs
new file mode 100644
index 00000000000..76bcf3fbd4d
--- /dev/null
+++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs
@@ -0,0 +1,9 @@
+struct Foo(isize, isize);
+
+fn main() {
+    let x = Foo(1, 2);
+    match x {   //~ ERROR non-exhaustive
+        Foo(1, b) => println!("{}", b),
+        Foo(2, b) => println!("{}", b)
+    }
+}
diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
new file mode 100644
index 00000000000..e2a65ff8524
--- /dev/null
+++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
@@ -0,0 +1,21 @@
+error[E0004]: non-exhaustive patterns: `Foo(_, _)` not covered
+  --> $DIR/tuple-struct-nonexhaustive.rs:5:11
+   |
+LL |     match x {
+   |           ^ pattern `Foo(_, _)` not covered
+   |
+note: `Foo` defined here
+  --> $DIR/tuple-struct-nonexhaustive.rs:1:8
+   |
+LL | struct Foo(isize, isize);
+   |        ^^^
+   = note: the matched value is of type `Foo`
+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 ~         Foo(2, b) => println!("{}", b),
+LL +         Foo(_, _) => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs
new file mode 100644
index 00000000000..cb44c1da76b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.rs
@@ -0,0 +1,36 @@
+#[deny(unreachable_patterns)]
+
+fn parse_data1(data: &[u8]) -> u32 {
+    match data {
+        b"" => 1,
+        _ => 2,
+    }
+}
+
+fn parse_data2(data: &[u8]) -> u32 {
+    match data { //~ ERROR non-exhaustive patterns: `&[_, ..]` not covered
+        b"" => 1,
+    }
+}
+
+fn parse_data3(data: &[u8; 0]) -> u8 {
+    match data {
+        b"" => 1,
+    }
+}
+
+fn parse_data4(data: &[u8]) -> u8 {
+    match data { //~ ERROR non-exhaustive patterns
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn parse_data5(data: &[u8; 3]) -> u8 {
+    match data {
+        b"aaa" => 0,
+        [_, _, _] => 1,
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr
new file mode 100644
index 00000000000..acae605dae3
--- /dev/null
+++ b/tests/ui/pattern/usefulness/type_polymorphic_byte_str_literals.stderr
@@ -0,0 +1,29 @@
+error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:11:11
+   |
+LL |     match data {
+   |           ^^^^ pattern `&[_, ..]` not covered
+   |
+   = 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 or an explicit pattern as shown
+   |
+LL ~         b"" => 1,
+LL ~         &[_, ..] => todo!(),
+   |
+
+error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+  --> $DIR/type_polymorphic_byte_str_literals.rs:23:11
+   |
+LL |     match data {
+   |           ^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered
+   |
+   = 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, or multiple match arms
+   |
+LL ~         [_, _, _] => 1,
+LL ~         _ => todo!(),
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/usefulness/uninhabited.rs b/tests/ui/pattern/usefulness/uninhabited.rs
new file mode 100644
index 00000000000..5622808d4c7
--- /dev/null
+++ b/tests/ui/pattern/usefulness/uninhabited.rs
@@ -0,0 +1,143 @@
+// check-pass
+// aux-build:empty.rs
+//
+// This tests plays with matching and uninhabited types. This also serves as a test for the
+// `Ty::is_inhabited_from` function.
+#![feature(never_type)]
+#![feature(never_type_fallback)]
+#![feature(exhaustive_patterns)]
+#![deny(unreachable_patterns)]
+
+macro_rules! assert_empty {
+    ($ty:ty) => {
+        const _: () = {
+            fn assert_empty(x: $ty) {
+                match x {}
+                match Some(x) {
+                    None => {}
+                }
+            }
+        };
+    };
+}
+macro_rules! assert_non_empty {
+    ($ty:ty) => {
+        const _: () = {
+            fn assert_non_empty(x: $ty) {
+                match x {
+                    _ => {}
+                }
+                match Some(x) {
+                    None => {}
+                    Some(_) => {}
+                }
+            }
+        };
+    };
+}
+
+extern crate empty;
+assert_empty!(empty::EmptyForeignEnum);
+assert_empty!(empty::VisiblyUninhabitedForeignStruct);
+assert_non_empty!(empty::SecretlyUninhabitedForeignStruct);
+
+enum Void {}
+assert_empty!(Void);
+
+enum Enum2 {
+    Foo(Void),
+    Bar(!),
+}
+assert_empty!(Enum2);
+
+enum Enum3 {
+    Foo(Void),
+    Bar {
+        x: u64,
+        y: !,
+    },
+}
+assert_empty!(Enum3);
+
+enum Enum4 {
+    Foo(u64),
+    Bar(!),
+}
+assert_non_empty!(Enum4);
+
+struct Struct1(empty::EmptyForeignEnum);
+assert_empty!(Struct1);
+
+struct Struct2 {
+    x: u64,
+    y: !,
+}
+assert_empty!(Struct2);
+
+union Union {
+    foo: !,
+}
+assert_non_empty!(Union);
+
+assert_empty!((!, String));
+
+assert_non_empty!(&'static !);
+assert_non_empty!(&'static Struct1);
+assert_non_empty!(&'static &'static &'static !);
+
+assert_empty!([!; 1]);
+assert_empty!([Void; 2]);
+assert_non_empty!([!; 0]);
+assert_non_empty!(&'static [!]);
+
+mod visibility {
+    /// This struct can only be seen to be inhabited in modules `b`, `c` or `d`, because otherwise
+    /// the uninhabitedness of both `SecretlyUninhabited` structs is hidden.
+    struct SometimesEmptyStruct {
+        x: a::b::SecretlyUninhabited,
+        y: c::AlsoSecretlyUninhabited,
+    }
+
+    /// This enum can only be seen to be inhabited in module `d`.
+    enum SometimesEmptyEnum {
+        X(c::AlsoSecretlyUninhabited),
+        Y(c::d::VerySecretlyUninhabited),
+    }
+
+    mod a {
+        use super::*;
+        pub mod b {
+            use super::*;
+            pub struct SecretlyUninhabited {
+                _priv: !,
+            }
+            assert_empty!(SometimesEmptyStruct);
+        }
+
+        assert_non_empty!(SometimesEmptyStruct);
+        assert_non_empty!(SometimesEmptyEnum);
+    }
+
+    mod c {
+        use super::*;
+        pub struct AlsoSecretlyUninhabited {
+            _priv: ::Struct1,
+        }
+        assert_empty!(SometimesEmptyStruct);
+        assert_non_empty!(SometimesEmptyEnum);
+
+        pub mod d {
+            use super::*;
+            pub struct VerySecretlyUninhabited {
+                _priv: !,
+            }
+            assert_empty!(SometimesEmptyStruct);
+            assert_empty!(SometimesEmptyEnum);
+        }
+    }
+
+    assert_non_empty!(SometimesEmptyStruct);
+    assert_non_empty!(SometimesEmptyEnum);
+}
+
+fn main() {}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.rs b/tests/ui/pattern/usefulness/unstable-gated-fields.rs
new file mode 100644
index 00000000000..2b473ae989b
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-fields.rs
@@ -0,0 +1,18 @@
+#![feature(unstable_test_feature)]
+
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableStruct;
+
+fn main() {
+    let UnstableStruct { stable, stable2, } = UnstableStruct::default();
+    //~^ pattern does not mention field `unstable`
+
+    let UnstableStruct { stable, unstable, } = UnstableStruct::default();
+    //~^ pattern does not mention field `stable2`
+
+    // OK: stable field is matched
+    let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default();
+}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-fields.stderr b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr
new file mode 100644
index 00000000000..e4f5fa06b3f
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-fields.stderr
@@ -0,0 +1,33 @@
+error[E0027]: pattern does not mention field `unstable`
+  --> $DIR/unstable-gated-fields.rs:10:9
+   |
+LL |     let UnstableStruct { stable, stable2, } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `unstable`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let UnstableStruct { stable, stable2, unstable } = UnstableStruct::default();
+   |                                         ~~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, stable2, .. } = UnstableStruct::default();
+   |                                         ~~~~~~
+
+error[E0027]: pattern does not mention field `stable2`
+  --> $DIR/unstable-gated-fields.rs:13:9
+   |
+LL |     let UnstableStruct { stable, unstable, } = UnstableStruct::default();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `stable2`
+   |
+help: include the missing field in the pattern
+   |
+LL |     let UnstableStruct { stable, unstable, stable2 } = UnstableStruct::default();
+   |                                          ~~~~~~~~~~~
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     let UnstableStruct { stable, unstable, .. } = UnstableStruct::default();
+   |                                          ~~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0027`.
diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.rs b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs
new file mode 100644
index 00000000000..7046555e0d2
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.rs
@@ -0,0 +1,22 @@
+#![feature(unstable_test_feature)]
+
+// aux-build:unstable.rs
+
+extern crate unstable;
+
+use unstable::UnstableEnum;
+
+fn main() {
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+    }
+    //~^^^^ non-exhaustive patterns: `UnstableEnum::Unstable` not covered
+
+    // Ok: all variants are explicitly matched
+    match UnstableEnum::Stable {
+        UnstableEnum::Stable => {}
+        UnstableEnum::Stable2 => {}
+        UnstableEnum::Unstable => {}
+    }
+}
diff --git a/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
new file mode 100644
index 00000000000..6dc9a405839
--- /dev/null
+++ b/tests/ui/pattern/usefulness/unstable-gated-patterns.stderr
@@ -0,0 +1,24 @@
+error[E0004]: non-exhaustive patterns: `UnstableEnum::Unstable` not covered
+  --> $DIR/unstable-gated-patterns.rs:10:11
+   |
+LL |     match UnstableEnum::Stable {
+   |           ^^^^^^^^^^^^^^^^^^^^ pattern `UnstableEnum::Unstable` not covered
+   |
+note: `UnstableEnum` defined here
+  --> $DIR/auxiliary/unstable.rs:11:5
+   |
+LL | pub enum UnstableEnum {
+   | ---------------------
+...
+LL |     Unstable,
+   |     ^^^^^^^^ not covered
+   = note: the matched value is of type `UnstableEnum`
+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 ~         UnstableEnum::Stable2 => {}
+LL +         UnstableEnum::Unstable => todo!()
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0004`.