diff options
| author | Taras Madan <tarasmadan@google.com> | 2022-09-05 14:27:54 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-05 12:27:54 +0000 |
| commit | b2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch) | |
| tree | 923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/Antonboom/errname | |
| parent | e6654faff4bcca4be92e9a8596fd4b77f747c39e (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/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/Antonboom/errname/pkg/analyzer/analyzer.go | 134 | ||||
| -rw-r--r-- | vendor/github.com/Antonboom/errname/pkg/analyzer/facts.go | 266 |
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 +} |
