11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2+ use clippy_utils:: is_lang_ctor;
23use clippy_utils:: source:: snippet;
3- use clippy_utils:: ty:: is_type_diagnostic_item;
4- use clippy_utils:: { differing_macro_contexts, is_lang_ctor} ;
54use if_chain:: if_chain;
65use rustc_errors:: Applicability ;
76use rustc_hir:: LangItem :: { OptionSome , ResultOk } ;
87use rustc_hir:: { Body , Expr , ExprKind , LangItem , MatchSource , QPath } ;
98use rustc_lint:: { LateContext , LateLintPass } ;
9+ use rustc_middle:: ty:: TyS ;
1010use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
11- use rustc_span:: sym;
1211
1312declare_clippy_lint ! {
1413 /// **What it does:**
@@ -63,12 +62,6 @@ declare_clippy_lint! {
6362
6463declare_lint_pass ! ( NeedlessQuestionMark => [ NEEDLESS_QUESTION_MARK ] ) ;
6564
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-
7265impl LateLintPass < ' _ > for NeedlessQuestionMark {
7366 /*
7467 * The question mark operator is compatible with both Result<T, E> and Option<T>,
@@ -90,104 +83,37 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
9083 */
9184
9285 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) ;
10088 }
10189 }
10290
10391 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 ( ) ) ;
11993 }
12094}
12195
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 ; }
125109 } ;
126-
127110 span_lint_and_sugg (
128111 cx,
129112 NEEDLESS_QUESTION_MARK ,
130- entire_expr . span ,
113+ expr . span ,
131114 "question mark operator is useless here" ,
132115 "try" ,
133116 format ! ( "{}" , snippet( cx, inner_expr. span, r#""...""# ) ) ,
134117 Applicability :: MachineApplicable ,
135118 ) ;
136119}
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