Re-work the failure to add official ICE support, and extract the reporting from the tests into the semantics, so everybody benefits.
184 lines
5.2 KiB
Rust
184 lines
5.2 KiB
Rust
use fine::parser::SyntaxTree;
|
|
use fine::semantics::{Semantics, Type};
|
|
use fine::tokens::Lines;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
fn rebase_concrete(source_path: &str, dump: &str) {
|
|
let contents = std::fs::read_to_string(source_path)
|
|
.expect(&format!("unable to read input file {}", source_path));
|
|
|
|
let mut result = String::new();
|
|
let mut lines = contents.lines();
|
|
|
|
// Search for the "concrete:" section.
|
|
let mut found_concrete_section = false;
|
|
while let Some(line) = lines.next() {
|
|
result.push_str(line);
|
|
result.push_str("\n");
|
|
|
|
if line == "// @concrete:" {
|
|
found_concrete_section = true;
|
|
break;
|
|
}
|
|
}
|
|
if !found_concrete_section {
|
|
panic!(
|
|
"unable to locate the concrete section in {}. Is there a line that starts with '// concrete:'?",
|
|
source_path
|
|
);
|
|
}
|
|
|
|
// We've found the section we care about, replace all the lines we care
|
|
// about with the actual lines.
|
|
let mut replaced_output = false;
|
|
while let Some(line) = lines.next() {
|
|
if line.starts_with("// | ") {
|
|
// Skip copying lines here, because we're skipping
|
|
// the existing concrete syntax tree.
|
|
} else {
|
|
// OK we're out of concrete syntax tree; copy in the
|
|
// new CST. (We do this inline so we don't lose
|
|
// `line`.)
|
|
for expected_line in dump.lines() {
|
|
result.push_str("// | ");
|
|
result.push_str(expected_line);
|
|
result.push_str("\n");
|
|
}
|
|
|
|
// (Make sure not to drop this line.)
|
|
result.push_str(line);
|
|
result.push_str("\n");
|
|
|
|
replaced_output = true;
|
|
break;
|
|
}
|
|
}
|
|
if !replaced_output {
|
|
panic!(
|
|
"didn't actually replace the output section in {}",
|
|
source_path
|
|
);
|
|
}
|
|
|
|
// Now just copy the rest of the lines.
|
|
while let Some(line) = lines.next() {
|
|
result.push_str(line);
|
|
result.push_str("\n");
|
|
}
|
|
|
|
// ... and re-write the file.
|
|
std::fs::write(source_path, result).expect("unable to write the new file!");
|
|
}
|
|
|
|
fn assert_concrete(tree: &SyntaxTree, expected: &str, source_path: &str) {
|
|
let dump = tree.dump(false);
|
|
let rebase = std::env::var("FINE_TEST_REBASE")
|
|
.unwrap_or(String::new())
|
|
.to_lowercase();
|
|
match rebase.as_str() {
|
|
"1" | "true" | "yes" | "y" => {
|
|
if dump != expected {
|
|
rebase_concrete(source_path, &dump)
|
|
}
|
|
}
|
|
_ => assert_eq!(expected, dump, "concrete syntax trees did not match (set FINE_TEST_REBASE=1 to auto-rebase if the diff is expected)"),
|
|
}
|
|
}
|
|
|
|
macro_rules! semantic_panic {
|
|
($semantics:expr, $tr:expr, $($t:tt)*) => {{
|
|
let message = format!($($t)*);
|
|
eprintln!("{message}!");
|
|
$semantics.dump_compiler_state($tr);
|
|
panic!("{message}");
|
|
}};
|
|
}
|
|
|
|
macro_rules! semantic_assert {
|
|
($semantics:expr, $tr:expr, $pred:expr, $($t:tt)*) => {{
|
|
if !$pred {
|
|
let message = format!($($t)*);
|
|
eprintln!("{message}!");
|
|
$semantics.dump_compiler_state($tr);
|
|
panic!("{message}");
|
|
}
|
|
}};
|
|
}
|
|
|
|
macro_rules! semantic_assert_eq {
|
|
($semantics:expr, $tr:expr, $left:expr, $right:expr, $($t:tt)*) => {{
|
|
let ll = $left;
|
|
let rr = $right;
|
|
if ll != rr {
|
|
let message = format!($($t)*);
|
|
eprintln!("{message}!");
|
|
$semantics.dump_compiler_state($tr);
|
|
assert_eq!(ll, rr, "{}", message);
|
|
}
|
|
}};
|
|
}
|
|
|
|
fn assert_type_at(
|
|
tree: &SyntaxTree,
|
|
lines: &Lines,
|
|
pos: usize,
|
|
expected: &str,
|
|
_source_path: &str,
|
|
) {
|
|
let semantics = Semantics::new(tree, lines);
|
|
let tree_ref = match tree.find_tree_at(pos) {
|
|
Some(t) => t,
|
|
None => semantic_panic!(
|
|
&semantics,
|
|
None,
|
|
"Unable to find the subtee at position {pos}"
|
|
),
|
|
};
|
|
|
|
let tree_type = semantics.type_of(tree_ref);
|
|
let actual = format!("{}", tree_type.unwrap_or(Type::Error));
|
|
semantic_assert_eq!(
|
|
&semantics,
|
|
Some(tree_ref),
|
|
expected,
|
|
actual,
|
|
"The type of the tree at position {pos} was incorrect"
|
|
);
|
|
}
|
|
|
|
fn assert_type_error_at(
|
|
tree: &SyntaxTree,
|
|
lines: &Lines,
|
|
pos: usize,
|
|
expected: &str,
|
|
_source_path: &str,
|
|
) {
|
|
let semantics = Semantics::new(tree, lines);
|
|
let tree_ref = match tree.find_tree_at(pos) {
|
|
Some(t) => t,
|
|
None => semantic_panic!(
|
|
&semantics,
|
|
None,
|
|
"Unable to find the subtee at position {pos}"
|
|
),
|
|
};
|
|
|
|
let tree_type = semantics.type_of(tree_ref);
|
|
semantic_assert!(
|
|
&semantics,
|
|
Some(tree_ref),
|
|
matches!(tree_type, Some(Type::Error)),
|
|
"The type of the {:?} tree at position {pos} was '{tree_type:?}', not an error",
|
|
tree[tree_ref].kind
|
|
);
|
|
|
|
let errors = semantics.snapshot_errors();
|
|
semantic_assert!(
|
|
&semantics,
|
|
Some(tree_ref),
|
|
errors.iter().any(|e| e.message == expected),
|
|
"Unable to find the expected error message '{expected}'"
|
|
);
|
|
}
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/generated_tests.rs"));
|