From ce0d7c014a65a9555788fbfb8ee5086ad9885ccd Mon Sep 17 00:00:00 2001 From: Jonathan Gautheron Date: Sun, 8 Nov 2020 14:23:56 +0100 Subject: [PATCH] Expose API --- api.go | 64 +++++++++++++++++++++++++++++++++++++++++++++ cmd/goconst/main.go | 27 +++++-------------- parser.go | 32 +++++++++++++++++++++-- tests/foo_test.go | 10 ++++--- tests/main.go | 10 +++++++ visitor.go | 9 +++++-- 6 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 api.go diff --git a/api.go b/api.go new file mode 100644 index 0000000..f867dd3 --- /dev/null +++ b/api.go @@ -0,0 +1,64 @@ +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 +} + +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, + ) + var issues []Issue + for _, f := range files { + ast.Walk(&treeVisitor{ + fileSet: fset, + packageName: "", + fileName: "", + p: p, + }, f) + } + + 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/cmd/goconst/main.go b/cmd/goconst/main.go index d55e38b..288c5d1 100644 --- a/cmd/goconst/main.go +++ b/cmd/goconst/main.go @@ -7,7 +7,6 @@ import ( "io" "log" "os" - "strconv" "strings" "github.com/jgautheron/goconst" @@ -91,38 +90,24 @@ func run(path string) (bool, error) { *flagIgnoreTests, *flagMatchConstant, *flagNumbers, + *flagMin, + *flagMax, *flagMinLength, + *flagMinOccurrences, ) strs, consts, err := gco.ParseTree() if err != nil { return false, err } - return printOutput(strs, consts, *flagOutput, *flagMinOccurrences, *flagMin, *flagMax) + return printOutput(strs, consts, *flagOutput) } func usage(out io.Writer) { fmt.Fprintf(out, usageDoc) } -func printOutput(strs goconst.Strings, consts goconst.Constants, output string, minOccurrences, min, max int) (bool, error) { - for str, item := range strs { - // Filter out items whose occurrences don't match the min value - if len(item) < minOccurrences { - delete(strs, str) - } - - // If the value is a number - if i, err := strconv.Atoi(str); err == nil { - if min != 0 && i < min { - delete(strs, str) - } - if max != 0 && i > max { - delete(strs, str) - } - } - } - +func printOutput(strs goconst.Strings, consts goconst.Constants, output string) (bool, error) { switch output { case "json": enc := json.NewEncoder(os.Stdout) @@ -162,7 +147,7 @@ func printOutput(strs goconst.Strings, consts goconst.Constants, output string, default: return false, fmt.Errorf(`Unsupported output format: %s`, output) } - return len(strs) + len(consts) > 0, nil + return len(strs)+len(consts) > 0, nil } func occurrences(item []goconst.ExtendedPos, current goconst.ExtendedPos) string { diff --git a/parser.go b/parser.go index 5f83723..d44709b 100644 --- a/parser.go +++ b/parser.go @@ -14,6 +14,7 @@ import ( "os" "path/filepath" "regexp" + "strconv" "strings" ) @@ -25,7 +26,8 @@ type Parser struct { // Meant to be passed via New() path, ignore string ignoreTests, matchConstant bool - minLength int + minLength, minOccurrences int + numberMin, numberMax int supportedTokens []token.Token @@ -36,7 +38,7 @@ type Parser struct { // 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 { +func New(path, ignore string, ignoreTests, matchConstant, numbers bool, numberMin, numberMax, minLength, minOccurrences int) *Parser { supportedTokens := []token.Token{token.STRING} if numbers { supportedTokens = append(supportedTokens, token.INT, token.FLOAT) @@ -48,6 +50,9 @@ func New(path, ignore string, ignoreTests, matchConstant, numbers bool, minLengt ignoreTests: ignoreTests, matchConstant: matchConstant, minLength: minLength, + minOccurrences: minOccurrences, + numberMin: numberMin, + numberMax: numberMax, supportedTokens: supportedTokens, // Initialize the maps @@ -77,9 +82,32 @@ func (p *Parser) ParseTree() (Strings, Constants, error) { } 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 { diff --git a/tests/foo_test.go b/tests/foo_test.go index 96395f1..8de5add 100644 --- a/tests/foo_test.go +++ b/tests/foo_test.go @@ -3,7 +3,11 @@ package main import "fmt" func foo() { - foo := "test" - boo := "test" - fmt.Println(foo, boo) + testString1 := "test" + testString2 := "test" + + testInt1 := 123 + testInt2 := 123 + + fmt.Println(testString1, testString2, testInt1, testInt2) } diff --git a/tests/main.go b/tests/main.go index 227ab61..d43e983 100644 --- a/tests/main.go +++ b/tests/main.go @@ -6,6 +6,7 @@ import ( ) const Foo = "bar" +const NumberConst = 123 var url string @@ -28,3 +29,12 @@ func testCase() string { } return "foo" } + +func testInt() int { + test := 123 + if test == 123 { + return 123 + } + + return 123 +} diff --git a/visitor.go b/visitor.go index 27cd5d8..8284bd9 100644 --- a/visitor.go +++ b/visitor.go @@ -1,6 +1,7 @@ package goconst import ( + "fmt" "go/ast" "go/token" "strings" @@ -111,8 +112,12 @@ func (v *treeVisitor) Visit(node ast.Node) ast.Visitor { // addString adds a string in the map along with its position in the tree. func (v *treeVisitor) addString(str string, pos token.Pos) { - // Drop first and last character, quote, backquote... - str = str[1 : len(str)-1] + if strings.HasPrefix(str, `"`) || strings.HasPrefix(str, "`") { + // Drop first and last character, quote, backquote... + str = str[1 : len(str)-1] + } + + fmt.Println(str) // Ignore empty strings if len(str) == 0 {