plotnik_compiler/analyze/validation/
empty_constructs.rs

1//! Semantic validation for empty constructs.
2//!
3//! Bans empty trees `()`, empty sequences `{}`, and empty alternations `[]`.
4
5use crate::SourceId;
6use crate::analyze::visitor::{Visitor, walk_alt_expr, walk_named_node, walk_seq_expr};
7use crate::diagnostics::{DiagnosticKind, Diagnostics};
8use crate::parser::{AltExpr, NamedNode, Root, SeqExpr};
9
10pub fn validate_empty_constructs(source_id: SourceId, ast: &Root, diag: &mut Diagnostics) {
11    let mut visitor = EmptyConstructsValidator { diag, source_id };
12    visitor.visit(ast);
13}
14
15struct EmptyConstructsValidator<'a> {
16    diag: &'a mut Diagnostics,
17    source_id: SourceId,
18}
19
20impl Visitor for EmptyConstructsValidator<'_> {
21    fn visit_named_node(&mut self, node: &NamedNode) {
22        // Check for truly empty tree: no child nodes at all in CST (only tokens like parens)
23        // This excludes invalid content like predicates which create Error nodes
24        if node.as_cst().children().next().is_none() && node.node_type().is_none() {
25            self.diag
26                .report(self.source_id, DiagnosticKind::EmptyTree, node.text_range())
27                .emit();
28        }
29        walk_named_node(self, node);
30    }
31
32    fn visit_seq_expr(&mut self, seq: &SeqExpr) {
33        if seq.children().next().is_none() {
34            self.diag
35                .report(
36                    self.source_id,
37                    DiagnosticKind::EmptySequence,
38                    seq.text_range(),
39                )
40                .emit();
41        }
42        walk_seq_expr(self, seq);
43    }
44
45    fn visit_alt_expr(&mut self, alt: &AltExpr) {
46        if alt.branches().next().is_none() {
47            self.diag
48                .report(
49                    self.source_id,
50                    DiagnosticKind::EmptyAlternation,
51                    alt.text_range(),
52                )
53                .emit();
54        }
55        walk_alt_expr(self, alt);
56    }
57}