use crate::{ast, Wrap}; #[derive(Debug, PartialEq, Eq)] pub struct Analysis { diagnostics: Vec, } impl Analysis { pub fn print(&self) { for Diagnostic { level, kind } in &self.diagnostics { eprintln!("{level} {kind}"); } } } #[derive(Debug, PartialEq, Eq)] pub struct Diagnostic { level: Level, kind: Kind, } #[derive(Debug, PartialEq, Eq)] pub enum Level { Warning, Error, } impl std::fmt::Display for Level { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Level::Warning => write!(f, "[warning]"), Level::Error => write!(f, "[error]"), } } } #[derive(Debug, PartialEq, Eq)] pub enum Kind { Pattern { invalid_node_kind: String, full_pattern: ast::Pattern, }, } impl std::fmt::Display for Kind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Pattern { invalid_node_kind, full_pattern, } => { write!( f, "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`" ) } } } } pub fn run(ctx: &super::Context) -> Analysis { [validate_patterns(ctx)] .into_iter() .flatten() .collect::>() .wrap(|diagnostics| Analysis { diagnostics }) } fn validate_patterns(ctx: &super::Context) -> Vec { fn validate_pattern( pattern: &ast::TreePattern, language: &tree_sitter::Language, ) -> Option { match pattern { ast::TreePattern::Atom(a) => { if language.id_for_node_kind(a, true) == 0 { Some(a.to_owned()) } else { None } } ast::TreePattern::List(items) => { for item in items { validate_pattern(item, language)?; } None } } } ctx.program .stanzas .iter() .flat_map(|s| match &s.pattern { ast::Pattern::Begin | ast::Pattern::End => None, ast::Pattern::Tree { matcher, .. } => { validate_pattern(matcher, &ctx.language).map(|invalid_node_kind| Kind::Pattern { invalid_node_kind, full_pattern: s.pattern.clone(), }) } }) .map(|kind| Diagnostic { level: Level::Error, kind, }) .collect() }