@@ -3,6 +3,7 @@ use crate::infer::InferCtxt;
3
3
use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder } ;
4
4
use rustc_hir as hir;
5
5
use rustc_hir:: def:: { DefKind , Namespace } ;
6
+ use rustc_hir:: def_id:: DefId ;
6
7
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
7
8
use rustc_hir:: { Body , Expr , ExprKind , FnRetTy , HirId , Local , Pat } ;
8
9
use rustc_middle:: hir:: map:: Map ;
@@ -25,6 +26,7 @@ struct FindHirNodeVisitor<'a, 'tcx> {
25
26
found_closure : Option < & ' tcx Expr < ' tcx > > ,
26
27
found_method_call : Option < & ' tcx Expr < ' tcx > > ,
27
28
found_exact_method_call : Option < & ' tcx Expr < ' tcx > > ,
29
+ found_use_diagnostic : Option < UseDiagnostic < ' tcx > > ,
28
30
}
29
31
30
32
impl < ' a , ' tcx > FindHirNodeVisitor < ' a , ' tcx > {
@@ -39,34 +41,43 @@ impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> {
39
41
found_closure : None ,
40
42
found_method_call : None ,
41
43
found_exact_method_call : None ,
44
+ found_use_diagnostic : None ,
42
45
}
43
46
}
44
47
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 ,
65
67
}
66
- _ => false ,
67
68
}
68
- } )
69
+ _ => false ,
70
+ }
69
71
} )
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) )
70
81
}
71
82
}
72
83
@@ -119,17 +130,91 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> {
119
130
// are handled specially, but instead they should be handled in `annotate_method_call`,
120
131
// which currently doesn't work because this evaluates to `false` for const arguments.
121
132
// 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 ) {
123
134
match expr. kind {
124
135
ExprKind :: Closure ( ..) => self . found_closure = Some ( & expr) ,
125
136
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
+ }
126
150
_ => { }
127
151
}
128
152
}
129
153
intravisit:: walk_expr ( self , expr) ;
130
154
}
131
155
}
132
156
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
+
133
218
/// Suggest giving an appropriate return type to a closure expression.
134
219
fn closure_return_type_suggestion (
135
220
span : Span ,
@@ -139,6 +224,7 @@ fn closure_return_type_suggestion(
139
224
descr : & str ,
140
225
name : & str ,
141
226
ret : & str ,
227
+ use_diag : Option < & UseDiagnostic < ' _ > > ,
142
228
parent_name : Option < String > ,
143
229
parent_descr : Option < & str > ,
144
230
) {
@@ -160,7 +246,15 @@ fn closure_return_type_suggestion(
160
246
) ;
161
247
err. span_label (
162
248
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
+ ) ,
164
258
) ;
165
259
}
166
260
@@ -420,7 +514,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
420
514
421
515
// When `arg_data.name` corresponds to a type argument, show the path of the full type we're
422
516
// trying to infer. In the following example, `ty_msg` contains
423
- // " in `std::result::Result<i32, E>`":
517
+ // " for `std::result::Result<i32, E>`":
424
518
// ```
425
519
// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
426
520
// --> file.rs:L:CC
@@ -438,6 +532,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
438
532
error_code,
439
533
) ;
440
534
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
+
441
542
let suffix = match local_visitor. found_node_ty {
442
543
Some ( ty) if ty. is_closure ( ) => {
443
544
let substs =
@@ -460,6 +561,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
460
561
& arg_data. description ,
461
562
& arg_data. name ,
462
563
& ret,
564
+ use_diag,
463
565
arg_data. parent_name ,
464
566
arg_data. parent_description ,
465
567
) ;
@@ -634,9 +736,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
634
736
err. span_label (
635
737
span,
636
738
InferCtxt :: cannot_infer_msg (
739
+ span,
637
740
kind_str,
638
741
& arg_data. name ,
639
742
& arg_data. description ,
743
+ use_diag,
640
744
arg_data. parent_name ,
641
745
arg_data. parent_description ,
642
746
) ,
@@ -646,6 +750,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
646
750
err
647
751
}
648
752
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
+
649
767
/// If the `FnSig` for the method call can be found and type arguments are identified as
650
768
/// needed, suggest annotating the call, otherwise point out the resulting type of the call.
651
769
fn annotate_method_call (
@@ -711,9 +829,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
711
829
err. span_label (
712
830
span,
713
831
InferCtxt :: cannot_infer_msg (
832
+ span,
714
833
"type" ,
715
834
& data. name ,
716
835
& data. description ,
836
+ None ,
717
837
data. parent_name ,
718
838
data. parent_description ,
719
839
) ,
@@ -722,25 +842,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
722
842
}
723
843
724
844
fn cannot_infer_msg (
845
+ span : Span ,
725
846
kind_str : & str ,
726
847
type_name : & str ,
727
848
descr : & str ,
849
+ use_diag : Option < & UseDiagnostic < ' _ > > ,
728
850
parent_name : Option < String > ,
729
851
parent_descr : Option < & str > ,
730
852
) -> String {
853
+ let use_diag = use_diag. filter ( |d| d. applies_to ( span) ) ;
854
+
731
855
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
+ }
733
861
} else {
734
- let parent_desc = if let Some ( parent_name) = parent_name {
862
+ let extra_descr = if let Some ( parent_name) = parent_name {
735
863
let parent_type_descr = if let Some ( parent_descr) = parent_descr {
736
864
format ! ( " the {}" , parent_descr)
737
865
} else {
738
866
"" . into ( )
739
867
} ;
740
868
741
869
format ! ( " declared on{} `{}`" , parent_type_descr, parent_name)
870
+ } else if let Some ( use_diag) = use_diag {
871
+ format ! ( " in {}" , use_diag. type_descr( ) )
742
872
} else {
743
- "" . to_string ( )
873
+ "" . into ( )
744
874
} ;
745
875
746
876
// FIXME: We really shouldn't be dealing with strings here
@@ -749,7 +879,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
749
879
// For example: "cannot infer type for type parameter `T`"
750
880
format ! (
751
881
"cannot infer {} {} {} `{}`{}" ,
752
- kind_str, preposition, descr, type_name, parent_desc
882
+ kind_str, preposition, descr, type_name, extra_descr
753
883
)
754
884
}
755
885
}
0 commit comments