plotnik_lib/query/
alt_kinds.rs1use rowan::TextRange;
7
8use super::Query;
9use super::invariants::ensure_both_branch_kinds;
10use crate::diagnostics::DiagnosticKind;
11use crate::parser::{AltExpr, AltKind, Branch, Expr};
12
13impl Query<'_> {
14 pub(super) fn validate_alt_kinds(&mut self) {
15 let defs: Vec<_> = self.ast.defs().collect();
16 for def in defs {
17 let Some(body) = def.body() else { continue };
18 self.validate_alt_expr(&body);
19 }
20
21 assert!(
22 self.ast.exprs().next().is_none(),
23 "alt_kind: unexpected bare Expr in Root (parser should wrap in Def)"
24 );
25 }
26
27 fn validate_alt_expr(&mut self, expr: &Expr) {
28 if let Expr::AltExpr(alt) = expr {
29 self.check_mixed_alternation(alt);
30 assert!(
31 alt.exprs().next().is_none(),
32 "alt_kind: unexpected bare Expr in Alt (parser should wrap in Branch)"
33 );
34 }
35
36 for child in expr.children() {
37 self.validate_alt_expr(&child);
38 }
39 }
40
41 fn check_mixed_alternation(&mut self, alt: &AltExpr) {
42 if alt.kind() != AltKind::Mixed {
43 return;
44 }
45
46 let branches: Vec<Branch> = alt.branches().collect();
47 let first_tagged = branches.iter().find(|b| b.label().is_some());
48 let first_untagged = branches.iter().find(|b| b.label().is_none());
49
50 let (tagged_branch, untagged_branch) =
51 ensure_both_branch_kinds(first_tagged, first_untagged);
52
53 let tagged_range = tagged_branch
54 .label()
55 .map(|t| t.text_range())
56 .unwrap_or_else(|| branch_range(tagged_branch));
57
58 let untagged_range = branch_range(untagged_branch);
59
60 self.alt_kind_diagnostics
61 .report(DiagnosticKind::MixedAltBranches, untagged_range)
62 .related_to("tagged branch here", tagged_range)
63 .emit();
64 }
65}
66
67fn branch_range(branch: &Branch) -> TextRange {
68 branch.text_range()
69}