@@ -3,6 +3,7 @@ use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as
33use crate :: errors:: { ClosureFnMutLabel , ClosureFnOnceLabel , ClosureKindMismatch } ;
44use crate :: infer:: error_reporting:: { TyCategory , TypeAnnotationNeeded as ErrorCode } ;
55use crate :: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
6+ use crate :: infer:: InferCtxtExt as _;
67use crate :: infer:: { self , InferCtxt } ;
78use crate :: traits:: error_reporting:: infer_ctxt_ext:: InferCtxtExt ;
89use crate :: traits:: error_reporting:: { ambiguity, ambiguity:: Ambiguity :: * } ;
@@ -40,7 +41,7 @@ use rustc_session::config::{DumpSolverProofTree, TraitSolver};
4041use rustc_session:: Limit ;
4142use rustc_span:: def_id:: LOCAL_CRATE ;
4243use rustc_span:: symbol:: sym;
43- use rustc_span:: { ExpnKind , Span , DUMMY_SP } ;
44+ use rustc_span:: { BytePos , ExpnKind , Span , Symbol , DUMMY_SP } ;
4445use std:: borrow:: Cow ;
4546use std:: fmt;
4647use std:: iter;
@@ -106,6 +107,13 @@ pub trait TypeErrCtxtExt<'tcx> {
106107
107108 fn fn_arg_obligation ( & self , obligation : & PredicateObligation < ' tcx > ) -> bool ;
108109
110+ fn try_conversion_context (
111+ & self ,
112+ obligation : & PredicateObligation < ' tcx > ,
113+ trait_ref : ty:: TraitRef < ' tcx > ,
114+ err : & mut Diagnostic ,
115+ ) -> bool ;
116+
109117 fn report_const_param_not_wf (
110118 & self ,
111119 ty : Ty < ' tcx > ,
@@ -509,6 +517,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
509517
510518 let mut err = struct_span_err ! ( self . tcx. sess, span, E0277 , "{}" , err_msg) ;
511519
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+
512525 if is_try_conversion && let Some ( ret_span) = self . return_type_span ( & obligation) {
513526 err. span_label (
514527 ret_span,
@@ -609,8 +622,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
609622
610623 self . suggest_floating_point_literal ( & obligation, & mut err, & trait_ref) ;
611624 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) ;
614626 suggested |= self . suggest_fn_call ( & obligation, & mut err, trait_predicate) ;
615627 let impl_candidates = self . find_similar_impl_candidates ( trait_predicate) ;
616628 suggested = if let & [ cand] = & impl_candidates[ ..] {
@@ -982,6 +994,223 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
982994 false
983995 }
984996
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+
9851214 fn report_const_param_not_wf (
9861215 & self ,
9871216 ty : Ty < ' tcx > ,
0 commit comments