1- use clippy_utils:: diagnostics:: span_lint_hir_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;
@@ -72,6 +72,27 @@ enum RetReplacement {
7272 Unit ,
7373}
7474
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+
7596declare_lint_pass ! ( Return => [ LET_AND_RETURN , NEEDLESS_RETURN ] ) ;
7697
7798impl < ' tcx > LateLintPass < ' tcx > for Return {
@@ -139,62 +160,67 @@ impl<'tcx> LateLintPass<'tcx> for Return {
139160 } else {
140161 RetReplacement :: Empty
141162 } ;
142- check_final_expr ( cx, body. value , Some ( body . value . span ) , replacement) ;
163+ check_final_expr ( cx, body. value , vec ! [ ] , replacement) ;
143164 } ,
144165 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 ! [ ] ) ;
148167 } ,
149168 }
150169 }
151170}
152171
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+ }
162192 }
163193 }
164194}
165195
166196fn check_final_expr < ' tcx > (
167197 cx : & LateContext < ' tcx > ,
168198 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 */
170201 replacement : RetReplacement ,
171202) {
172- match expr. kind {
203+ let peeled_drop_expr = expr. peel_drop_temps ( ) ;
204+ match & peeled_drop_expr. kind {
173205 // simple return is always "bad"
174206 ExprKind :: Ret ( ref inner) => {
175207 if cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
176208 let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
177209 if !borrows {
178210 emit_return_lint (
179211 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 ,
182214 inner. as_ref ( ) . map ( |i| i. span ) ,
183215 replacement,
184216 ) ;
185217 }
186218 }
187219 } ,
188- // a whole block? check it!
189- ExprKind :: Block ( block, _) => {
190- check_block_return ( cx, block) ;
191- } ,
192220 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 ( ) ) ;
196222 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 ) ;
198224 }
199225 } ,
200226 // a match expr, check all arms
@@ -203,93 +229,44 @@ fn check_final_expr<'tcx>(
203229 // (except for unit type functions) so we don't match it
204230 ExprKind :: Match ( _, arms, MatchSource :: Normal ) => {
205231 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 ) ;
207233 }
208234 } ,
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 ) ,
211237 }
212238}
213239
214240fn emit_return_lint (
215241 cx : & LateContext < ' _ > ,
216- emission_place : HirId ,
217242 ret_span : Span ,
243+ semi_spans : Vec < Span > ,
218244 inner_span : Option < Span > ,
219245 replacement : RetReplacement ,
220246) {
221247 if ret_span. from_expansion ( ) {
222248 return ;
223249 }
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 ( )
291256 } ,
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+ } ) ;
293270}
294271
295272fn last_statement_borrows < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
0 commit comments