@@ -3,6 +3,7 @@ use crate::infer::InferCtxt;
33use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
44use rustc_hir as hir;
55use rustc_hir:: def:: { DefKind , Namespace } ;
6+ use rustc_hir:: def_id:: DefId ;
67use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
78use rustc_hir:: { Body , Expr , ExprKind , FnRetTy , HirId , Local , Pat } ;
89use rustc_middle:: hir:: map:: Map ;
@@ -25,6 +26,7 @@ struct FindHirNodeVisitor<'a, 'tcx> {
2526 found_closure : Option < & ' tcx Expr < ' tcx > > ,
2627 found_method_call : Option < & ' tcx Expr < ' tcx > > ,
2728 found_exact_method_call : Option < & ' tcx Expr < ' tcx > > ,
29+ found_use_diagnostic : Option < UseDiagnostic < ' tcx > > ,
2830}
2931
3032impl < ' a , ' tcx > FindHirNodeVisitor < ' a , ' tcx > {
@@ -39,34 +41,43 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
3941 found_closure : None ,
4042 found_method_call : None ,
4143 found_exact_method_call : None ,
44+ found_use_diagnostic : None ,
4245 }
4346 }
4447
45- fn node_ty_contains_target ( & mut self , hir_id : HirId ) -> Option < Ty < ' tcx > > {
46- self . infcx
47- . in_progress_typeck_results
48- . and_then ( |typeck_results| typeck_results. borrow ( ) . node_type_opt ( hir_id) )
49- . map ( |ty| self . infcx . resolve_vars_if_possible ( ty) )
50- . filter ( |ty| {
51- ty. walk ( ) . any ( |inner| {
52- inner == self . target
53- || match ( inner. unpack ( ) , self . target . unpack ( ) ) {
54- ( GenericArgKind :: Type ( inner_ty) , GenericArgKind :: Type ( target_ty) ) => {
55- use ty:: { Infer , TyVar } ;
56- match ( inner_ty. kind ( ) , target_ty. kind ( ) ) {
57- ( & Infer ( TyVar ( a_vid) ) , & Infer ( TyVar ( b_vid) ) ) => self
58- . infcx
59- . inner
60- . borrow_mut ( )
61- . type_variables ( )
62- . sub_unified ( a_vid, b_vid) ,
63- _ => false ,
64- }
48+ fn node_type_opt ( & self , hir_id : HirId ) -> Option < Ty < ' tcx > > {
49+ self . infcx . in_progress_typeck_results ?. borrow ( ) . node_type_opt ( hir_id)
50+ }
51+
52+ fn node_ty_contains_target ( & self , hir_id : HirId ) -> Option < Ty < ' tcx > > {
53+ self . node_type_opt ( hir_id) . map ( |ty| self . infcx . resolve_vars_if_possible ( ty) ) . filter ( |ty| {
54+ ty. walk ( ) . any ( |inner| {
55+ inner == self . target
56+ || match ( inner. unpack ( ) , self . target . unpack ( ) ) {
57+ ( GenericArgKind :: Type ( inner_ty) , GenericArgKind :: Type ( target_ty) ) => {
58+ use ty:: { Infer , TyVar } ;
59+ match ( inner_ty. kind ( ) , target_ty. kind ( ) ) {
60+ ( & Infer ( TyVar ( a_vid) ) , & Infer ( TyVar ( b_vid) ) ) => self
61+ . infcx
62+ . inner
63+ . borrow_mut ( )
64+ . type_variables ( )
65+ . sub_unified ( a_vid, b_vid) ,
66+ _ => false ,
6567 }
66- _ => false ,
6768 }
68- } )
69+ _ => false ,
70+ }
6971 } )
72+ } )
73+ }
74+
75+ /// Determine whether the expression, assumed to be the callee within a `Call`,
76+ /// corresponds to the `From::from` emitted in desugaring of the `?` operator.
77+ fn is_try_conversion ( & self , callee : & Expr < ' tcx > ) -> bool {
78+ self . infcx
79+ . trait_def_from_hir_fn ( callee. hir_id )
80+ . map_or ( false , |def_id| self . infcx . is_try_conversion ( callee. span , def_id) )
7081 }
7182}
7283
@@ -119,17 +130,91 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
119130 // are handled specially, but instead they should be handled in `annotate_method_call`,
120131 // which currently doesn't work because this evaluates to `false` for const arguments.
121132 // See https://github.com/rust-lang/rust/pull/77758 for more details.
122- if self . node_ty_contains_target ( expr. hir_id ) . is_some ( ) {
133+ if let Some ( ty ) = self . node_ty_contains_target ( expr. hir_id ) {
123134 match expr. kind {
124135 ExprKind :: Closure ( ..) => self . found_closure = Some ( & expr) ,
125136 ExprKind :: MethodCall ( ..) => self . found_method_call = Some ( & expr) ,
137+
138+ // If the given expression falls within the target span and is a
139+ // `From::from(e)` call emitted during desugaring of the `?` operator,
140+ // extract the types inferred before and after the call
141+ ExprKind :: Call ( callee, [ arg] )
142+ if self . target_span . contains ( expr. span )
143+ && self . found_use_diagnostic . is_none ( )
144+ && self . is_try_conversion ( callee) =>
145+ {
146+ self . found_use_diagnostic = self . node_type_opt ( arg. hir_id ) . map ( |pre_ty| {
147+ UseDiagnostic :: TryConversion { pre_ty, post_ty : ty, span : callee. span }
148+ } ) ;
149+ }
126150 _ => { }
127151 }
128152 }
129153 intravisit:: walk_expr ( self , expr) ;
130154 }
131155}
132156
157+ /// An observation about the use site of a type to be emitted as an additional
158+ /// note in an inference failure error.
159+ enum UseDiagnostic < ' tcx > {
160+ /// Records the types inferred before and after `From::from` is called on the
161+ /// error value within the desugaring of the `?` operator.
162+ TryConversion { pre_ty : Ty < ' tcx > , post_ty : Ty < ' tcx > , span : Span } ,
163+ }
164+
165+ impl UseDiagnostic < ' _ > {
166+ /// Return a descriptor of the value at the use site
167+ fn descr ( & self ) -> & ' static str {
168+ match self {
169+ Self :: TryConversion { .. } => "`?` error" ,
170+ }
171+ }
172+
173+ /// Return a descriptor of the type at the use site
174+ fn type_descr ( & self ) -> & ' static str {
175+ match self {
176+ Self :: TryConversion { .. } => "`?` error type" ,
177+ }
178+ }
179+
180+ fn applies_to ( & self , span : Span ) -> bool {
181+ match * self {
182+ // In some cases the span for an inference failure due to try
183+ // conversion contains the antecedent expression as well as the `?`
184+ Self :: TryConversion { span : s, .. } => span. contains ( s) && span. hi ( ) == s. hi ( ) ,
185+ }
186+ }
187+
188+ fn attach_note ( & self , err : & mut DiagnosticBuilder < ' _ > ) {
189+ match * self {
190+ Self :: TryConversion { pre_ty, post_ty, .. } => {
191+ let pre_ty = pre_ty. to_string ( ) ;
192+ let post_ty = post_ty. to_string ( ) ;
193+
194+ let intro = "the `?` operation implicitly converts the error value" ;
195+
196+ let msg = match ( pre_ty. as_str ( ) , post_ty. as_str ( ) ) {
197+ ( "_" , "_" ) => format ! ( "{} using the `From` trait" , intro) ,
198+ ( _, "_" ) => {
199+ format ! ( "{} into a type implementing `From<{}>`" , intro, pre_ty)
200+ }
201+ ( "_" , _) => {
202+ format ! ( "{} into `{}` using the `From` trait" , intro, post_ty)
203+ }
204+ ( _, _) => {
205+ format ! (
206+ "{} into `{}` using its implementation of `From<{}>`" ,
207+ intro, post_ty, pre_ty
208+ )
209+ }
210+ } ;
211+
212+ err. note ( & msg) ;
213+ }
214+ }
215+ }
216+ }
217+
133218/// Suggest giving an appropriate return type to a closure expression.
134219fn closure_return_type_suggestion (
135220 span : Span ,
@@ -139,6 +224,7 @@ fn closure_return_type_suggestion(
139224 descr : & str ,
140225 name : & str ,
141226 ret : & str ,
227+ use_diag : Option < & UseDiagnostic < ' _ > > ,
142228 parent_name : Option < String > ,
143229 parent_descr : Option < & str > ,
144230) {
@@ -160,7 +246,15 @@ fn closure_return_type_suggestion(
160246 ) ;
161247 err. span_label (
162248 span,
163- InferCtxt :: cannot_infer_msg ( "type" , & name, & descr, parent_name, parent_descr) ,
249+ InferCtxt :: cannot_infer_msg (
250+ span,
251+ "type" ,
252+ & name,
253+ & descr,
254+ use_diag,
255+ parent_name,
256+ parent_descr,
257+ ) ,
164258 ) ;
165259}
166260
@@ -420,7 +514,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
420514
421515 // When `arg_data.name` corresponds to a type argument, show the path of the full type we're
422516 // trying to infer. In the following example, `ty_msg` contains
423- // " in `std::result::Result<i32, E>`":
517+ // " for `std::result::Result<i32, E>`":
424518 // ```
425519 // error[E0282]: type annotations needed for `std::result::Result<i32, E>`
426520 // --> file.rs:L:CC
@@ -438,6 +532,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
438532 error_code,
439533 ) ;
440534
535+ let use_diag = local_visitor. found_use_diagnostic . as_ref ( ) ;
536+ if let Some ( use_diag) = use_diag {
537+ if use_diag. applies_to ( err_span) {
538+ use_diag. attach_note ( & mut err) ;
539+ }
540+ }
541+
441542 let suffix = match local_visitor. found_node_ty {
442543 Some ( ty) if ty. is_closure ( ) => {
443544 let substs =
@@ -460,6 +561,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
460561 & arg_data. description ,
461562 & arg_data. name ,
462563 & ret,
564+ use_diag,
463565 arg_data. parent_name ,
464566 arg_data. parent_description ,
465567 ) ;
@@ -634,9 +736,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
634736 err. span_label (
635737 span,
636738 InferCtxt :: cannot_infer_msg (
739+ span,
637740 kind_str,
638741 & arg_data. name ,
639742 & arg_data. description ,
743+ use_diag,
640744 arg_data. parent_name ,
641745 arg_data. parent_description ,
642746 ) ,
@@ -646,6 +750,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
646750 err
647751 }
648752
753+ fn trait_def_from_hir_fn ( & self , hir_id : hir:: HirId ) -> Option < DefId > {
754+ // The DefId will be the method's trait item ID unless this is an inherent impl
755+ if let Some ( ( DefKind :: AssocFn , def_id) ) =
756+ self . in_progress_typeck_results ?. borrow ( ) . type_dependent_def ( hir_id)
757+ {
758+ return self
759+ . tcx
760+ . parent ( def_id)
761+ . filter ( |& parent_def_id| self . tcx . is_trait ( parent_def_id) ) ;
762+ }
763+
764+ None
765+ }
766+
649767 /// If the `FnSig` for the method call can be found and type arguments are identified as
650768 /// needed, suggest annotating the call, otherwise point out the resulting type of the call.
651769 fn annotate_method_call (
@@ -711,9 +829,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
711829 err. span_label (
712830 span,
713831 InferCtxt :: cannot_infer_msg (
832+ span,
714833 "type" ,
715834 & data. name ,
716835 & data. description ,
836+ None ,
717837 data. parent_name ,
718838 data. parent_description ,
719839 ) ,
@@ -722,25 +842,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
722842 }
723843
724844 fn cannot_infer_msg (
845+ span : Span ,
725846 kind_str : & str ,
726847 type_name : & str ,
727848 descr : & str ,
849+ use_diag : Option < & UseDiagnostic < ' _ > > ,
728850 parent_name : Option < String > ,
729851 parent_descr : Option < & str > ,
730852 ) -> String {
853+ let use_diag = use_diag. filter ( |d| d. applies_to ( span) ) ;
854+
731855 if type_name == "_" {
732- format ! ( "cannot infer {}" , kind_str)
856+ if let Some ( use_diag) = use_diag {
857+ format ! ( "cannot infer {} of {}" , kind_str, use_diag. descr( ) )
858+ } else {
859+ format ! ( "cannot infer {}" , kind_str)
860+ }
733861 } else {
734- let parent_desc = if let Some ( parent_name) = parent_name {
862+ let extra_descr = if let Some ( parent_name) = parent_name {
735863 let parent_type_descr = if let Some ( parent_descr) = parent_descr {
736864 format ! ( " the {}" , parent_descr)
737865 } else {
738866 "" . into ( )
739867 } ;
740868
741869 format ! ( " declared on{} `{}`" , parent_type_descr, parent_name)
870+ } else if let Some ( use_diag) = use_diag {
871+ format ! ( " in {}" , use_diag. type_descr( ) )
742872 } else {
743- "" . to_string ( )
873+ "" . into ( )
744874 } ;
745875
746876 // FIXME: We really shouldn't be dealing with strings here
@@ -749,7 +879,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
749879 // For example: "cannot infer type for type parameter `T`"
750880 format ! (
751881 "cannot infer {} {} {} `{}`{}" ,
752- kind_str, preposition, descr, type_name, parent_desc
882+ kind_str, preposition, descr, type_name, extra_descr
753883 )
754884 }
755885 }
0 commit comments