aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Antonboom/errname
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/Antonboom/errname
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/github.com/Antonboom/errname')
-rw-r--r--vendor/github.com/Antonboom/errname/LICENSE21
-rw-r--r--vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go134
-rw-r--r--vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go266
3 files changed, 421 insertions, 0 deletions
diff --git a/vendor/github.com/Antonboom/errname/LICENSE b/vendor/github.com/Antonboom/errname/LICENSE
new file mode 100644
index 000000000..e2002e4d4
--- /dev/null
+++ b/vendor/github.com/Antonboom/errname/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Anton Telyshev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go b/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go
new file mode 100644
index 000000000..6425db137
--- /dev/null
+++ b/vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go
@@ -0,0 +1,134 @@
+package analyzer
+
+import (
+ "go/ast"
+ "go/token"
+ "strconv"
+ "strings"
+ "unicode"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+)
+
+// New returns new errname analyzer.
+func New() *analysis.Analyzer {
+ return &analysis.Analyzer{
+ Name: "errname",
+ Doc: "Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.",
+ Run: run,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ }
+}
+
+type stringSet = map[string]struct{}
+
+var (
+ imports = []ast.Node{(*ast.ImportSpec)(nil)}
+ types = []ast.Node{(*ast.TypeSpec)(nil)}
+ funcs = []ast.Node{(*ast.FuncDecl)(nil)}
+)
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+ pkgAliases := map[string]string{}
+ insp.Preorder(imports, func(node ast.Node) {
+ i := node.(*ast.ImportSpec)
+ if n := i.Name; n != nil && i.Path != nil {
+ if path, err := strconv.Unquote(i.Path.Value); err == nil {
+ pkgAliases[n.Name] = getPkgFromPath(path)
+ }
+ }
+ })
+
+ allTypes := stringSet{}
+ typesSpecs := map[string]*ast.TypeSpec{}
+ insp.Preorder(types, func(node ast.Node) {
+ t := node.(*ast.TypeSpec)
+ allTypes[t.Name.Name] = struct{}{}
+ typesSpecs[t.Name.Name] = t
+ })
+
+ errorTypes := stringSet{}
+ insp.Preorder(funcs, func(node ast.Node) {
+ f := node.(*ast.FuncDecl)
+ t, ok := isMethodError(f)
+ if !ok {
+ return
+ }
+ errorTypes[t] = struct{}{}
+
+ tSpec, ok := typesSpecs[t]
+ if !ok {
+ panic("no specification for type " + t)
+ }
+
+ if _, ok := tSpec.Type.(*ast.ArrayType); ok {
+ if !isValidErrorArrayTypeName(t) {
+ reportAboutErrorType(pass, tSpec.Pos(), t, true)
+ }
+ } else if !isValidErrorTypeName(t) {
+ reportAboutErrorType(pass, tSpec.Pos(), t, false)
+ }
+ })
+
+ errorFuncs := stringSet{}
+ insp.Preorder(funcs, func(node ast.Node) {
+ f := node.(*ast.FuncDecl)
+ if isFuncReturningErr(f.Type, allTypes, errorTypes) {
+ errorFuncs[f.Name.Name] = struct{}{}
+ }
+ })
+
+ inspectPkgLevelVarsOnly := func(node ast.Node) bool {
+ switch v := node.(type) {
+ case *ast.FuncDecl:
+ return false
+
+ case *ast.ValueSpec:
+ if name, ok := isSentinelError(v, pkgAliases, allTypes, errorTypes, errorFuncs); ok && !isValidErrorVarName(name) {
+ reportAboutErrorVar(pass, v.Pos(), name)
+ }
+ }
+ return true
+ }
+ for _, f := range pass.Files {
+ ast.Inspect(f, inspectPkgLevelVarsOnly)
+ }
+
+ return nil, nil //nolint:nilnil
+}
+
+func reportAboutErrorType(pass *analysis.Pass, typePos token.Pos, typeName string, isArrayType bool) {
+ var form string
+ if unicode.IsLower([]rune(typeName)[0]) {
+ form = "xxxError"
+ } else {
+ form = "XxxError"
+ }
+
+ if isArrayType {
+ form += "s"
+ }
+ pass.Reportf(typePos, "the type name `%s` should conform to the `%s` format", typeName, form)
+}
+
+func reportAboutErrorVar(pass *analysis.Pass, pos token.Pos, varName string) {
+ var form string
+ if unicode.IsLower([]rune(varName)[0]) {
+ form = "errXxx"
+ } else {
+ form = "ErrXxx"
+ }
+ pass.Reportf(pos, "the variable name `%s` should conform to the `%s` format", varName, form)
+}
+
+func getPkgFromPath(p string) string {
+ idx := strings.LastIndex(p, "/")
+ if idx == -1 {
+ return p
+ }
+ return p[idx+1:]
+}
diff --git a/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go b/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go
new file mode 100644
index 000000000..8711f9cf5
--- /dev/null
+++ b/vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go
@@ -0,0 +1,266 @@
+package analyzer
+
+import (
+ "go/ast"
+ "go/token"
+ "strings"
+ "unicode"
+)
+
+func isMethodError(f *ast.FuncDecl) (typeName string, ok bool) {
+ if f.Recv == nil || len(f.Recv.List) != 1 {
+ return "", false
+ }
+ if f.Name == nil || f.Name.Name != "Error" {
+ return "", false
+ }
+
+ if f.Type == nil || f.Type.Results == nil || len(f.Type.Results.List) != 1 {
+ return "", false
+ }
+
+ returnType, ok := f.Type.Results.List[0].Type.(*ast.Ident)
+ if !ok {
+ return "", false
+ }
+
+ var receiverType string
+
+ unwrapIdentName := func(e ast.Expr) string {
+ switch v := e.(type) {
+ case *ast.Ident:
+ return v.Name
+ case *ast.IndexExpr:
+ if i, ok := v.X.(*ast.Ident); ok {
+ return i.Name
+ }
+ }
+ return ""
+ }
+
+ switch rt := f.Recv.List[0].Type; v := rt.(type) {
+ case *ast.Ident, *ast.IndexExpr: // SomeError, SomeError[T]
+ receiverType = unwrapIdentName(rt)
+
+ case *ast.StarExpr: // *SomeError, *SomeError[T]
+ receiverType = unwrapIdentName(v.X)
+ }
+
+ return receiverType, returnType.Name == "string"
+}
+
+func isValidErrorTypeName(s string) bool {
+ if isInitialism(s) {
+ return true
+ }
+
+ words := split(s)
+ wordsCnt := wordsCount(words)
+
+ if wordsCnt["error"] != 1 {
+ return false
+ }
+ return words[len(words)-1] == "error"
+}
+
+func isValidErrorArrayTypeName(s string) bool {
+ if isInitialism(s) {
+ return true
+ }
+
+ words := split(s)
+ wordsCnt := wordsCount(words)
+
+ if wordsCnt["errors"] != 1 {
+ return false
+ }
+ return words[len(words)-1] == "errors"
+}
+
+func isFuncReturningErr(fType *ast.FuncType, allTypes, errorTypes stringSet) bool {
+ if fType == nil || fType.Results == nil || len(fType.Results.List) != 1 {
+ return false
+ }
+
+ var returnTypeName string
+ switch rt := fType.Results.List[0].Type.(type) {
+ case *ast.Ident:
+ returnTypeName = rt.Name
+ case *ast.StarExpr:
+ if i, ok := rt.X.(*ast.Ident); ok {
+ returnTypeName = i.Name
+ }
+ }
+
+ return isErrorType(returnTypeName, allTypes, errorTypes)
+}
+
+func isErrorType(tName string, allTypes, errorTypes stringSet) bool {
+ _, isUserType := allTypes[tName]
+ _, isErrType := errorTypes[tName]
+ return isErrType || (tName == "error" && !isUserType)
+}
+
+var knownErrConstructors = stringSet{
+ "fmt.Errorf": {},
+ "errors.Errorf": {},
+ "errors.New": {},
+ "errors.Newf": {},
+ "errors.NewWithDepth": {},
+ "errors.NewWithDepthf": {},
+ "errors.NewAssertionErrorWithWrappedErrf": {},
+}
+
+func isSentinelError( //nolint:gocognit,gocyclo
+ v *ast.ValueSpec,
+ pkgAliases map[string]string,
+ allTypes, errorTypes, errorFuncs stringSet,
+) (varName string, ok bool) {
+ if len(v.Names) != 1 {
+ return "", false
+ }
+ varName = v.Names[0].Name
+
+ switch vv := v.Type.(type) {
+ // var ErrEndOfFile error
+ // var ErrEndOfFile SomeErrType
+ case *ast.Ident:
+ if isErrorType(vv.Name, allTypes, errorTypes) {
+ return varName, true
+ }
+
+ // var ErrEndOfFile *SomeErrType
+ case *ast.StarExpr:
+ if i, ok := vv.X.(*ast.Ident); ok && isErrorType(i.Name, allTypes, errorTypes) {
+ return varName, true
+ }
+ }
+
+ if len(v.Values) != 1 {
+ return "", false
+ }
+
+ switch vv := v.Values[0].(type) {
+ case *ast.CallExpr:
+ switch fun := vv.Fun.(type) {
+ // var ErrEndOfFile = errors.New("end of file")
+ case *ast.SelectorExpr:
+ pkg, ok := fun.X.(*ast.Ident)
+ if !ok {
+ return "", false
+ }
+ pkgFun := fun.Sel
+
+ pkgName := pkg.Name
+ if a, ok := pkgAliases[pkgName]; ok {
+ pkgName = a
+ }
+
+ _, ok = knownErrConstructors[pkgName+"."+pkgFun.Name]
+ return varName, ok
+
+ // var ErrEndOfFile = newErrEndOfFile()
+ // var ErrEndOfFile = new(EndOfFileError)
+ // const ErrEndOfFile = constError("end of file")
+ // var statusCodeError = new(SomePtrError[string])
+ case *ast.Ident:
+ if isErrorType(fun.Name, allTypes, errorTypes) {
+ return varName, true
+ }
+
+ if _, ok := errorFuncs[fun.Name]; ok {
+ return varName, true
+ }
+
+ if fun.Name == "new" && len(vv.Args) == 1 {
+ switch i := vv.Args[0].(type) {
+ case *ast.Ident:
+ return varName, isErrorType(i.Name, allTypes, errorTypes)
+ case *ast.IndexExpr:
+ if ii, ok := i.X.(*ast.Ident); ok {
+ return varName, isErrorType(ii.Name, allTypes, errorTypes)
+ }
+ }
+ }
+
+ // var ErrEndOfFile = func() error { ... }
+ case *ast.FuncLit:
+ return varName, isFuncReturningErr(fun.Type, allTypes, errorTypes)
+ }
+
+ // var ErrEndOfFile = &EndOfFileError{}
+ // var ErrOK = &SomePtrError[string]{Code: "200 OK"}
+ case *ast.UnaryExpr:
+ if vv.Op == token.AND { // &
+ if lit, ok := vv.X.(*ast.CompositeLit); ok {
+ switch i := lit.Type.(type) {
+ case *ast.Ident:
+ return varName, isErrorType(i.Name, allTypes, errorTypes)
+ case *ast.IndexExpr:
+ if ii, ok := i.X.(*ast.Ident); ok {
+ return varName, isErrorType(ii.Name, allTypes, errorTypes)
+ }
+ }
+ }
+ }
+
+ // var ErrEndOfFile = EndOfFileError{}
+ // var ErrNotFound = SomeError[string]{Code: "Not Found"}
+ case *ast.CompositeLit:
+ switch i := vv.Type.(type) {
+ case *ast.Ident:
+ return varName, isErrorType(i.Name, allTypes, errorTypes)
+ case *ast.IndexExpr:
+ if ii, ok := i.X.(*ast.Ident); ok {
+ return varName, isErrorType(ii.Name, allTypes, errorTypes)
+ }
+ }
+ }
+
+ return "", false
+}
+
+func isValidErrorVarName(s string) bool {
+ if isInitialism(s) {
+ return true
+ }
+
+ words := split(s)
+ wordsCnt := wordsCount(words)
+
+ if wordsCnt["err"] != 1 {
+ return false
+ }
+ return words[0] == "err"
+}
+
+func isInitialism(s string) bool {
+ return strings.ToLower(s) == s || strings.ToUpper(s) == s
+}
+
+func split(s string) []string {
+ var words []string
+ ss := []rune(s)
+
+ var b strings.Builder
+ b.WriteRune(ss[0])
+
+ for _, r := range ss[1:] {
+ if unicode.IsUpper(r) {
+ words = append(words, strings.ToLower(b.String()))
+ b.Reset()
+ }
+ b.WriteRune(r)
+ }
+
+ words = append(words, strings.ToLower(b.String()))
+ return words
+}
+
+func wordsCount(w []string) map[string]int {
+ result := make(map[string]int, len(w))
+ for _, ww := range w {
+ result[ww]++
+ }
+ return result
+}