Source file
src/go/types/errorcodes_test.go
1
2
3
4
5 package types_test
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/constant"
11 "go/importer"
12 "go/parser"
13 "go/token"
14 "reflect"
15 "strings"
16 "testing"
17
18 . "go/types"
19 )
20
21 func TestErrorCodeExamples(t *testing.T) {
22 walkCodes(t, func(name string, value int, spec *ast.ValueSpec) {
23 t.Run(name, func(t *testing.T) {
24 doc := spec.Doc.Text()
25 examples := strings.Split(doc, "Example:")
26 for i := 1; i < len(examples); i++ {
27 example := examples[i]
28 err := checkExample(t, example)
29 if err == nil {
30 t.Fatalf("no error in example #%d", i)
31 }
32 typerr, ok := err.(Error)
33 if !ok {
34 t.Fatalf("not a types.Error: %v", err)
35 }
36 if got := readCode(typerr); got != value {
37 t.Errorf("%s: example #%d returned code %d (%s), want %d", name, i, got, err, value)
38 }
39 }
40 })
41 })
42 }
43
44 func walkCodes(t *testing.T, f func(string, int, *ast.ValueSpec)) {
45 t.Helper()
46 fset := token.NewFileSet()
47 files, err := pkgFiles(fset, ".", parser.ParseComments)
48 if err != nil {
49 t.Fatal(err)
50 }
51 conf := Config{Importer: importer.Default()}
52 info := &Info{
53 Types: make(map[ast.Expr]TypeAndValue),
54 Defs: make(map[*ast.Ident]Object),
55 Uses: make(map[*ast.Ident]Object),
56 }
57 _, err = conf.Check("types", fset, files, info)
58 if err != nil {
59 t.Fatal(err)
60 }
61 for _, file := range files {
62 for _, decl := range file.Decls {
63 decl, ok := decl.(*ast.GenDecl)
64 if !ok || decl.Tok != token.CONST {
65 continue
66 }
67 for _, spec := range decl.Specs {
68 spec, ok := spec.(*ast.ValueSpec)
69 if !ok || len(spec.Names) == 0 {
70 continue
71 }
72 obj := info.ObjectOf(spec.Names[0])
73 if named, ok := obj.Type().(*Named); ok && named.Obj().Name() == "errorCode" {
74 if len(spec.Names) != 1 {
75 t.Fatalf("bad Code declaration for %q: got %d names, want exactly 1", spec.Names[0].Name, len(spec.Names))
76 }
77 codename := spec.Names[0].Name
78 value := int(constant.Val(obj.(*Const).Val()).(int64))
79 f(codename, value, spec)
80 }
81 }
82 }
83 }
84 }
85
86 func readCode(err Error) int {
87 v := reflect.ValueOf(err)
88 return int(v.FieldByName("go116code").Int())
89 }
90
91 func checkExample(t *testing.T, example string) error {
92 t.Helper()
93 fset := token.NewFileSet()
94 src := fmt.Sprintf("package p\n\n%s", example)
95 file, err := parser.ParseFile(fset, "example.go", src, 0)
96 if err != nil {
97 t.Fatal(err)
98 }
99 conf := Config{
100 FakeImportC: true,
101 Importer: importer.Default(),
102 }
103 _, err = conf.Check("example", fset, []*ast.File{file}, nil)
104 return err
105 }
106
107 func TestErrorCodeStyle(t *testing.T) {
108
109
110 forbiddenInIdent := []string{
111
112 "illegal",
113
114 "argument",
115 "assertion",
116 "assignment",
117 "boolean",
118 "channel",
119 "condition",
120 "declaration",
121 "expression",
122 "function",
123 "initial",
124 "integer",
125 "interface",
126 "iterat",
127 "literal",
128 "operation",
129 "package",
130 "pointer",
131 "receiver",
132 "signature",
133 "statement",
134 "variable",
135 }
136 forbiddenInComment := []string{
137
138 "lhs", "rhs",
139
140 "builtin",
141
142 "ellipsis",
143 }
144 nameHist := make(map[int]int)
145 longestName := ""
146 maxValue := 0
147
148 walkCodes(t, func(name string, value int, spec *ast.ValueSpec) {
149 if name == "_" {
150 return
151 }
152 nameHist[len(name)]++
153 if value > maxValue {
154 maxValue = value
155 }
156 if len(name) > len(longestName) {
157 longestName = name
158 }
159 if token.IsExported(name) {
160
161
162 t.Errorf("%q is exported", name)
163 }
164 if name[0] != '_' || !token.IsExported(name[1:]) {
165 t.Errorf("%q should start with _, followed by an exported identifier", name)
166 }
167 lower := strings.ToLower(name)
168 for _, bad := range forbiddenInIdent {
169 if strings.Contains(lower, bad) {
170 t.Errorf("%q contains forbidden word %q", name, bad)
171 }
172 }
173 doc := spec.Doc.Text()
174 if !strings.HasPrefix(doc, name) {
175 t.Errorf("doc for %q does not start with identifier", name)
176 }
177 lowerComment := strings.ToLower(strings.TrimPrefix(doc, name))
178 for _, bad := range forbiddenInComment {
179 if strings.Contains(lowerComment, bad) {
180 t.Errorf("doc for %q contains forbidden word %q", name, bad)
181 }
182 }
183 })
184
185 if testing.Verbose() {
186 var totChars, totCount int
187 for chars, count := range nameHist {
188 totChars += chars * count
189 totCount += count
190 }
191 avg := float64(totChars) / float64(totCount)
192 fmt.Println()
193 fmt.Printf("%d error codes\n", totCount)
194 fmt.Printf("average length: %.2f chars\n", avg)
195 fmt.Printf("max length: %d (%s)\n", len(longestName), longestName)
196 }
197 }
198
View as plain text