Reject a repeated alternation arm
An alternation picks its arms evenly, so {a|a|b} was a covert 3:1 skew — a
third spelling of what a choice's weight is for, reachable only by
experiment. The check runs over the arms as written, before a reference arm
is let through, so {..a|..a} is caught too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+23
-1
@@ -133,7 +133,11 @@ func checkTokens(format string, fields map[string]node) error {
|
||||
if strings.IndexByte(t.body, '(') >= 0 { // a function token, not a field
|
||||
return checkFunc(t.body, fields)
|
||||
}
|
||||
for _, name := range strings.Split(t.body, "|") {
|
||||
names := strings.Split(t.body, "|")
|
||||
if err := checkNoRepeatedArm(t.body, names); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, name := range names {
|
||||
if isRef(name) {
|
||||
if name == refPrefix {
|
||||
return fmt.Errorf("token {%s}: reference has no path", t.body)
|
||||
@@ -159,6 +163,24 @@ func checkTokens(format string, fields map[string]node) error {
|
||||
})
|
||||
}
|
||||
|
||||
// checkNoRepeatedArm rejects {a|a|b}. An alternation picks its arms evenly, so a
|
||||
// repeated one skews the odds — a third spelling of what weight is for, and one an
|
||||
// author is far likelier to have typed by accident than meant. The error names the
|
||||
// spelling that does skew a pick.
|
||||
func checkNoRepeatedArm(body string, names []string) error {
|
||||
if len(names) < 2 {
|
||||
return nil
|
||||
}
|
||||
seen := make(map[string]bool, len(names))
|
||||
for _, name := range names {
|
||||
if seen[name] {
|
||||
return fmt.Errorf("token {%s}: arm %q is repeated; an alternation picks its arms evenly, so skew the odds with a choice's weights instead", body, name)
|
||||
}
|
||||
seen[name] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fieldTokens returns the field and reference names a format renders via {name}
|
||||
// or {a|..b} tokens (function tokens, which carry no field edges, are excluded).
|
||||
// These are exactly the child nodes expand's resolve recurses into.
|
||||
|
||||
Reference in New Issue
Block a user