diff options
Diffstat (limited to 'crates/hir_ty/src/tests.rs')
-rw-r--r-- | crates/hir_ty/src/tests.rs | 151 |
1 files changed, 109 insertions, 42 deletions
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index b873585c4..c4f981b44 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -59,14 +59,16 @@ fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> { | |||
59 | } | 59 | } |
60 | 60 | ||
61 | fn check_types(ra_fixture: &str) { | 61 | fn check_types(ra_fixture: &str) { |
62 | check_types_impl(ra_fixture, false) | 62 | check_impl(ra_fixture, false, true) |
63 | } | 63 | } |
64 | 64 | ||
65 | fn check_types_source_code(ra_fixture: &str) { | 65 | fn check_types_source_code(ra_fixture: &str) { |
66 | // TODO | ||
66 | check_types_impl(ra_fixture, true) | 67 | check_types_impl(ra_fixture, true) |
67 | } | 68 | } |
68 | 69 | ||
69 | fn check_types_impl(ra_fixture: &str, display_source: bool) { | 70 | fn check_types_impl(ra_fixture: &str, display_source: bool) { |
71 | // TODO | ||
70 | let _tracing = setup_tracing(); | 72 | let _tracing = setup_tracing(); |
71 | let db = TestDB::with_files(ra_fixture); | 73 | let db = TestDB::with_files(ra_fixture); |
72 | let mut checked_one = false; | 74 | let mut checked_one = false; |
@@ -88,22 +90,47 @@ fn check_types_impl(ra_fixture: &str, display_source: bool) { | |||
88 | } | 90 | } |
89 | 91 | ||
90 | fn check_no_mismatches(ra_fixture: &str) { | 92 | fn check_no_mismatches(ra_fixture: &str) { |
91 | check_mismatches_impl(ra_fixture, true) | 93 | check_impl(ra_fixture, true, false) |
92 | } | 94 | } |
93 | 95 | ||
94 | #[allow(unused)] | 96 | fn check(ra_fixture: &str) { |
95 | fn check_mismatches(ra_fixture: &str) { | 97 | check_impl(ra_fixture, false, false) |
96 | check_mismatches_impl(ra_fixture, false) | ||
97 | } | 98 | } |
98 | 99 | ||
99 | fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | 100 | fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool) { |
100 | let _tracing = setup_tracing(); | 101 | let _tracing = setup_tracing(); |
101 | let (db, file_id) = TestDB::with_single_file(ra_fixture); | 102 | let (db, files) = TestDB::with_many_files(ra_fixture); |
102 | let module = db.module_for_file(file_id); | 103 | |
103 | let def_map = module.def_map(&db); | 104 | let mut had_annotations = false; |
105 | let mut mismatches = HashMap::new(); | ||
106 | let mut types = HashMap::new(); | ||
107 | for (file_id, annotations) in db.extract_annotations() { | ||
108 | for (range, expected) in annotations { | ||
109 | let file_range = FileRange { file_id, range }; | ||
110 | if only_types { | ||
111 | types.insert(file_range, expected); | ||
112 | } else if expected.starts_with("type: ") { | ||
113 | types.insert(file_range, expected.trim_start_matches("type: ").to_string()); | ||
114 | } else if expected.starts_with("expected") { | ||
115 | mismatches.insert(file_range, expected); | ||
116 | } else { | ||
117 | panic!("unexpected annotation: {}", expected); | ||
118 | } | ||
119 | had_annotations = true; | ||
120 | } | ||
121 | } | ||
122 | assert!(had_annotations || allow_none, "no `//^` annotations found"); | ||
104 | 123 | ||
105 | let mut defs: Vec<DefWithBodyId> = Vec::new(); | 124 | let mut defs: Vec<DefWithBodyId> = Vec::new(); |
106 | visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); | 125 | for file_id in files { |
126 | let module = db.module_for_file_opt(file_id); | ||
127 | let module = match module { | ||
128 | Some(m) => m, | ||
129 | None => continue, | ||
130 | }; | ||
131 | let def_map = module.def_map(&db); | ||
132 | visit_module(&db, &def_map, module.local_id, &mut |it| defs.push(it)); | ||
133 | } | ||
107 | defs.sort_by_key(|def| match def { | 134 | defs.sort_by_key(|def| match def { |
108 | DefWithBodyId::FunctionId(it) => { | 135 | DefWithBodyId::FunctionId(it) => { |
109 | let loc = it.lookup(&db); | 136 | let loc = it.lookup(&db); |
@@ -118,25 +145,46 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | |||
118 | loc.source(&db).value.syntax().text_range().start() | 145 | loc.source(&db).value.syntax().text_range().start() |
119 | } | 146 | } |
120 | }); | 147 | }); |
121 | let mut mismatches = HashMap::new(); | 148 | let mut unexpected_type_mismatches = String::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 | }; | ||
135 | for def in defs { | 149 | for def in defs { |
136 | let (_body, body_source_map) = db.body_with_source_map(def); | 150 | let (_body, body_source_map) = db.body_with_source_map(def); |
137 | let inference_result = db.infer(def); | 151 | let inference_result = db.infer(def); |
152 | |||
153 | for (pat, ty) in inference_result.type_of_pat.iter() { | ||
154 | let node = match body_source_map.pat_syntax(pat) { | ||
155 | Ok(sp) => { | ||
156 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
157 | sp.map(|ptr| { | ||
158 | ptr.either( | ||
159 | |it| it.to_node(&root).syntax().clone(), | ||
160 | |it| it.to_node(&root).syntax().clone(), | ||
161 | ) | ||
162 | }) | ||
163 | } | ||
164 | Err(SyntheticSyntax) => continue, | ||
165 | }; | ||
166 | let range = node.as_ref().original_file_range(&db); | ||
167 | if let Some(annotation) = types.remove(&range) { | ||
168 | assert_eq!(ty.display_test(&db).to_string(), annotation); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | for (expr, ty) in inference_result.type_of_expr.iter() { | ||
173 | let node = match body_source_map.expr_syntax(expr) { | ||
174 | Ok(sp) => { | ||
175 | let root = db.parse_or_expand(sp.file_id).unwrap(); | ||
176 | sp.map(|ptr| ptr.to_node(&root).syntax().clone()) | ||
177 | } | ||
178 | Err(SyntheticSyntax) => continue, | ||
179 | }; | ||
180 | let range = node.as_ref().original_file_range(&db); | ||
181 | if let Some(annotation) = types.remove(&range) { | ||
182 | assert_eq!(ty.display_test(&db).to_string(), annotation); | ||
183 | } | ||
184 | } | ||
185 | |||
138 | for (pat, mismatch) in inference_result.pat_type_mismatches() { | 186 | for (pat, mismatch) in inference_result.pat_type_mismatches() { |
139 | let syntax_ptr = match body_source_map.pat_syntax(pat) { | 187 | let node = match body_source_map.pat_syntax(pat) { |
140 | Ok(sp) => { | 188 | Ok(sp) => { |
141 | let root = db.parse_or_expand(sp.file_id).unwrap(); | 189 | let root = db.parse_or_expand(sp.file_id).unwrap(); |
142 | sp.map(|ptr| { | 190 | sp.map(|ptr| { |
@@ -148,7 +196,17 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | |||
148 | } | 196 | } |
149 | Err(SyntheticSyntax) => continue, | 197 | Err(SyntheticSyntax) => continue, |
150 | }; | 198 | }; |
151 | push_mismatch(syntax_ptr, mismatch.clone()); | 199 | let range = node.as_ref().original_file_range(&db); |
200 | let actual = format!( | ||
201 | "expected {}, got {}", | ||
202 | mismatch.expected.display_test(&db), | ||
203 | mismatch.actual.display_test(&db) | ||
204 | ); | ||
205 | if let Some(annotation) = mismatches.remove(&range) { | ||
206 | assert_eq!(actual, annotation); | ||
207 | } else { | ||
208 | format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual); | ||
209 | } | ||
152 | } | 210 | } |
153 | for (expr, mismatch) in inference_result.expr_type_mismatches() { | 211 | for (expr, mismatch) in inference_result.expr_type_mismatches() { |
154 | let node = match body_source_map.expr_syntax(expr) { | 212 | let node = match body_source_map.expr_syntax(expr) { |
@@ -158,28 +216,37 @@ fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { | |||
158 | } | 216 | } |
159 | Err(SyntheticSyntax) => continue, | 217 | Err(SyntheticSyntax) => continue, |
160 | }; | 218 | }; |
161 | push_mismatch(node, mismatch.clone()); | 219 | let range = node.as_ref().original_file_range(&db); |
162 | } | 220 | let actual = format!( |
163 | } | 221 | "expected {}, got {}", |
164 | let mut checked_one = false; | 222 | mismatch.expected.display_test(&db), |
165 | for (file_id, annotations) in db.extract_annotations() { | 223 | mismatch.actual.display_test(&db) |
166 | for (range, expected) in annotations { | 224 | ); |
167 | let file_range = FileRange { file_id, range }; | 225 | if let Some(annotation) = mismatches.remove(&range) { |
168 | if let Some(mismatch) = mismatches.remove(&file_range) { | 226 | assert_eq!(actual, annotation); |
169 | assert_eq!(mismatch, expected); | ||
170 | } else { | 227 | } else { |
171 | assert!(false, "Expected mismatch not encountered: {}\n", expected); | 228 | format_to!(unexpected_type_mismatches, "{:?}: {}\n", range.range, actual); |
172 | } | 229 | } |
173 | checked_one = true; | ||
174 | } | 230 | } |
175 | } | 231 | } |
232 | |||
176 | let mut buf = String::new(); | 233 | let mut buf = String::new(); |
177 | for (range, mismatch) in mismatches { | 234 | if !unexpected_type_mismatches.is_empty() { |
178 | format_to!(buf, "{:?}: {}\n", range.range, mismatch,); | 235 | format_to!(buf, "Unexpected type mismatches:\n{}", unexpected_type_mismatches); |
179 | } | 236 | } |
180 | assert!(buf.is_empty(), "Unexpected type mismatches:\n{}", buf); | 237 | if !mismatches.is_empty() { |
181 | 238 | format_to!(buf, "Unchecked mismatch annotations:\n"); | |
182 | assert!(checked_one || allow_none, "no `//^` annotations found"); | 239 | for m in mismatches { |
240 | format_to!(buf, "{:?}: {}\n", m.0.range, m.1); | ||
241 | } | ||
242 | } | ||
243 | if !types.is_empty() { | ||
244 | format_to!(buf, "Unchecked type annotations:\n"); | ||
245 | for t in types { | ||
246 | format_to!(buf, "{:?}: type {}\n", t.0.range, t.1); | ||
247 | } | ||
248 | } | ||
249 | assert!(buf.is_empty(), "{}", buf); | ||
183 | } | 250 | } |
184 | 251 | ||
185 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { | 252 | fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { |