@@ -4,10 +4,10 @@ use clippy_utils::is_lang_ctor;
44use clippy_utils:: source:: snippet_with_applicability;
55use clippy_utils:: sugg:: Sugg ;
66use 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} ;
88use if_chain:: if_chain;
99use rustc_errors:: Applicability ;
10- use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
10+ use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
1111use rustc_hir:: { BindingAnnotation , Block , Expr , ExprKind , PatKind , StmtKind } ;
1212use rustc_lint:: { LateContext , LateLintPass } ;
1313use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -48,16 +48,20 @@ impl QuestionMark {
4848 /// }
4949 /// ```
5050 ///
51+ /// ```ignore
52+ /// if result.is_err() {
53+ /// return result;
54+ /// }
55+ /// ```
56+ ///
5157 /// 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 < ' _ > ) {
5359 if_chain ! {
5460 if let Some ( higher:: If { cond, then, r#else } ) = higher:: If :: hir( expr) ;
5561 if let ExprKind :: MethodCall ( segment, _, args, _) = & cond. kind;
56- if segment. ident. name == sym!( is_none) ;
57- if Self :: expression_returns_none( cx, then) ;
5862 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 ) ) ;
6165 then {
6266 let mut applicability = Applicability :: MachineApplicable ;
6367 let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
@@ -95,31 +99,24 @@ impl QuestionMark {
9599 }
96100 }
97101
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 < ' _ > ) {
99103 if_chain ! {
100104 if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else) } )
101105 = higher:: IfLet :: hir( cx, expr) ;
102- if Self :: is_option( cx, let_expr) ;
103-
104106 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+
106110 if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
107111 let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
108-
109112 if let ExprKind :: Block ( block, None ) = if_then. kind;
110113 if block. stmts. is_empty( ) ;
111114 if let Some ( trailing_expr) = & block. expr;
112115 if path_to_local_id( trailing_expr, bind_id) ;
113-
114- if Self :: expression_returns_none( cx, if_else) ;
115116 then {
116117 let mut applicability = Applicability :: MachineApplicable ;
117118 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 { "" } , ) ;
123120
124121 span_lint_and_sugg(
125122 cx,
@@ -134,6 +131,14 @@ impl QuestionMark {
134131 }
135132 }
136133
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+
137142 fn moves_by_default ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
138143 let expr_ty = cx. typeck_results ( ) . expr_ty ( expression) ;
139144
@@ -146,6 +151,12 @@ impl QuestionMark {
146151 is_type_diagnostic_item ( cx, expr_ty, sym:: Option )
147152 }
148153
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+
149160 fn expression_returns_none ( cx : & LateContext < ' _ > , expression : & Expr < ' _ > ) -> bool {
150161 match expression. kind {
151162 ExprKind :: Block ( block, _) => {
@@ -161,6 +172,27 @@ impl QuestionMark {
161172 }
162173 }
163174
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+
164196 fn return_expression < ' tcx > ( block : & Block < ' tcx > ) -> Option < & ' tcx Expr < ' tcx > > {
165197 // Check if last expression is a return statement. Then, return the expression
166198 if_chain ! {
@@ -189,7 +221,7 @@ impl QuestionMark {
189221
190222impl < ' tcx > LateLintPass < ' tcx > for QuestionMark {
191223 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) ;
194226 }
195227}
0 commit comments