aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/jgautheron
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2021-02-22 20:37:25 +0100
committerDmitry Vyukov <dvyukov@google.com>2021-02-22 21:02:12 +0100
commitfcc6d71be2c3ce7d9305c04fc2e87af554571bac (patch)
treeb01dbb3d1e2988e28ea158d2d543d603ec0b9569 /vendor/github.com/jgautheron
parent8f23c528ad5a943b9ffec5dcaf332fd0f614006e (diff)
go.mod: update golangci-lint to v1.37
Diffstat (limited to 'vendor/github.com/jgautheron')
-rw-r--r--vendor/github.com/jgautheron/goconst/LICENSE21
-rw-r--r--vendor/github.com/jgautheron/goconst/README.md50
-rw-r--r--vendor/github.com/jgautheron/goconst/api.go67
-rw-r--r--vendor/github.com/jgautheron/goconst/go.mod3
-rw-r--r--vendor/github.com/jgautheron/goconst/parser.go176
-rw-r--r--vendor/github.com/jgautheron/goconst/visitor.go160
6 files changed, 477 insertions, 0 deletions
diff --git a/vendor/github.com/jgautheron/goconst/LICENSE b/vendor/github.com/jgautheron/goconst/LICENSE
new file mode 100644
index 000000000..e92649543
--- /dev/null
+++ b/vendor/github.com/jgautheron/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/jgautheron/goconst/README.md b/vendor/github.com/jgautheron/goconst/README.md
new file mode 100644
index 000000000..8dd093baf
--- /dev/null
+++ b/vendor/github.com/jgautheron/goconst/README.md
@@ -0,0 +1,50 @@
+# 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 <directory>
+
+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)
+ -set-exit-status Set exit status to 2 if any issues are found
+
+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/jgautheron/goconst/api.go b/vendor/github.com/jgautheron/goconst/api.go
new file mode 100644
index 000000000..e58894bc4
--- /dev/null
+++ b/vendor/github.com/jgautheron/goconst/api.go
@@ -0,0 +1,67 @@
+package goconst
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+type Issue struct {
+ Pos token.Position
+ OccurrencesCount int
+ Str string
+ MatchingConst string
+}
+
+type Config struct {
+ MatchWithConstants bool
+ MinStringLength int
+ MinOccurrences int
+ ParseNumbers bool
+ NumberMin int
+ NumberMax int
+ ExcludeTypes map[Type]bool
+}
+
+func Run(files []*ast.File, fset *token.FileSet, cfg *Config) ([]Issue, error) {
+ p := New(
+ "",
+ "",
+ false,
+ cfg.MatchWithConstants,
+ cfg.ParseNumbers,
+ cfg.NumberMin,
+ cfg.NumberMax,
+ cfg.MinStringLength,
+ cfg.MinOccurrences,
+ cfg.ExcludeTypes,
+ )
+ var issues []Issue
+ for _, f := range files {
+ ast.Walk(&treeVisitor{
+ fileSet: fset,
+ packageName: "",
+ fileName: "",
+ p: p,
+ }, f)
+ }
+ p.ProcessResults()
+
+ for str, item := range p.strs {
+ fi := item[0]
+ i := Issue{
+ Pos: fi.Position,
+ OccurrencesCount: 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/jgautheron/goconst/go.mod b/vendor/github.com/jgautheron/goconst/go.mod
new file mode 100644
index 000000000..53dbfbbb9
--- /dev/null
+++ b/vendor/github.com/jgautheron/goconst/go.mod
@@ -0,0 +1,3 @@
+module github.com/jgautheron/goconst
+
+go 1.13
diff --git a/vendor/github.com/jgautheron/goconst/parser.go b/vendor/github.com/jgautheron/goconst/parser.go
new file mode 100644
index 000000000..2ed7a9a90
--- /dev/null
+++ b/vendor/github.com/jgautheron/goconst/parser.go
@@ -0,0 +1,176 @@
+// 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"
+ "strconv"
+ "strings"
+)
+
+const (
+ testSuffix = "_test.go"
+)
+
+type Parser struct {
+ // Meant to be passed via New()
+ path, ignore string
+ ignoreTests, matchConstant bool
+ minLength, minOccurrences int
+ numberMin, numberMax int
+ excludeTypes map[Type]bool
+
+ 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, numberMin, numberMax, minLength, minOccurrences int, excludeTypes map[Type]bool) *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,
+ minOccurrences: minOccurrences,
+ numberMin: numberMin,
+ numberMax: numberMax,
+ supportedTokens: supportedTokens,
+ excludeTypes: excludeTypes,
+
+ // 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)
+ }
+
+ p.ProcessResults()
+
+ return p.strs, p.consts, nil
+}
+
+// ProcessResults post-processes the raw results.
+func (p *Parser) ProcessResults() {
+ for str, item := range p.strs {
+ // Filter out items whose occurrences don't match the min value
+ if len(item) < p.minOccurrences {
+ delete(p.strs, str)
+ }
+
+ // If the value is a number
+ if i, err := strconv.Atoi(str); err == nil {
+ if p.numberMin != 0 && i < p.numberMin {
+ delete(p.strs, str)
+ }
+ if p.numberMax != 0 && i > p.numberMax {
+ delete(p.strs, str)
+ }
+ }
+ }
+}
+
+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 Type int
+
+const (
+ Assignment Type = iota
+ Binary
+ Case
+ Return
+ Call
+)
diff --git a/vendor/github.com/jgautheron/goconst/visitor.go b/vendor/github.com/jgautheron/goconst/visitor.go
new file mode 100644
index 000000000..c0974da8f
--- /dev/null
+++ b/vendor/github.com/jgautheron/goconst/visitor.go
@@ -0,0 +1,160 @@
+package goconst
+
+import (
+ "go/ast"
+ "go/token"
+ "strconv"
+ "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(), Assignment)
+ }
+
+ // 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(), Binary)
+ }
+
+ lit, ok = t.Y.(*ast.BasicLit)
+ if ok && v.isSupported(lit.Kind) {
+ v.addString(lit.Value, lit.Pos(), Binary)
+ }
+
+ // 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(), Case)
+ }
+ }
+
+ // 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)
+ }
+ }
+
+ // fn("http://")
+ case *ast.CallExpr:
+ for _, item := range t.Args {
+ lit, ok := item.(*ast.BasicLit)
+ if ok && v.isSupported(lit.Kind) {
+ v.addString(lit.Value, lit.Pos(), Call)
+ }
+ }
+ }
+
+ 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, typ Type) {
+ ok, excluded := v.p.excludeTypes[typ]
+ if ok && excluded {
+ return
+ }
+ // Drop quotes if any
+ if strings.HasPrefix(str, `"`) || strings.HasPrefix(str, "`") {
+ str, _ = strconv.Unquote(str)
+ }
+
+ // 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
+}