8
8
// option. This file may not be copied, modified, or distributed
9
9
// except according to those terms.
10
10
11
- use hir:: { self , Local , Pat , Body , HirId } ;
12
- use hir:: intravisit:: { self , Visitor , NestedVisitorMap } ;
11
+ use hir:: { self , HirId } ;
13
12
use infer:: InferCtxt ;
14
13
use infer:: type_variable:: TypeVariableOrigin ;
14
+ use traits;
15
15
use ty:: { self , Ty , Infer , TyVar } ;
16
16
use syntax:: source_map:: CompilerDesugaringKind ;
17
- use syntax_pos:: Span ;
18
17
use errors:: DiagnosticBuilder ;
19
18
20
- struct FindLocalByTypeVisitor < ' a , ' gcx : ' a + ' tcx , ' tcx : ' a > {
21
- infcx : & ' a InferCtxt < ' a , ' gcx , ' tcx > ,
22
- target_ty : & ' a Ty < ' tcx > ,
23
- hir_map : & ' a hir:: map:: Map < ' gcx > ,
24
- found_local_pattern : Option < & ' gcx Pat > ,
25
- found_arg_pattern : Option < & ' gcx Pat > ,
26
- }
27
-
28
- impl < ' a , ' gcx , ' tcx > FindLocalByTypeVisitor < ' a , ' gcx , ' tcx > {
29
- fn node_matches_type ( & mut self , node_id : HirId ) -> bool {
30
- let ty_opt = self . infcx . in_progress_tables . and_then ( |tables| {
31
- tables. borrow ( ) . node_id_to_type_opt ( node_id)
32
- } ) ;
33
- match ty_opt {
34
- Some ( ty) => {
35
- let ty = self . infcx . resolve_type_vars_if_possible ( & ty) ;
36
- ty. walk ( ) . any ( |inner_ty| {
37
- inner_ty == * self . target_ty || match ( & inner_ty. sty , & self . target_ty . sty ) {
38
- ( & Infer ( TyVar ( a_vid) ) , & Infer ( TyVar ( b_vid) ) ) => {
39
- self . infcx
40
- . type_variables
41
- . borrow_mut ( )
42
- . sub_unified ( a_vid, b_vid)
43
- }
44
- _ => false ,
45
- }
46
- } )
47
- }
48
- None => false ,
49
- }
50
- }
51
- }
52
-
53
- impl < ' a , ' gcx , ' tcx > Visitor < ' gcx > for FindLocalByTypeVisitor < ' a , ' gcx , ' tcx > {
54
- fn nested_visit_map < ' this > ( & ' this mut self ) -> NestedVisitorMap < ' this , ' gcx > {
55
- NestedVisitorMap :: OnlyBodies ( & self . hir_map )
56
- }
57
-
58
- fn visit_local ( & mut self , local : & ' gcx Local ) {
59
- if self . found_local_pattern . is_none ( ) && self . node_matches_type ( local. hir_id ) {
60
- self . found_local_pattern = Some ( & * local. pat ) ;
61
- }
62
- intravisit:: walk_local ( self , local) ;
63
- }
64
-
65
- fn visit_body ( & mut self , body : & ' gcx Body ) {
66
- for argument in & body. arguments {
67
- if self . found_arg_pattern . is_none ( ) && self . node_matches_type ( argument. hir_id ) {
68
- self . found_arg_pattern = Some ( & * argument. pat ) ;
69
- }
70
- }
71
- intravisit:: walk_body ( self , body) ;
72
- }
73
- }
74
-
75
-
76
19
impl < ' a , ' gcx , ' tcx > InferCtxt < ' a , ' gcx , ' tcx > {
77
20
pub fn extract_type_name ( & self , ty : & ' a Ty < ' tcx > ) -> String {
78
21
if let ty:: Infer ( ty:: TyVar ( ty_vid) ) = ( * ty) . sty {
@@ -89,38 +32,85 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
89
32
}
90
33
91
34
pub fn need_type_info_err ( & self ,
92
- body_id : Option < hir:: BodyId > ,
93
- span : Span ,
94
- ty : Ty < ' tcx > )
95
- -> DiagnosticBuilder < ' gcx > {
35
+ cause : & traits:: ObligationCause < ' tcx > ,
36
+ ty : Ty < ' tcx > )
37
+ -> DiagnosticBuilder < ' gcx > {
96
38
let ty = self . resolve_type_vars_if_possible ( & ty) ;
97
39
let name = self . extract_type_name ( & ty) ;
98
40
99
- let mut err_span = span;
100
41
let mut labels = vec ! [ (
101
- span,
42
+ cause . span,
102
43
if & name == "_" {
103
44
"cannot infer type" . to_string( )
104
45
} else {
105
46
format!( "cannot infer type for `{}`" , name)
106
47
} ,
107
48
) ] ;
49
+ let mut span = cause. span ;
108
50
109
- let mut local_visitor = FindLocalByTypeVisitor {
110
- infcx : & self ,
111
- target_ty : & ty,
112
- hir_map : & self . tcx . hir ,
113
- found_local_pattern : None ,
114
- found_arg_pattern : None ,
115
- } ;
116
-
117
- if let Some ( body_id) = body_id {
118
- let expr = self . tcx . hir . expect_expr ( body_id. node_id ) ;
119
- local_visitor. visit_expr ( expr) ;
51
+ // NB. Lower values are more preferred.
52
+ #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
53
+ enum LocalKind {
54
+ ClosureArg ,
55
+ Let ,
120
56
}
121
57
122
- if let Some ( pattern) = local_visitor. found_arg_pattern {
123
- err_span = pattern. span ;
58
+ let found_local = self . in_progress_tables . and_then ( |tables| {
59
+ let tables = tables. borrow ( ) ;
60
+ let local_id_root = tables. local_id_root ?;
61
+ assert ! ( local_id_root. is_local( ) ) ;
62
+
63
+ tables. node_types ( ) . iter ( ) . filter_map ( |( & local_id, & node_ty) | {
64
+ let node_id = self . tcx . hir . hir_to_node_id ( HirId {
65
+ owner : local_id_root. index ,
66
+ local_id,
67
+ } ) ;
68
+
69
+ let ( kind, pattern) = match self . tcx . hir . find ( node_id) {
70
+ Some ( hir:: Node :: Local ( local) ) => {
71
+ ( LocalKind :: Let , & * local. pat )
72
+ }
73
+
74
+ Some ( hir:: Node :: Binding ( pat) ) |
75
+ Some ( hir:: Node :: Pat ( pat) ) => {
76
+ let parent_id = self . tcx . hir . get_parent_node ( node_id) ;
77
+ match self . tcx . hir . find ( parent_id) {
78
+ Some ( hir:: Node :: Expr ( e) ) => {
79
+ match e. node {
80
+ hir:: ExprKind :: Closure ( ..) => { }
81
+ _ => return None ,
82
+ }
83
+ }
84
+ _ => return None ,
85
+ }
86
+
87
+ ( LocalKind :: ClosureArg , pat)
88
+ }
89
+
90
+ _ => return None
91
+ } ;
92
+
93
+ let node_ty = self . resolve_type_vars_if_possible ( & node_ty) ;
94
+ let matches_type = node_ty. walk ( ) . any ( |inner_ty| {
95
+ inner_ty == ty || match ( & inner_ty. sty , & ty. sty ) {
96
+ ( & Infer ( TyVar ( a_vid) ) , & Infer ( TyVar ( b_vid) ) ) => {
97
+ self . type_variables
98
+ . borrow_mut ( )
99
+ . sub_unified ( a_vid, b_vid)
100
+ }
101
+ _ => false ,
102
+ }
103
+ } ) ;
104
+ if !matches_type {
105
+ return None ;
106
+ }
107
+
108
+ Some ( ( kind, pattern) )
109
+ } ) . min_by_key ( |& ( kind, pattern) | ( kind, pattern. hir_id . local_id ) )
110
+ } ) ;
111
+
112
+ if let Some ( ( LocalKind :: ClosureArg , pattern) ) = found_local {
113
+ span = pattern. span ;
124
114
// We don't want to show the default label for closures.
125
115
//
126
116
// So, before clearing, the output would look something like this:
@@ -139,7 +129,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
139
129
labels. clear ( ) ;
140
130
labels. push (
141
131
( pattern. span , "consider giving this closure parameter a type" . to_string ( ) ) ) ;
142
- } else if let Some ( pattern) = local_visitor . found_local_pattern {
132
+ } else if let Some ( ( LocalKind :: Let , pattern) ) = found_local {
143
133
if let Some ( simple_ident) = pattern. simple_ident ( ) {
144
134
match pattern. span . compiler_desugaring_kind ( ) {
145
135
None => labels. push ( ( pattern. span ,
@@ -155,10 +145,22 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
155
145
}
156
146
}
157
147
158
- let mut err = struct_span_err ! ( self . tcx. sess,
159
- err_span,
160
- E0282 ,
161
- "type annotations needed" ) ;
148
+ let lint = self . get_lint_from_cause_code ( & cause. code ) ;
149
+ macro_rules! struct_span_err_or_lint {
150
+ ( $code: ident, $( $message: tt) * ) => {
151
+ match lint {
152
+ Some ( ( lint, id) ) => {
153
+ let message = format!( $( $message) * ) ;
154
+ self . tcx. struct_span_lint_node( lint, id, span, & message)
155
+ }
156
+ None => {
157
+ struct_span_err!( self . tcx. sess, span, $code, $( $message) * )
158
+ }
159
+ }
160
+ }
161
+ }
162
+
163
+ let mut err = struct_span_err_or_lint ! ( E0282 , "type annotations needed" ) ;
162
164
163
165
for ( target_span, label_message) in labels {
164
166
err. span_label ( target_span, label_message) ;
0 commit comments