plotnik_compiler/analyze/validation/
alt_kinds.rs1use crate::SourceId;
7use crate::analyze::invariants::ensure_both_branch_kinds;
8use crate::analyze::visitor::{Visitor, walk, walk_alt_expr};
9use crate::diagnostics::{DiagnosticKind, Diagnostics};
10use crate::parser::{AltExpr, AltKind, Branch, Root};
11
12pub fn validate_alt_kinds(source_id: SourceId, ast: &Root, diag: &mut Diagnostics) {
13 let mut visitor = AltKindsValidator { diag, source_id };
14 visitor.visit(ast);
15}
16
17struct AltKindsValidator<'a> {
18 diag: &'a mut Diagnostics,
19 source_id: SourceId,
20}
21
22impl Visitor for AltKindsValidator<'_> {
23 fn visit(&mut self, root: &Root) {
24 assert!(
25 root.exprs().next().is_none(),
26 "alt_kind: unexpected bare Expr in Root (parser should wrap in Def)"
27 );
28 walk(self, root);
29 }
30
31 fn visit_alt_expr(&mut self, alt: &AltExpr) {
32 self.check_mixed_alternation(alt);
33 assert!(
34 alt.exprs().next().is_none(),
35 "alt_kind: unexpected bare Expr in Alt (parser should wrap in Branch)"
36 );
37 walk_alt_expr(self, alt);
38 }
39}
40
41impl AltKindsValidator<'_> {
42 fn check_mixed_alternation(&mut self, alt: &AltExpr) {
43 if alt.kind() != AltKind::Mixed {
44 return;
45 }
46
47 let branches: Vec<Branch> = alt.branches().collect();
48 let first_tagged = branches.iter().find(|b| b.label().is_some());
49 let first_untagged = branches.iter().find(|b| b.label().is_none());
50
51 let (tagged_branch, untagged_branch) =
52 ensure_both_branch_kinds(first_tagged, first_untagged);
53
54 let tagged_range = tagged_branch
55 .label()
56 .expect("tagged branch found via filter must have label")
57 .text_range();
58
59 self.diag
60 .report(
61 self.source_id,
62 DiagnosticKind::MixedAltBranches,
63 untagged_branch.text_range(),
64 )
65 .related_to(self.source_id, tagged_range, "tagged branch here")
66 .emit();
67 }
68}