1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
+ use clippy_utils:: is_lang_ctor;
2
3
use clippy_utils:: source:: snippet;
3
- use clippy_utils:: ty:: is_type_diagnostic_item;
4
- use clippy_utils:: { differing_macro_contexts, is_lang_ctor} ;
5
4
use if_chain:: if_chain;
6
5
use rustc_errors:: Applicability ;
7
6
use rustc_hir:: LangItem :: { OptionSome , ResultOk } ;
8
7
use rustc_hir:: { Body , Expr , ExprKind , LangItem , MatchSource , QPath } ;
9
8
use rustc_lint:: { LateContext , LateLintPass } ;
9
+ use rustc_middle:: ty:: TyS ;
10
10
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11
- use rustc_span:: sym;
12
11
13
12
declare_clippy_lint ! {
14
13
/// **What it does:**
@@ -63,12 +62,6 @@ declare_clippy_lint! {
63
62
64
63
declare_lint_pass ! ( NeedlessQuestionMark => [ NEEDLESS_QUESTION_MARK ] ) ;
65
64
66
- #[ derive( Debug ) ]
67
- enum SomeOkCall < ' a > {
68
- SomeCall ( & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
69
- OkCall ( & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
70
- }
71
-
72
65
impl LateLintPass < ' _ > for NeedlessQuestionMark {
73
66
/*
74
67
* The question mark operator is compatible with both Result<T, E> and Option<T>,
@@ -90,104 +83,37 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
90
83
*/
91
84
92
85
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & ' _ Expr < ' _ > ) {
93
- let e = match & expr. kind {
94
- ExprKind :: Ret ( Some ( e) ) => e,
95
- _ => return ,
96
- } ;
97
-
98
- if let Some ( ok_some_call) = is_some_or_ok_call ( cx, e) {
99
- emit_lint ( cx, & ok_some_call) ;
86
+ if let ExprKind :: Ret ( Some ( e) ) = expr. kind {
87
+ check ( cx, e) ;
100
88
}
101
89
}
102
90
103
91
fn check_body ( & mut self , cx : & LateContext < ' _ > , body : & ' _ Body < ' _ > ) {
104
- // Function / Closure block
105
- let expr_opt = if let ExprKind :: Block ( block, _) = & body. value . kind {
106
- block. expr
107
- } else {
108
- // Single line closure
109
- Some ( & body. value )
110
- } ;
111
-
112
- if_chain ! {
113
- if let Some ( expr) = expr_opt;
114
- if let Some ( ok_some_call) = is_some_or_ok_call( cx, expr) ;
115
- then {
116
- emit_lint( cx, & ok_some_call) ;
117
- }
118
- } ;
92
+ check ( cx, body. value . peel_blocks ( ) ) ;
119
93
}
120
94
}
121
95
122
- fn emit_lint ( cx : & LateContext < ' _ > , expr : & SomeOkCall < ' _ > ) {
123
- let ( entire_expr, inner_expr) = match expr {
124
- SomeOkCall :: OkCall ( outer, inner) | SomeOkCall :: SomeCall ( outer, inner) => ( outer, inner) ,
96
+ fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
97
+ let inner_expr = if_chain ! {
98
+ if let ExprKind :: Call ( path, [ arg] ) = & expr. kind;
99
+ if let ExprKind :: Path ( ref qpath) = & path. kind;
100
+ if is_lang_ctor( cx, qpath, OptionSome ) || is_lang_ctor( cx, qpath, ResultOk ) ;
101
+ if let ExprKind :: Match ( inner_expr_with_q, _, MatchSource :: TryDesugar ) = & arg. kind;
102
+ if let ExprKind :: Call ( called, [ inner_expr] ) = & inner_expr_with_q. kind;
103
+ if let ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryIntoResult , _) ) = & called. kind;
104
+ if expr. span. ctxt( ) == inner_expr. span. ctxt( ) ;
105
+ let expr_ty = cx. typeck_results( ) . expr_ty( expr) ;
106
+ let inner_ty = cx. typeck_results( ) . expr_ty( inner_expr) ;
107
+ if TyS :: same_type( expr_ty, inner_ty) ;
108
+ then { inner_expr } else { return ; }
125
109
} ;
126
-
127
110
span_lint_and_sugg (
128
111
cx,
129
112
NEEDLESS_QUESTION_MARK ,
130
- entire_expr . span ,
113
+ expr . span ,
131
114
"question mark operator is useless here" ,
132
115
"try" ,
133
116
format ! ( "{}" , snippet( cx, inner_expr. span, r#""...""# ) ) ,
134
117
Applicability :: MachineApplicable ,
135
118
) ;
136
119
}
137
-
138
- fn is_some_or_ok_call < ' a > ( cx : & ' a LateContext < ' _ > , expr : & ' a Expr < ' _ > ) -> Option < SomeOkCall < ' a > > {
139
- if_chain ! {
140
- // Check outer expression matches CALL_IDENT(ARGUMENT) format
141
- if let ExprKind :: Call ( path, args) = & expr. kind;
142
- if let ExprKind :: Path ( ref qpath) = & path. kind;
143
- if is_lang_ctor( cx, qpath, OptionSome ) || is_lang_ctor( cx, qpath, ResultOk ) ;
144
-
145
- // Extract inner expression from ARGUMENT
146
- if let ExprKind :: Match ( inner_expr_with_q, _, MatchSource :: TryDesugar ) = & args[ 0 ] . kind;
147
- if let ExprKind :: Call ( called, args) = & inner_expr_with_q. kind;
148
- if args. len( ) == 1 ;
149
-
150
- if let ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryIntoResult , _) ) = & called. kind;
151
- then {
152
- // Extract inner expr type from match argument generated by
153
- // question mark operator
154
- let inner_expr = & args[ 0 ] ;
155
-
156
- // if the inner expr is inside macro but the outer one is not, do not lint (#6921)
157
- if differing_macro_contexts( expr. span, inner_expr. span) {
158
- return None ;
159
- }
160
-
161
- let inner_ty = cx. typeck_results( ) . expr_ty( inner_expr) ;
162
- let outer_ty = cx. typeck_results( ) . expr_ty( expr) ;
163
-
164
- // Check if outer and inner type are Option
165
- let outer_is_some = is_type_diagnostic_item( cx, outer_ty, sym:: option_type) ;
166
- let inner_is_some = is_type_diagnostic_item( cx, inner_ty, sym:: option_type) ;
167
-
168
- // Check for Option MSRV
169
- if outer_is_some && inner_is_some {
170
- return Some ( SomeOkCall :: SomeCall ( expr, inner_expr) ) ;
171
- }
172
-
173
- // Check if outer and inner type are Result
174
- let outer_is_result = is_type_diagnostic_item( cx, outer_ty, sym:: result_type) ;
175
- let inner_is_result = is_type_diagnostic_item( cx, inner_ty, sym:: result_type) ;
176
-
177
- // Additional check: if the error type of the Result can be converted
178
- // via the From trait, then don't match
179
- let does_not_call_from = !has_implicit_error_from( cx, expr, inner_expr) ;
180
-
181
- // Must meet Result MSRV
182
- if outer_is_result && inner_is_result && does_not_call_from {
183
- return Some ( SomeOkCall :: OkCall ( expr, inner_expr) ) ;
184
- }
185
- }
186
- }
187
-
188
- None
189
- }
190
-
191
- fn has_implicit_error_from ( cx : & LateContext < ' _ > , entire_expr : & Expr < ' _ > , inner_result_expr : & Expr < ' _ > ) -> bool {
192
- return cx. typeck_results ( ) . expr_ty ( entire_expr) != cx. typeck_results ( ) . expr_ty ( inner_result_expr) ;
193
- }
0 commit comments