@@ -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