@@ -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,25 @@ 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 :: is_option( cx, subject)
64
+ && Self :: expression_returns_none( cx, then)
65
+ && segment. ident. name == sym!( is_none) )
66
+ ||
67
+ ( Self :: is_result( cx, subject)
68
+ && Self :: expression_returns_unmodified_err( cx, then, subject)
69
+ && segment. ident. name == sym!( is_err) ) ;
61
70
then {
62
71
let mut applicability = Applicability :: MachineApplicable ;
63
72
let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
@@ -95,31 +104,29 @@ impl QuestionMark {
95
104
}
96
105
}
97
106
98
- fn check_if_let_some_and_early_return_none ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
107
+ fn check_if_let_some_or_err_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
99
108
if_chain ! {
100
109
if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else) } )
101
110
= higher:: IfLet :: hir( cx, expr) ;
102
- if Self :: is_option( cx, let_expr) ;
103
-
104
111
if let PatKind :: TupleStruct ( ref path1, fields, None ) = let_pat. kind;
105
- if is_lang_ctor( cx, path1, OptionSome ) ;
112
+ if ( Self :: is_option( cx, let_expr)
113
+ && Self :: expression_returns_none( cx, if_else)
114
+ && is_lang_ctor( cx, path1, OptionSome ) )
115
+ ||
116
+ ( Self :: is_result( cx, let_expr)
117
+ && Self :: expression_returns_unmodified_err( cx, if_else, let_expr)
118
+ && is_lang_ctor( cx, path1, ResultOk ) ) ;
119
+
106
120
if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
107
121
let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
108
-
109
122
if let ExprKind :: Block ( block, None ) = if_then. kind;
110
123
if block. stmts. is_empty( ) ;
111
124
if let Some ( trailing_expr) = & block. expr;
112
125
if path_to_local_id( trailing_expr, bind_id) ;
113
-
114
- if Self :: expression_returns_none( cx, if_else) ;
115
126
then {
116
127
let mut applicability = Applicability :: MachineApplicable ;
117
128
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
- ) ;
129
+ let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
123
130
124
131
span_lint_and_sugg(
125
132
cx,
@@ -146,6 +153,12 @@ impl QuestionMark {
146
153
is_type_diagnostic_item ( cx, expr_ty, sym:: Option )
147
154
}
148
155
156
+ fn is_result ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
157
+ let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
158
+
159
+ is_type_diagnostic_item ( cx, expr_ty, sym:: Result )
160
+ }
161
+
149
162
fn expression_returns_none ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
150
163
match expression. kind {
151
164
ExprKind :: Block ( block, _) => {
@@ -161,6 +174,26 @@ impl QuestionMark {
161
174
}
162
175
}
163
176
177
+ fn expression_returns_unmodified_err (
178
+ cx : & LateContext < ' _ > ,
179
+ expression : & Expr < ' _ > ,
180
+ origin_hir_id : & Expr < ' _ > ,
181
+ ) -> bool {
182
+ match expression. kind {
183
+ ExprKind :: Block ( block, _) => {
184
+ if let Some ( return_expression) = Self :: return_expression ( block) {
185
+ return Self :: expression_returns_unmodified_err ( cx, return_expression, origin_hir_id) ;
186
+ }
187
+
188
+ false
189
+ } ,
190
+ ExprKind :: Ret ( Some ( expr) ) => Self :: expression_returns_unmodified_err ( cx, expr, origin_hir_id) ,
191
+ ExprKind :: Call ( expr, _) => Self :: expression_returns_unmodified_err ( cx, expr, origin_hir_id) ,
192
+ ExprKind :: Path ( _) => path_to_local ( expression) == path_to_local ( origin_hir_id) ,
193
+ _ => false ,
194
+ }
195
+ }
196
+
164
197
fn return_expression < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
165
198
// Check if last expression is a return statement. Then, return the expression
166
199
if_chain ! {
@@ -189,7 +222,7 @@ impl QuestionMark {
189
222
190
223
impl < ' tcx > LateLintPass < ' tcx > for QuestionMark {
191
224
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) ;
225
+ Self :: check_is_none_or_err_and_early_return ( cx, expr) ;
226
+ Self :: check_if_let_some_or_err_and_early_return ( cx, expr) ;
194
227
}
195
228
}
0 commit comments