@@ -69,31 +69,35 @@ declare_clippy_lint! {
6969 "using a return statement like `return expr;` where an expression would suffice"
7070}
7171
72- #[ derive( PartialEq , Eq , Copy , Clone ) ]
72+ #[ derive( PartialEq , Eq , Clone ) ]
7373enum RetReplacement {
7474 Empty ,
7575 Block ,
7676 Unit ,
77+ IfSequence ( String ) ,
78+ Expr ( String ) ,
7779}
7880
7981impl RetReplacement {
8082 fn sugg_help ( self ) -> & ' static str {
8183 match self {
82- Self :: Empty => "remove `return`" ,
84+ Self :: Empty | Self :: Expr ( _ ) => "remove `return`" ,
8385 Self :: Block => "replace `return` with an empty block" ,
8486 Self :: Unit => "replace `return` with a unit value" ,
87+ Self :: IfSequence ( _) => "remove `return` and wrap the sequence with parentheses" ,
8588 }
8689 }
8790}
8891
8992impl ToString for RetReplacement {
9093 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 ( ) ,
95100 }
96- . to_string ( )
97101 }
98102}
99103
@@ -210,28 +214,46 @@ fn check_final_expr<'tcx>(
210214 match & peeled_drop_expr. kind {
211215 // simple return is always "bad"
212216 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+
220248 if !cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
221249 return ;
222250 }
223251 let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
224252 if borrows {
225253 return ;
226254 }
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- } ;
233255
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) ;
235257 } ,
236258 ExprKind :: If ( _, then, else_clause_opt) => {
237259 check_block_return ( cx, & then. kind , peeled_drop_expr. span , semi_spans. clone ( ) ) ;
@@ -253,29 +275,21 @@ fn check_final_expr<'tcx>(
253275 }
254276}
255277
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 ) {
263287 if ret_span. from_expansion ( ) {
264288 return ;
265289 }
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 ( ) ;
279293 span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
280294 diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
281295 // for each parent statement, we need to remove the semicolon
0 commit comments