From 396ae3bcd27cc77ddcdb9fb4001acd6cecb8c03a Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Fri, 18 Sep 2026 18:39:04 +0200 Subject: [PATCH] Refuse a clock-only layout in date() naming time(), and name the fix for a name repeating inside a parent --- builtins.go | 21 ++++++++++++--------- table.go | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/builtins.go b/builtins.go index fda4c27..6a83172 100644 --- a/builtins.go +++ b/builtins.go @@ -472,17 +472,14 @@ func iban(r rng, cc string) string { const dayLayout = "2006-01-02" -// The two instants a layout is proved against: alike in nothing, so a layout that -// formats them alike names no field, and one that tells their days apart at the same -// clock names a date field. +// Alike in no field; layoutDay differs from layoutProbe in its date fields only. var ( layoutProbe = time.Date(2001, 2, 3, 4, 5, 6, 0, time.UTC) layoutProbe2 = time.Date(2010, 11, 12, 13, 14, 15, 0, time.UTC) layoutDay = time.Date(2010, 11, 12, 4, 5, 6, 0, time.UTC) ) -// layoutArg is the Go layout a quoted arg holds; unquoted, it is refused naming the -// quoted spelling, since a layout may carry the comma that splits args. +// layoutArg is the Go layout a quoted arg holds, refused when unquoted or constant. func layoutArg(a string) (string, error) { if len(a) < 2 || a[0] != '\'' || a[len(a)-1] != '\'' { return "", fmt.Errorf("layout %s is not quoted; write '%s'", a, strings.Trim(a, "'")) @@ -503,8 +500,8 @@ func layoutOf(a string) string { return layout } -// layoutArity checks a call that ends in a layout takes n args; an unquoted layout -// carrying a comma splits into more, so the error names its quoted spelling. +// layoutArity checks a call ending in a layout takes n args, naming the quoted +// layout when an unquoted one split into more. func layoutArity(name string, n int, a []string) error { if len(a) == n { return nil @@ -531,8 +528,14 @@ func dateArgs(_ map[string]node, a []string) error { if !from.Before(to) { return fmt.Errorf("date(from,to,layout): from %s is not before to %s", a[0], a[1]) } - _, err = layoutArg(a[2]) - return err + layout, err := layoutArg(a[2]) + if err != nil { + return err + } + if layoutProbe.Format(layout) == layoutDay.Format(layout) { + return fmt.Errorf("date(from,to,layout): '%s' names no date field; write time('%s')", layout, layout) + } + return nil } func timeArg(_ map[string]node, a []string) error { diff --git a/table.go b/table.go index 37f6ae1..3aae198 100644 --- a/table.go +++ b/table.go @@ -243,7 +243,7 @@ func (t *table) proveNamesInsideParent() error { for r := 0; r < t.rows(); r++ { k := t.cell(r, t.parent) + "\t" + t.cell(r, t.name) if first, dup := inside[k]; dup { - return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent)) + return fmt.Errorf("%s line %d: name %q repeats line %d inside %s %q; a name selects one row inside its parent; drop one, or add a key column", t.file, r+2, t.cell(r, t.name), first+2, t.columns[t.parent], t.cell(r, t.parent)) } inside[k] = r }