@@ -69,31 +69,35 @@ declare_clippy_lint! {
69
69
"using a return statement like `return expr;` where an expression would suffice"
70
70
}
71
71
72
- #[ derive( PartialEq , Eq , Copy , Clone ) ]
72
+ #[ derive( PartialEq , Eq , Clone ) ]
73
73
enum RetReplacement {
74
74
Empty ,
75
75
Block ,
76
76
Unit ,
77
+ IfSequence ( String ) ,
78
+ Expr ( String ) ,
77
79
}
78
80
79
81
impl RetReplacement {
80
82
fn sugg_help ( self ) -> & ' static str {
81
83
match self {
82
- Self :: Empty => "remove `return`" ,
84
+ Self :: Empty | Self :: Expr ( _ ) => "remove `return`" ,
83
85
Self :: Block => "replace `return` with an empty block" ,
84
86
Self :: Unit => "replace `return` with a unit value" ,
87
+ Self :: IfSequence ( _) => "remove `return` and wrap the sequence with parentheses" ,
85
88
}
86
89
}
87
90
}
88
91
89
92
impl ToString for RetReplacement {
90
93
fn to_string ( & self ) -> String {
91
- match * self {
92
- Self :: Empty => "" ,
93
- Self :: Block => "{}" ,
94
- Self :: Unit => "()" ,
94
+ match self {
95
+ Self :: Empty => String :: new ( ) ,
96
+ Self :: Block => "{}" . to_string ( ) ,
97
+ Self :: Unit => "()" . to_string ( ) ,
98
+ Self :: IfSequence ( inner) => format ! ( "({inner})" ) ,
99
+ Self :: Expr ( inner) => inner. clone ( ) ,
95
100
}
96
- . to_string ( )
97
101
}
98
102
}
99
103
@@ -210,28 +214,46 @@ fn check_final_expr<'tcx>(
210
214
match & peeled_drop_expr. kind {
211
215
// simple return is always "bad"
212
216
ExprKind :: Ret ( ref inner) => {
213
- // if desugar of `do yeet`, don't lint
214
- if let Some ( inner_expr) = inner
215
- && let ExprKind :: Call ( path_expr, _) = inner_expr. kind
216
- && let ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryTraitFromYeet , _, _) ) = path_expr. kind
217
- {
218
- return ;
219
- }
217
+ // check if expr return nothing
218
+ let ret_span = if inner. is_none ( ) && replacement == RetReplacement :: Empty {
219
+ extend_span_to_previous_non_ws ( cx, peeled_drop_expr. span )
220
+ } else {
221
+ peeled_drop_expr. span
222
+ } ;
223
+
224
+ let replacement = if let Some ( inner_expr) = inner {
225
+ // if desugar of `do yeet`, don't lint
226
+ if let ExprKind :: Call ( path_expr, _) = inner_expr. kind
227
+ && let ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryTraitFromYeet , _, _) ) = path_expr. kind
228
+ {
229
+ return ;
230
+ }
231
+
232
+ let ( snippet, _) = snippet_with_context (
233
+ cx,
234
+ inner_expr. span ,
235
+ ret_span. ctxt ( ) ,
236
+ ".." ,
237
+ & mut Applicability :: MachineApplicable ,
238
+ ) ;
239
+ if expr_contains_if ( inner_expr) {
240
+ RetReplacement :: IfSequence ( snippet. to_string ( ) )
241
+ } else {
242
+ RetReplacement :: Expr ( snippet. to_string ( ) )
243
+ }
244
+ } else {
245
+ replacement
246
+ } ;
247
+
220
248
if !cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
221
249
return ;
222
250
}
223
251
let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
224
252
if borrows {
225
253
return ;
226
254
}
227
- // check if expr return nothing
228
- let ret_span = if inner. is_none ( ) && replacement == RetReplacement :: Empty {
229
- extend_span_to_previous_non_ws ( cx, peeled_drop_expr. span )
230
- } else {
231
- peeled_drop_expr. span
232
- } ;
233
255
234
- emit_return_lint ( cx, ret_span, semi_spans, inner . as_ref ( ) . map ( |i| i . span ) , replacement) ;
256
+ emit_return_lint ( cx, ret_span, semi_spans, replacement) ;
235
257
} ,
236
258
ExprKind :: If ( _, then, else_clause_opt) => {
237
259
check_block_return ( cx, & then. kind , peeled_drop_expr. span , semi_spans. clone ( ) ) ;
@@ -253,29 +275,21 @@ fn check_final_expr<'tcx>(
253
275
}
254
276
}
255
277
256
- fn emit_return_lint (
257
- cx : & LateContext < ' _ > ,
258
- ret_span : Span ,
259
- semi_spans : Vec < Span > ,
260
- inner_span : Option < Span > ,
261
- replacement : RetReplacement ,
262
- ) {
278
+ fn expr_contains_if < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> bool {
279
+ match expr. kind {
280
+ ExprKind :: If ( ..) => true ,
281
+ ExprKind :: Binary ( _, left, right) => expr_contains_if ( left) || expr_contains_if ( right) ,
282
+ _ => false ,
283
+ }
284
+ }
285
+
286
+ fn emit_return_lint ( cx : & LateContext < ' _ > , ret_span : Span , semi_spans : Vec < Span > , replacement : RetReplacement ) {
263
287
if ret_span. from_expansion ( ) {
264
288
return ;
265
289
}
266
- let mut applicability = Applicability :: MachineApplicable ;
267
- let return_replacement = inner_span. map_or_else (
268
- || replacement. to_string ( ) ,
269
- |inner_span| {
270
- let ( snippet, _) = snippet_with_context ( cx, inner_span, ret_span. ctxt ( ) , ".." , & mut applicability) ;
271
- snippet. to_string ( )
272
- } ,
273
- ) ;
274
- let sugg_help = if inner_span. is_some ( ) {
275
- "remove `return`"
276
- } else {
277
- replacement. sugg_help ( )
278
- } ;
290
+ let applicability = Applicability :: MachineApplicable ;
291
+ let return_replacement = replacement. to_string ( ) ;
292
+ let sugg_help = replacement. sugg_help ( ) ;
279
293
span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
280
294
diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
281
295
// for each parent statement, we need to remove the semicolon
0 commit comments