@@ -4,11 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then;
4
4
use clippy_utils:: higher:: IfLetOrMatch ;
5
5
use clippy_utils:: source:: snippet_with_context;
6
6
use clippy_utils:: ty:: is_type_diagnostic_item;
7
- use clippy_utils:: { is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks} ;
7
+ use clippy_utils:: {
8
+ MaybePath , is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks,
9
+ } ;
8
10
use rustc_ast:: BindingMode ;
9
11
use rustc_data_structures:: fx:: FxHashMap ;
10
12
use rustc_errors:: Applicability ;
11
- use rustc_hir:: { Expr , ExprKind , MatchSource , Pat , PatExpr , PatExprKind , PatKind , QPath , Stmt , StmtKind } ;
13
+ use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
14
+ use rustc_hir:: { Arm , Expr , ExprKind , HirId , MatchSource , Pat , PatExpr , PatExprKind , PatKind , QPath , Stmt , StmtKind } ;
12
15
use rustc_lint:: { LateContext , LintContext } ;
13
16
14
17
use rustc_span:: Span ;
@@ -91,14 +94,15 @@ impl<'tcx> QuestionMark {
91
94
let Some ( ( idx, diverging_arm) ) = diverging_arm_opt else {
92
95
return ;
93
96
} ;
97
+
98
+ let pat_arm = & arms[ 1 - idx] ;
94
99
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
95
100
// However, if it arrives in second position, its pattern may cover some cases already covered
96
101
// by the diverging one.
97
- // TODO: accept the non-diverging arm as a second position if patterns are disjointed.
98
- if idx == 0 {
102
+ if idx == 0 && !is_arms_disjointed ( cx, diverging_arm, pat_arm) {
99
103
return ;
100
104
}
101
- let pat_arm = & arms [ 1 - idx ] ;
105
+
102
106
let Some ( ident_map) = expr_simple_identity_map ( local. pat , pat_arm. pat , pat_arm. body ) else {
103
107
return ;
104
108
} ;
@@ -110,6 +114,63 @@ impl<'tcx> QuestionMark {
110
114
}
111
115
}
112
116
117
+ /// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple
118
+ /// enum variants without nested patterns or bindings.
119
+ ///
120
+ /// TODO: Support more complex patterns.
121
+ fn is_arms_disjointed ( cx : & LateContext < ' _ > , arm1 : & Arm < ' _ > , arm2 : & Arm < ' _ > ) -> bool {
122
+ if arm1. guard . is_some ( ) || arm2. guard . is_some ( ) {
123
+ return false ;
124
+ }
125
+
126
+ if !is_enum_variant ( cx, arm1. pat ) || !is_enum_variant ( cx, arm2. pat ) {
127
+ return false ;
128
+ }
129
+
130
+ true
131
+ }
132
+
133
+ /// Returns `true` if the given pattern is a variant of an enum.
134
+ pub fn is_enum_variant ( cx : & LateContext < ' _ > , pat : & Pat < ' _ > ) -> bool {
135
+ struct Pat < ' hir > ( & ' hir rustc_hir:: Pat < ' hir > ) ;
136
+
137
+ impl < ' hir > MaybePath < ' hir > for Pat < ' hir > {
138
+ fn qpath_opt ( & self ) -> Option < & QPath < ' hir > > {
139
+ match self . 0 . kind {
140
+ PatKind :: Struct ( ref qpath, fields, _)
141
+ if fields
142
+ . iter ( )
143
+ . all ( |field| is_wild ( field. pat ) || matches ! ( field. pat. kind, PatKind :: Binding ( ..) ) ) =>
144
+ {
145
+ Some ( qpath)
146
+ } ,
147
+ PatKind :: TupleStruct ( ref qpath, pats, _)
148
+ if pats
149
+ . iter ( )
150
+ . all ( |pat| is_wild ( pat) || matches ! ( pat. kind, PatKind :: Binding ( ..) ) ) =>
151
+ {
152
+ Some ( qpath)
153
+ } ,
154
+ PatKind :: Expr ( & PatExpr {
155
+ kind : PatExprKind :: Path ( ref qpath) ,
156
+ ..
157
+ } ) => Some ( qpath) ,
158
+ _ => None ,
159
+ }
160
+ }
161
+
162
+ fn hir_id ( & self ) -> HirId {
163
+ self . 0 . hir_id
164
+ }
165
+ }
166
+
167
+ let res = path_res ( cx, & Pat ( pat) ) ;
168
+ matches ! (
169
+ res,
170
+ Res :: Def ( DefKind :: Variant , ..) | Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , _) , _)
171
+ )
172
+ }
173
+
113
174
fn emit_manual_let_else (
114
175
cx : & LateContext < ' _ > ,
115
176
span : Span ,
0 commit comments