@@ -14,6 +14,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
14
14
use rustc_span:: def_id:: LocalDefId ;
15
15
use rustc_span:: source_map:: Span ;
16
16
use rustc_span:: { BytePos , Pos } ;
17
+ use std:: borrow:: Cow ;
17
18
18
19
declare_clippy_lint ! {
19
20
/// ### What it does
@@ -69,31 +70,41 @@ declare_clippy_lint! {
69
70
"using a return statement like `return expr;` where an expression would suffice"
70
71
}
71
72
72
- #[ derive( PartialEq , Eq , Copy , Clone ) ]
73
- enum RetReplacement {
73
+ #[ derive( PartialEq , Eq , Clone ) ]
74
+ enum RetReplacement < ' tcx > {
74
75
Empty ,
75
76
Block ,
76
77
Unit ,
78
+ IfSequence ( Cow < ' tcx , str > , Applicability ) ,
79
+ Expr ( Cow < ' tcx , str > , Applicability ) ,
77
80
}
78
81
79
- impl RetReplacement {
82
+ impl < ' tcx > RetReplacement < ' tcx > {
80
83
fn sugg_help ( self ) -> & ' static str {
81
84
match self {
82
- Self :: Empty => "remove `return`" ,
85
+ Self :: Empty | Self :: Expr ( .. ) => "remove `return`" ,
83
86
Self :: Block => "replace `return` with an empty block" ,
84
87
Self :: Unit => "replace `return` with a unit value" ,
88
+ Self :: IfSequence ( ..) => "remove `return` and wrap the sequence with parentheses" ,
89
+ }
90
+ }
91
+ fn applicability ( & self ) -> Option < Applicability > {
92
+ match self {
93
+ Self :: Expr ( _, ap) | Self :: IfSequence ( _, ap) => Some ( * ap) ,
94
+ _ => None ,
85
95
}
86
96
}
87
97
}
88
98
89
- impl ToString for RetReplacement {
99
+ impl < ' tcx > ToString for RetReplacement < ' tcx > {
90
100
fn to_string ( & self ) -> String {
91
- match * self {
92
- Self :: Empty => "" ,
93
- Self :: Block => "{}" ,
94
- Self :: Unit => "()" ,
101
+ match self {
102
+ Self :: Empty => String :: new ( ) ,
103
+ Self :: Block => "{}" . to_string ( ) ,
104
+ Self :: Unit => "()" . to_string ( ) ,
105
+ Self :: IfSequence ( inner, _) => format ! ( "({inner})" ) ,
106
+ Self :: Expr ( inner, _) => inner. to_string ( ) ,
95
107
}
96
- . to_string ( )
97
108
}
98
109
}
99
110
@@ -204,34 +215,47 @@ fn check_final_expr<'tcx>(
204
215
expr : & ' tcx Expr < ' tcx > ,
205
216
semi_spans : Vec < Span > , /* containing all the places where we would need to remove semicolons if finding an
206
217
* needless return */
207
- replacement : RetReplacement ,
218
+ replacement : RetReplacement < ' tcx > ,
208
219
) {
209
220
let peeled_drop_expr = expr. peel_drop_temps ( ) ;
210
221
match & peeled_drop_expr. kind {
211
222
// simple return is always "bad"
212
223
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
- }
224
+ // check if expr return nothing
225
+ let ret_span = if inner. is_none ( ) && replacement == RetReplacement :: Empty {
226
+ extend_span_to_previous_non_ws ( cx, peeled_drop_expr. span )
227
+ } else {
228
+ peeled_drop_expr. span
229
+ } ;
230
+
231
+ let replacement = if let Some ( inner_expr) = inner {
232
+ // if desugar of `do yeet`, don't lint
233
+ if let ExprKind :: Call ( path_expr, _) = inner_expr. kind
234
+ && let ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryTraitFromYeet , _, _) ) = path_expr. kind
235
+ {
236
+ return ;
237
+ }
238
+
239
+ let mut applicability = Applicability :: MachineApplicable ;
240
+ let ( snippet, _) = snippet_with_context ( cx, inner_expr. span , ret_span. ctxt ( ) , ".." , & mut applicability) ;
241
+ if expr_contains_conjunctive_ifs ( inner_expr) {
242
+ RetReplacement :: IfSequence ( snippet, applicability)
243
+ } else {
244
+ RetReplacement :: Expr ( snippet, applicability)
245
+ }
246
+ } else {
247
+ replacement
248
+ } ;
249
+
220
250
if !cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
221
251
return ;
222
252
}
223
253
let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
224
254
if borrows {
225
255
return ;
226
256
}
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
257
234
- emit_return_lint ( cx, ret_span, semi_spans, inner . as_ref ( ) . map ( |i| i . span ) , replacement) ;
258
+ emit_return_lint ( cx, ret_span, semi_spans, replacement) ;
235
259
} ,
236
260
ExprKind :: If ( _, then, else_clause_opt) => {
237
261
check_block_return ( cx, & then. kind , peeled_drop_expr. span , semi_spans. clone ( ) ) ;
@@ -253,29 +277,25 @@ fn check_final_expr<'tcx>(
253
277
}
254
278
}
255
279
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
- ) {
280
+ fn expr_contains_conjunctive_ifs < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> bool {
281
+ fn contains_if ( expr : & Expr < ' _ > , on_if : bool ) -> bool {
282
+ match expr. kind {
283
+ ExprKind :: If ( ..) => on_if,
284
+ ExprKind :: Binary ( _, left, right) => contains_if ( left, true ) || contains_if ( right, true ) ,
285
+ _ => false ,
286
+ }
287
+ }
288
+
289
+ contains_if ( expr, false )
290
+ }
291
+
292
+ fn emit_return_lint ( cx : & LateContext < ' _ > , ret_span : Span , semi_spans : Vec < Span > , replacement : RetReplacement < ' _ > ) {
263
293
if ret_span. from_expansion ( ) {
264
294
return ;
265
295
}
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
- } ;
296
+ let applicability = replacement. applicability ( ) . unwrap_or ( Applicability :: MachineApplicable ) ;
297
+ let return_replacement = replacement. to_string ( ) ;
298
+ let sugg_help = replacement. sugg_help ( ) ;
279
299
span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
280
300
diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
281
301
// for each parent statement, we need to remove the semicolon
0 commit comments