@@ -14,6 +14,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
1414use rustc_span:: def_id:: LocalDefId ;
1515use rustc_span:: source_map:: Span ;
1616use rustc_span:: { BytePos , Pos } ;
17+ use std:: borrow:: Cow ;
1718
1819declare_clippy_lint ! {
1920 /// ### What it does
@@ -69,31 +70,41 @@ declare_clippy_lint! {
6970 "using a return statement like `return expr;` where an expression would suffice"
7071}
7172
72- #[ derive( PartialEq , Eq , Copy , Clone ) ]
73- enum RetReplacement {
73+ #[ derive( PartialEq , Eq , Clone ) ]
74+ enum RetReplacement < ' tcx > {
7475 Empty ,
7576 Block ,
7677 Unit ,
78+ IfSequence ( Cow < ' tcx , str > , Applicability ) ,
79+ Expr ( Cow < ' tcx , str > , Applicability ) ,
7780}
7881
79- impl RetReplacement {
82+ impl < ' tcx > RetReplacement < ' tcx > {
8083 fn sugg_help ( self ) -> & ' static str {
8184 match self {
82- Self :: Empty => "remove `return`" ,
85+ Self :: Empty | Self :: Expr ( .. ) => "remove `return`" ,
8386 Self :: Block => "replace `return` with an empty block" ,
8487 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 ,
8595 }
8696 }
8797}
8898
89- impl ToString for RetReplacement {
99+ impl < ' tcx > ToString for RetReplacement < ' tcx > {
90100 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 ( ) ,
95107 }
96- . to_string ( )
97108 }
98109}
99110
@@ -204,34 +215,47 @@ fn check_final_expr<'tcx>(
204215 expr : & ' tcx Expr < ' tcx > ,
205216 semi_spans : Vec < Span > , /* containing all the places where we would need to remove semicolons if finding an
206217 * needless return */
207- replacement : RetReplacement ,
218+ replacement : RetReplacement < ' tcx > ,
208219) {
209220 let peeled_drop_expr = expr. peel_drop_temps ( ) ;
210221 match & peeled_drop_expr. kind {
211222 // simple return is always "bad"
212223 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+
220250 if !cx. tcx . hir ( ) . attrs ( expr. hir_id ) . is_empty ( ) {
221251 return ;
222252 }
223253 let borrows = inner. map_or ( false , |inner| last_statement_borrows ( cx, inner) ) ;
224254 if borrows {
225255 return ;
226256 }
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- } ;
233257
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) ;
235259 } ,
236260 ExprKind :: If ( _, then, else_clause_opt) => {
237261 check_block_return ( cx, & then. kind , peeled_drop_expr. span , semi_spans. clone ( ) ) ;
@@ -253,29 +277,25 @@ fn check_final_expr<'tcx>(
253277 }
254278}
255279
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 < ' _ > ) {
263293 if ret_span. from_expansion ( ) {
264294 return ;
265295 }
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 ( ) ;
279299 span_lint_and_then ( cx, NEEDLESS_RETURN , ret_span, "unneeded `return` statement" , |diag| {
280300 diag. span_suggestion_hidden ( ret_span, sugg_help, return_replacement, applicability) ;
281301 // for each parent statement, we need to remove the semicolon
0 commit comments