From c7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 4 Jul 2020 11:12:55 +0200 Subject: go.mod: vendor golangci-lint --- vendor/github.com/golangci/goconst/LICENSE | 21 +++ vendor/github.com/golangci/goconst/README.md | 49 +++++++ vendor/github.com/golangci/goconst/parser.go | 188 ++++++++++++++++++++++++++ vendor/github.com/golangci/goconst/visitor.go | 143 ++++++++++++++++++++ 4 files changed, 401 insertions(+) create mode 100644 vendor/github.com/golangci/goconst/LICENSE create mode 100644 vendor/github.com/golangci/goconst/README.md create mode 100644 vendor/github.com/golangci/goconst/parser.go create mode 100644 vendor/github.com/golangci/goconst/visitor.go (limited to 'vendor/github.com/golangci/goconst') diff --git a/vendor/github.com/golangci/goconst/LICENSE b/vendor/github.com/golangci/goconst/LICENSE new file mode 100644 index 000000000..e92649543 --- /dev/null +++ b/vendor/github.com/golangci/goconst/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Jonathan Gautheron + +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/golangci/goconst/README.md b/vendor/github.com/golangci/goconst/README.md new file mode 100644 index 000000000..04fc9b015 --- /dev/null +++ b/vendor/github.com/golangci/goconst/README.md @@ -0,0 +1,49 @@ +# goconst + +Find repeated strings that could be replaced by a constant. + +### Motivation + +There are obvious benefits to using constants instead of repeating strings, mostly to ease maintenance. Cannot argue against changing a single constant versus many strings. + +While this could be considered a beginner mistake, across time, multiple packages and large codebases, some repetition could have slipped in. + +### Get Started + + $ go get github.com/jgautheron/goconst/cmd/goconst + $ goconst ./... + +### Usage + +``` +Usage: + + goconst ARGS + +Flags: + + -ignore exclude files matching the given regular expression + -ignore-tests exclude tests from the search (default: true) + -min-occurrences report from how many occurrences (default: 2) + -min-length only report strings with the minimum given length (default: 3) + -match-constant look for existing constants matching the values + -numbers search also for duplicated numbers + -min minimum value, only works with -numbers + -max maximum value, only works with -numbers + -output output formatting (text or json) + +Examples: + + goconst ./... + goconst -ignore "yacc|\.pb\." $GOPATH/src/github.com/cockroachdb/cockroach/... + goconst -min-occurrences 3 -output json $GOPATH/src/github.com/cockroachdb/cockroach + goconst -numbers -min 60 -max 512 . +``` + +### Other static analysis tools + +- [gogetimports](https://github.com/jgautheron/gogetimports): Get a JSON-formatted list of imports. +- [usedexports](https://github.com/jgautheron/usedexports): Find exported variables that could be unexported. + +### License +MIT diff --git a/vendor/github.com/golangci/goconst/parser.go b/vendor/github.com/golangci/goconst/parser.go new file mode 100644 index 000000000..ab5f99a50 --- /dev/null +++ b/vendor/github.com/golangci/goconst/parser.go @@ -0,0 +1,188 @@ +// Package goconst finds repeated strings that could be replaced by a constant. +// +// There are obvious benefits to using constants instead of repeating strings, +// mostly to ease maintenance. Cannot argue against changing a single constant versus many strings. +// While this could be considered a beginner mistake, across time, +// multiple packages and large codebases, some repetition could have slipped in. +package goconst + +import ( + "go/ast" + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +const ( + testSuffix = "_test.go" +) + +type Parser struct { + // Meant to be passed via New() + path, ignore string + ignoreTests, matchConstant bool + minLength int + + supportedTokens []token.Token + + // Internals + strs Strings + consts Constants +} + +// New creates a new instance of the parser. +// This is your entry point if you'd like to use goconst as an API. +func New(path, ignore string, ignoreTests, matchConstant, numbers bool, minLength int) *Parser { + supportedTokens := []token.Token{token.STRING} + if numbers { + supportedTokens = append(supportedTokens, token.INT, token.FLOAT) + } + + return &Parser{ + path: path, + ignore: ignore, + ignoreTests: ignoreTests, + matchConstant: matchConstant, + minLength: minLength, + supportedTokens: supportedTokens, + + // Initialize the maps + strs: Strings{}, + consts: Constants{}, + } +} + +// ParseTree will search the given path for occurrences that could be moved into constants. +// If "..." is appended, the search will be recursive. +func (p *Parser) ParseTree() (Strings, Constants, error) { + pathLen := len(p.path) + // Parse recursively the given path if the recursive notation is found + if pathLen >= 5 && p.path[pathLen-3:] == "..." { + filepath.Walk(p.path[:pathLen-3], func(path string, f os.FileInfo, err error) error { + if err != nil { + log.Println(err) + // resume walking + return nil + } + + if f.IsDir() { + p.parseDir(path) + } + return nil + }) + } else { + p.parseDir(p.path) + } + return p.strs, p.consts, nil +} + +func (p *Parser) parseDir(dir string) error { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool { + valid, name := true, info.Name() + + if p.ignoreTests { + if strings.HasSuffix(name, testSuffix) { + valid = false + } + } + + if len(p.ignore) != 0 { + match, err := regexp.MatchString(p.ignore, dir+name) + if err != nil { + log.Fatal(err) + return true + } + if match { + valid = false + } + } + + return valid + }, 0) + if err != nil { + return err + } + + for _, pkg := range pkgs { + for fn, f := range pkg.Files { + ast.Walk(&treeVisitor{ + fileSet: fset, + packageName: pkg.Name, + fileName: fn, + p: p, + }, f) + } + } + + return nil +} + +type Strings map[string][]ExtendedPos +type Constants map[string]ConstType + +type ConstType struct { + token.Position + Name, packageName string +} + +type ExtendedPos struct { + token.Position + packageName string +} + +type Issue struct { + Pos token.Position + OccurencesCount int + Str string + MatchingConst string +} + +type Config struct { + MatchWithConstants bool + MinStringLength int + MinOccurrences int +} + +func Run(files []*ast.File, fset *token.FileSet, cfg *Config) ([]Issue, error) { + p := New("", "", false, cfg.MatchWithConstants, false, cfg.MinStringLength) + var issues []Issue + for _, f := range files { + ast.Walk(&treeVisitor{ + fileSet: fset, + packageName: "", + fileName: "", + p: p, + }, f) + } + + for str, item := range p.strs { + // Filter out items whose occurrences don't match the min value + if len(item) < cfg.MinOccurrences { + delete(p.strs, str) + } + } + + for str, item := range p.strs { + fi := item[0] + i := Issue{ + Pos: fi.Position, + OccurencesCount: len(item), + Str: str, + } + + if len(p.consts) != 0 { + if cst, ok := p.consts[str]; ok { + // const should be in the same package and exported + i.MatchingConst = cst.Name + } + } + issues = append(issues, i) + } + + return issues, nil +} diff --git a/vendor/github.com/golangci/goconst/visitor.go b/vendor/github.com/golangci/goconst/visitor.go new file mode 100644 index 000000000..a86421f6d --- /dev/null +++ b/vendor/github.com/golangci/goconst/visitor.go @@ -0,0 +1,143 @@ +package goconst + +import ( + "go/ast" + "go/token" + "strings" +) + +// treeVisitor carries the package name and file name +// for passing it to the imports map, and the fileSet for +// retrieving the token.Position. +type treeVisitor struct { + p *Parser + fileSet *token.FileSet + packageName, fileName string +} + +// Visit browses the AST tree for strings that could be potentially +// replaced by constants. +// A map of existing constants is built as well (-match-constant). +func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { + if node == nil { + return v + } + + // A single case with "ast.BasicLit" would be much easier + // but then we wouldn't be able to tell in which context + // the string is defined (could be a constant definition). + switch t := node.(type) { + // Scan for constants in an attempt to match strings with existing constants + case *ast.GenDecl: + if !v.p.matchConstant { + return v + } + if t.Tok != token.CONST { + return v + } + + for _, spec := range t.Specs { + val := spec.(*ast.ValueSpec) + for i, str := range val.Values { + lit, ok := str.(*ast.BasicLit) + if !ok || !v.isSupported(lit.Kind) { + continue + } + + v.addConst(val.Names[i].Name, lit.Value, val.Names[i].Pos()) + } + } + + // foo := "moo" + case *ast.AssignStmt: + for _, rhs := range t.Rhs { + lit, ok := rhs.(*ast.BasicLit) + if !ok || !v.isSupported(lit.Kind) { + continue + } + + v.addString(lit.Value, rhs.(*ast.BasicLit).Pos()) + } + + // if foo == "moo" + case *ast.BinaryExpr: + if t.Op != token.EQL && t.Op != token.NEQ { + return v + } + + var lit *ast.BasicLit + var ok bool + + lit, ok = t.X.(*ast.BasicLit) + if ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos()) + } + + lit, ok = t.Y.(*ast.BasicLit) + if ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos()) + } + + // case "foo": + case *ast.CaseClause: + for _, item := range t.List { + lit, ok := item.(*ast.BasicLit) + if ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos()) + } + } + + // return "boo" + case *ast.ReturnStmt: + for _, item := range t.Results { + lit, ok := item.(*ast.BasicLit) + if ok && v.isSupported(lit.Kind) { + v.addString(lit.Value, lit.Pos()) + } + } + } + + return v +} + +// addString adds a string in the map along with its position in the tree. +func (v *treeVisitor) addString(str string, pos token.Pos) { + str = strings.Replace(str, `"`, "", 2) + + // Ignore empty strings + if len(str) == 0 { + return + } + + if len(str) < v.p.minLength { + return + } + + _, ok := v.p.strs[str] + if !ok { + v.p.strs[str] = make([]ExtendedPos, 0) + } + v.p.strs[str] = append(v.p.strs[str], ExtendedPos{ + packageName: v.packageName, + Position: v.fileSet.Position(pos), + }) +} + +// addConst adds a const in the map along with its position in the tree. +func (v *treeVisitor) addConst(name string, val string, pos token.Pos) { + val = strings.Replace(val, `"`, "", 2) + v.p.consts[val] = ConstType{ + Name: name, + packageName: v.packageName, + Position: v.fileSet.Position(pos), + } +} + +func (v *treeVisitor) isSupported(tk token.Token) bool { + for _, s := range v.p.supportedTokens { + if tk == s { + return true + } + } + return false +} -- cgit mrf-deployment