aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/jgautheron/goconst/parser.go
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/goconst/parser.go
parent8f23c528ad5a943b9ffec5dcaf332fd0f614006e (diff)
go.mod: update golangci-lint to v1.37
Diffstat (limited to 'vendor/github.com/jgautheron/goconst/parser.go')
-rw-r--r--vendor/github.com/jgautheron/goconst/parser.go176
1 files changed, 176 insertions, 0 deletions
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
+)