@@ -3,6 +3,7 @@ use crate::astconv::AstConv;
33use  crate :: errors:: { AddReturnTypeSuggestion ,  ExpectedReturnTypeLabel } ; 
44
55use  rustc_ast:: util:: parser:: ExprPrecedence ; 
6+ use  rustc_data_structures:: stable_set:: FxHashSet ; 
67use  rustc_errors:: { Applicability ,  Diagnostic ,  MultiSpan } ; 
78use  rustc_hir as  hir; 
89use  rustc_hir:: def:: { CtorOf ,  DefKind } ; 
@@ -11,12 +12,12 @@ use rustc_hir::{
1112    Expr ,  ExprKind ,  GenericBound ,  Node ,  Path ,  QPath ,  Stmt ,  StmtKind ,  TyKind ,  WherePredicate , 
1213} ; 
1314use  rustc_infer:: infer:: { self ,  TyCtxtInferExt } ; 
14- use  rustc_infer:: traits; 
15+ use  rustc_infer:: traits:: { self ,   StatementAsExpression } ; 
1516use  rustc_middle:: lint:: in_external_macro; 
16- use  rustc_middle:: ty:: { self ,  Binder ,  IsSuggestable ,  Subst ,  ToPredicate ,  Ty } ; 
17+ use  rustc_middle:: ty:: { self ,  Binder ,  IsSuggestable ,  Subst ,  ToPredicate ,  Ty ,   TypeVisitable } ; 
1718use  rustc_span:: symbol:: sym; 
1819use  rustc_span:: Span ; 
19- use  rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ; 
20+ use  rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt   as  _ ; 
2021
2122impl < ' a ,  ' tcx >  FnCtxt < ' a ,  ' tcx >  { 
2223    pub ( in  super :: super )  fn  suggest_semicolon_at_end ( & self ,  span :  Span ,  err :  & mut  Diagnostic )  { 
@@ -864,4 +865,156 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
864865            ) ; 
865866        } 
866867    } 
868+ 
869+     /// A common error is to add an extra semicolon: 
870+ /// 
871+ /// ```compile_fail,E0308 
872+ /// fn foo() -> usize { 
873+ ///     22; 
874+ /// } 
875+ /// ``` 
876+ /// 
877+ /// This routine checks if the final statement in a block is an 
878+ /// expression with an explicit semicolon whose type is compatible 
879+ /// with `expected_ty`. If so, it suggests removing the semicolon. 
880+ pub ( crate )  fn  consider_removing_semicolon ( 
881+         & self , 
882+         blk :  & ' tcx  hir:: Block < ' tcx > , 
883+         expected_ty :  Ty < ' tcx > , 
884+         err :  & mut  Diagnostic , 
885+     )  -> bool  { 
886+         if  let  Some ( ( span_semi,  boxed) )  = self . could_remove_semicolon ( blk,  expected_ty)  { 
887+             if  let  StatementAsExpression :: NeedsBoxing  = boxed { 
888+                 err. span_suggestion_verbose ( 
889+                     span_semi, 
890+                     "consider removing this semicolon and boxing the expression" , 
891+                     "" , 
892+                     Applicability :: HasPlaceholders , 
893+                 ) ; 
894+             }  else  { 
895+                 err. span_suggestion_short ( 
896+                     span_semi, 
897+                     "remove this semicolon" , 
898+                     "" , 
899+                     Applicability :: MachineApplicable , 
900+                 ) ; 
901+             } 
902+             true 
903+         }  else  { 
904+             false 
905+         } 
906+     } 
907+ 
908+     pub ( crate )  fn  consider_returning_binding ( 
909+         & self , 
910+         blk :  & ' tcx  hir:: Block < ' tcx > , 
911+         expected_ty :  Ty < ' tcx > , 
912+         err :  & mut  Diagnostic , 
913+     )  { 
914+         let  mut  shadowed = FxHashSet :: default ( ) ; 
915+         let  mut  candidate_idents = vec ! [ ] ; 
916+         let  mut  find_compatible_candidates = |pat :  & hir:: Pat < ' _ > | { 
917+             if  let  hir:: PatKind :: Binding ( _,  hir_id,  ident,  _)  = & pat. kind 
918+                 && let  Some ( pat_ty)  = self . typeck_results . borrow ( ) . node_type_opt ( * hir_id) 
919+             { 
920+                 let  pat_ty = self . resolve_vars_if_possible ( pat_ty) ; 
921+                 if  self . can_coerce ( pat_ty,  expected_ty) 
922+                     && !( pat_ty,  expected_ty) . references_error ( ) 
923+                     && shadowed. insert ( ident. name ) 
924+                 { 
925+                     candidate_idents. push ( ( * ident,  pat_ty) ) ; 
926+                 } 
927+             } 
928+             true 
929+         } ; 
930+ 
931+         let  hir = self . tcx . hir ( ) ; 
932+         for  stmt in  blk. stmts . iter ( ) . rev ( )  { 
933+             let  StmtKind :: Local ( local)  = & stmt. kind  else  {  continue ;  } ; 
934+             local. pat . walk ( & mut  find_compatible_candidates) ; 
935+         } 
936+         match  hir. find ( hir. get_parent_node ( blk. hir_id ) )  { 
937+             Some ( hir:: Node :: Expr ( hir:: Expr  {  hir_id,  .. } ) )  => { 
938+                 match  hir. find ( hir. get_parent_node ( * hir_id) )  { 
939+                     Some ( hir:: Node :: Arm ( hir:: Arm  {  pat,  .. } ) )  => { 
940+                         pat. walk ( & mut  find_compatible_candidates) ; 
941+                     } 
942+                     Some ( 
943+                         hir:: Node :: Item ( hir:: Item  {  kind :  hir:: ItemKind :: Fn ( _,  _,  body) ,  .. } ) 
944+                         | hir:: Node :: ImplItem ( hir:: ImplItem  { 
945+                             kind :  hir:: ImplItemKind :: Fn ( _,  body) , 
946+                             ..
947+                         } ) 
948+                         | hir:: Node :: TraitItem ( hir:: TraitItem  { 
949+                             kind :  hir:: TraitItemKind :: Fn ( _,  hir:: TraitFn :: Provided ( body) ) , 
950+                             ..
951+                         } ) 
952+                         | hir:: Node :: Expr ( hir:: Expr  { 
953+                             kind :  hir:: ExprKind :: Closure ( hir:: Closure  {  body,  .. } ) , 
954+                             ..
955+                         } ) , 
956+                     )  => { 
957+                         for  param in  hir. body ( * body) . params  { 
958+                             param. pat . walk ( & mut  find_compatible_candidates) ; 
959+                         } 
960+                     } 
961+                     Some ( hir:: Node :: Expr ( hir:: Expr  { 
962+                         kind : 
963+                             hir:: ExprKind :: If ( 
964+                                 hir:: Expr  {  kind :  hir:: ExprKind :: Let ( let_) ,  .. } , 
965+                                 then_block, 
966+                                 _, 
967+                             ) , 
968+                         ..
969+                     } ) )  if  then_block. hir_id  == * hir_id => { 
970+                         let_. pat . walk ( & mut  find_compatible_candidates) ; 
971+                     } 
972+                     _ => { } 
973+                 } 
974+             } 
975+             _ => { } 
976+         } 
977+ 
978+         match  & candidate_idents[ ..]  { 
979+             [ ( ident,  _ty) ]  => { 
980+                 let  sm = self . tcx . sess . source_map ( ) ; 
981+                 if  let  Some ( stmt)  = blk. stmts . last ( )  { 
982+                     let  stmt_span = sm. stmt_span ( stmt. span ,  blk. span ) ; 
983+                     let  sugg = if  sm. is_multiline ( blk. span ) 
984+                         && let  Some ( spacing)  = sm. indentation_before ( stmt_span) 
985+                     { 
986+                         format ! ( "\n {spacing}{ident}" ) 
987+                     }  else  { 
988+                         format ! ( " {ident}" ) 
989+                     } ; 
990+                     err. span_suggestion_verbose ( 
991+                         stmt_span. shrink_to_hi ( ) , 
992+                         format ! ( "consider returning the local binding `{ident}`" ) , 
993+                         sugg, 
994+                         Applicability :: MachineApplicable , 
995+                     ) ; 
996+                 }  else  { 
997+                     let  sugg = if  sm. is_multiline ( blk. span ) 
998+                         && let  Some ( spacing)  = sm. indentation_before ( blk. span . shrink_to_lo ( ) ) 
999+                     { 
1000+                         format ! ( "\n {spacing}    {ident}\n {spacing}" ) 
1001+                     }  else  { 
1002+                         format ! ( " {ident} " ) 
1003+                     } ; 
1004+                     let  left_span = sm. span_through_char ( blk. span ,  '{' ) . shrink_to_hi ( ) ; 
1005+                     err. span_suggestion_verbose ( 
1006+                         sm. span_extend_while ( left_span,  |c| c. is_whitespace ( ) ) . unwrap_or ( left_span) , 
1007+                         format ! ( "consider returning the local binding `{ident}`" ) , 
1008+                         sugg, 
1009+                         Applicability :: MachineApplicable , 
1010+                     ) ; 
1011+                 } 
1012+             } 
1013+             values if  ( 1 ..3 ) . contains ( & values. len ( ) )  => { 
1014+                 let  spans = values. iter ( ) . map ( |( ident,  _) | ident. span ) . collect :: < Vec < _ > > ( ) ; 
1015+                 err. span_note ( spans,  "consider returning one of these bindings" ) ; 
1016+             } 
1017+             _ => { } 
1018+         } 
1019+     } 
8671020} 
0 commit comments