1
- use clippy_utils:: diagnostics:: { span_lint_hir_and_then , span_lint_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;
@@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
9
9
use rustc_middle:: lint:: in_external_macro;
10
10
use rustc_middle:: ty:: subst:: GenericArgKind ;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
- use rustc_span:: source_map:: Span ;
12
+ use rustc_span:: source_map:: { Span , DUMMY_SP } ;
13
13
14
14
declare_clippy_lint ! {
15
15
/// ### What it does
@@ -73,8 +73,8 @@ enum RetReplacement {
73
73
}
74
74
75
75
impl RetReplacement {
76
- fn sugg_help ( & self ) -> & ' static str {
77
- match * self {
76
+ fn sugg_help ( self ) -> & ' static str {
77
+ match self {
78
78
Self :: Empty => "remove `return`" ,
79
79
Self :: Block => "replace `return` with an empty block" ,
80
80
Self :: Unit => "replace `return` with a unit value" ,
@@ -160,51 +160,65 @@ impl<'tcx> LateLintPass<'tcx> for Return {
160
160
} else {
161
161
RetReplacement :: Empty
162
162
} ;
163
- check_final_expr ( cx, body. value , body . value . span , replacement) ;
163
+ check_final_expr ( cx, body. value , vec ! [ ] , replacement) ;
164
164
} ,
165
165
FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => {
166
- check_block_return ( cx, & body. value . kind ) ;
166
+ check_block_return ( cx, & body. value . kind , vec ! [ ] ) ;
167
167
} ,
168
168
}
169
169
}
170
170
}
171
171
172
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 > ) {
173
+ fn check_block_return < ' tcx > ( cx : & LateContext < ' tcx > , expr_kind : & ExprKind < ' tcx > , semi_spans : Vec < Span > ) {
174
174
if let ExprKind :: Block ( block, _) = expr_kind {
175
175
if let Some ( block_expr) = block. expr {
176
- check_final_expr ( cx, block_expr, block_expr . span , RetReplacement :: Empty ) ;
176
+ check_final_expr ( cx, block_expr, semi_spans , RetReplacement :: Empty ) ;
177
177
} else if let Some ( stmt) = block. stmts . iter ( ) . last ( ) {
178
178
match stmt. kind {
179
- StmtKind :: Expr ( expr) | StmtKind :: Semi ( expr) => {
180
- check_final_expr ( cx, expr, stmt. span , RetReplacement :: Empty ) ;
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
+ semi_spans_and_this_one. push ( stmt. span . trim_start ( semi_expr. span ) . unwrap_or ( DUMMY_SP ) ) ;
186
+ check_final_expr ( cx, semi_expr, semi_spans_and_this_one, RetReplacement :: Empty ) ;
181
187
} ,
182
188
_ => ( ) ,
183
189
}
184
190
}
185
191
}
186
192
}
187
193
188
- fn check_final_expr < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , span : Span , replacement : RetReplacement ) {
189
- match & expr. peel_drop_temps ( ) . kind {
194
+ fn check_final_expr < ' tcx > (
195
+ cx : & LateContext < ' tcx > ,
196
+ expr : & ' tcx Expr < ' tcx > ,
197
+ semi_spans : Vec < Span > , /* containing all the places where we would need to remove semicolons if finding an
198
+ * needless return */
199
+ replacement : RetReplacement ,
200
+ ) {
201
+ let peeled_drop_expr = expr. peel_drop_temps ( ) ;
202
+ match & peeled_drop_expr. kind {
190
203
// simple return is always "bad"
191
204
ExprKind :: Ret ( ref inner) => {
192
205
if cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
193
206
let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
194
207
if !borrows {
195
208
emit_return_lint (
196
209
cx,
197
- span,
210
+ peeled_drop_expr. span ,
211
+ semi_spans,
198
212
inner. as_ref ( ) . map ( |i| i. span ) ,
199
213
replacement,
200
214
) ;
201
215
}
202
216
}
203
217
} ,
204
218
ExprKind :: If ( _, then, else_clause_opt) => {
205
- check_block_return ( cx, & then. kind ) ;
219
+ check_block_return ( cx, & then. kind , semi_spans . clone ( ) ) ;
206
220
if let Some ( else_clause) = else_clause_opt {
207
- check_block_return ( cx, & else_clause. kind )
221
+ check_block_return ( cx, & else_clause. kind , semi_spans ) ;
208
222
}
209
223
} ,
210
224
// a match expr, check all arms
@@ -213,17 +227,18 @@ fn check_final_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, span:
213
227
// (except for unit type functions) so we don't match it
214
228
ExprKind :: Match ( _, arms, MatchSource :: Normal ) => {
215
229
for arm in arms. iter ( ) {
216
- check_final_expr ( cx, arm. body , arm . body . span , RetReplacement :: Unit ) ;
230
+ check_final_expr ( cx, arm. body , semi_spans . clone ( ) , RetReplacement :: Unit ) ;
217
231
}
218
232
} ,
219
233
// if it's a whole block, check it
220
- other_expr_kind => check_block_return ( cx, & other_expr_kind) ,
234
+ other_expr_kind => check_block_return ( cx, other_expr_kind, semi_spans ) ,
221
235
}
222
236
}
223
237
224
238
fn emit_return_lint (
225
239
cx : & LateContext < ' _ > ,
226
240
ret_span : Span ,
241
+ semi_spans : Vec < Span > ,
227
242
inner_span : Option < Span > ,
228
243
replacement : RetReplacement ,
229
244
) {
@@ -243,15 +258,13 @@ fn emit_return_lint(
243
258
} else {
244
259
replacement. sugg_help ( )
245
260
} ;
246
- span_lint_and_then (
247
- cx,
248
- NEEDLESS_RETURN ,
249
- ret_span,
250
- "unneeded `return` statement" ,
251
- |diag| {
252
- diag. span_suggestion ( ret_span, sugg_help, return_replacement, applicability) ;
253
- } ,
254
- ) ;
261
+ span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
262
+ diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
263
+ // for each parent statement, we need to remove the semicolon
264
+ for semi_stmt_span in semi_spans {
265
+ diag. tool_only_span_suggestion ( semi_stmt_span, "remove this semicolon" , "" , applicability) ;
266
+ }
267
+ } ) ;
255
268
}
256
269
257
270
fn last_statement_borrows < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
0 commit comments