Merge pull request #983 from markus-wa/master

deps: upgrade mnd from v1.1.1 to 2.0.0
This commit is contained in:
Aleksandr Razumov 2020-03-12 20:08:05 +03:00 committed by GitHub
commit 517f7e5f63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 244 additions and 130 deletions

2
go.mod
View File

@ -39,7 +39,7 @@ require (
github.com/spf13/viper v1.6.1
github.com/stretchr/testify v1.4.0
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e
github.com/tommy-muehle/go-mnd v1.1.1
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa
github.com/ultraware/funlen v0.0.2
github.com/ultraware/whitespace v0.0.4
github.com/uudashr/gocognit v1.0.1

2
go.sum
View File

@ -260,6 +260,8 @@ github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiff
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tommy-muehle/go-mnd v1.1.1 h1:4D0wuPKjOTiK2garzuPGGvm4zZ/wLYDOH8TJSABC7KU=
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa h1:RC4maTWLKKwb7p1cnoygsbKIgNlJqSYBeAFON3Ar8As=
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo=

View File

@ -9,7 +9,7 @@ import (
func UseMagicNumber() {
c := &http.Client{
Timeout: 1 * time.Second, // ERROR : "Magic number: 1, in <assign> detected"
Timeout: 2 * time.Second, // ERROR : "Magic number: 2, in <assign> detected"
}
res, err := c.Get("http://www.google.com")

View File

@ -10,19 +10,21 @@ builds:
- amd64
ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.buildTime={{.Date}}`.
archive:
format: tar.gz
format_overrides:
archives:
-
format: tar.gz
format_overrides:
- goos: windows
format: zip
brew:
name: mnd
github:
brews:
-
name: mnd
github:
owner: tommy-muehle
name: homebrew-tap
folder: Formula
homepage: https://github.com/tommy-muehle/go-mnd
description: Magic number detector for Go
test: |
folder: Formula
homepage: https://github.com/tommy-muehle/go-mnd
description: Magic number detector for Go
test: |
system "#{bin}/mnd --version"

View File

@ -1,8 +1,12 @@
language: go
go:
- 1.13.x
- 1.12.x
- tip
script:
- go test -v ./...
notifications:
email: false

View File

@ -42,6 +42,10 @@ mnd ./...
The ```-checks``` option let's you define a comma separated list of checks.
The ```-ignored-numbers``` option let's you define a comma separated list of numbers to ignore.
The ```-excludes``` option let's you define a comma separated list of regexp patterns to exclude.
## Checks
By default this detector analyses arguments, assigns, cases, conditions, operations and return statements.
@ -88,9 +92,19 @@ y = 10 * x
return 3
```
## Notices
## Excludes
By default the number 0 is excluded!
By default the numbers 0 and 1 as well as test files are excluded!
### Further known excludes
The function "Date" in the "Time" package.
```
t := time.Date(2017, time.September, 26, 12, 13, 14, 0, time.UTC)
```
Additional custom excludes can be defined via option flag.
## License

View File

@ -5,6 +5,7 @@ import (
"go/ast"
"github.com/tommy-muehle/go-mnd/checks"
"github.com/tommy-muehle/go-mnd/config"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
@ -29,40 +30,59 @@ type Checker interface {
func options() flag.FlagSet {
options := flag.NewFlagSet("", flag.ExitOnError)
options.String("checks", "", "comma separated list of checks")
options.String("excludes", "", "comma separated list of patterns to exclude from analysis")
options.String("ignored-numbers", "", "comma separated list of numbers excluded from analysis")
options.String(
"checks",
checks.ArgumentCheck+","+
checks.CaseCheck+","+
checks.ConditionCheck+","+
checks.OperationCheck+","+
checks.ReturnCheck+","+
checks.AssignCheck,
"comma separated list of checks",
)
return *options
}
func run(pass *analysis.Pass) (interface{}, error) {
config := WithOptions(
WithCustomChecks(pass.Analyzer.Flags.Lookup("checks").Value.String()),
conf := config.WithOptions(
config.WithCustomChecks(pass.Analyzer.Flags.Lookup("checks").Value.String()),
config.WithExcludes(pass.Analyzer.Flags.Lookup("excludes").Value.String()),
config.WithIgnoredNumbers(pass.Analyzer.Flags.Lookup("ignored-numbers").Value.String()),
)
var checker []Checker
if config.IsCheckEnabled(checks.ArgumentCheck) {
checker = append(checker, checks.NewArgumentAnalyzer(pass))
if conf.IsCheckEnabled(checks.ArgumentCheck) {
checker = append(checker, checks.NewArgumentAnalyzer(pass, conf))
}
if config.IsCheckEnabled(checks.CaseCheck) {
checker = append(checker, checks.NewCaseAnalyzer(pass))
if conf.IsCheckEnabled(checks.CaseCheck) {
checker = append(checker, checks.NewCaseAnalyzer(pass, conf))
}
if config.IsCheckEnabled(checks.ConditionCheck) {
checker = append(checker, checks.NewConditionAnalyzer(pass))
if conf.IsCheckEnabled(checks.ConditionCheck) {
checker = append(checker, checks.NewConditionAnalyzer(pass, conf))
}
if config.IsCheckEnabled(checks.OperationCheck) {
checker = append(checker, checks.NewOperationAnalyzer(pass))
if conf.IsCheckEnabled(checks.OperationCheck) {
checker = append(checker, checks.NewOperationAnalyzer(pass, conf))
}
if config.IsCheckEnabled(checks.ReturnCheck) {
checker = append(checker, checks.NewReturnAnalyzer(pass))
if conf.IsCheckEnabled(checks.ReturnCheck) {
checker = append(checker, checks.NewReturnAnalyzer(pass, conf))
}
if config.IsCheckEnabled(checks.AssignCheck) {
checker = append(checker, checks.NewAssignAnalyzer(pass))
if conf.IsCheckEnabled(checks.AssignCheck) {
checker = append(checker, checks.NewAssignAnalyzer(pass, conf))
}
i := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
for _, c := range checker {
i.Preorder(c.NodeFilter(), func(node ast.Node) {
for _, exclude := range conf.Excludes {
if exclude.MatchString(pass.Fset.Position(node.Pos()).Filename) {
return
}
}
c.Check(node)
})
}

View File

@ -2,8 +2,11 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const ArgumentCheck = "argument"
@ -15,12 +18,14 @@ var argumentExcludes = map[string]string{
}
type ArgumentAnalyzer struct {
pass *analysis.Pass
config *config.Config
pass *analysis.Pass
}
func NewArgumentAnalyzer(pass *analysis.Pass) *ArgumentAnalyzer {
func NewArgumentAnalyzer(pass *analysis.Pass, config *config.Config) *ArgumentAnalyzer {
return &ArgumentAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -44,7 +49,7 @@ func (a *ArgumentAnalyzer) Check(n ast.Node) {
for i, arg := range expr.Args {
switch x := arg.(type) {
case *ast.BasicLit:
if !isMagicNumber(x) {
if !a.isMagicNumber(x) {
continue
}
// If it's a magic number and has no previous element, report it
@ -55,7 +60,7 @@ func (a *ArgumentAnalyzer) Check(n ast.Node) {
switch expr.Args[i-1].(type) {
case *ast.ChanType:
// When it's not a simple buffered channel, report it
if x.Value != "1" {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, ArgumentCheck)
}
}
@ -84,15 +89,19 @@ func (a *ArgumentAnalyzer) isExcluded(expr *ast.SelectorExpr) bool {
func (a *ArgumentAnalyzer) checkBinaryExpr(expr *ast.BinaryExpr) {
switch x := expr.X.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, ArgumentCheck)
}
}
switch y := expr.Y.(type) {
case *ast.BasicLit:
if isMagicNumber(y) {
if a.isMagicNumber(y) {
a.pass.Reportf(y.Pos(), reportMsg, y.Value, ArgumentCheck)
}
}
}
func (a *ArgumentAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -2,19 +2,24 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const AssignCheck = "assign"
type AssignAnalyzer struct {
pass *analysis.Pass
pass *analysis.Pass
config *config.Config
}
func NewAssignAnalyzer(pass *analysis.Pass) *AssignAnalyzer {
func NewAssignAnalyzer(pass *analysis.Pass, config *config.Config) *AssignAnalyzer {
return &AssignAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -32,7 +37,7 @@ func (a *AssignAnalyzer) Check(n ast.Node) {
switch x := expr.Value.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, AssignCheck)
}
case *ast.BinaryExpr:
@ -43,15 +48,19 @@ func (a *AssignAnalyzer) Check(n ast.Node) {
func (a *AssignAnalyzer) checkBinaryExpr(expr *ast.BinaryExpr) {
switch x := expr.X.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, AssignCheck)
}
}
switch y := expr.Y.(type) {
case *ast.BasicLit:
if isMagicNumber(y) {
if a.isMagicNumber(y) {
a.pass.Reportf(y.Pos(), reportMsg, y.Value, AssignCheck)
}
}
}
func (a *AssignAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -2,19 +2,24 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const CaseCheck = "case"
type CaseAnalyzer struct {
pass *analysis.Pass
pass *analysis.Pass
config *config.Config
}
func NewCaseAnalyzer(pass *analysis.Pass) *CaseAnalyzer {
func NewCaseAnalyzer(pass *analysis.Pass, config *config.Config) *CaseAnalyzer {
return &CaseAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -33,7 +38,7 @@ func (a *CaseAnalyzer) Check(n ast.Node) {
for _, c := range caseClause.List {
switch x := c.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, CaseCheck)
}
case *ast.BinaryExpr:
@ -45,15 +50,19 @@ func (a *CaseAnalyzer) Check(n ast.Node) {
func (a *CaseAnalyzer) checkBinaryExpr(expr *ast.BinaryExpr) {
switch x := expr.X.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, CaseCheck)
}
}
switch y := expr.Y.(type) {
case *ast.BasicLit:
if isMagicNumber(y) {
if a.isMagicNumber(y) {
a.pass.Reportf(y.Pos(), reportMsg, y.Value, CaseCheck)
}
}
}
func (a *CaseAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -1,12 +1,3 @@
package checks
import (
"go/ast"
"go/token"
)
const reportMsg = "Magic number: %v, in <%s> detected"
func isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && l.Value != "0"
}

View File

@ -2,19 +2,24 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const ConditionCheck = "condition"
type ConditionAnalyzer struct {
pass *analysis.Pass
pass *analysis.Pass
config *config.Config
}
func NewConditionAnalyzer(pass *analysis.Pass) *ConditionAnalyzer {
func NewConditionAnalyzer(pass *analysis.Pass, config *config.Config) *ConditionAnalyzer {
return &ConditionAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -32,15 +37,19 @@ func (a *ConditionAnalyzer) Check(n ast.Node) {
switch x := expr.X.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, ConditionCheck)
}
}
switch y := expr.Y.(type) {
case *ast.BasicLit:
if isMagicNumber(y) {
if a.isMagicNumber(y) {
a.pass.Reportf(y.Pos(), reportMsg, y.Value, ConditionCheck)
}
}
}
func (a *ConditionAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -2,19 +2,24 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const OperationCheck = "operation"
type OperationAnalyzer struct {
pass *analysis.Pass
pass *analysis.Pass
config *config.Config
}
func NewOperationAnalyzer(pass *analysis.Pass) *OperationAnalyzer {
func NewOperationAnalyzer(pass *analysis.Pass, config *config.Config) *OperationAnalyzer {
return &OperationAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -50,15 +55,19 @@ func (a *OperationAnalyzer) Check(n ast.Node) {
func (a *OperationAnalyzer) checkBinaryExpr(expr *ast.BinaryExpr) {
switch x := expr.X.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, OperationCheck)
}
}
switch y := expr.Y.(type) {
case *ast.BasicLit:
if isMagicNumber(y) {
if a.isMagicNumber(y) {
a.pass.Reportf(y.Pos(), reportMsg, y.Value, OperationCheck)
}
}
}
func (a *OperationAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -2,19 +2,24 @@ package checks
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/analysis"
config "github.com/tommy-muehle/go-mnd/config"
)
const ReturnCheck = "return"
type ReturnAnalyzer struct {
pass *analysis.Pass
pass *analysis.Pass
config *config.Config
}
func NewReturnAnalyzer(pass *analysis.Pass) *ReturnAnalyzer {
func NewReturnAnalyzer(pass *analysis.Pass, config *config.Config) *ReturnAnalyzer {
return &ReturnAnalyzer{
pass: pass,
pass: pass,
config: config,
}
}
@ -33,9 +38,13 @@ func (a *ReturnAnalyzer) Check(n ast.Node) {
for _, expr := range stmt.Results {
switch x := expr.(type) {
case *ast.BasicLit:
if isMagicNumber(x) {
if a.isMagicNumber(x) {
a.pass.Reportf(x.Pos(), reportMsg, x.Value, ReturnCheck)
}
}
}
}
func (a *ReturnAnalyzer) isMagicNumber(l *ast.BasicLit) bool {
return (l.Kind == token.FLOAT || l.Kind == token.INT) && !a.config.IsIgnoredNumber(l.Value)
}

View File

@ -1,58 +0,0 @@
package magic_numbers
import (
"strings"
"github.com/tommy-muehle/go-mnd/checks"
)
var knownChecks = map[string]bool{
checks.ArgumentCheck: true,
checks.CaseCheck: true,
checks.ConditionCheck: true,
checks.OperationCheck: true,
checks.ReturnCheck: true,
checks.AssignCheck: true,
}
type Config struct {
Checks map[string]bool
}
type Option func(config *Config)
func DefaultConfig() *Config {
return &Config{
Checks: knownChecks,
}
}
func WithOptions(options ...Option) *Config {
c := DefaultConfig()
for _, option := range options {
option(c)
}
return c
}
func WithCustomChecks(checks string) Option {
return func(config *Config) {
config.Checks = knownChecks
if checks == "" {
return
}
for name, _ := range knownChecks {
config.Checks[name] = false
}
for _, name := range strings.Split(checks, ",") {
config.Checks[name] = true
}
}
}
func (c *Config) IsCheckEnabled(name string) bool {
return c.Checks[name]
}

84
vendor/github.com/tommy-muehle/go-mnd/config/config.go generated vendored Normal file
View File

@ -0,0 +1,84 @@
package config
import (
"regexp"
"strings"
)
type Config struct {
Checks map[string]bool
IgnoredNumbers map[string]struct{}
Excludes []*regexp.Regexp
}
type Option func(config *Config)
func DefaultConfig() *Config {
return &Config{
Checks: map[string]bool{},
IgnoredNumbers: map[string]struct{}{
"0": {},
"1": {},
},
Excludes: []*regexp.Regexp{
regexp.MustCompile(`_test.go`),
},
}
}
func WithOptions(options ...Option) *Config {
c := DefaultConfig()
for _, option := range options {
option(c)
}
return c
}
func WithExcludes(excludes string) Option {
return func(config *Config) {
if excludes == "" {
return
}
for _, exclude := range strings.Split(excludes, ",") {
config.Excludes = append(config.Excludes, regexp.MustCompile(exclude))
}
}
}
func WithIgnoredNumbers(numbers string) Option {
return func(config *Config) {
if numbers == "" {
return
}
for _, number := range strings.Split(numbers, ",") {
config.IgnoredNumbers[number] = struct{}{}
}
}
}
func WithCustomChecks(checks string) Option {
return func(config *Config) {
if checks == "" {
return
}
for name, _ := range config.Checks {
config.Checks[name] = false
}
for _, name := range strings.Split(checks, ",") {
config.Checks[name] = true
}
}
}
func (c *Config) IsCheckEnabled(name string) bool {
return c.Checks[name]
}
func (c *Config) IsIgnoredNumber(number string) bool {
_, ok := c.IgnoredNumbers[number]
return ok
}

3
vendor/modules.txt vendored
View File

@ -176,9 +176,10 @@ github.com/stretchr/testify/require
github.com/subosito/gotenv
# github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e
github.com/timakin/bodyclose/passes/bodyclose
# github.com/tommy-muehle/go-mnd v1.1.1
# github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa
github.com/tommy-muehle/go-mnd
github.com/tommy-muehle/go-mnd/checks
github.com/tommy-muehle/go-mnd/config
# github.com/ultraware/funlen v0.0.2
github.com/ultraware/funlen
# github.com/ultraware/whitespace v0.0.4