aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/tests.rs')
-rw-r--r--crates/hir_ty/src/tests.rs151
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
61fn check_types(ra_fixture: &str) { 61fn check_types(ra_fixture: &str) {
62 check_types_impl(ra_fixture, false) 62 check_impl(ra_fixture, false, true)
63} 63}
64 64
65fn check_types_source_code(ra_fixture: &str) { 65fn 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
69fn check_types_impl(ra_fixture: &str, display_source: bool) { 70fn 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
90fn check_no_mismatches(ra_fixture: &str) { 92fn 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)] 96fn check(ra_fixture: &str) {
95fn check_mismatches(ra_fixture: &str) { 97 check_impl(ra_fixture, false, false)
96 check_mismatches_impl(ra_fixture, false)
97} 98}
98 99
99fn check_mismatches_impl(ra_fixture: &str, allow_none: bool) { 100fn 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
185fn type_at_range(db: &TestDB, pos: FileRange) -> Ty { 252fn type_at_range(db: &TestDB, pos: FileRange) -> Ty {