@@ -3,6 +3,7 @@ use crate::astconv::AstConv;
3
3
use crate :: errors:: { AddReturnTypeSuggestion , ExpectedReturnTypeLabel } ;
4
4
5
5
use rustc_ast:: util:: parser:: ExprPrecedence ;
6
+ use rustc_data_structures:: stable_set:: FxHashSet ;
6
7
use rustc_errors:: { Applicability , Diagnostic , MultiSpan } ;
7
8
use rustc_hir as hir;
8
9
use rustc_hir:: def:: { CtorOf , DefKind } ;
@@ -11,12 +12,12 @@ use rustc_hir::{
11
12
Expr , ExprKind , GenericBound , Node , Path , QPath , Stmt , StmtKind , TyKind , WherePredicate ,
12
13
} ;
13
14
use rustc_infer:: infer:: { self , TyCtxtInferExt } ;
14
- use rustc_infer:: traits;
15
+ use rustc_infer:: traits:: { self , StatementAsExpression } ;
15
16
use 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 } ;
17
18
use rustc_span:: symbol:: sym;
18
19
use rustc_span:: Span ;
19
- use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
20
+ use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt as _ ;
20
21
21
22
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
22
23
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> {
864
865
) ;
865
866
}
866
867
}
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
+ }
867
1020
}
0 commit comments