Require a fill header to name a function, not just a symbol
This commit is contained in:
+43
-22
@@ -6,6 +6,7 @@ import (
|
|||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -17,22 +18,33 @@ var backticked = regexp.MustCompile("`([^`\n]+)`")
|
|||||||
func TestVocabularyNamesDeclaredSymbols(t *testing.T) {
|
func TestVocabularyNamesDeclaredSymbols(t *testing.T) {
|
||||||
_, files := sourceFiles(t)
|
_, files := sourceFiles(t)
|
||||||
declared := declaredSymbols(files)
|
declared := declaredSymbols(files)
|
||||||
if namesDeclared(t, "the Vocabulary", vocabulary(t, files), declared) == 0 {
|
if len(namesDeclared(t, "the Vocabulary", vocabulary(t, files), declared)) == 0 {
|
||||||
t.Fatal("the Vocabulary names no symbol, so nothing holds a renamed unit to its entry")
|
t.Fatal("the Vocabulary names no symbol, so nothing holds a renamed unit to its entry")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// namesDeclared reports how many symbols text backticks, failing for each one the
|
// namesDeclared is every symbol text backticks, failing for each one the package
|
||||||
// package declares nowhere.
|
// declares nowhere.
|
||||||
func namesDeclared(t *testing.T, where, text string, declared map[string]bool) int {
|
func namesDeclared(t *testing.T, where, text string, declared map[string]bool) []string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
named := backticked.FindAllStringSubmatch(text, -1)
|
var named []string
|
||||||
for _, m := range named {
|
for _, m := range backticked.FindAllStringSubmatch(text, -1) {
|
||||||
if !declared[m[1]] {
|
if !declared[m[1]] {
|
||||||
t.Errorf("%s names %s, which the package declares nowhere", where, m[1])
|
t.Errorf("%s names %s, which the package declares nowhere", where, m[1])
|
||||||
}
|
}
|
||||||
|
named = append(named, m[1])
|
||||||
|
}
|
||||||
|
return named
|
||||||
|
}
|
||||||
|
|
||||||
|
// namesAPass fails unless one symbol text backticks is a function: a field names
|
||||||
|
// what a pass reads or writes, never the pass.
|
||||||
|
func namesAPass(t *testing.T, where, text string, declared, funcs map[string]bool) {
|
||||||
|
t.Helper()
|
||||||
|
named := namesDeclared(t, where, text, declared)
|
||||||
|
if !slices.ContainsFunc(named, func(n string) bool { return funcs[n] }) {
|
||||||
|
t.Errorf("%s names no function, so it names no pass", where)
|
||||||
}
|
}
|
||||||
return len(named)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sourceFiles(t *testing.T) (*token.FileSet, []*ast.File) {
|
func sourceFiles(t *testing.T) (*token.FileSet, []*ast.File) {
|
||||||
@@ -73,17 +85,30 @@ func vocabulary(t *testing.T, files []*ast.File) string {
|
|||||||
// declaredSymbols is every name the package declares at the top level, a method and
|
// 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: draws.pin, tableRead.whole.
|
||||||
func declaredSymbols(files []*ast.File) map[string]bool {
|
func declaredSymbols(files []*ast.File) map[string]bool {
|
||||||
|
names := declaredFuncs(files)
|
||||||
|
for _, f := range files {
|
||||||
|
for _, decl := range f.Decls {
|
||||||
|
if d, isGen := decl.(*ast.GenDecl); isGen {
|
||||||
|
addSpecs(names, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// declaredFuncs is every function the package declares, a method keyed under its
|
||||||
|
// type: template.compileFormat.
|
||||||
|
func declaredFuncs(files []*ast.File) map[string]bool {
|
||||||
names := map[string]bool{}
|
names := map[string]bool{}
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
for _, decl := range f.Decls {
|
for _, decl := range f.Decls {
|
||||||
switch d := decl.(type) {
|
d, isFunc := decl.(*ast.FuncDecl)
|
||||||
case *ast.FuncDecl:
|
if !isFunc {
|
||||||
names[d.Name.Name] = true
|
continue
|
||||||
if recv := receiverType(d.Recv); recv != "" {
|
}
|
||||||
names[recv+"."+d.Name.Name] = true
|
names[d.Name.Name] = true
|
||||||
}
|
if recv := receiverType(d.Recv); recv != "" {
|
||||||
case *ast.GenDecl:
|
names[recv+"."+d.Name.Name] = true
|
||||||
addSpecs(names, d)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,11 +162,9 @@ const fillHeader = "Filled by "
|
|||||||
// fills, and to the template doc.
|
// fills, and to the template doc.
|
||||||
func TestTemplateFieldGroupsNameADeclaredPass(t *testing.T) {
|
func TestTemplateFieldGroupsNameADeclaredPass(t *testing.T) {
|
||||||
fset, files := sourceFiles(t)
|
fset, files := sourceFiles(t)
|
||||||
declared := declaredSymbols(files)
|
declared, funcs := declaredSymbols(files), declaredFuncs(files)
|
||||||
doc, st, headers := templateStruct(t, fset, files)
|
doc, st, headers := templateStruct(t, fset, files)
|
||||||
if namesDeclared(t, "the template doc", doc, declared) == 0 {
|
namesAPass(t, "the template doc", doc, declared, funcs)
|
||||||
t.Error("the template doc names no pass, so nothing says when a field is final")
|
|
||||||
}
|
|
||||||
for _, field := range st.Fields.List {
|
for _, field := range st.Fields.List {
|
||||||
if len(field.Names) == 0 {
|
if len(field.Names) == 0 {
|
||||||
t.Error("an embedded field of template sits under no header, so nothing says which pass fills it")
|
t.Error("an embedded field of template sits under no header, so nothing says which pass fills it")
|
||||||
@@ -152,9 +175,7 @@ func TestTemplateFieldGroupsNameADeclaredPass(t *testing.T) {
|
|||||||
t.Errorf("template.%s sits under no %q header, so nothing says which pass fills it", id.Name, fillHeader)
|
t.Errorf("template.%s sits under no %q header, so nothing says which pass fills it", id.Name, fillHeader)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if namesDeclared(t, "the header above template."+id.Name, header, declared) == 0 {
|
namesAPass(t, "the header above template."+id.Name, header, declared, funcs)
|
||||||
t.Errorf("the header above template.%s names no symbol, so it names no pass", id.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user