1- use crate :: utils:: { match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg} ;
1+ use crate :: utils:: {
2+ is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet, snippet_with_macro_callsite,
3+ span_lint_and_sugg,
4+ } ;
25use if_chain:: if_chain;
36use rustc_errors:: Applicability ;
4- use rustc_hir:: { Arm , Expr , ExprKind , MatchSource } ;
7+ use rustc_hir:: { Expr , ExprKind , MatchSource } ;
58use rustc_lint:: { LateContext , LateLintPass } ;
69use rustc_middle:: lint:: in_external_macro;
7- use rustc_middle:: ty:: Ty ;
10+ use rustc_middle:: ty:: { self , Ty } ;
811use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
912
1013declare_clippy_lint ! {
@@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
6568 if let Some ( ref err_arg) = err_args. get( 0 ) ;
6669 if let ExprKind :: Path ( ref err_fun_path) = err_fun. kind;
6770 if match_qpath( err_fun_path, & paths:: RESULT_ERR ) ;
68- if let Some ( return_type) = find_err_return_type( cx, & expr. kind) ;
69-
71+ if let Some ( return_ty) = find_return_type( cx, & expr. kind) ;
7072 then {
71- let err_type = cx. typeck_results( ) . expr_ty( err_arg) ;
73+ let prefix;
74+ let suffix;
75+ let err_ty;
76+
77+ if let Some ( ty) = result_error_type( cx, return_ty) {
78+ prefix = "Err(" ;
79+ suffix = ")" ;
80+ err_ty = ty;
81+ } else if let Some ( ty) = poll_result_error_type( cx, return_ty) {
82+ prefix = "Poll::Ready(Err(" ;
83+ suffix = "))" ;
84+ err_ty = ty;
85+ } else if let Some ( ty) = poll_option_result_error_type( cx, return_ty) {
86+ prefix = "Poll::Ready(Some(Err(" ;
87+ suffix = ")))" ;
88+ err_ty = ty;
89+ } else {
90+ return ;
91+ } ;
92+
93+ let expr_err_ty = cx. typeck_results( ) . expr_ty( err_arg) ;
94+
7295 let origin_snippet = if err_arg. span. from_expansion( ) {
7396 snippet_with_macro_callsite( cx, err_arg. span, "_" )
7497 } else {
7598 snippet( cx, err_arg. span, "_" )
7699 } ;
77- let suggestion = if err_type == return_type {
78- format!( "return Err({}) " , origin_snippet)
100+ let suggestion = if err_ty == expr_err_ty {
101+ format!( "return {}{}{} " , prefix , origin_snippet, suffix )
79102 } else {
80- format!( "return Err({} .into()) " , origin_snippet)
103+ format!( "return {}{} .into(){} " , prefix , origin_snippet, suffix )
81104 } ;
82105
83106 span_lint_and_sugg(
@@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
94117 }
95118}
96119
97- // In order to determine whether to suggest `.into()` or not, we need to find the error type the
98- // function returns. To do that, we look for the From::from call (see tree above), and capture
99- // its output type.
100- fn find_err_return_type < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx ExprKind < ' _ > ) -> Option < Ty < ' tcx > > {
120+ /// Finds function return type by examining return expressions in match arms.
121+ fn find_return_type < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx ExprKind < ' _ > ) -> Option < Ty < ' tcx > > {
101122 if let ExprKind :: Match ( _, ref arms, MatchSource :: TryDesugar ) = expr {
102- arms. iter ( ) . find_map ( |ty| find_err_return_type_arm ( cx, ty) )
103- } else {
104- None
123+ for arm in arms. iter ( ) {
124+ if let ExprKind :: Ret ( Some ( ref ret) ) = arm. body . kind {
125+ return Some ( cx. typeck_results ( ) . expr_ty ( ret) ) ;
126+ }
127+ }
105128 }
129+ None
106130}
107131
108- // Check for From::from in one of the match arms .
109- fn find_err_return_type_arm < ' tcx > ( cx : & LateContext < ' tcx > , arm : & ' tcx Arm < ' _ > ) -> Option < Ty < ' tcx > > {
132+ /// Extracts the error type from Result<T, E> .
133+ fn result_error_type < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
110134 if_chain ! {
111- if let ExprKind :: Ret ( Some ( ref err_ret) ) = arm. body. kind;
112- if let ExprKind :: Call ( ref from_error_path, ref from_error_args) = err_ret. kind;
113- if let ExprKind :: Path ( ref from_error_fn) = from_error_path. kind;
114- if match_qpath( from_error_fn, & paths:: TRY_FROM_ERROR ) ;
115- if let Some ( from_error_arg) = from_error_args. get( 0 ) ;
135+ if let ty:: Adt ( _, subst) = ty. kind;
136+ if is_type_diagnostic_item( cx, ty, sym!( result_type) ) ;
137+ let err_ty = subst. type_at( 1 ) ;
138+ then {
139+ Some ( err_ty)
140+ } else {
141+ None
142+ }
143+ }
144+ }
145+
146+ /// Extracts the error type from Poll<Result<T, E>>.
147+ fn poll_result_error_type < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
148+ if_chain ! {
149+ if let ty:: Adt ( def, subst) = ty. kind;
150+ if match_def_path( cx, def. did, & paths:: POLL ) ;
151+ let ready_ty = subst. type_at( 0 ) ;
152+
153+ if let ty:: Adt ( ready_def, ready_subst) = ready_ty. kind;
154+ if cx. tcx. is_diagnostic_item( sym!( result_type) , ready_def. did) ;
155+ let err_ty = ready_subst. type_at( 1 ) ;
156+
157+ then {
158+ Some ( err_ty)
159+ } else {
160+ None
161+ }
162+ }
163+ }
164+
165+ /// Extracts the error type from Poll<Option<Result<T, E>>>.
166+ fn poll_option_result_error_type < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < Ty < ' tcx > > {
167+ if_chain ! {
168+ if let ty:: Adt ( def, subst) = ty. kind;
169+ if match_def_path( cx, def. did, & paths:: POLL ) ;
170+ let ready_ty = subst. type_at( 0 ) ;
171+
172+ if let ty:: Adt ( ready_def, ready_subst) = ready_ty. kind;
173+ if cx. tcx. is_diagnostic_item( sym!( option_type) , ready_def. did) ;
174+ let some_ty = ready_subst. type_at( 0 ) ;
175+
176+ if let ty:: Adt ( some_def, some_subst) = some_ty. kind;
177+ if cx. tcx. is_diagnostic_item( sym!( result_type) , some_def. did) ;
178+ let err_ty = some_subst. type_at( 1 ) ;
179+
116180 then {
117- Some ( cx . typeck_results ( ) . expr_ty ( from_error_arg ) )
181+ Some ( err_ty )
118182 } else {
119183 None
120184 }
0 commit comments