From c1222eaf72da27cb4ca97745b3b72b495d17b4f4 Mon Sep 17 00:00:00 2001 From: M Date: Sun, 30 Aug 2026 22:18:50 +0200 Subject: [PATCH] Label the error fences, and settle a tie by where the format writes it --- README.md | 4 ++-- template.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2b60e93..37ba855 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ always agrees, and the other is a load error. This covers every way a level can be rendered: a token, a `{calc()}` operand, and a `{..path}` reference — wherever the reference sits, including in a field the format renders. -``` +```text token {p} renders a level that {p.first} reads a path into; name the fields you want instead ``` @@ -381,7 +381,7 @@ nested template keeps its own. A field no dotted token addresses is unaffected: `New` checks a path the way `Fake` resolves one: every variant of a multi-variant choice must carry the whole path, so a row missing a field is named at load: -``` +```text token {place.postal-code}: field "place": not every variant of this 2-way choice carries "postal-code"; all carry [locality] ``` diff --git a/template.go b/template.go index a4bc480..0792f5e 100644 --- a/template.go +++ b/template.go @@ -229,7 +229,9 @@ func checkNoOverlap(ops []op, bound map[string]string, format string) error { names = append(names, reader{name, fmt.Sprintf("calc operand %q", name)}) } } - sort.Slice(names, func(i, j int) bool { return names[i].name < names[j].name }) + // Stable, so two readers of one name (a token and a calc operand both naming + // "p") are reported in the order the format writes them. + sort.SliceStable(names, func(i, j int) bool { return names[i].name < names[j].name }) for i, level := range names { for _, path := range names[i+1:] { if strings.HasPrefix(path.name, level.name+".") {