@@ -4,7 +4,7 @@ use crate::utils::{
4
4
} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_errors:: Applicability ;
7
- use rustc_hir:: intravisit:: { FnKind , NestedVisitorMap , Visitor } ;
7
+ use rustc_hir:: intravisit:: { FnKind , Visitor } ;
8
8
use rustc_hir:: * ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
10
use rustc_middle:: { hir:: map:: Map , ty:: subst:: GenericArgKind } ;
@@ -71,57 +71,31 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap {
71
71
}
72
72
}
73
73
74
- if let ExprKind :: Block ( ref block, ..) = body. value . kind {
75
- let path = if is_type_diagnostic_item ( cx, return_ty ( cx, hir_id) , sym ! ( option_type) ) {
76
- & paths:: OPTION_SOME
77
- } else if is_type_diagnostic_item ( cx, return_ty ( cx, hir_id) , sym ! ( result_type) ) {
78
- & paths:: RESULT_OK
79
- } else {
80
- return ;
81
- } ;
82
-
83
- let mut visitor = UnnecessaryWrapVisitor { result : Vec :: new ( ) } ;
84
- visitor. visit_block ( block) ;
85
- let result = visitor. result ;
86
-
87
- if result. iter ( ) . any ( |expr| {
88
- if_chain ! {
89
- if let ExprKind :: Call ( ref func, ref args) = expr. kind;
90
- if let ExprKind :: Path ( ref qpath) = func. kind;
91
- if match_qpath( qpath, path) ;
92
- if args. len( ) == 1 ;
93
- then {
94
- false
95
- } else {
96
- true
97
- }
74
+ let path = if is_type_diagnostic_item ( cx, return_ty ( cx, hir_id) , sym ! ( option_type) ) {
75
+ & paths:: OPTION_SOME
76
+ } else if is_type_diagnostic_item ( cx, return_ty ( cx, hir_id) , sym ! ( result_type) ) {
77
+ & paths:: RESULT_OK
78
+ } else {
79
+ return ;
80
+ } ;
81
+
82
+ let mut suggs = Vec :: new ( ) ;
83
+ let can_sugg = find_all_ret_expressions ( cx, & body. value , |ret_expr| {
84
+ if_chain ! {
85
+ if let ExprKind :: Call ( ref func, ref args) = ret_expr. kind;
86
+ if let ExprKind :: Path ( ref qpath) = func. kind;
87
+ if match_qpath( qpath, path) ;
88
+ if args. len( ) == 1 ;
89
+ then {
90
+ suggs. push( ( ret_expr. span, snippet( cx, args[ 0 ] . span. source_callsite( ) , ".." ) . to_string( ) ) ) ;
91
+ true
92
+ } else {
93
+ false
98
94
}
99
- } ) {
100
- return ;
101
95
}
96
+ } ) ;
102
97
103
- let suggs = result
104
- . iter ( )
105
- . filter_map ( |expr| {
106
- let snippet = if let ExprKind :: Call ( _, ref args) = expr. kind {
107
- Some ( snippet ( cx, args[ 0 ] . span , ".." ) . to_string ( ) )
108
- } else {
109
- None
110
- } ;
111
- snippet. map ( |snip| ( expr. span , snip) )
112
- } )
113
- . chain ( {
114
- let inner_ty = return_ty ( cx, hir_id)
115
- . walk ( )
116
- . skip ( 1 ) // skip `std::option::Option` or `std::result::Result`
117
- . take ( 1 ) // first outermost inner type is needed
118
- . filter_map ( |inner| match inner. unpack ( ) {
119
- GenericArgKind :: Type ( inner_ty) => Some ( inner_ty. to_string ( ) ) ,
120
- _ => None ,
121
- } ) ;
122
- inner_ty. map ( |inner_ty| ( fn_decl. output . span ( ) , inner_ty) )
123
- } ) ;
124
-
98
+ if can_sugg {
125
99
span_lint_and_then (
126
100
cx,
127
101
UNNECESSARY_WRAP ,
@@ -132,59 +106,143 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWrap {
132
106
diag,
133
107
"factor this out to" ,
134
108
Applicability :: MachineApplicable ,
135
- suggs,
109
+ suggs. into_iter ( ) . chain ( {
110
+ let inner_ty = return_ty ( cx, hir_id)
111
+ . walk ( )
112
+ . skip ( 1 ) // skip `std::option::Option` or `std::result::Result`
113
+ . take ( 1 ) // take the first outermost inner type
114
+ . filter_map ( |inner| match inner. unpack ( ) {
115
+ GenericArgKind :: Type ( inner_ty) => Some ( inner_ty. to_string ( ) ) ,
116
+ _ => None ,
117
+ } ) ;
118
+ inner_ty. map ( |inner_ty| ( fn_decl. output . span ( ) , inner_ty) )
119
+ } ) ,
136
120
) ;
137
121
} ,
138
122
) ;
139
123
}
140
124
}
141
125
}
142
126
143
- struct UnnecessaryWrapVisitor < ' tcx > {
144
- result : Vec < & ' tcx Expr < ' tcx > > ,
145
- }
127
+ // code below is copied from `bind_instead_of_map`
128
+
129
+ fn find_all_ret_expressions < ' hir , F > ( _cx : & LateContext < ' _ > , expr : & ' hir Expr < ' hir > , callback : F ) -> bool
130
+ where
131
+ F : FnMut ( & ' hir Expr < ' hir > ) -> bool ,
132
+ {
133
+ struct RetFinder < F > {
134
+ in_stmt : bool ,
135
+ failed : bool ,
136
+ cb : F ,
137
+ }
138
+
139
+ struct WithStmtGuarg < ' a , F > {
140
+ val : & ' a mut RetFinder < F > ,
141
+ prev_in_stmt : bool ,
142
+ }
146
143
147
- impl < ' tcx > Visitor < ' tcx > for UnnecessaryWrapVisitor < ' tcx > {
148
- type Map = Map < ' tcx > ;
144
+ impl < F > RetFinder < F > {
145
+ fn inside_stmt ( & mut self , in_stmt : bool ) -> WithStmtGuarg < ' _ , F > {
146
+ let prev_in_stmt = std:: mem:: replace ( & mut self . in_stmt , in_stmt) ;
147
+ WithStmtGuarg {
148
+ val : self ,
149
+ prev_in_stmt,
150
+ }
151
+ }
152
+ }
149
153
150
- fn visit_block ( & mut self , block : & ' tcx Block < ' tcx > ) {
151
- for stmt in block. stmts {
152
- self . visit_stmt ( stmt) ;
154
+ impl < F > std:: ops:: Deref for WithStmtGuarg < ' _ , F > {
155
+ type Target = RetFinder < F > ;
156
+
157
+ fn deref ( & self ) -> & Self :: Target {
158
+ self . val
153
159
}
154
- if let Some ( expr) = block. expr {
155
- self . visit_expr ( expr)
160
+ }
161
+
162
+ impl < F > std:: ops:: DerefMut for WithStmtGuarg < ' _ , F > {
163
+ fn deref_mut ( & mut self ) -> & mut Self :: Target {
164
+ self . val
156
165
}
157
166
}
158
167
159
- fn visit_stmt ( & mut self , stmt : & ' tcx Stmt < ' tcx > ) {
160
- match stmt. kind {
161
- StmtKind :: Semi ( ref expr) => {
162
- if let ExprKind :: Ret ( Some ( value) ) = expr. kind {
163
- self . result . push ( value) ;
164
- }
165
- } ,
166
- StmtKind :: Expr ( ref expr) => self . visit_expr ( expr) ,
167
- _ => ( ) ,
168
+ impl < F > Drop for WithStmtGuarg < ' _ , F > {
169
+ fn drop ( & mut self ) {
170
+ self . val . in_stmt = self . prev_in_stmt ;
168
171
}
169
172
}
170
173
171
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
172
- match expr. kind {
173
- ExprKind :: Ret ( Some ( value) ) => self . result . push ( value) ,
174
- ExprKind :: Call ( ..) | ExprKind :: Path ( ..) => self . result . push ( expr) ,
175
- ExprKind :: Block ( ref block, _) | ExprKind :: Loop ( ref block, ..) => {
176
- self . visit_block ( block) ;
177
- } ,
178
- ExprKind :: Match ( _, arms, _) => {
179
- for arm in arms {
180
- self . visit_expr ( arm. body ) ;
174
+ impl < ' hir , F : FnMut ( & ' hir Expr < ' hir > ) -> bool > intravisit:: Visitor < ' hir > for RetFinder < F > {
175
+ type Map = Map < ' hir > ;
176
+
177
+ fn nested_visit_map ( & mut self ) -> intravisit:: NestedVisitorMap < Self :: Map > {
178
+ intravisit:: NestedVisitorMap :: None
179
+ }
180
+
181
+ fn visit_stmt ( & mut self , stmt : & ' hir Stmt < ' _ > ) {
182
+ intravisit:: walk_stmt ( & mut * self . inside_stmt ( true ) , stmt)
183
+ }
184
+
185
+ fn visit_expr ( & mut self , expr : & ' hir Expr < ' _ > ) {
186
+ if self . failed {
187
+ return ;
188
+ }
189
+ if self . in_stmt {
190
+ match expr. kind {
191
+ ExprKind :: Ret ( Some ( expr) ) => self . inside_stmt ( false ) . visit_expr ( expr) ,
192
+ _ => intravisit:: walk_expr ( self , expr) ,
193
+ }
194
+ } else {
195
+ match expr. kind {
196
+ ExprKind :: Match ( cond, arms, _) => {
197
+ self . inside_stmt ( true ) . visit_expr ( cond) ;
198
+ for arm in arms {
199
+ self . visit_expr ( arm. body ) ;
200
+ }
201
+ } ,
202
+ ExprKind :: Block ( ..) => intravisit:: walk_expr ( self , expr) ,
203
+ ExprKind :: Ret ( Some ( expr) ) => self . visit_expr ( expr) ,
204
+ _ => self . failed |= !( self . cb ) ( expr) ,
181
205
}
182
- } ,
183
- _ => intravisit:: walk_expr ( self , expr) ,
206
+ }
184
207
}
185
208
}
186
209
187
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
188
- NestedVisitorMap :: None
210
+ !contains_try ( expr) && {
211
+ let mut ret_finder = RetFinder {
212
+ in_stmt : false ,
213
+ failed : false ,
214
+ cb : callback,
215
+ } ;
216
+ ret_finder. visit_expr ( expr) ;
217
+ !ret_finder. failed
218
+ }
219
+ }
220
+
221
+ /// returns `true` if expr contains match expr desugared from try
222
+ fn contains_try ( expr : & Expr < ' _ > ) -> bool {
223
+ struct TryFinder {
224
+ found : bool ,
225
+ }
226
+
227
+ impl < ' hir > intravisit:: Visitor < ' hir > for TryFinder {
228
+ type Map = Map < ' hir > ;
229
+
230
+ fn nested_visit_map ( & mut self ) -> intravisit:: NestedVisitorMap < Self :: Map > {
231
+ intravisit:: NestedVisitorMap :: None
232
+ }
233
+
234
+ fn visit_expr ( & mut self , expr : & ' hir Expr < ' hir > ) {
235
+ if self . found {
236
+ return ;
237
+ }
238
+ match expr. kind {
239
+ ExprKind :: Match ( _, _, MatchSource :: TryDesugar ) => self . found = true ,
240
+ _ => intravisit:: walk_expr ( self , expr) ,
241
+ }
242
+ }
189
243
}
244
+
245
+ let mut visitor = TryFinder { found : false } ;
246
+ visitor. visit_expr ( expr) ;
247
+ visitor. found
190
248
}
0 commit comments