.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / go / analysis / passes / tests / tests.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/go/analysis/passes/tests/tests.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/go/analysis/passes/tests/tests.go
new file mode 100644 (file)
index 0000000..8232276
--- /dev/null
@@ -0,0 +1,188 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package tests defines an Analyzer that checks for common mistaken
+// usages of tests and examples.
+package tests
+
+import (
+       "go/ast"
+       "go/types"
+       "strings"
+       "unicode"
+       "unicode/utf8"
+
+       "golang.org/x/tools/go/analysis"
+)
+
+const Doc = `check for common mistaken usages of tests and examples
+
+The tests checker walks Test, Benchmark and Example functions checking
+malformed names, wrong signatures and examples documenting non-existent
+identifiers.
+
+Please see the documentation for package testing in golang.org/pkg/testing
+for the conventions that are enforced for Tests, Benchmarks, and Examples.`
+
+var Analyzer = &analysis.Analyzer{
+       Name: "tests",
+       Doc:  Doc,
+       Run:  run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       for _, f := range pass.Files {
+               if !strings.HasSuffix(pass.Fset.File(f.Pos()).Name(), "_test.go") {
+                       continue
+               }
+               for _, decl := range f.Decls {
+                       fn, ok := decl.(*ast.FuncDecl)
+                       if !ok || fn.Recv != nil {
+                               // Ignore non-functions or functions with receivers.
+                               continue
+                       }
+
+                       switch {
+                       case strings.HasPrefix(fn.Name.Name, "Example"):
+                               checkExample(pass, fn)
+                       case strings.HasPrefix(fn.Name.Name, "Test"):
+                               checkTest(pass, fn, "Test")
+                       case strings.HasPrefix(fn.Name.Name, "Benchmark"):
+                               checkTest(pass, fn, "Benchmark")
+                       }
+               }
+       }
+       return nil, nil
+}
+
+func isExampleSuffix(s string) bool {
+       r, size := utf8.DecodeRuneInString(s)
+       return size > 0 && unicode.IsLower(r)
+}
+
+func isTestSuffix(name string) bool {
+       if len(name) == 0 {
+               // "Test" is ok.
+               return true
+       }
+       r, _ := utf8.DecodeRuneInString(name)
+       return !unicode.IsLower(r)
+}
+
+func isTestParam(typ ast.Expr, wantType string) bool {
+       ptr, ok := typ.(*ast.StarExpr)
+       if !ok {
+               // Not a pointer.
+               return false
+       }
+       // No easy way of making sure it's a *testing.T or *testing.B:
+       // ensure the name of the type matches.
+       if name, ok := ptr.X.(*ast.Ident); ok {
+               return name.Name == wantType
+       }
+       if sel, ok := ptr.X.(*ast.SelectorExpr); ok {
+               return sel.Sel.Name == wantType
+       }
+       return false
+}
+
+func lookup(pkg *types.Package, name string) []types.Object {
+       if o := pkg.Scope().Lookup(name); o != nil {
+               return []types.Object{o}
+       }
+
+       var ret []types.Object
+       // Search through the imports to see if any of them define name.
+       // It's hard to tell in general which package is being tested, so
+       // for the purposes of the analysis, allow the object to appear
+       // in any of the imports. This guarantees there are no false positives
+       // because the example needs to use the object so it must be defined
+       // in the package or one if its imports. On the other hand, false
+       // negatives are possible, but should be rare.
+       for _, imp := range pkg.Imports() {
+               if obj := imp.Scope().Lookup(name); obj != nil {
+                       ret = append(ret, obj)
+               }
+       }
+       return ret
+}
+
+func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
+       fnName := fn.Name.Name
+       if params := fn.Type.Params; len(params.List) != 0 {
+               pass.Reportf(fn.Pos(), "%s should be niladic", fnName)
+       }
+       if results := fn.Type.Results; results != nil && len(results.List) != 0 {
+               pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
+       }
+
+       if fnName == "Example" {
+               // Nothing more to do.
+               return
+       }
+
+       var (
+               exName = strings.TrimPrefix(fnName, "Example")
+               elems  = strings.SplitN(exName, "_", 3)
+               ident  = elems[0]
+               objs   = lookup(pass.Pkg, ident)
+       )
+       if ident != "" && len(objs) == 0 {
+               // Check ExampleFoo and ExampleBadFoo.
+               pass.Reportf(fn.Pos(), "%s refers to unknown identifier: %s", fnName, ident)
+               // Abort since obj is absent and no subsequent checks can be performed.
+               return
+       }
+       if len(elems) < 2 {
+               // Nothing more to do.
+               return
+       }
+
+       if ident == "" {
+               // Check Example_suffix and Example_BadSuffix.
+               if residual := strings.TrimPrefix(exName, "_"); !isExampleSuffix(residual) {
+                       pass.Reportf(fn.Pos(), "%s has malformed example suffix: %s", fnName, residual)
+               }
+               return
+       }
+
+       mmbr := elems[1]
+       if !isExampleSuffix(mmbr) {
+               // Check ExampleFoo_Method and ExampleFoo_BadMethod.
+               found := false
+               // Check if Foo.Method exists in this package or its imports.
+               for _, obj := range objs {
+                       if obj, _, _ := types.LookupFieldOrMethod(obj.Type(), true, obj.Pkg(), mmbr); obj != nil {
+                               found = true
+                               break
+                       }
+               }
+               if !found {
+                       pass.Reportf(fn.Pos(), "%s refers to unknown field or method: %s.%s", fnName, ident, mmbr)
+               }
+       }
+       if len(elems) == 3 && !isExampleSuffix(elems[2]) {
+               // Check ExampleFoo_Method_suffix and ExampleFoo_Method_Badsuffix.
+               pass.Reportf(fn.Pos(), "%s has malformed example suffix: %s", fnName, elems[2])
+       }
+}
+
+func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
+       // Want functions with 0 results and 1 parameter.
+       if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
+               fn.Type.Params == nil ||
+               len(fn.Type.Params.List) != 1 ||
+               len(fn.Type.Params.List[0].Names) > 1 {
+               return
+       }
+
+       // The param must look like a *testing.T or *testing.B.
+       if !isTestParam(fn.Type.Params.List[0].Type, prefix[:1]) {
+               return
+       }
+
+       if !isTestSuffix(fn.Name.Name[len(prefix):]) {
+               pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
+       }
+}