@@ -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 , 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
+ ) ;
116
+
109
117
fn report_const_param_not_wf (
110
118
& self ,
111
119
ty : Ty < ' tcx > ,
@@ -509,6 +517,10 @@ 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
+ if is_try_conversion {
521
+ self . try_conversion_context ( & obligation, trait_ref. skip_binder ( ) , & mut err) ;
522
+ }
523
+
512
524
if is_try_conversion && let Some ( ret_span) = self . return_type_span ( & obligation) {
513
525
err. span_label (
514
526
ret_span,
@@ -982,6 +994,136 @@ 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
+ ) {
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 . 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 ,
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 ;
1036
+ } ;
1037
+ let Some ( typeck) = & self . typeck_results else {
1038
+ return ;
1039
+ } ;
1040
+ let Some ( ( ObligationCauseCode :: QuestionMark , Some ( y) ) ) = obligation. cause . code ( ) . parent ( )
1041
+ else {
1042
+ return ;
1043
+ } ;
1044
+ if !self . tcx . is_diagnostic_item ( sym:: FromResidual , y. def_id ( ) ) {
1045
+ return ;
1046
+ }
1047
+ let self_ty = trait_ref. self_ty ( ) ;
1048
+
1049
+ let mut prev_ty = self . resolve_vars_if_possible (
1050
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1051
+ ) ;
1052
+ let mut annotate_expr = |span : Span , prev_ty : Ty < ' tcx > , self_ty : Ty < ' tcx > | -> bool {
1053
+ // We always look at the `E` type, because that's the only one affected by `?`. If the
1054
+ // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole
1055
+ // expression, after the `?` has "unwrapped" the `T`.
1056
+ let ty:: Adt ( def, args) = prev_ty. kind ( ) else {
1057
+ return false ;
1058
+ } ;
1059
+ let Some ( arg) = args. get ( 1 ) else {
1060
+ return false ;
1061
+ } ;
1062
+ if !self . tcx . is_diagnostic_item ( sym:: Result , def. did ( ) ) {
1063
+ return false ;
1064
+ }
1065
+ let can = if self
1066
+ . infcx
1067
+ . type_implements_trait (
1068
+ self . tcx . get_diagnostic_item ( sym:: From ) . unwrap ( ) ,
1069
+ [ self_ty. into ( ) , * arg] ,
1070
+ obligation. param_env ,
1071
+ )
1072
+ . must_apply_modulo_regions ( )
1073
+ {
1074
+ "can"
1075
+ } else {
1076
+ "can't"
1077
+ } ;
1078
+ err. span_label (
1079
+ span,
1080
+ format ! ( "this {can} be annotated with `?` because it has type `{prev_ty}`" ) ,
1081
+ ) ;
1082
+ true
1083
+ } ;
1084
+
1085
+ // The following logic is simlar to `point_at_chain`, but that's focused on associated types
1086
+ let mut expr = expr;
1087
+ while let hir:: ExprKind :: MethodCall ( _path_segment, rcvr_expr, _args, span) = expr. kind {
1088
+ // Point at every method call in the chain with the `Result` type.
1089
+ // let foo = bar.iter().map(mapper)?;
1090
+ // ------ -----------
1091
+ expr = rcvr_expr;
1092
+ if !annotate_expr ( span, prev_ty, self_ty) {
1093
+ break ;
1094
+ }
1095
+
1096
+ prev_ty = self . resolve_vars_if_possible (
1097
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1098
+ ) ;
1099
+
1100
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1101
+ && let hir:: Path { res : hir:: def:: Res :: Local ( hir_id) , .. } = path
1102
+ && let Some ( hir:: Node :: Pat ( binding) ) = self . tcx . hir ( ) . find ( * hir_id)
1103
+ && let Some ( parent) = self . tcx . hir ( ) . find_parent ( binding. hir_id )
1104
+ {
1105
+ // We've reached the root of the method call chain...
1106
+ if let hir:: Node :: Local ( local) = parent
1107
+ && let Some ( binding_expr) = local. init
1108
+ {
1109
+ // ...and it is a binding. Get the binding creation and continue the chain.
1110
+ expr = binding_expr;
1111
+ }
1112
+ if let hir:: Node :: Param ( _param) = parent {
1113
+ // ...and it is a an fn argument.
1114
+ break ;
1115
+ }
1116
+ }
1117
+ }
1118
+ // `expr` is now the "root" expression of the method call chain, which can be any
1119
+ // expression kind, like a method call or a path. If this expression is `Result<T, E>` as
1120
+ // well, then we also point at it.
1121
+ prev_ty = self . resolve_vars_if_possible (
1122
+ typeck. expr_ty_adjusted_opt ( expr) . unwrap_or ( Ty :: new_misc_error ( self . tcx ) ) ,
1123
+ ) ;
1124
+ annotate_expr ( expr. span , prev_ty, self_ty) ;
1125
+ }
1126
+
985
1127
fn report_const_param_not_wf (
986
1128
& self ,
987
1129
ty : Ty < ' tcx > ,
0 commit comments