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
+ } ;
2
5
use if_chain:: if_chain;
3
6
use rustc_errors:: Applicability ;
4
- use rustc_hir:: { Arm , Expr , ExprKind , MatchSource } ;
7
+ use rustc_hir:: { Expr , ExprKind , MatchSource } ;
5
8
use rustc_lint:: { LateContext , LateLintPass } ;
6
9
use rustc_middle:: lint:: in_external_macro;
7
- use rustc_middle:: ty:: Ty ;
10
+ use rustc_middle:: ty:: { self , Ty } ;
8
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9
12
10
13
declare_clippy_lint ! {
@@ -65,19 +68,39 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
65
68
if let Some ( ref err_arg) = err_args. get( 0 ) ;
66
69
if let ExprKind :: Path ( ref err_fun_path) = err_fun. kind;
67
70
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) ;
70
72
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
+
72
95
let origin_snippet = if err_arg. span. from_expansion( ) {
73
96
snippet_with_macro_callsite( cx, err_arg. span, "_" )
74
97
} else {
75
98
snippet( cx, err_arg. span, "_" )
76
99
} ;
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 )
79
102
} else {
80
- format!( "return Err({} .into()) " , origin_snippet)
103
+ format!( "return {}{} .into(){} " , prefix , origin_snippet, suffix )
81
104
} ;
82
105
83
106
span_lint_and_sugg(
@@ -94,27 +117,68 @@ impl<'tcx> LateLintPass<'tcx> for TryErr {
94
117
}
95
118
}
96
119
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 > > {
101
122
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
+ }
105
128
}
129
+ None
106
130
}
107
131
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 > > {
110
134
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
+
116
180
then {
117
- Some ( cx . typeck_results ( ) . expr_ty ( from_error_arg ) )
181
+ Some ( err_ty )
118
182
} else {
119
183
None
120
184
}
0 commit comments