@@ -3,14 +3,14 @@ use clippy_utils::higher;
3
3
use clippy_utils:: source:: snippet_with_applicability;
4
4
use clippy_utils:: sugg:: Sugg ;
5
5
use clippy_utils:: ty:: is_type_diagnostic_item;
6
- use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id , peel_blocks, peel_blocks_with_stmt} ;
6
+ use clippy_utils:: { eq_expr_value, is_lang_ctor, path_to_local, peel_blocks, peel_blocks_with_stmt} ;
7
7
use if_chain:: if_chain;
8
8
use rustc_errors:: Applicability ;
9
- use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultOk } ;
10
- use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind } ;
9
+ use rustc_hir:: LangItem :: { OptionNone , OptionSome , ResultErr , ResultOk } ;
10
+ use rustc_hir:: { BindingAnnotation , Expr , ExprKind , PatKind , QPath } ;
11
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
13
- use rustc_span:: sym;
13
+ use rustc_span:: { sym, symbol :: Symbol } ;
14
14
15
15
declare_clippy_lint ! {
16
16
/// ### What it does
@@ -61,21 +61,21 @@ impl QuestionMark {
61
61
if let ExprKind :: MethodCall ( segment, _, args, _) = & cond. kind;
62
62
if let Some ( subject) = args. get( 0 ) ;
63
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) ) ;
64
+ ( Self :: result_check_and_early_return( cx, subject, then, None ) && segment. ident. name == sym!( is_err) ) ;
65
65
then {
66
66
let mut applicability = Applicability :: MachineApplicable ;
67
- let receiver_str = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
67
+ let suggestion = & Sugg :: hir_with_applicability( cx, subject, ".." , & mut applicability) ;
68
68
let mut replacement: Option <String > = None ;
69
69
if let Some ( else_inner) = r#else {
70
70
if eq_expr_value( cx, subject, peel_blocks( else_inner) ) {
71
- replacement = Some ( format!( "Some({}?)" , receiver_str ) ) ;
71
+ replacement = Some ( format!( "Some({}?)" , suggestion ) ) ;
72
72
}
73
73
} else if Self :: moves_by_default( cx, subject)
74
74
&& !matches!( subject. kind, ExprKind :: Call ( ..) | ExprKind :: MethodCall ( ..) )
75
75
{
76
- replacement = Some ( format!( "{}.as_ref()?;" , receiver_str ) ) ;
76
+ replacement = Some ( format!( "{}.as_ref()?;" , suggestion ) ) ;
77
77
} else {
78
- replacement = Some ( format!( "{}?;" , receiver_str ) ) ;
78
+ replacement = Some ( format!( "{}?;" , suggestion ) ) ;
79
79
}
80
80
81
81
if let Some ( replacement_str) = replacement {
@@ -95,19 +95,20 @@ impl QuestionMark {
95
95
96
96
fn check_if_let_some_or_err_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
97
97
if_chain ! {
98
- if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else: Some ( if_else ) } )
98
+ if let Some ( higher:: IfLet { let_pat, let_expr, if_then, if_else } )
99
99
= higher:: IfLet :: hir( cx, expr) ;
100
100
if let PatKind :: TupleStruct ( ref path1, fields, None ) = let_pat. kind;
101
- if ( Self :: option_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, OptionSome ) ) ||
102
- ( Self :: result_check_and_early_return( cx, let_expr, if_else) && is_lang_ctor( cx, path1, ResultOk ) ) ;
103
-
104
- if let PatKind :: Binding ( annot, bind_id, _, _) = fields[ 0 ] . kind;
101
+ // Only check one of the blocks
102
+ let nested_expr = if_else. unwrap_or( if_then) ;
103
+ if Self :: check_lang_items( cx, path1, & [ OptionSome , ResultOk , ResultErr ] ) ;
104
+ if let PatKind :: Binding ( annot, _, ident, _) = fields[ 0 ] . kind;
105
+ if Self :: result_check_and_early_return( cx, let_expr, nested_expr, Some ( ident. name) )
106
+ || Self :: option_check_and_early_return( cx, let_expr, nested_expr) ;
105
107
let by_ref = matches!( annot, BindingAnnotation :: Ref | BindingAnnotation :: RefMut ) ;
106
- if path_to_local_id( peel_blocks( if_then) , bind_id) ;
107
108
then {
108
109
let mut applicability = Applicability :: MachineApplicable ;
109
110
let receiver_str = snippet_with_applicability( cx, let_expr. span, ".." , & mut applicability) ;
110
- let replacement = format!( "{}{}?" , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111
+ let replacement = format!( "{}{}?; " , receiver_str, if by_ref { ".as_ref()" } else { "" } , ) ;
111
112
112
113
span_lint_and_sugg(
113
114
cx,
@@ -122,8 +123,22 @@ impl QuestionMark {
122
123
}
123
124
}
124
125
125
- fn result_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
126
- Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr)
126
+ fn check_lang_items ( cx : & LateContext < ' _ > , qpath : & QPath < ' _ > , items : & [ rustc_hir:: LangItem ] ) -> bool {
127
+ for lang_item in items {
128
+ if is_lang_ctor ( cx, qpath, * lang_item) {
129
+ return true ;
130
+ }
131
+ }
132
+ false
133
+ }
134
+
135
+ fn result_check_and_early_return (
136
+ cx : & LateContext < ' _ > ,
137
+ expr : & Expr < ' _ > ,
138
+ nested_expr : & Expr < ' _ > ,
139
+ symbol : Option < Symbol > ,
140
+ ) -> bool {
141
+ Self :: is_result ( cx, expr) && Self :: expression_returns_unmodified_err ( cx, nested_expr, expr, symbol)
127
142
}
128
143
129
144
fn option_check_and_early_return ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , nested_expr : & Expr < ' _ > ) -> bool {
@@ -156,10 +171,23 @@ impl QuestionMark {
156
171
}
157
172
}
158
173
159
- fn expression_returns_unmodified_err ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , cond_expr : & Expr < ' _ > ) -> bool {
160
- match peel_blocks_with_stmt ( expr) . kind {
161
- ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expression_returns_unmodified_err ( cx, ret_expr, cond_expr) ,
174
+ fn expression_returns_unmodified_err (
175
+ cx : & LateContext < ' _ > ,
176
+ expr : & Expr < ' _ > ,
177
+ cond_expr : & Expr < ' _ > ,
178
+ symbol : Option < Symbol > ,
179
+ ) -> bool {
180
+ match & peel_blocks_with_stmt ( expr) . kind {
181
+ ExprKind :: Ret ( Some ( ret_expr) ) => Self :: expression_returns_unmodified_err ( cx, ret_expr, cond_expr, symbol) ,
162
182
ExprKind :: Path ( _) => path_to_local ( expr) . is_some ( ) && path_to_local ( expr) == path_to_local ( cond_expr) ,
183
+ ExprKind :: Call ( _, args_expr) => {
184
+ if let Some ( arg) = args_expr. first ( ) {
185
+ if let Some ( name) = symbol {
186
+ return clippy_utils:: contains_name ( name, arg) ;
187
+ }
188
+ }
189
+ false
190
+ } ,
163
191
_ => false ,
164
192
}
165
193
}
0 commit comments