plotnik_compiler/analyze/validation/
anchors.rs1use crate::SourceId;
10use crate::analyze::visitor::{Visitor, walk_named_node, walk_seq_expr};
11use crate::diagnostics::{DiagnosticKind, Diagnostics};
12use crate::parser::{NamedNode, Root, SeqExpr, SeqItem};
13
14pub fn validate_anchors(source_id: SourceId, ast: &Root, diag: &mut Diagnostics) {
15 let mut visitor = AnchorValidator {
16 diag,
17 source_id,
18 in_named_node: false,
19 };
20 visitor.visit(ast);
21}
22
23struct AnchorValidator<'a> {
24 diag: &'a mut Diagnostics,
25 source_id: SourceId,
26 in_named_node: bool,
27}
28
29impl Visitor for AnchorValidator<'_> {
30 fn visit_named_node(&mut self, node: &NamedNode) {
31 let prev = self.in_named_node;
32 self.in_named_node = true;
33
34 self.check_items(node.items());
36
37 walk_named_node(self, node);
40
41 self.in_named_node = prev;
42 }
43
44 fn visit_seq_expr(&mut self, seq: &SeqExpr) {
45 self.check_items(seq.items());
47
48 walk_seq_expr(self, seq);
49 }
50}
51
52impl AnchorValidator<'_> {
53 fn check_items(&mut self, items: impl Iterator<Item = SeqItem>) {
54 let items: Vec<_> = items.collect();
55 let len = items.len();
56
57 for (i, item) in items.iter().enumerate() {
58 if let SeqItem::Anchor(anchor) = item {
59 let is_boundary = i == 0 || i == len - 1;
60
61 if is_boundary && !self.in_named_node {
62 self.diag
63 .report(
64 self.source_id,
65 DiagnosticKind::AnchorWithoutContext,
66 anchor.text_range(),
67 )
68 .emit();
69 }
70 }
71 }
72 }
73}