#![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
use zerodds_idl_cst::Span;
use zerodds_idl_parse::{Diagnostic, DiagnosticCode, MemoryLoader, Preprocessed, preprocess};
fn token(result: &Preprocessed, text: &str, n: usize) -> Span {
result
.tokens
.iter()
.filter(|token| token.span.slice(&result.text) == Some(text))
.nth(n)
.unwrap_or_else(|| panic!("no token `{text}` #{n}"))
.span
}
fn slice(source: &str, span: Span) -> &str {
&source[span.start as usize..span.end as usize]
}
const UNIT: &str = "// one\n/* two\n three */\n#define WIDTH 8\nmodule m {\n struct S { long field[WIDTH]; };\n};\n";
#[test]
fn a_token_after_comments_maps_to_its_own_bytes() {
let result = preprocess("main.idl", UNIT, &MemoryLoader::new());
let (file, span) = result
.map
.original(token(&result, "field", 0))
.expect("mapped");
assert_eq!(file, zerodds_idl_parse::SourceMap::ROOT);
assert_eq!(slice(UNIT, span), "field");
}
#[test]
fn a_range_over_several_tokens_runs_from_the_first_to_the_last() {
let result = preprocess("main.idl", UNIT, &MemoryLoader::new());
let from = token(&result, "struct", 0);
let to = token(&result, "}", 0);
let (_, span) = result
.map
.original(Span::new(from.start, to.end))
.expect("mapped");
assert_eq!(slice(UNIT, span), "struct S { long field[WIDTH]; }");
}
#[test]
fn an_expanded_token_maps_to_the_invocation() {
let result = preprocess("main.idl", UNIT, &MemoryLoader::new());
let (_, span) = result.map.original(token(&result, "8", 0)).expect("mapped");
assert_eq!(slice(UNIT, span), "WIDTH");
}
#[test]
fn a_token_from_an_include_names_that_file() {
let included = "// header\n\nmodule inc {\n struct T { long t; };\n};\n";
let loader = MemoryLoader::new().with("inc.idl", included);
let unit = "// main\n#include \"inc.idl\"\nmodule m { struct U { long u; }; };\n";
let result = preprocess("main.idl", unit, &loader);
let mut diagnostic = Diagnostic::error(
DiagnosticCode::UnexpectedToken,
token(&result, "t", 0),
String::from("in the include"),
"test",
)
.with_label(token(&result, "u", 0), String::from("in the unit"));
result.locate(&mut diagnostic);
assert_eq!(diagnostic.file.as_deref(), Some("inc.idl"));
assert_eq!(slice(included, diagnostic.primary), "t");
let (span, note) = &diagnostic.labels[0];
assert_eq!(slice(unit, *span), "u");
assert_eq!(note, "main.idl: in the unit");
let mut in_unit = Diagnostic::error(
DiagnosticCode::UnexpectedToken,
token(&result, "U", 0),
String::new(),
"test",
);
result.locate(&mut in_unit);
assert_eq!(in_unit.file, None, "the unit is not named");
assert_eq!(slice(unit, in_unit.primary), "U");
}
#[test]
fn a_directive_error_in_an_include_names_that_file() {
let included = "\n#if 1 / 0\n#endif\n";
let loader = MemoryLoader::new().with("bad.idl", included);
let result = preprocess("main.idl", "#include \"bad.idl\"\n", &loader);
let error = result
.diagnostics
.iter()
.find(|d| d.message.contains("division by zero"))
.expect("the division is reported");
assert_eq!(error.file.as_deref(), Some("bad.idl"));
}
#[test]
fn a_range_of_the_tree_is_moved_to_the_expanded_text() {
let unit = "#pragma prefix \"p\"\nmodule m {\n struct S { long field; };\n};\n";
let result = preprocess("main.idl", unit, &MemoryLoader::new());
let parse = zerodds_idl_parse::parse_preprocessed(&result);
let mut seen = Vec::new();
for node in parse.syntax().descendants() {
for (span, token) in node.tokens() {
if matches!(token.text(), "m" | "S" | "field") {
let expanded = parse.source_span(span);
assert_eq!(slice(&result.text, expanded), token.text(), "{span:?}");
seen.push(token.text().to_string());
}
}
}
assert_eq!(seen, ["m", "S", "field"]);
}