aboutsummaryrefslogtreecommitdiff
path: root/src/eval/analysis.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/eval/analysis.rs')
-rw-r--r--src/eval/analysis.rs76
1 files changed, 65 insertions, 11 deletions
diff --git a/src/eval/analysis.rs b/src/eval/analysis.rs
index b3a0083..d5a7165 100644
--- a/src/eval/analysis.rs
+++ b/src/eval/analysis.rs
@@ -6,10 +6,17 @@ pub struct Analysis {
6} 6}
7 7
8impl Analysis { 8impl Analysis {
9 pub fn print(&self) { 9 pub fn contains_error(&self) -> bool {
10 self.diagnostics.iter().any(|d| d.level == Level::Error)
11 }
12}
13
14impl std::fmt::Display for Analysis {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
10 for Diagnostic { level, kind } in &self.diagnostics { 16 for Diagnostic { level, kind } in &self.diagnostics {
11 eprintln!("{level} {kind}"); 17 writeln!(f, "{level} {kind}")?;
12 } 18 }
19 Ok(())
13 } 20 }
14} 21}
15 22
@@ -36,16 +43,20 @@ impl std::fmt::Display for Level {
36 43
37#[derive(Debug, PartialEq, Eq)] 44#[derive(Debug, PartialEq, Eq)]
38pub enum Kind { 45pub enum Kind {
39 Pattern { 46 InvalidPattern {
40 invalid_node_kind: String, 47 invalid_node_kind: String,
41 full_pattern: ast::Pattern, 48 full_pattern: ast::Pattern,
42 }, 49 },
50 SimplifiablePattern {
51 pattern: ast::TreePattern,
52 simplified: ast::TreePattern,
53 },
43} 54}
44 55
45impl std::fmt::Display for Kind { 56impl std::fmt::Display for Kind {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self { 58 match self {
48 Self::Pattern { 59 Self::InvalidPattern {
49 invalid_node_kind, 60 invalid_node_kind,
50 full_pattern, 61 full_pattern,
51 } => { 62 } => {
@@ -54,12 +65,21 @@ impl std::fmt::Display for Kind {
54 "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`" 65 "invalid node kind `{invalid_node_kind}` in pattern `{full_pattern}`"
55 ) 66 )
56 } 67 }
68 Self::SimplifiablePattern {
69 pattern,
70 simplified,
71 } => {
72 write!(
73 f,
74 "the pattern `{pattern}` can be reduced to just `{simplified}`"
75 )
76 }
57 } 77 }
58 } 78 }
59} 79}
60 80
61pub fn run(ctx: &super::Context) -> Analysis { 81pub fn run(ctx: &super::Context) -> Analysis {
62 [validate_patterns(ctx)] 82 [validate_patterns(ctx), redundant_list_pattern(ctx)]
63 .into_iter() 83 .into_iter()
64 .flatten() 84 .flatten()
65 .collect::<Vec<_>>() 85 .collect::<Vec<_>>()
@@ -67,11 +87,9 @@ pub fn run(ctx: &super::Context) -> Analysis {
67} 87}
68 88
69fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> { 89fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
70 fn validate_pattern( 90 fn check(pattern: &ast::TreePattern, language: &tree_sitter::Language) -> Option<String> {
71 pattern: &ast::TreePattern,
72 language: &tree_sitter::Language,
73 ) -> Option<String> {
74 match pattern { 91 match pattern {
92 // base case
75 ast::TreePattern::Atom(a) => { 93 ast::TreePattern::Atom(a) => {
76 if language.id_for_node_kind(a, true) == 0 { 94 if language.id_for_node_kind(a, true) == 0 {
77 Some(a.to_owned()) 95 Some(a.to_owned())
@@ -79,9 +97,10 @@ fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
79 None 97 None
80 } 98 }
81 } 99 }
100 // recursive case
82 ast::TreePattern::List(items) => { 101 ast::TreePattern::List(items) => {
83 for item in items { 102 for item in items {
84 validate_pattern(item, language)?; 103 check(item, language)?;
85 } 104 }
86 None 105 None
87 } 106 }
@@ -94,7 +113,7 @@ fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
94 .flat_map(|s| match &s.pattern { 113 .flat_map(|s| match &s.pattern {
95 ast::Pattern::Begin | ast::Pattern::End => None, 114 ast::Pattern::Begin | ast::Pattern::End => None,
96 ast::Pattern::Tree { matcher, .. } => { 115 ast::Pattern::Tree { matcher, .. } => {
97 validate_pattern(matcher, &ctx.language).map(|invalid_node_kind| Kind::Pattern { 116 check(matcher, &ctx.language).map(|invalid_node_kind| Kind::InvalidPattern {
98 invalid_node_kind, 117 invalid_node_kind,
99 full_pattern: s.pattern.clone(), 118 full_pattern: s.pattern.clone(),
100 }) 119 })
@@ -106,3 +125,38 @@ fn validate_patterns(ctx: &super::Context) -> Vec<Diagnostic> {
106 }) 125 })
107 .collect() 126 .collect()
108} 127}
128
129fn redundant_list_pattern(ctx: &super::Context) -> Vec<Diagnostic> {
130 fn simplify(pattern: &ast::TreePattern) -> ast::TreePattern {
131 match pattern {
132 ast::TreePattern::Atom(a) => ast::TreePattern::Atom(a.to_owned()),
133 ast::TreePattern::List(l) => match l.as_slice() {
134 [a] => simplify(a),
135 items => ast::TreePattern::List(items.iter().map(simplify).collect::<Vec<_>>()),
136 },
137 }
138 }
139
140 ctx.program
141 .stanzas
142 .iter()
143 .flat_map(|s| match &s.pattern {
144 ast::Pattern::Begin | ast::Pattern::End => None,
145 ast::Pattern::Tree { matcher, .. } => {
146 let simplified = simplify(matcher);
147 if &simplified != matcher {
148 Some(Kind::SimplifiablePattern {
149 pattern: matcher.clone(),
150 simplified,
151 })
152 } else {
153 None
154 }
155 }
156 })
157 .map(|kind| Diagnostic {
158 level: Level::Warning,
159 kind,
160 })
161 .collect()
162}