@@ -3,6 +3,7 @@ use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as
3
3
use crate :: errors:: { ClosureFnMutLabel , ClosureFnOnceLabel , ClosureKindMismatch } ;
4
4
use crate :: infer:: error_reporting:: { TyCategory , TypeAnnotationNeeded as ErrorCode } ;
5
5
use crate :: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
6
+ use crate :: infer:: InferCtxtExt as _;
6
7
use crate :: infer:: { self , InferCtxt } ;
7
8
use crate :: traits:: error_reporting:: infer_ctxt_ext:: InferCtxtExt ;
8
9
use crate :: traits:: error_reporting:: { ambiguity, ambiguity:: Ambiguity :: * } ;
@@ -40,7 +41,7 @@ use rustc_session::config::{DumpSolverProofTree, TraitSolver};
40
41
use rustc_session:: Limit ;
41
42
use rustc_span:: def_id:: LOCAL_CRATE ;
42
43
use rustc_span:: symbol:: sym;
43
- use rustc_span:: { ExpnKind , Span , DUMMY_SP } ;
44
+ use rustc_span:: { BytePos , ExpnKind , Span , Symbol , DUMMY_SP } ;
44
45
use std:: borrow:: Cow ;
45
46
use std:: fmt;
46
47
use std:: iter;
@@ -106,6 +107,13 @@ pub trait TypeErrCtxtExt<'tcx> {
106
107
107
108
fn fn_arg_obligation ( & self , obligation : & PredicateObligation < ' tcx > ) -> bool ;
108
109
110
+ fn try_conversion_context (
111
+ & self ,
112
+ obligation : & PredicateObligation < ' tcx > ,
113
+ trait_ref : ty:: TraitRef < ' tcx > ,
114
+ err : & mut Diagnostic ,
115
+ ) -> bool ;
116
+
109
117
fn report_const_param_not_wf (
110
118
& self ,
111
119
ty : Ty < ' tcx > ,
@@ -509,6 +517,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
509
517
510
518
let mut err = struct_span_err ! ( self . tcx. sess, span, E0277 , "{}" , err_msg) ;
511
519
520
+ let mut suggested = false ;
521
+ if is_try_conversion {
522
+ suggested = self . try_conversion_context ( & obligation, trait_ref. skip_binder ( ) , & mut err) ;
523
+ }
524
+
512
525
if is_try_conversion && let Some ( ret_span) = self . return_type_span ( & obligation) {
513
526
err. span_label (
514
527
ret_span,
@@ -609,8 +622,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
609
622
610
623
self . suggest_floating_point_literal ( & obligation, & mut err, & trait_ref) ;
611
624
self . suggest_dereferencing_index ( & obligation, & mut err, trait_predicate) ;
612
- let mut suggested =
613
- self . suggest_dereferences ( & obligation, & mut err, trait_predicate) ;
625
+ suggested |= self . suggest_dereferences ( & obligation, & mut err, trait_predicate) ;
614
626
suggested |= self . suggest_fn_call ( & obligation, & mut err, trait_predicate) ;
615
627
let impl_candidates = self . find_similar_impl_candidates ( trait_predicate) ;
616
628
suggested = if let & [ cand] = & impl_candidates[ ..] {
@@ -982,6 +994,223 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
982
994
false
983
995
}
984
996
997
+ /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
998
+ /// identify thoe method chain sub-expressions that could or could not have been annotated
999
+ /// with `?`.
1000
+ fn try_conversion_context (
1001
+ & self ,
1002
+ obligation : & PredicateObligation < ' tcx > ,
1003
+ trait_ref : ty:: TraitRef < ' tcx > ,
1004
+ err : & mut Diagnostic ,
1005
+ ) -> bool {
1006
+ let span = obligation. cause . span ;
1007
+ struct V < ' v > {
1008
+ search_span : Span ,
1009
+ found : Option < & ' v hir:: Expr < ' v > > ,
1010
+ }
1011
+ impl < ' v > Visitor < ' v > for V < ' v > {
1012
+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
1013
+ if let hir:: ExprKind :: Match ( expr, _arms, hir:: MatchSource :: TryDesugar ( _) ) = ex. kind
1014
+ {
1015
+ if ex. span . with_lo ( ex. span . hi ( ) - BytePos ( 1 ) ) . source_equal ( self . search_span ) {
1016
+ if let hir:: ExprKind :: Call ( _, [ expr, ..] ) = expr. kind {
1017
+ self . found = Some ( expr) ;
1018
+ return ;
1019
+ }
1020
+ }
1021
+ }
1022
+ hir:: intravisit:: walk_expr ( self , ex) ;
1023
+ }
1024
+ }
1025
+ let hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( obligation. cause . body_id ) ;
1026
+ let body_id = match self . tcx . hir ( ) . find ( hir_id) {
1027
+ Some ( hir:: Node :: Item ( hir:: Item { kind : hir:: ItemKind :: Fn ( _, _, body_id) , .. } ) ) => {
1028
+ body_id
1029
+ }
1030
+ _ => return false ,
1031
+ } ;
1032
+ let mut v = V { search_span : span, found : None } ;
1033
+ v. visit_body ( self . tcx . hir ( ) . body ( * body_id) ) ;
1034
+ let Some ( expr) = v. found else {
1035
+ return false ;
1036
+ } ;
1037
+ let Some ( typeck) = & self . typeck_results else {
1038
+ return false ;
1039
+ } ;
1040
+ let Some ( ( ObligationCauseCode :: QuestionMark , Some ( y) ) ) = obligation. cause . code ( ) . parent ( )
1041
+ else {
1042
+ return false ;
1043
+ } ;
1044
+ if !self . tcx . is_diagnostic_item ( sym:: FromResidual , y. def_id ( ) ) {
1045
+ return false ;
1046
+ }
1047
+ let self_ty = trait_ref. self_ty ( ) ;
1048
+ let found_ty = trait_ref. args . get ( 1 ) . and_then ( |a| a. as_type ( ) ) ;
1049
+
1050
+ let mut prev_ty = self . resolve_vars_if_possible (
1051
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1052
+ ) ;
1053
+
1054
+ // We always look at the `E` type, because that's the only one affected by `?`. If the
1055
+ // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole
1056
+ // expression, after the `?` has "unwrapped" the `T`.
1057
+ let get_e_type = |prev_ty : Ty < ' tcx > | -> Option < Ty < ' tcx > > {
1058
+ let ty:: Adt ( def, args) = prev_ty. kind ( ) else {
1059
+ return None ;
1060
+ } ;
1061
+ let Some ( arg) = args. get ( 1 ) else {
1062
+ return None ;
1063
+ } ;
1064
+ if !self . tcx . is_diagnostic_item ( sym:: Result , def. did ( ) ) {
1065
+ return None ;
1066
+ }
1067
+ Some ( arg. as_type ( ) ?)
1068
+ } ;
1069
+
1070
+ let mut suggested = false ;
1071
+ let mut chain = vec ! [ ] ;
1072
+
1073
+ // The following logic is simlar to `point_at_chain`, but that's focused on associated types
1074
+ let mut expr = expr;
1075
+ while let hir:: ExprKind :: MethodCall ( path_segment, rcvr_expr, args, span) = expr. kind {
1076
+ // Point at every method call in the chain with the `Result` type.
1077
+ // let foo = bar.iter().map(mapper)?;
1078
+ // ------ -----------
1079
+ expr = rcvr_expr;
1080
+ chain. push ( ( span, prev_ty) ) ;
1081
+
1082
+ let next_ty = self . resolve_vars_if_possible (
1083
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1084
+ ) ;
1085
+
1086
+ let is_diagnostic_item = |symbol : Symbol , ty : Ty < ' tcx > | {
1087
+ let ty:: Adt ( def, _) = ty. kind ( ) else {
1088
+ return false ;
1089
+ } ;
1090
+ self . tcx . is_diagnostic_item ( symbol, def. did ( ) )
1091
+ } ;
1092
+ // For each method in the chain, see if this is `Result::map_err` or
1093
+ // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect
1094
+ // trailing `;`.
1095
+ if let Some ( ty) = get_e_type ( prev_ty)
1096
+ && let Some ( found_ty) = found_ty
1097
+ // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100%
1098
+ // accurate check, but we are in the wrong stage to do that and looking for
1099
+ // `Result::map_err` by checking the Self type and the path segment is enough.
1100
+ // sym::ok_or_else
1101
+ && (
1102
+ ( // Result::map_err
1103
+ path_segment. ident . name == sym:: map_err
1104
+ && is_diagnostic_item ( sym:: Result , next_ty)
1105
+ ) || ( // Option::ok_or_else
1106
+ path_segment. ident . name == sym:: ok_or_else
1107
+ && is_diagnostic_item ( sym:: Option , next_ty)
1108
+ )
1109
+ )
1110
+ // Found `Result<_, ()>?`
1111
+ && let ty:: Tuple ( tys) = found_ty. kind ( )
1112
+ && tys. is_empty ( )
1113
+ // The current method call returns `Result<_, ()>`
1114
+ && self . can_eq ( obligation. param_env , ty, found_ty)
1115
+ // There's a single argument in the method call and it is a closure
1116
+ && args. len ( ) == 1
1117
+ && let Some ( arg) = args. get ( 0 )
1118
+ && let hir:: ExprKind :: Closure ( closure) = arg. kind
1119
+ // The closure has a block for its body with no tail expression
1120
+ && let body = self . tcx . hir ( ) . body ( closure. body )
1121
+ && let hir:: ExprKind :: Block ( block, _) = body. value . kind
1122
+ && let None = block. expr
1123
+ // The last statement is of a type that can be converted to the return error type
1124
+ && let [ .., stmt] = block. stmts
1125
+ && let hir:: StmtKind :: Semi ( expr) = stmt. kind
1126
+ && let expr_ty = self . resolve_vars_if_possible (
1127
+ typeck. expr_ty_adjusted_opt ( expr)
1128
+ . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1129
+ )
1130
+ && self
1131
+ . infcx
1132
+ . type_implements_trait (
1133
+ self . tcx . get_diagnostic_item ( sym:: From ) . unwrap ( ) ,
1134
+ [ self_ty, expr_ty] ,
1135
+ obligation. param_env ,
1136
+ )
1137
+ . must_apply_modulo_regions ( )
1138
+ {
1139
+ suggested = true ;
1140
+ err. span_suggestion_short (
1141
+ stmt. span . with_lo ( expr. span . hi ( ) ) ,
1142
+ "remove this semicolon" ,
1143
+ String :: new ( ) ,
1144
+ Applicability :: MachineApplicable ,
1145
+ ) ;
1146
+ }
1147
+
1148
+ prev_ty = next_ty;
1149
+
1150
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1151
+ && let hir:: Path { res : hir:: def:: Res :: Local ( hir_id) , .. } = path
1152
+ && let Some ( hir:: Node :: Pat ( binding) ) = self . tcx . hir ( ) . find ( * hir_id)
1153
+ && let Some ( parent) = self . tcx . hir ( ) . find_parent ( binding. hir_id )
1154
+ {
1155
+ // We've reached the root of the method call chain...
1156
+ if let hir:: Node :: Local ( local) = parent
1157
+ && let Some ( binding_expr) = local. init
1158
+ {
1159
+ // ...and it is a binding. Get the binding creation and continue the chain.
1160
+ expr = binding_expr;
1161
+ }
1162
+ if let hir:: Node :: Param ( _param) = parent {
1163
+ // ...and it is a an fn argument.
1164
+ break ;
1165
+ }
1166
+ }
1167
+ }
1168
+ // `expr` is now the "root" expression of the method call chain, which can be any
1169
+ // expression kind, like a method call or a path. If this expression is `Result<T, E>` as
1170
+ // well, then we also point at it.
1171
+ prev_ty = self . resolve_vars_if_possible (
1172
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1173
+ ) ;
1174
+ chain. push ( ( expr. span , prev_ty) ) ;
1175
+
1176
+ let mut prev = None ;
1177
+ for ( span, err_ty) in chain. into_iter ( ) . rev ( ) {
1178
+ let err_ty = get_e_type ( err_ty) ;
1179
+ let err_ty = match ( err_ty, prev) {
1180
+ ( Some ( err_ty) , Some ( prev) ) if !self . can_eq ( obligation. param_env , err_ty, prev) => {
1181
+ err_ty
1182
+ }
1183
+ ( Some ( err_ty) , None ) => err_ty,
1184
+ _ => {
1185
+ prev = err_ty;
1186
+ continue ;
1187
+ }
1188
+ } ;
1189
+ if self
1190
+ . infcx
1191
+ . type_implements_trait (
1192
+ self . tcx . get_diagnostic_item ( sym:: From ) . unwrap ( ) ,
1193
+ [ self_ty, err_ty] ,
1194
+ obligation. param_env ,
1195
+ )
1196
+ . must_apply_modulo_regions ( )
1197
+ {
1198
+ if !suggested {
1199
+ err. span_label ( span, format ! ( "this has type `Result<_, {err_ty}>`" ) ) ;
1200
+ }
1201
+ } else {
1202
+ err. span_label (
1203
+ span,
1204
+ format ! (
1205
+ "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`" ,
1206
+ ) ,
1207
+ ) ;
1208
+ }
1209
+ prev = Some ( err_ty) ;
1210
+ }
1211
+ suggested
1212
+ }
1213
+
985
1214
fn report_const_param_not_wf (
986
1215
& self ,
987
1216
ty : Ty < ' tcx > ,
0 commit comments