use std::collections::HashMap; use rottlib::arena::Arena; use rottlib::diagnostics::{Diagnostic, Severity}; use rottlib::lexer::{TokenPosition, TokenSpan, TokenizedFile}; use rottlib::parser::Parser; mod block_items; mod control_flow_expressions; mod primary_expressions; mod selector_expressions; mod switch_expressions; #[derive(Debug)] pub(super) struct ExpectedLabel { pub span: TokenSpan, pub message: &'static str, } #[derive(Debug)] pub(super) struct ExpectedDiagnostic<'a> { pub headline: &'static str, pub severity: Severity, pub code: Option<&'static str>, pub primary_label: Option, pub secondary_labels: &'a [ExpectedLabel], pub help: Option<&'static str>, pub notes: &'a [&'static str], } #[track_caller] pub(super) fn assert_diagnostic(actual: &Diagnostic, expected: &ExpectedDiagnostic<'_>) { assert_eq!(actual.headline(), expected.headline); assert_eq!(actual.severity(), expected.severity); assert_eq!(actual.code(), expected.code); assert_eq!(actual.help(), expected.help); match (actual.primary_label(), expected.primary_label.as_ref()) { (None, None) => {} (Some(actual), Some(expected)) => { assert_eq!(actual.span, expected.span); assert_eq!(actual.message, expected.message); } _ => panic!("primary label mismatch"), } let actual_secondary = actual.secondary_labels(); assert_eq!(actual_secondary.len(), expected.secondary_labels.len()); for (actual, expected) in actual_secondary .iter() .zip(expected.secondary_labels.iter()) { assert_eq!(actual.span, expected.span); assert_eq!(actual.message, expected.message); } let actual_notes = actual.notes(); assert_eq!(actual_notes.len(), expected.notes.len()); for (actual, expected) in actual_notes.iter().zip(expected.notes.iter()) { assert_eq!(actual, expected); } } #[derive(Debug, Clone, Copy)] pub(super) struct Fixture { pub label: &'static str, pub source: &'static str, } pub(super) type FixtureRun = Vec; pub(super) struct FixtureRuns { runs: HashMap<&'static str, FixtureRun>, } impl FixtureRuns { #[track_caller] pub fn get(&self, label: &str) -> Option> { self.runs.get(label).map(|fixture_run| fixture_run.clone()) } #[track_caller] pub fn get_any(&self, label: &str) -> Diagnostic { self.runs .get(label) .map(|fixture_run| fixture_run[0].clone()) .unwrap() } #[track_caller] pub fn get_by_code(&self, label: &str, code: &str) -> Diagnostic { self.runs .get(label) .unwrap_or_else(|| panic!("no fixture run for `{label}`")) .iter() .find(|diagnostic| diagnostic.code().as_deref() == Some(code)) .unwrap_or_else(|| panic!("no `{code}` diagnostic in fixture `{label}`")) .clone() } } fn run_fixture(fixture: &'static Fixture) -> FixtureRun { let arena = Arena::new(); let file = TokenizedFile::tokenize(fixture.source); let mut parser = Parser::new(&file, &arena); let _ = parser.parse_expression(); let diagnostics = parser.diagnostics.clone(); for diagnostic in &diagnostics { diagnostic.render(&file, fixture.label); println!(); } diagnostics } pub(super) fn run_fixtures(fixtures: &'static [Fixture]) -> FixtureRuns { let mut runs = HashMap::new(); for fixture in fixtures { runs.insert(fixture.label, run_fixture(fixture)); } FixtureRuns { runs } }