@@ -4,10 +4,10 @@ use clippy_utils::is_lang_ctor;
4
4
use clippy_utils:: source:: snippet_with_applicability;
5
5
use clippy_utils:: sugg:: Sugg ;
6
6
use clippy_utils:: ty:: is_type_diagnostic_item;
7
- use clippy_utils:: { eq_expr_value, path_to_local_id} ;
7
+ use clippy_utils:: { eq_expr_value, path_to_local , path_to_local_id} ;
8
8
use if_chain:: if_chain;
9
9
use rustc_errors:: Applicability ;
10
- use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
10
+ use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
11
11
use rustc_hir:: { BindingAnnotation , Block , Expr , ExprKind , PatKind , StmtKind } ;
12
12
use rustc_lint:: { LateContext , LateLintPass } ;
13
13
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -48,16 +48,20 @@ impl QuestionMark {
48
48
/// }
49
49
/// ```
50
50
///
51
+ /// ```ignore
52
+ /// if result.is_err() {
53
+ /// return result;
54
+ /// }
55
+ /// ```
56
+ ///
51
57
/// If it matches, it will suggest to use the question mark operator instead
52
- fn check_is_none_and_early_return_none ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
58
+ fn check_is_none_or_err_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
53
59
if_chain ! {
54
60
if let Some ( higher:: If { cond, then, r#else } ) = higher:: If :: hir( expr) ;
55
61
if let ExprKind :: MethodCall ( segment, _, args, _) = & cond. kind;
56
- if segment. ident. name == sym!( is_none) ;
57
- if Self :: expression_returns_none( cx, then) ;
58
62
if let Some ( subject) = args. get( 0 ) ;
59
- if Self :: is_option ( cx, subject) ;
60
-
63
+ if ( Self :: option_check_and_early_return ( cx, subject, then ) && segment . ident . name == sym! ( is_none ) ) ||
64
+ ( Self :: result_check_and_early_return ( cx , subject , then ) && segment . ident . name == sym! ( is_err ) ) ;
61
65
then {
62
66
let mut applicability = Applicability :: MachineApplicable ;
63
67
let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
@@ -95,31 +99,24 @@ impl QuestionMark {
95
99
}
96
100
}
97
101
98
- fn check_if_let_some_and_early_return_none ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
102
+ fn check_if_let_some_or_err_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
99
103
if_chain ! {
100
104
if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else) } )
101
105
= higher:: IfLet :: hir( cx, expr) ;
102
- if Self :: is_option( cx, let_expr) ;
103
-
104
106
if let PatKind :: TupleStruct ( ref path1, fields, None ) = let_pat. kind;
105
- if is_lang_ctor( cx, path1, OptionSome ) ;
107
+ if ( Self :: option_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, OptionSome ) ) ||
108
+ ( Self :: result_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, ResultOk ) ) ;
109
+
106
110
if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
107
111
let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
108
-
109
112
if let ExprKind :: Block ( block, None ) = if_then. kind;
110
113
if block. stmts. is_empty( ) ;
111
114
if let Some ( trailing_expr) = & block. expr;
112
115
if path_to_local_id( trailing_expr, bind_id) ;
113
-
114
- if Self :: expression_returns_none( cx, if_else) ;
115
116
then {
116
117
let mut applicability = Applicability :: MachineApplicable ;
117
118
let receiver_str = snippet_with_applicability( cx, let_expr. span, ".." , & mut applicability) ;
118
- let replacement = format!(
119
- "{}{}?" ,
120
- receiver_str,
121
- if by_ref { ".as_ref()" } else { "" } ,
122
- ) ;
119
+ let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
123
120
124
121
span_lint_and_sugg(
125
122
cx,
@@ -134,6 +131,14 @@ impl QuestionMark {
134
131
}
135
132
}
136
133
134
+ fn result_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
135
+ Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr)
136
+ }
137
+
138
+ fn option_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
139
+ Self :: is_option ( cx, expr) && Self :: expression_returns_none ( cx, nested_expr)
140
+ }
141
+
137
142
fn moves_by_default ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
138
143
let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
139
144
@@ -146,6 +151,12 @@ impl QuestionMark {
146
151
is_type_diagnostic_item ( cx, expr_ty, sym:: Option )
147
152
}
148
153
154
+ fn is_result ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
155
+ let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
156
+
157
+ is_type_diagnostic_item ( cx, expr_ty, sym:: Result )
158
+ }
159
+
149
160
fn expression_returns_none ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
150
161
match expression. kind {
151
162
ExprKind :: Block ( block, _) => {
@@ -161,6 +172,27 @@ impl QuestionMark {
161
172
}
162
173
}
163
174
175
+ fn expression_returns_unmodified_err (
176
+ cx : & LateContext < ' _ > ,
177
+ expression : & Expr < ' _ > ,
178
+ origin_hir_id : & Expr < ' _ > ,
179
+ ) -> bool {
180
+ match expression. kind {
181
+ ExprKind :: Block ( block, _) => {
182
+ if let Some ( return_expression) = Self :: return_expression ( block) {
183
+ return Self :: expression_returns_unmodified_err ( cx, return_expression, origin_hir_id) ;
184
+ }
185
+
186
+ false
187
+ } ,
188
+ ExprKind :: Ret ( Some ( expr) ) | ExprKind :: Call ( expr, _) => {
189
+ Self :: expression_returns_unmodified_err ( cx, expr, origin_hir_id)
190
+ } ,
191
+ ExprKind :: Path ( _) => path_to_local ( expression) == path_to_local ( origin_hir_id) ,
192
+ _ => false ,
193
+ }
194
+ }
195
+
164
196
fn return_expression < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
165
197
// Check if last expression is a return statement. Then, return the expression
166
198
if_chain ! {
@@ -189,7 +221,7 @@ impl QuestionMark {
189
221
190
222
impl < ' tcx > LateLintPass < ' tcx > for QuestionMark {
191
223
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
192
- Self :: check_is_none_and_early_return_none ( cx, expr) ;
193
- Self :: check_if_let_some_and_early_return_none ( cx, expr) ;
224
+ Self :: check_is_none_or_err_and_early_return ( cx, expr) ;
225
+ Self :: check_if_let_some_or_err_and_early_return ( cx, expr) ;
194
226
}
195
227
}
0 commit comments