aboutsummaryrefslogtreecommitdiff
path: root/src/eval/analysis.rs
blob: b3a00831407d2203b9554a85d175d50d7784ec86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use crate::{ast, Wrap};

#[derive(Debug, PartialEq, Eq)]
pub struct Analysis {
    diagnostics: Vec<Diagnostic>,
}

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::<Vec<_>>()
        .wrap(|diagnostics| Analysis { diagnostics })
}

fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
    fn validate_pattern(
        pattern: &ast::TreePattern,
        language: &tree_sitter::Language,
    ) -> Option<String> {
        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()
}