Source file src/pkg/cmd/vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
1
2
3
4
5
6
7 package tests
8
9 import (
10 "go/ast"
11 "go/types"
12 "strings"
13 "unicode"
14 "unicode/utf8"
15
16 "golang.org/x/tools/go/analysis"
17 )
18
19 const Doc = `check for common mistaken usages of tests and examples
20
21 The tests checker walks Test, Benchmark and Example functions checking
22 malformed names, wrong signatures and examples documenting non-existent
23 identifiers.
24
25 Please see the documentation for package testing in golang.org/pkg/testing
26 for the conventions that are enforced for Tests, Benchmarks, and Examples.`
27
28 var Analyzer = &analysis.Analyzer{
29 Name: "tests",
30 Doc: Doc,
31 Run: run,
32 }
33
34 func run(pass *analysis.Pass) (interface{}, error) {
35 for _, f := range pass.Files {
36 if !strings.HasSuffix(pass.Fset.File(f.Pos()).Name(), "_test.go") {
37 continue
38 }
39 for _, decl := range f.Decls {
40 fn, ok := decl.(*ast.FuncDecl)
41 if !ok || fn.Recv != nil {
42
43 continue
44 }
45
46 switch {
47 case strings.HasPrefix(fn.Name.Name, "Example"):
48 checkExample(pass, fn)
49 case strings.HasPrefix(fn.Name.Name, "Test"):
50 checkTest(pass, fn, "Test")
51 case strings.HasPrefix(fn.Name.Name, "Benchmark"):
52 checkTest(pass, fn, "Benchmark")
53 }
54 }
55 }
56 return nil, nil
57 }
58
59 func isExampleSuffix(s string) bool {
60 r, size := utf8.DecodeRuneInString(s)
61 return size > 0 && unicode.IsLower(r)
62 }
63
64 func isTestSuffix(name string) bool {
65 if len(name) == 0 {
66
67 return true
68 }
69 r, _ := utf8.DecodeRuneInString(name)
70 return !unicode.IsLower(r)
71 }
72
73 func isTestParam(typ ast.Expr, wantType string) bool {
74 ptr, ok := typ.(*ast.StarExpr)
75 if !ok {
76
77 return false
78 }
79
80
81 if name, ok := ptr.X.(*ast.Ident); ok {
82 return name.Name == wantType
83 }
84 if sel, ok := ptr.X.(*ast.SelectorExpr); ok {
85 return sel.Sel.Name == wantType
86 }
87 return false
88 }
89
90 func lookup(pkg *types.Package, name string) []types.Object {
91 if o := pkg.Scope().Lookup(name); o != nil {
92 return []types.Object{o}
93 }
94
95 var ret []types.Object
96
97
98
99
100
101
102
103 for _, imp := range pkg.Imports() {
104 if obj := imp.Scope().Lookup(name); obj != nil {
105 ret = append(ret, obj)
106 }
107 }
108 return ret
109 }
110
111 func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
112 fnName := fn.Name.Name
113 if params := fn.Type.Params; len(params.List) != 0 {
114 pass.Reportf(fn.Pos(), "%s should be niladic", fnName)
115 }
116 if results := fn.Type.Results; results != nil && len(results.List) != 0 {
117 pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
118 }
119
120 if fnName == "Example" {
121
122 return
123 }
124
125 var (
126 exName = strings.TrimPrefix(fnName, "Example")
127 elems = strings.SplitN(exName, "_", 3)
128 ident = elems[0]
129 objs = lookup(pass.Pkg, ident)
130 )
131 if ident != "" && len(objs) == 0 {
132
133 pass.Reportf(fn.Pos(), "%s refers to unknown identifier: %s", fnName, ident)
134
135 return
136 }
137 if len(elems) < 2 {
138
139 return
140 }
141
142 if ident == "" {
143
144 if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
145 pass.Reportf(fn.Pos(), "%s has malformed example suffix: %s", fnName, residual)
146 }
147 return
148 }
149
150 mmbr := elems[1]
151 if !isExampleSuffix(mmbr) {
152
153 found := false
154
155 for _, obj := range objs {
156 if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj != nil {
157 found = true
158 break
159 }
160 }
161 if !found {
162 pass.Reportf(fn.Pos(), "%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
163 }
164 }
165 if len(elems) == 3 && !isExampleSuffix(elems[2]) {
166
167 pass.Reportf(fn.Pos(), "%s has malformed example suffix: %s", fnName, elems[2])
168 }
169 }
170
171 func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
172
173 if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
174 fn.Type.Params == nil ||
175 len(fn.Type.Params.List) != 1 ||
176 len(fn.Type.Params.List[0].Names) > 1 {
177 return
178 }
179
180
181 if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) {
182 return
183 }
184
185 if !isTestSuffix(fn.Name.Name[len(prefix):]) {
186 pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
187 }
188 }
189
View as plain text