@@ -11,23 +11,21 @@ mod incremental;
11
11
12
12
use std:: { collections:: HashMap , env, sync:: Arc } ;
13
13
14
- use base_db:: { fixture:: WithFixture , FileRange , SourceDatabase , SourceDatabaseExt } ;
14
+ use base_db:: { fixture:: WithFixture , FileRange , SourceDatabaseExt } ;
15
15
use expect_test:: Expect ;
16
16
use hir_def:: {
17
17
body:: { Body , BodySourceMap , SyntheticSyntax } ,
18
- child_by_source:: ChildBySource ,
19
18
db:: DefDatabase ,
19
+ expr:: { ExprId , PatId } ,
20
20
item_scope:: ItemScope ,
21
- keys,
22
21
nameres:: DefMap ,
23
22
src:: HasSource ,
24
- AssocItemId , DefWithBodyId , LocalModuleId , Lookup , ModuleDefId ,
23
+ AssocItemId , DefWithBodyId , HasModule , LocalModuleId , Lookup , ModuleDefId ,
25
24
} ;
26
25
use hir_expand:: { db:: AstDatabase , InFile } ;
27
26
use once_cell:: race:: OnceBool ;
28
27
use stdx:: format_to;
29
28
use syntax:: {
30
- algo,
31
29
ast:: { self , AstNode , NameOwner } ,
32
30
SyntaxNode ,
33
31
} ;
@@ -59,51 +57,55 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
59
57
}
60
58
61
59
fn check_types ( ra_fixture : & str ) {
62
- check_types_impl ( ra_fixture, false )
60
+ check_impl ( ra_fixture, false , true , false )
63
61
}
64
62
65
63
fn check_types_source_code ( ra_fixture : & str ) {
66
- check_types_impl ( ra_fixture, true )
67
- }
68
-
69
- fn check_types_impl ( ra_fixture : & str , display_source : bool ) {
70
- let _tracing = setup_tracing ( ) ;
71
- let db = TestDB :: with_files ( ra_fixture) ;
72
- let mut checked_one = false ;
73
- for ( file_id, annotations) in db. extract_annotations ( ) {
74
- for ( range, expected) in annotations {
75
- let ty = type_at_range ( & db, FileRange { file_id, range } ) ;
76
- let actual = if display_source {
77
- let module = db. module_for_file ( file_id) ;
78
- ty. display_source_code ( & db, module) . unwrap ( )
79
- } else {
80
- ty. display_test ( & db) . to_string ( )
81
- } ;
82
- assert_eq ! ( expected, actual) ;
83
- checked_one = true ;
84
- }
85
- }
86
-
87
- assert ! ( checked_one, "no `//^` annotations found" ) ;
64
+ check_impl ( ra_fixture, false , true , true )
88
65
}
89
66
90
67
fn check_no_mismatches ( ra_fixture : & str ) {
91
- check_mismatches_impl ( ra_fixture, true )
68
+ check_impl ( ra_fixture, true , false , false )
92
69
}
93
70
94
- #[ allow( unused) ]
95
- fn check_mismatches ( ra_fixture : & str ) {
96
- check_mismatches_impl ( ra_fixture, false )
71
+ fn check ( ra_fixture : & str ) {
72
+ check_impl ( ra_fixture, false , false , false )
97
73
}
98
74
99
- fn check_mismatches_impl ( ra_fixture : & str , allow_none : bool ) {
75
+ fn check_impl ( ra_fixture : & str , allow_none : bool , only_types : bool , display_source : bool ) {
100
76
let _tracing = setup_tracing ( ) ;
101
- let ( db, file_id) = TestDB :: with_single_file ( ra_fixture) ;
102
- let module = db. module_for_file ( file_id) ;
103
- let def_map = module. def_map ( & db) ;
77
+ let ( db, files) = TestDB :: with_many_files ( ra_fixture) ;
78
+
79
+ let mut had_annotations = false ;
80
+ let mut mismatches = HashMap :: new ( ) ;
81
+ let mut types = HashMap :: new ( ) ;
82
+ for ( file_id, annotations) in db. extract_annotations ( ) {
83
+ for ( range, expected) in annotations {
84
+ let file_range = FileRange { file_id, range } ;
85
+ if only_types {
86
+ types. insert ( file_range, expected) ;
87
+ } else if expected. starts_with ( "type: " ) {
88
+ types. insert ( file_range, expected. trim_start_matches ( "type: " ) . to_string ( ) ) ;
89
+ } else if expected. starts_with ( "expected" ) {
90
+ mismatches. insert ( file_range, expected) ;
91
+ } else {
92
+ panic ! ( "unexpected annotation: {}" , expected) ;
93
+ }
94
+ had_annotations = true ;
95
+ }
96
+ }
97
+ assert ! ( had_annotations || allow_none, "no `//^` annotations found" ) ;
104
98
105
99
let mut defs: Vec < DefWithBodyId > = Vec :: new ( ) ;
106
- visit_module ( & db, & def_map, module. local_id , & mut |it| defs. push ( it) ) ;
100
+ for file_id in files {
101
+ let module = db. module_for_file_opt ( file_id) ;
102
+ let module = match module {
103
+ Some ( m) => m,
104
+ None => continue ,
105
+ } ;
106
+ let def_map = module. def_map ( & db) ;
107
+ visit_module ( & db, & def_map, module. local_id , & mut |it| defs. push ( it) ) ;
108
+ }
107
109
defs. sort_by_key ( |def| match def {
108
110
DefWithBodyId :: FunctionId ( it) => {
109
111
let loc = it. lookup ( & db) ;
@@ -118,37 +120,59 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
118
120
loc. source ( & db) . value . syntax ( ) . text_range ( ) . start ( )
119
121
}
120
122
} ) ;
121
- let mut mismatches = HashMap :: new ( ) ;
122
- let mut push_mismatch = |src_ptr : InFile < SyntaxNode > , mismatch : TypeMismatch | {
123
- let range = src_ptr. value . text_range ( ) ;
124
- if src_ptr. file_id . call_node ( & db) . is_some ( ) {
125
- panic ! ( "type mismatch in macro expansion" ) ;
126
- }
127
- let file_range = FileRange { file_id : src_ptr. file_id . original_file ( & db) , range } ;
128
- let actual = format ! (
129
- "expected {}, got {}" ,
130
- mismatch. expected. display_test( & db) ,
131
- mismatch. actual. display_test( & db)
132
- ) ;
133
- mismatches. insert ( file_range, actual) ;
134
- } ;
123
+ let mut unexpected_type_mismatches = String :: new ( ) ;
135
124
for def in defs {
136
125
let ( _body, body_source_map) = db. body_with_source_map ( def) ;
137
126
let inference_result = db. infer ( def) ;
127
+
128
+ for ( pat, ty) in inference_result. type_of_pat . iter ( ) {
129
+ let node = match pat_node ( & body_source_map, pat, & db) {
130
+ Some ( value) => value,
131
+ None => continue ,
132
+ } ;
133
+ let range = node. as_ref ( ) . original_file_range ( & db) ;
134
+ if let Some ( expected) = types. remove ( & range) {
135
+ let actual = if display_source {
136
+ ty. display_source_code ( & db, def. module ( & db) ) . unwrap ( )
137
+ } else {
138
+ ty. display_test ( & db) . to_string ( )
139
+ } ;
140
+ assert_eq ! ( actual, expected) ;
141
+ }
142
+ }
143
+
144
+ for ( expr, ty) in inference_result. type_of_expr . iter ( ) {
145
+ let node = match expr_node ( & body_source_map, expr, & db) {
146
+ Some ( value) => value,
147
+ None => continue ,
148
+ } ;
149
+ let range = node. as_ref ( ) . original_file_range ( & db) ;
150
+ if let Some ( expected) = types. remove ( & range) {
151
+ let actual = if display_source {
152
+ ty. display_source_code ( & db, def. module ( & db) ) . unwrap ( )
153
+ } else {
154
+ ty. display_test ( & db) . to_string ( )
155
+ } ;
156
+ assert_eq ! ( actual, expected) ;
157
+ }
158
+ }
159
+
138
160
for ( pat, mismatch) in inference_result. pat_type_mismatches ( ) {
139
- let syntax_ptr = match body_source_map. pat_syntax ( pat) {
140
- Ok ( sp) => {
141
- let root = db. parse_or_expand ( sp. file_id ) . unwrap ( ) ;
142
- sp. map ( |ptr| {
143
- ptr. either (
144
- |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
145
- |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
146
- )
147
- } )
148
- }
149
- Err ( SyntheticSyntax ) => continue ,
161
+ let node = match pat_node ( & body_source_map, pat, & db) {
162
+ Some ( value) => value,
163
+ None => continue ,
150
164
} ;
151
- push_mismatch ( syntax_ptr, mismatch. clone ( ) ) ;
165
+ let range = node. as_ref ( ) . original_file_range ( & db) ;
166
+ let actual = format ! (
167
+ "expected {}, got {}" ,
168
+ mismatch. expected. display_test( & db) ,
169
+ mismatch. actual. display_test( & db)
170
+ ) ;
171
+ if let Some ( annotation) = mismatches. remove ( & range) {
172
+ assert_eq ! ( actual, annotation) ;
173
+ } else {
174
+ format_to ! ( unexpected_type_mismatches, "{:?}: {}\n " , range. range, actual) ;
175
+ }
152
176
}
153
177
for ( expr, mismatch) in inference_result. expr_type_mismatches ( ) {
154
178
let node = match body_source_map. expr_syntax ( expr) {
@@ -158,45 +182,70 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
158
182
}
159
183
Err ( SyntheticSyntax ) => continue ,
160
184
} ;
161
- push_mismatch ( node, mismatch. clone ( ) ) ;
162
- }
163
- }
164
- let mut checked_one = false ;
165
- for ( file_id, annotations) in db. extract_annotations ( ) {
166
- for ( range, expected) in annotations {
167
- let file_range = FileRange { file_id, range } ;
168
- if let Some ( mismatch) = mismatches. remove ( & file_range) {
169
- assert_eq ! ( mismatch, expected) ;
185
+ let range = node. as_ref ( ) . original_file_range ( & db) ;
186
+ let actual = format ! (
187
+ "expected {}, got {}" ,
188
+ mismatch. expected. display_test( & db) ,
189
+ mismatch. actual. display_test( & db)
190
+ ) ;
191
+ if let Some ( annotation) = mismatches. remove ( & range) {
192
+ assert_eq ! ( actual, annotation) ;
170
193
} else {
171
- assert ! ( false , "Expected mismatch not encountered: {}\n " , expected ) ;
194
+ format_to ! ( unexpected_type_mismatches , "{:?}: {}\n " , range . range , actual ) ;
172
195
}
173
- checked_one = true ;
174
196
}
175
197
}
198
+
176
199
let mut buf = String :: new ( ) ;
177
- for ( range, mismatch) in mismatches {
178
- format_to ! ( buf, "{:?}: {}\n " , range. range, mismatch, ) ;
200
+ if !unexpected_type_mismatches. is_empty ( ) {
201
+ format_to ! ( buf, "Unexpected type mismatches:\n {}" , unexpected_type_mismatches) ;
202
+ }
203
+ if !mismatches. is_empty ( ) {
204
+ format_to ! ( buf, "Unchecked mismatch annotations:\n " ) ;
205
+ for m in mismatches {
206
+ format_to ! ( buf, "{:?}: {}\n " , m. 0 . range, m. 1 ) ;
207
+ }
179
208
}
180
- assert ! ( buf. is_empty( ) , "Unexpected type mismatches:\n {}" , buf) ;
209
+ if !types. is_empty ( ) {
210
+ format_to ! ( buf, "Unchecked type annotations:\n " ) ;
211
+ for t in types {
212
+ format_to ! ( buf, "{:?}: type {}\n " , t. 0 . range, t. 1 ) ;
213
+ }
214
+ }
215
+ assert ! ( buf. is_empty( ) , "{}" , buf) ;
216
+ }
181
217
182
- assert ! ( checked_one || allow_none, "no `//^` annotations found" ) ;
218
+ fn expr_node (
219
+ body_source_map : & BodySourceMap ,
220
+ expr : ExprId ,
221
+ db : & TestDB ,
222
+ ) -> Option < InFile < SyntaxNode > > {
223
+ Some ( match body_source_map. expr_syntax ( expr) {
224
+ Ok ( sp) => {
225
+ let root = db. parse_or_expand ( sp. file_id ) . unwrap ( ) ;
226
+ sp. map ( |ptr| ptr. to_node ( & root) . syntax ( ) . clone ( ) )
227
+ }
228
+ Err ( SyntheticSyntax ) => return None ,
229
+ } )
183
230
}
184
231
185
- fn type_at_range ( db : & TestDB , pos : FileRange ) -> Ty {
186
- let file = db. parse ( pos. file_id ) . ok ( ) . unwrap ( ) ;
187
- let expr = algo:: find_node_at_range :: < ast:: Expr > ( file. syntax ( ) , pos. range ) . unwrap ( ) ;
188
- let fn_def = expr. syntax ( ) . ancestors ( ) . find_map ( ast:: Fn :: cast) . unwrap ( ) ;
189
- let module = db. module_for_file ( pos. file_id ) ;
190
- let func = * module. child_by_source ( db) [ keys:: FUNCTION ]
191
- . get ( & InFile :: new ( pos. file_id . into ( ) , fn_def) )
192
- . unwrap ( ) ;
193
-
194
- let ( _body, source_map) = db. body_with_source_map ( func. into ( ) ) ;
195
- if let Some ( expr_id) = source_map. node_expr ( InFile :: new ( pos. file_id . into ( ) , & expr) ) {
196
- let infer = db. infer ( func. into ( ) ) ;
197
- return infer[ expr_id] . clone ( ) ;
198
- }
199
- panic ! ( "Can't find expression" )
232
+ fn pat_node (
233
+ body_source_map : & BodySourceMap ,
234
+ pat : PatId ,
235
+ db : & TestDB ,
236
+ ) -> Option < InFile < SyntaxNode > > {
237
+ Some ( match body_source_map. pat_syntax ( pat) {
238
+ Ok ( sp) => {
239
+ let root = db. parse_or_expand ( sp. file_id ) . unwrap ( ) ;
240
+ sp. map ( |ptr| {
241
+ ptr. either (
242
+ |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
243
+ |it| it. to_node ( & root) . syntax ( ) . clone ( ) ,
244
+ )
245
+ } )
246
+ }
247
+ Err ( SyntheticSyntax ) => return None ,
248
+ } )
200
249
}
201
250
202
251
fn infer ( ra_fixture : & str ) -> String {
0 commit comments