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 } ;
22use clippy_utils:: source:: { snippet_opt, snippet_with_context} ;
33use clippy_utils:: { fn_def_id, path_to_local_id} ;
44use if_chain:: if_chain;
@@ -9,7 +9,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
99use rustc_middle:: lint:: in_external_macro;
1010use rustc_middle:: ty:: subst:: GenericArgKind ;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12- use rustc_span:: source_map:: Span ;
12+ use rustc_span:: source_map:: { Span , DUMMY_SP } ;
1313
1414declare_clippy_lint ! {
1515 /// ### What it does
@@ -73,8 +73,8 @@ enum RetReplacement {
7373}
7474
7575impl RetReplacement {
76- fn sugg_help ( & self ) -> & ' static str {
77- match * self {
76+ fn sugg_help ( self ) -> & ' static str {
77+ match self {
7878 Self :: Empty => "remove `return`" ,
7979 Self :: Block => "replace `return` with an empty block" ,
8080 Self :: Unit => "replace `return` with a unit value" ,
@@ -160,51 +160,65 @@ impl<'tcx> LateLintPass<'tcx> for Return {
160160 } else {
161161 RetReplacement :: Empty
162162 } ;
163- check_final_expr ( cx, body. value , body . value . span , replacement) ;
163+ check_final_expr ( cx, body. value , vec ! [ ] , replacement) ;
164164 } ,
165165 FnKind :: ItemFn ( ..) | FnKind :: Method ( ..) => {
166- check_block_return ( cx, & body. value . kind ) ;
166+ check_block_return ( cx, & body. value . kind , vec ! [ ] ) ;
167167 } ,
168168 }
169169 }
170170}
171171
172172// 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 > ) {
174174 if let ExprKind :: Block ( block, _) = expr_kind {
175175 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 ) ;
177177 } else if let Some ( stmt) = block. stmts . iter ( ) . last ( ) {
178178 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 ) ;
181187 } ,
182188 _ => ( ) ,
183189 }
184190 }
185191 }
186192}
187193
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 {
190203 // simple return is always "bad"
191204 ExprKind :: Ret ( ref inner) => {
192205 if cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
193206 let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
194207 if !borrows {
195208 emit_return_lint (
196209 cx,
197- span,
210+ peeled_drop_expr. span ,
211+ semi_spans,
198212 inner. as_ref ( ) . map ( |i| i. span ) ,
199213 replacement,
200214 ) ;
201215 }
202216 }
203217 } ,
204218 ExprKind :: If ( _, then, else_clause_opt) => {
205- check_block_return ( cx, & then. kind ) ;
219+ check_block_return ( cx, & then. kind , semi_spans . clone ( ) ) ;
206220 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 ) ;
208222 }
209223 } ,
210224 // a match expr, check all arms
@@ -213,17 +227,18 @@ fn check_final_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, span:
213227 // (except for unit type functions) so we don't match it
214228 ExprKind :: Match ( _, arms, MatchSource :: Normal ) => {
215229 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 ) ;
217231 }
218232 } ,
219233 // 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 ) ,
221235 }
222236}
223237
224238fn emit_return_lint (
225239 cx : & LateContext < ' _ > ,
226240 ret_span : Span ,
241+ semi_spans : Vec < Span > ,
227242 inner_span : Option < Span > ,
228243 replacement : RetReplacement ,
229244) {
@@ -243,15 +258,13 @@ fn emit_return_lint(
243258 } else {
244259 replacement. sugg_help ( )
245260 } ;
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+ } ) ;
255268}
256269
257270fn last_statement_borrows < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
0 commit comments