@@ -11,23 +11,21 @@ mod incremental;
1111
1212use std:: { collections:: HashMap , env, sync:: Arc } ;
1313
14- use base_db:: { fixture:: WithFixture , FileRange , SourceDatabase , SourceDatabaseExt } ;
14+ use base_db:: { fixture:: WithFixture , FileRange , SourceDatabaseExt } ;
1515use expect_test:: Expect ;
1616use hir_def:: {
1717 body:: { Body , BodySourceMap , SyntheticSyntax } ,
18- child_by_source:: ChildBySource ,
1918 db:: DefDatabase ,
19+ expr:: { ExprId , PatId } ,
2020 item_scope:: ItemScope ,
21- keys,
2221 nameres:: DefMap ,
2322 src:: HasSource ,
24- AssocItemId , DefWithBodyId , LocalModuleId , Lookup , ModuleDefId ,
23+ AssocItemId , DefWithBodyId , HasModule , LocalModuleId , Lookup , ModuleDefId ,
2524} ;
2625use hir_expand:: { db:: AstDatabase , InFile } ;
2726use once_cell:: race:: OnceBool ;
2827use stdx:: format_to;
2928use syntax:: {
30- algo,
3129 ast:: { self , AstNode , NameOwner } ,
3230 SyntaxNode ,
3331} ;
@@ -59,51 +57,55 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
5957}
6058
6159fn check_types ( ra_fixture : & str ) {
62- check_types_impl ( ra_fixture, false )
60+ check_impl ( ra_fixture, false , true , false )
6361}
6462
6563fn 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 )
8865}
8966
9067fn check_no_mismatches ( ra_fixture : & str ) {
91- check_mismatches_impl ( ra_fixture, true )
68+ check_impl ( ra_fixture, true , false , false )
9269}
9370
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 )
9773}
9874
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 ) {
10076 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" ) ;
10498
10599 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+ }
107109 defs. sort_by_key ( |def| match def {
108110 DefWithBodyId :: FunctionId ( it) => {
109111 let loc = it. lookup ( & db) ;
@@ -118,37 +120,59 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
118120 loc. source ( & db) . value . syntax ( ) . text_range ( ) . start ( )
119121 }
120122 } ) ;
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 ( ) ;
135124 for def in defs {
136125 let ( _body, body_source_map) = db. body_with_source_map ( def) ;
137126 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+
138160 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 ,
150164 } ;
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+ }
152176 }
153177 for ( expr, mismatch) in inference_result. expr_type_mismatches ( ) {
154178 let node = match body_source_map. expr_syntax ( expr) {
@@ -158,45 +182,70 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) {
158182 }
159183 Err ( SyntheticSyntax ) => continue ,
160184 } ;
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) ;
170193 } else {
171- assert ! ( false , "Expected mismatch not encountered: {}\n " , expected ) ;
194+ format_to ! ( unexpected_type_mismatches , "{:?}: {}\n " , range . range , actual ) ;
172195 }
173- checked_one = true ;
174196 }
175197 }
198+
176199 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+ }
179208 }
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+ }
181217
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+ } )
183230}
184231
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+ } )
200249}
201250
202251fn infer ( ra_fixture : & str ) -> String {
0 commit comments