1
- use clippy_utils:: diagnostics:: span_lint_hir_and_then;
1
+ use clippy_utils:: diagnostics:: { span_lint_and_then , span_lint_hir_and_then} ;
2
2
use clippy_utils:: source:: { snippet_opt, snippet_with_context} ;
3
3
use clippy_utils:: { fn_def_id, path_to_local_id} ;
4
4
use if_chain:: if_chain;
@@ -72,6 +72,27 @@ enum RetReplacement {
72
72
Unit ,
73
73
}
74
74
75
+ impl RetReplacement {
76
+ fn sugg_help ( self ) -> & ' static str {
77
+ match self {
78
+ Self :: Empty => "remove `return`" ,
79
+ Self :: Block => "replace `return` with an empty block" ,
80
+ Self :: Unit => "replace `return` with a unit value" ,
81
+ }
82
+ }
83
+ }
84
+
85
+ impl ToString for RetReplacement {
86
+ fn to_string ( & self ) -> String {
87
+ match * self {
88
+ Self :: Empty => "" ,
89
+ Self :: Block => "{}" ,
90
+ Self :: Unit => "()" ,
91
+ }
92
+ . to_string ( )
93
+ }
94
+ }
95
+
75
96
declare_lint_pass ! ( Return => [ LET_AND_RETURN , NEEDLESS_RETURN ] ) ;
76
97
77
98
impl < ' tcx > LateLintPass < ' tcx > for Return {
@@ -139,62 +160,67 @@ impl<'tcx> LateLintPass<'tcx> for Return {
139
160
} else {
140
161
RetReplacement :: Empty
141
162
} ;
142
- check_final_expr ( cx, body. value , Some ( body . value . span ) , replacement) ;
163
+ check_final_expr ( cx, body. value , vec ! [ ] , replacement) ;
143
164
} ,
144
165
FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => {
145
- if let ExprKind :: Block ( block, _) = body. value . kind {
146
- check_block_return ( cx, block) ;
147
- }
166
+ check_block_return ( cx, & body. value . kind , vec ! [ ] ) ;
148
167
} ,
149
168
}
150
169
}
151
170
}
152
171
153
- fn check_block_return < ' tcx > ( cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
154
- if let Some ( expr) = block. expr {
155
- check_final_expr ( cx, expr, Some ( expr. span ) , RetReplacement :: Empty ) ;
156
- } else if let Some ( stmt) = block. stmts . iter ( ) . last ( ) {
157
- match stmt. kind {
158
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => {
159
- check_final_expr ( cx, expr, Some ( stmt. span ) , RetReplacement :: Empty ) ;
160
- } ,
161
- _ => ( ) ,
172
+ // if `expr` is a block, check if there are needless returns in it
173
+ fn check_block_return < ' tcx > ( cx : & LateContext < ' tcx > , expr_kind : & ExprKind < ' tcx > , semi_spans : Vec < Span > ) {
174
+ if let ExprKind :: Block ( block, _) = expr_kind {
175
+ if let Some ( block_expr) = block. expr {
176
+ check_final_expr ( cx, block_expr, semi_spans, RetReplacement :: Empty ) ;
177
+ } else if let Some ( stmt) = block. stmts . iter ( ) . last ( ) {
178
+ match stmt. kind {
179
+ StmtKind :: Expr ( expr) => {
180
+ check_final_expr ( cx, expr, semi_spans, RetReplacement :: Empty ) ;
181
+ } ,
182
+ StmtKind :: Semi ( semi_expr) => {
183
+ let mut semi_spans_and_this_one = semi_spans;
184
+ // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382`
185
+ if let Some ( semicolon_span) = stmt. span . trim_start ( semi_expr. span ) {
186
+ semi_spans_and_this_one. push ( semicolon_span) ;
187
+ check_final_expr ( cx, semi_expr, semi_spans_and_this_one, RetReplacement :: Empty ) ;
188
+ }
189
+ } ,
190
+ _ => ( ) ,
191
+ }
162
192
}
163
193
}
164
194
}
165
195
166
196
fn check_final_expr < ' tcx > (
167
197
cx : & LateContext < ' tcx > ,
168
198
expr : & ' tcx Expr < ' tcx > ,
169
- span : Option < Span > ,
199
+ semi_spans : Vec < Span > , /* containing all the places where we would need to remove semicolons if finding an
200
+ * needless return */
170
201
replacement : RetReplacement ,
171
202
) {
172
- match expr. kind {
203
+ let peeled_drop_expr = expr. peel_drop_temps ( ) ;
204
+ match & peeled_drop_expr. kind {
173
205
// simple return is always "bad"
174
206
ExprKind :: Ret ( ref inner) => {
175
207
if cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
176
208
let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
177
209
if !borrows {
178
210
emit_return_lint (
179
211
cx,
180
- inner . map_or ( expr . hir_id , |inner| inner . hir_id ) ,
181
- span . expect ( "`else return` is not possible" ) ,
212
+ peeled_drop_expr . span ,
213
+ semi_spans ,
182
214
inner. as_ref ( ) . map ( |i| i. span ) ,
183
215
replacement,
184
216
) ;
185
217
}
186
218
}
187
219
} ,
188
- // a whole block? check it!
189
- ExprKind :: Block ( block, _) => {
190
- check_block_return ( cx, block) ;
191
- } ,
192
220
ExprKind :: If ( _, then, else_clause_opt) => {
193
- if let ExprKind :: Block ( ifblock, _) = then. kind {
194
- check_block_return ( cx, ifblock) ;
195
- }
221
+ check_block_return ( cx, & then. kind , semi_spans. clone ( ) ) ;
196
222
if let Some ( else_clause) = else_clause_opt {
197
- check_final_expr ( cx, else_clause, None , RetReplacement :: Empty ) ;
223
+ check_block_return ( cx, & else_clause. kind , semi_spans ) ;
198
224
}
199
225
} ,
200
226
// a match expr, check all arms
@@ -203,93 +229,44 @@ fn check_final_expr<'tcx>(
203
229
// (except for unit type functions) so we don't match it
204
230
ExprKind :: Match ( _, arms, MatchSource :: Normal ) => {
205
231
for arm in arms. iter ( ) {
206
- check_final_expr ( cx, arm. body , Some ( arm . body . span ) , RetReplacement :: Unit ) ;
232
+ check_final_expr ( cx, arm. body , semi_spans . clone ( ) , RetReplacement :: Unit ) ;
207
233
}
208
234
} ,
209
- ExprKind :: DropTemps ( expr ) => check_final_expr ( cx , expr , None , RetReplacement :: Empty ) ,
210
- _ => ( ) ,
235
+ // if it's a whole block, check it
236
+ other_expr_kind => check_block_return ( cx , other_expr_kind , semi_spans ) ,
211
237
}
212
238
}
213
239
214
240
fn emit_return_lint (
215
241
cx : & LateContext < ' _ > ,
216
- emission_place : HirId ,
217
242
ret_span : Span ,
243
+ semi_spans : Vec < Span > ,
218
244
inner_span : Option < Span > ,
219
245
replacement : RetReplacement ,
220
246
) {
221
247
if ret_span. from_expansion ( ) {
222
248
return ;
223
249
}
224
- match inner_span {
225
- Some ( inner_span) => {
226
- let mut applicability = Applicability :: MachineApplicable ;
227
- span_lint_hir_and_then (
228
- cx,
229
- NEEDLESS_RETURN ,
230
- emission_place,
231
- ret_span,
232
- "unneeded `return` statement" ,
233
- |diag| {
234
- let ( snippet, _) = snippet_with_context ( cx, inner_span, ret_span. ctxt ( ) , ".." , & mut applicability) ;
235
- diag. span_suggestion ( ret_span, "remove `return`" , snippet, applicability) ;
236
- } ,
237
- ) ;
238
- } ,
239
- None => match replacement {
240
- RetReplacement :: Empty => {
241
- span_lint_hir_and_then (
242
- cx,
243
- NEEDLESS_RETURN ,
244
- emission_place,
245
- ret_span,
246
- "unneeded `return` statement" ,
247
- |diag| {
248
- diag. span_suggestion (
249
- ret_span,
250
- "remove `return`" ,
251
- String :: new ( ) ,
252
- Applicability :: MachineApplicable ,
253
- ) ;
254
- } ,
255
- ) ;
256
- } ,
257
- RetReplacement :: Block => {
258
- span_lint_hir_and_then (
259
- cx,
260
- NEEDLESS_RETURN ,
261
- emission_place,
262
- ret_span,
263
- "unneeded `return` statement" ,
264
- |diag| {
265
- diag. span_suggestion (
266
- ret_span,
267
- "replace `return` with an empty block" ,
268
- "{}" . to_string ( ) ,
269
- Applicability :: MachineApplicable ,
270
- ) ;
271
- } ,
272
- ) ;
273
- } ,
274
- RetReplacement :: Unit => {
275
- span_lint_hir_and_then (
276
- cx,
277
- NEEDLESS_RETURN ,
278
- emission_place,
279
- ret_span,
280
- "unneeded `return` statement" ,
281
- |diag| {
282
- diag. span_suggestion (
283
- ret_span,
284
- "replace `return` with a unit value" ,
285
- "()" . to_string ( ) ,
286
- Applicability :: MachineApplicable ,
287
- ) ;
288
- } ,
289
- ) ;
290
- } ,
250
+ let mut applicability = Applicability :: MachineApplicable ;
251
+ let return_replacement = inner_span. map_or_else (
252
+ || replacement. to_string ( ) ,
253
+ |inner_span| {
254
+ let ( snippet, _) = snippet_with_context ( cx, inner_span, ret_span. ctxt ( ) , ".." , & mut applicability) ;
255
+ snippet. to_string ( )
291
256
} ,
292
- }
257
+ ) ;
258
+ let sugg_help = if inner_span. is_some ( ) {
259
+ "remove `return`"
260
+ } else {
261
+ replacement. sugg_help ( )
262
+ } ;
263
+ span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
264
+ diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
265
+ // for each parent statement, we need to remove the semicolon
266
+ for semi_stmt_span in semi_spans {
267
+ diag. tool_only_span_suggestion ( semi_stmt_span, "remove this semicolon" , "" , applicability) ;
268
+ }
269
+ } ) ;
293
270
}
294
271
295
272
fn last_statement_borrows < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
0 commit comments