11use  clippy_utils:: diagnostics:: { span_lint_and_sugg,  span_lint_hir_and_then} ; 
2- use  clippy_utils:: source:: { SpanRangeExt ,  snippet_with_context } ; 
2+ use  clippy_utils:: source:: SpanRangeExt ; 
33use  clippy_utils:: sugg:: has_enclosing_paren; 
44use  clippy_utils:: visitors:: for_each_expr; 
55use  clippy_utils:: { 
6-     binary_expr_needs_parentheses,  fn_def_id,  is_from_proc_macro,  is_inside_let_else,  is_res_lang_ctor, 
7-     leaks_droppable_temporary_with_limited_lifetime,  path_res,  path_to_local_id,  span_contains_cfg, 
8-     span_find_starting_semi,  sym, 
6+     binary_expr_needs_parentheses,  fn_def_id,  is_from_proc_macro,  is_inside_let_else,  is_res_lang_ctor,  path_res, 
7+     path_to_local_id,  span_contains_cfg, 
98} ; 
109use  core:: ops:: ControlFlow ; 
11- use  rustc_ast:: MetaItemInner ; 
1210use  rustc_errors:: Applicability ; 
1311use  rustc_hir:: LangItem :: ResultErr ; 
1412use  rustc_hir:: intravisit:: FnKind ; 
1513use  rustc_hir:: { 
16-     Block ,  Body ,  Expr ,  ExprKind ,  FnDecl ,  HirId ,  ItemKind ,  LangItem ,  MatchSource ,  Node ,  OwnerNode ,  PatKind ,  QPath ,  Stmt , 
17-     StmtKind , 
14+     Block ,  Body ,  Expr ,  ExprKind ,  FnDecl ,  HirId ,  ItemKind ,  MatchSource ,  Node ,  OwnerNode ,  PatKind ,  Stmt ,  StmtKind , 
1815} ; 
19- use  rustc_lint:: { LateContext ,  LateLintPass ,  Level ,  LintContext } ; 
16+ use  rustc_lint:: { LateContext ,  LateLintPass ,  LintContext } ; 
17+ use  rustc_middle:: ty:: GenericArgKind ; 
2018use  rustc_middle:: ty:: adjustment:: Adjust ; 
21- use  rustc_middle:: ty:: { self ,  GenericArgKind ,  Ty } ; 
2219use  rustc_session:: declare_lint_pass; 
20+ use  rustc_span:: Span ; 
2321use  rustc_span:: def_id:: LocalDefId ; 
2422use  rustc_span:: edition:: Edition ; 
25- use  rustc_span:: { BytePos ,  Pos ,  Span } ; 
26- use  std:: borrow:: Cow ; 
27- use  std:: fmt:: Display ; 
23+ 
24+ mod  needless_return; 
2825
2926declare_clippy_lint !  { 
3027    /// ### What it does 
@@ -132,45 +129,6 @@ declare_clippy_lint! {
132129    "using a return statement like `return Err(expr)?;` where removing it would suffice" 
133130} 
134131
135- #[ derive( PartialEq ,  Eq ) ]  
136- enum  RetReplacement < ' tcx >  { 
137-     Empty , 
138-     Block , 
139-     Unit , 
140-     NeedsPar ( Cow < ' tcx ,  str > ,  Applicability ) , 
141-     Expr ( Cow < ' tcx ,  str > ,  Applicability ) , 
142- } 
143- 
144- impl  RetReplacement < ' _ >  { 
145-     fn  sugg_help ( & self )  -> & ' static  str  { 
146-         match  self  { 
147-             Self :: Empty  | Self :: Expr ( ..)  => "remove `return`" , 
148-             Self :: Block  => "replace `return` with an empty block" , 
149-             Self :: Unit  => "replace `return` with a unit value" , 
150-             Self :: NeedsPar ( ..)  => "remove `return` and wrap the sequence with parentheses" , 
151-         } 
152-     } 
153- 
154-     fn  applicability ( & self )  -> Applicability  { 
155-         match  self  { 
156-             Self :: Expr ( _,  ap)  | Self :: NeedsPar ( _,  ap)  => * ap, 
157-             _ => Applicability :: MachineApplicable , 
158-         } 
159-     } 
160- } 
161- 
162- impl  Display  for  RetReplacement < ' _ >  { 
163-     fn  fmt ( & self ,  f :  & mut  std:: fmt:: Formatter < ' _ > )  -> std:: fmt:: Result  { 
164-         match  self  { 
165-             Self :: Empty  => f. write_str ( "" ) , 
166-             Self :: Block  => f. write_str ( "{}" ) , 
167-             Self :: Unit  => f. write_str ( "()" ) , 
168-             Self :: NeedsPar ( inner,  _)  => write ! ( f,  "({inner})" ) , 
169-             Self :: Expr ( inner,  _)  => write ! ( f,  "{inner}" ) , 
170-         } 
171-     } 
172- } 
173- 
174132declare_lint_pass ! ( Return  => [ LET_AND_RETURN ,  NEEDLESS_RETURN ,  NEEDLESS_RETURN_WITH_QUESTION_MARK ] ) ; 
175133
176134/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This 
@@ -288,197 +246,10 @@ impl<'tcx> LateLintPass<'tcx> for Return {
288246        sp :  Span , 
289247        _:  LocalDefId , 
290248    )  { 
291-         if  sp. from_expansion ( )  { 
292-             return ; 
293-         } 
294- 
295-         match  kind { 
296-             FnKind :: Closure  => { 
297-                 // when returning without value in closure, replace this `return` 
298-                 // with an empty block to prevent invalid suggestion (see #6501) 
299-                 let  replacement = if  let  ExprKind :: Ret ( None )  = & body. value . kind  { 
300-                     RetReplacement :: Block 
301-                 }  else  { 
302-                     RetReplacement :: Empty 
303-                 } ; 
304-                 check_final_expr ( cx,  body. value ,  vec ! [ ] ,  replacement,  None ) ; 
305-             } , 
306-             FnKind :: ItemFn ( ..)  | FnKind :: Method ( ..)  => { 
307-                 check_block_return ( cx,  & body. value . kind ,  sp,  vec ! [ ] ) ; 
308-             } , 
309-         } 
310-     } 
311- } 
312- 
313- // if `expr` is a block, check if there are needless returns in it 
314- fn  check_block_return < ' tcx > ( cx :  & LateContext < ' tcx > ,  expr_kind :  & ExprKind < ' tcx > ,  sp :  Span ,  mut  semi_spans :  Vec < Span > )  { 
315-     if  let  ExprKind :: Block ( block,  _)  = expr_kind { 
316-         if  let  Some ( block_expr)  = block. expr  { 
317-             check_final_expr ( cx,  block_expr,  semi_spans,  RetReplacement :: Empty ,  None ) ; 
318-         }  else  if  let  Some ( stmt)  = block. stmts . last ( )  { 
319-             match  stmt. kind  { 
320-                 StmtKind :: Expr ( expr)  => { 
321-                     check_final_expr ( cx,  expr,  semi_spans,  RetReplacement :: Empty ,  None ) ; 
322-                 } , 
323-                 StmtKind :: Semi ( semi_expr)  => { 
324-                     // Remove ending semicolons and any whitespace ' ' in between. 
325-                     // Without `return`, the suggestion might not compile if the semicolon is retained 
326-                     if  let  Some ( semi_span)  = stmt. span . trim_start ( semi_expr. span )  { 
327-                         let  semi_span_to_remove =
328-                             span_find_starting_semi ( cx. sess ( ) . source_map ( ) ,  semi_span. with_hi ( sp. hi ( ) ) ) ; 
329-                         semi_spans. push ( semi_span_to_remove) ; 
330-                     } 
331-                     check_final_expr ( cx,  semi_expr,  semi_spans,  RetReplacement :: Empty ,  None ) ; 
332-                 } , 
333-                 _ => ( ) , 
334-             } 
335-         } 
249+         needless_return:: check_fn ( cx,  kind,  body,  sp) ; 
336250    } 
337251} 
338252
339- fn  check_final_expr < ' tcx > ( 
340-     cx :  & LateContext < ' tcx > , 
341-     expr :  & ' tcx  Expr < ' tcx > , 
342-     semi_spans :  Vec < Span > ,  /* containing all the places where we would need to remove semicolons if finding an 
343-                             * needless return */ 
344-     replacement :  RetReplacement < ' tcx > , 
345-     match_ty_opt :  Option < Ty < ' _ > > , 
346- )  { 
347-     let  peeled_drop_expr = expr. peel_drop_temps ( ) ; 
348-     match  & peeled_drop_expr. kind  { 
349-         // simple return is always "bad" 
350-         ExprKind :: Ret ( inner)  => { 
351-             // check if expr return nothing 
352-             let  ret_span = if  inner. is_none ( )  && replacement == RetReplacement :: Empty  { 
353-                 extend_span_to_previous_non_ws ( cx,  peeled_drop_expr. span ) 
354-             }  else  { 
355-                 peeled_drop_expr. span 
356-             } ; 
357- 
358-             let  replacement = if  let  Some ( inner_expr)  = inner { 
359-                 // if desugar of `do yeet`, don't lint 
360-                 if  let  ExprKind :: Call ( path_expr,  [ _] )  = inner_expr. kind 
361-                     && let  ExprKind :: Path ( QPath :: LangItem ( LangItem :: TryTraitFromYeet ,  ..) )  = path_expr. kind 
362-                 { 
363-                     return ; 
364-                 } 
365- 
366-                 let  mut  applicability = Applicability :: MachineApplicable ; 
367-                 let  ( snippet,  _)  = snippet_with_context ( cx,  inner_expr. span ,  ret_span. ctxt ( ) ,  ".." ,  & mut  applicability) ; 
368-                 if  binary_expr_needs_parentheses ( inner_expr)  { 
369-                     RetReplacement :: NeedsPar ( snippet,  applicability) 
370-                 }  else  { 
371-                     RetReplacement :: Expr ( snippet,  applicability) 
372-                 } 
373-             }  else  { 
374-                 match  match_ty_opt { 
375-                     Some ( match_ty)  => { 
376-                         match  match_ty. kind ( )  { 
377-                             // If the code got till here with 
378-                             // tuple not getting detected before it, 
379-                             // then we are sure it's going to be Unit 
380-                             // type 
381-                             ty:: Tuple ( _)  => RetReplacement :: Unit , 
382-                             // We don't want to anything in this case 
383-                             // cause we can't predict what the user would 
384-                             // want here 
385-                             _ => return , 
386-                         } 
387-                     } , 
388-                     None  => replacement, 
389-                 } 
390-             } ; 
391- 
392-             if  inner. is_some_and ( |inner| leaks_droppable_temporary_with_limited_lifetime ( cx,  inner) )  { 
393-                 return ; 
394-             } 
395- 
396-             if  ret_span. from_expansion ( )  || is_from_proc_macro ( cx,  expr)  { 
397-                 return ; 
398-             } 
399- 
400-             // Returns may be used to turn an expression into a statement in rustc's AST. 
401-             // This allows the addition of attributes, like `#[allow]` (See: clippy#9361) 
402-             // `#[expect(clippy::needless_return)]` needs to be handled separately to 
403-             // actually fulfill the expectation (clippy::#12998) 
404-             match  cx. tcx . hir_attrs ( expr. hir_id )  { 
405-                 [ ]  => { } , 
406-                 [ attr]  => { 
407-                     if  matches ! ( Level :: from_attr( attr) ,  Some ( ( Level :: Expect ,  _) ) ) 
408-                         && let  metas = attr. meta_item_list ( ) 
409-                         && let  Some ( lst)  = metas
410-                         && let  [ MetaItemInner :: MetaItem ( meta_item) ,  ..]  = lst. as_slice ( ) 
411-                         && let  [ tool,  lint_name]  = meta_item. path . segments . as_slice ( ) 
412-                         && tool. ident . name  == sym:: clippy
413-                         && matches ! ( 
414-                             lint_name. ident. name, 
415-                             sym:: needless_return | sym:: style | sym:: all | sym:: warnings
416-                         ) 
417-                     { 
418-                         // This is an expectation of the `needless_return` lint 
419-                     }  else  { 
420-                         return ; 
421-                     } 
422-                 } , 
423-                 _ => return , 
424-             } 
425- 
426-             emit_return_lint ( 
427-                 cx, 
428-                 peeled_drop_expr. span , 
429-                 ret_span, 
430-                 semi_spans, 
431-                 & replacement, 
432-                 expr. hir_id , 
433-             ) ; 
434-         } , 
435-         ExprKind :: If ( _,  then,  else_clause_opt)  => { 
436-             check_block_return ( cx,  & then. kind ,  peeled_drop_expr. span ,  semi_spans. clone ( ) ) ; 
437-             if  let  Some ( else_clause)  = else_clause_opt { 
438-                 // The `RetReplacement` won't be used there as `else_clause` will be either a block or 
439-                 // a `if` expression. 
440-                 check_final_expr ( cx,  else_clause,  semi_spans,  RetReplacement :: Empty ,  match_ty_opt) ; 
441-             } 
442-         } , 
443-         // a match expr, check all arms 
444-         // an if/if let expr, check both exprs 
445-         // note, if without else is going to be a type checking error anyways 
446-         // (except for unit type functions) so we don't match it 
447-         ExprKind :: Match ( _,  arms,  MatchSource :: Normal )  => { 
448-             let  match_ty = cx. typeck_results ( ) . expr_ty ( peeled_drop_expr) ; 
449-             for  arm in  * arms { 
450-                 check_final_expr ( cx,  arm. body ,  semi_spans. clone ( ) ,  RetReplacement :: Unit ,  Some ( match_ty) ) ; 
451-             } 
452-         } , 
453-         // if it's a whole block, check it 
454-         other_expr_kind => check_block_return ( cx,  other_expr_kind,  peeled_drop_expr. span ,  semi_spans) , 
455-     } 
456- } 
457- 
458- fn  emit_return_lint ( 
459-     cx :  & LateContext < ' _ > , 
460-     lint_span :  Span , 
461-     ret_span :  Span , 
462-     semi_spans :  Vec < Span > , 
463-     replacement :  & RetReplacement < ' _ > , 
464-     at :  HirId , 
465- )  { 
466-     span_lint_hir_and_then ( 
467-         cx, 
468-         NEEDLESS_RETURN , 
469-         at, 
470-         lint_span, 
471-         "unneeded `return` statement" , 
472-         |diag| { 
473-             let  suggestions = std:: iter:: once ( ( ret_span,  replacement. to_string ( ) ) ) 
474-                 . chain ( semi_spans. into_iter ( ) . map ( |span| ( span,  String :: new ( ) ) ) ) 
475-                 . collect ( ) ; 
476- 
477-             diag. multipart_suggestion_verbose ( replacement. sugg_help ( ) ,  suggestions,  replacement. applicability ( ) ) ; 
478-         } , 
479-     ) ; 
480- } 
481- 
482253fn  last_statement_borrows < ' tcx > ( cx :  & LateContext < ' tcx > ,  expr :  & ' tcx  Expr < ' tcx > )  -> bool  { 
483254    for_each_expr ( cx,  expr,  |e| { 
484255        if  let  Some ( def_id)  = fn_def_id ( cx,  e) 
@@ -498,16 +269,3 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
498269    } ) 
499270    . is_some ( ) 
500271} 
501- 
502- // Go backwards while encountering whitespace and extend the given Span to that point. 
503- fn  extend_span_to_previous_non_ws ( cx :  & LateContext < ' _ > ,  sp :  Span )  -> Span  { 
504-     if  let  Ok ( prev_source)  = cx. sess ( ) . source_map ( ) . span_to_prev_source ( sp)  { 
505-         let  ws = [ b' ' ,  b'\t' ,  b'\n' ] ; 
506-         if  let  Some ( non_ws_pos)  = prev_source. bytes ( ) . rposition ( |c| !ws. contains ( & c) )  { 
507-             let  len = prev_source. len ( )  - non_ws_pos - 1 ; 
508-             return  sp. with_lo ( sp. lo ( )  - BytePos :: from_usize ( len) ) ; 
509-         } 
510-     } 
511- 
512-     sp
513- } 
0 commit comments