Widen the naming fence to the tests, and record what each stem names
This commit is contained in:
@@ -1042,7 +1042,7 @@ the Development section below, and who ships a register the four above then draw
|
||||
all, and making them anyway cost about a fifth of the cheapest render, so it makes
|
||||
them on the first read instead, at two heap allocations for a render that does share
|
||||
a draw. The allocation gate over a repeat of a reference path and over a named draw
|
||||
group prices that, and pins the two measures that keep a draw set off the heap.
|
||||
group prices that, and pins the two measures that keep a hold set off the heap.
|
||||
- **A category never references itself, and a record's fences run at load.** A category
|
||||
is one unit: a reference back into it — `{/users.first}` inside `users` — describes a
|
||||
draw other than the fields beside it, so `New` refuses it and the sibling path stays
|
||||
@@ -1077,6 +1077,17 @@ the Development section below, and who ships a register the four above then draw
|
||||
- **`Column` carries text, not a Go value.** `Value` is the rendered string beside
|
||||
`DataType` and `Null`, which each serializer writes as the load check proved it; a
|
||||
`Value any` would hand every caller a type switch.
|
||||
- **`hold` names what a render keeps, `draw` what it draws.** Goal 2 wants a name to
|
||||
reach one unit: `hold`, `holdSet` and `renderScope` are what a render holds while it
|
||||
renders, and `draw`, `drawGroup` and `drawfence.go`'s units are the draws and the
|
||||
load fence over them. So `holdset.go` is fenced by `drawfence.go`, which does not
|
||||
pair by name as `hold.go` and `holdfence.go` do: that fence is over reference draws,
|
||||
wherever a render keeps them. Valid while the load fences sit in files of their own.
|
||||
- **A function may not spell a method; two types may.** A call writes a method with
|
||||
its receiver and a function bare, so one name on both greps as one unit, which is
|
||||
the reaching cost goal 2 counts, while the receiver before a shared method name
|
||||
says which type answers. A type is outside the rule, never being called.
|
||||
`TestNoFunctionSpellsAMethod` holds it over the package, its tests included.
|
||||
- **The package stays flat.** Go ties a package to one directory, so folders would
|
||||
split the API into packages.
|
||||
- **The performance gate asserts allocations, not wall-clock time.** `AllocsPerRun`
|
||||
|
||||
+9
-4
@@ -47,20 +47,25 @@ func namesAPass(t *testing.T, where, text string, declared, funcs map[string]boo
|
||||
}
|
||||
}
|
||||
|
||||
func goFiles(t *testing.T) []string {
|
||||
func packageDir(t *testing.T) *build.Package {
|
||||
t.Helper()
|
||||
pkg, err := build.ImportDir(".", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("read the package directory: %v", err)
|
||||
}
|
||||
return pkg.GoFiles
|
||||
return pkg
|
||||
}
|
||||
|
||||
func sourceFiles(t *testing.T) (*token.FileSet, []*ast.File) {
|
||||
t.Helper()
|
||||
return parseFiles(t, packageDir(t).GoFiles)
|
||||
}
|
||||
|
||||
func parseFiles(t *testing.T, names []string) (*token.FileSet, []*ast.File) {
|
||||
t.Helper()
|
||||
fset := token.NewFileSet()
|
||||
var files []*ast.File
|
||||
for _, name := range goFiles(t) {
|
||||
for _, name := range names {
|
||||
f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("parse %s: %v", name, err)
|
||||
@@ -88,7 +93,7 @@ func vocabulary(t *testing.T, files []*ast.File) string {
|
||||
}
|
||||
|
||||
// declaredSymbols is every name the package declares at the top level, a method and
|
||||
// a struct field keyed under its type: draws.pin, tableRead.whole.
|
||||
// a struct field keyed under its type: hold.pin, tableRead.whole.
|
||||
func declaredSymbols(files []*ast.File) map[string]bool {
|
||||
names := declaredFuncs(files)
|
||||
for _, f := range files {
|
||||
|
||||
+21
-13
@@ -2,33 +2,41 @@ package fejkdata
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNoFunctionSpellsAMethod holds a call to one unit. A method is written with
|
||||
// its receiver and a function bare, so one name on both reads alike at a call site
|
||||
// and greps as one unit; two types may share a method name, since the receiver
|
||||
// standing before it says which.
|
||||
// TestNoFunctionSpellsAMethod holds a call to one unit, over the tests as well,
|
||||
// since a test file is this package too.
|
||||
func TestNoFunctionSpellsAMethod(t *testing.T) {
|
||||
_, files := sourceFiles(t)
|
||||
funcs, methods := declaredCalls(files)
|
||||
funcs, methods := declaredCalls(namespaceFiles(t))
|
||||
names := make([]string, 0, len(funcs))
|
||||
for name := range funcs {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
if recv := methods[name]; recv != "" {
|
||||
t.Errorf("func %s and %s.%s are two units under one name; name them apart", name, recv, name)
|
||||
if recv := methods[name]; len(recv) > 0 {
|
||||
t.Errorf("func %s and %s are two units under one name; name them apart", name, strings.Join(recv, " and "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// declaredCalls is every name the package declares as a function, and every name
|
||||
// it declares as a method with one type declaring it.
|
||||
func declaredCalls(files []*ast.File) (funcs map[string]bool, methods map[string]string) {
|
||||
funcs, methods = map[string]bool{}, map[string]string{}
|
||||
// namespaceFiles is every file declaring into the package namespace, its in-package
|
||||
// tests included.
|
||||
func namespaceFiles(t *testing.T) []*ast.File {
|
||||
t.Helper()
|
||||
pkg := packageDir(t)
|
||||
_, files := parseFiles(t, slices.Concat(pkg.GoFiles, pkg.TestGoFiles))
|
||||
return files
|
||||
}
|
||||
|
||||
// declaredCalls is what each name is called as: a function, and the methods
|
||||
// spelling it under the types declaring them.
|
||||
func declaredCalls(files []*ast.File) (funcs map[string]bool, methods map[string][]string) {
|
||||
funcs, methods = map[string]bool{}, map[string][]string{}
|
||||
for _, f := range files {
|
||||
for _, decl := range f.Decls {
|
||||
d, isFunc := decl.(*ast.FuncDecl)
|
||||
@@ -36,7 +44,7 @@ func declaredCalls(files []*ast.File) (funcs map[string]bool, methods map[string
|
||||
continue
|
||||
}
|
||||
if recv := receiverType(d.Recv); recv != "" {
|
||||
methods[d.Name.Name] = recv
|
||||
methods[d.Name.Name] = append(methods[d.Name.Name], recv+"."+d.Name.Name)
|
||||
continue
|
||||
}
|
||||
funcs[d.Name.Name] = true
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ func TestLayoutNamesEverySourceFile(t *testing.T) {
|
||||
listed[name] = true
|
||||
}
|
||||
}
|
||||
for _, name := range goFiles(t) {
|
||||
for _, name := range packageDir(t).GoFiles {
|
||||
if !listed[name] {
|
||||
t.Errorf("the README Layout names no %s, so nothing says what it holds", name)
|
||||
}
|
||||
|
||||
+5
-5
@@ -42,14 +42,14 @@ func shippedShape(f *Generator) string {
|
||||
walk(join(prefix, name), n.children[name])
|
||||
}
|
||||
case *choice:
|
||||
facts[prefix] = reads(n)
|
||||
facts[prefix] = readsFact(n)
|
||||
case *table:
|
||||
facts[prefix] = "\tformat " + strconv.Quote(n.format.format) + tableFacts(n) + reads(n)
|
||||
facts[prefix] = "\tformat " + strconv.Quote(n.format.format) + tableFacts(n) + readsFact(n)
|
||||
for _, name := range n.columns {
|
||||
facts[join(prefix, name)] = "\tstring"
|
||||
}
|
||||
case *template:
|
||||
facts[prefix] = "\tformat " + strconv.Quote(n.format) + reads(n)
|
||||
facts[prefix] = "\tformat " + strconv.Quote(n.format) + readsFact(n)
|
||||
if _, columns, err := recordOf(n); err == nil {
|
||||
for _, c := range columns {
|
||||
fact := "\t" + c.DataType.String()
|
||||
@@ -85,8 +85,8 @@ func tableFacts(t *table) string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// reads names the categories any template under n references, sorted.
|
||||
func reads(n node) string {
|
||||
// readsFact names the categories any template under n references, sorted.
|
||||
func readsFact(n node) string {
|
||||
set := map[string]bool{}
|
||||
var collect func(node)
|
||||
collect = func(n node) {
|
||||
|
||||
+3
-3
@@ -369,7 +369,7 @@ type formatOps struct {
|
||||
heldLocal bool // some held name is kept by the expansion itself rather than the render's draws
|
||||
}
|
||||
|
||||
func (c *formatOps) hold(a arm, label string) {
|
||||
func (c *formatOps) holdName(a arm, label string) {
|
||||
if c.held == nil {
|
||||
c.held = map[string]bool{}
|
||||
c.holder = map[string]string{}
|
||||
@@ -396,7 +396,7 @@ func (c *formatOps) function(body string, refs map[string]refBinding) {
|
||||
var operands []arm
|
||||
for _, operand := range tokenOperands(body) {
|
||||
a := splitArm(operand, refs)
|
||||
c.hold(a, fmt.Sprintf("%s operand %q", name, operand))
|
||||
c.holdName(a, fmt.Sprintf("%s operand %q", name, operand))
|
||||
operands = append(operands, a)
|
||||
}
|
||||
c.ops = append(c.ops, op{kind: 'b', call: builtins[name].prep(args), operands: operands})
|
||||
@@ -406,7 +406,7 @@ func (c *formatOps) field(body string, refs map[string]refBinding) {
|
||||
arms := splitArms(body, refs)
|
||||
for _, a := range arms {
|
||||
if len(a.tail) > 0 {
|
||||
c.hold(a, "token {"+a.name+"}")
|
||||
c.holdName(a, "token {"+a.name+"}")
|
||||
}
|
||||
}
|
||||
c.ops = append(c.ops, op{kind: 'f', arms: arms})
|
||||
|
||||
@@ -15,9 +15,10 @@ scoring run names too little to steer one. A fresh run replaces the items below.
|
||||
|
||||
Scores, newest last: 5.9 on 2026-09-20 (Navigation 7.0, Locality 5.2, Shape 5.9,
|
||||
Self-sufficiency 5.7), eight of nine seats capped by Locality or Self-sufficiency, all
|
||||
nine naming `hold.go`, `draw.go` and `family.go` first for reading cost: 1,138 lines in
|
||||
which each file carries both a load-time fence and render-time machinery, so no file
|
||||
name says whether a unit runs at `New` or at `Fake`.
|
||||
nine naming `hold.go`, `draw.go` — now `holdset.go` and `drawfence.go` — and
|
||||
`family.go` first for reading cost: 1,138 lines in which each file carries both a
|
||||
load-time fence and render-time machinery, so no file name says whether a unit runs at
|
||||
`New` or at `Fake`.
|
||||
|
||||
This round, in order:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user