1
0

[CI] Add more checks (#43)

This commit is contained in:
konrad
2018-12-28 22:15:05 +00:00
committed by Gitea
parent 3814b8a504
commit 018dd8164c
73 changed files with 16332 additions and 6 deletions

21
vendor/github.com/jgautheron/goconst/LICENSE generated vendored Normal file
View File

@ -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.

49
vendor/github.com/jgautheron/goconst/README.md generated vendored Normal file
View File

@ -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 <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)
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

View File

@ -0,0 +1,166 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
"github.com/jgautheron/goconst"
)
const usageDoc = `goconst: find repeated strings that could be replaced by a constant
Usage:
goconst ARGS <directory> [<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 strings
-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 .
`
var (
flagIgnore = flag.String("ignore", "", "ignore files matching the given regular expression")
flagIgnoreTests = flag.Bool("ignore-tests", true, "exclude tests from the search")
flagMinOccurrences = flag.Int("min-occurrences", 2, "report from how many occurrences")
flagMinLength = flag.Int("min-length", 3, "only report strings with the minimum given length")
flagMatchConstant = flag.Bool("match-constant", false, "look for existing constants matching the strings")
flagNumbers = flag.Bool("numbers", false, "search also for duplicated numbers")
flagMin = flag.Int("min", 0, "minimum value, only works with -numbers")
flagMax = flag.Int("max", 0, "maximum value, only works with -numbers")
flagOutput = flag.String("output", "text", "output formatting")
)
func main() {
flag.Usage = func() {
usage(os.Stderr)
}
flag.Parse()
log.SetPrefix("goconst: ")
args := flag.Args()
if len(args) < 1 {
usage(os.Stderr)
os.Exit(1)
}
for _, path := range args {
if err := run(path); err != nil {
log.Println(err)
os.Exit(1)
}
}
}
func run(path string) error {
gco := goconst.New(
path,
*flagIgnore,
*flagIgnoreTests,
*flagMatchConstant,
*flagNumbers,
*flagMinLength,
)
strs, consts, err := gco.ParseTree()
if err != nil {
return err
}
return printOutput(strs, consts, *flagOutput, *flagMinOccurrences, *flagMin, *flagMax)
}
func usage(out io.Writer) {
fmt.Fprintf(out, usageDoc)
}
func printOutput(strs goconst.Strings, consts goconst.Constants, output string, minOccurrences, min, max int) 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)
}
}
}
switch output {
case "json":
enc := json.NewEncoder(os.Stdout)
err := enc.Encode(struct {
Strings goconst.Strings `json:"strings,omitEmpty"`
Constants goconst.Constants `json:"constants,omitEmpty"`
}{
strs, consts,
})
if err != nil {
return err
}
case "text":
for str, item := range strs {
for _, xpos := range item {
fmt.Printf(
`%s:%d:%d:%d other occurrence(s) of "%s" found in: %s`,
xpos.Filename,
xpos.Line,
xpos.Column,
len(item)-1,
str,
occurrences(item, xpos),
)
fmt.Print("\n")
}
if len(consts) == 0 {
continue
}
if cst, ok := consts[str]; ok {
// const should be in the same package and exported
fmt.Printf(`A matching constant has been found for "%s": %s`, str, cst.Name)
fmt.Printf("\n\t%s\n", cst.String())
}
}
default:
return fmt.Errorf(`Unsupported output format: %s`, output)
}
return nil
}
func occurrences(item []goconst.ExtendedPos, current goconst.ExtendedPos) string {
occurrences := []string{}
for _, xpos := range item {
if xpos == current {
continue
}
occurrences = append(occurrences, fmt.Sprintf(
"%s:%d:%d", xpos.Filename, xpos.Line, xpos.Column,
))
}
return strings.Join(occurrences, " ")
}

136
vendor/github.com/jgautheron/goconst/parser.go generated vendored Normal file
View File

@ -0,0 +1,136 @@
// 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
}

143
vendor/github.com/jgautheron/goconst/visitor.go generated vendored Normal file
View File

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