api/vendor/github.com/jgautheron/goconst/parser.go

137 lines
2.9 KiB
Go

// 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
}