diff --git a/RULES_DESCRIPTIONS.md b/RULES_DESCRIPTIONS.md index ece29e9..56ad711 100644 --- a/RULES_DESCRIPTIONS.md +++ b/RULES_DESCRIPTIONS.md @@ -85,12 +85,13 @@ _Configuration_: * `allowStr`: (string) comma-separated list of allowed string literals * `allowInts`: (string) comma-separated list of allowed integers * `allowFloats`: (string) comma-separated list of allowed floats +* `ignoreFuncs`: (string) comma-separated list of function names regexp patterns to exclude Example: ```toml [rule.add-constant] - arguments = [{maxLitCount = "3",allowStrs ="\"\"",allowInts="0,1,2",allowFloats="0.0,0.,1.0,1.,2.0,2."}] + arguments = [{maxLitCount = "3",allowStrs ="\"\"",allowInts="0,1,2",allowFloats="0.0,0.,1.0,1.,2.0,2.","ignoreFuncs": "os.*,fmt.Println,make"}] ``` ## argument-limit diff --git a/rule/add-constant.go b/rule/add-constant.go index 414be38..36a7003 100644 --- a/rule/add-constant.go +++ b/rule/add-constant.go @@ -3,6 +3,7 @@ package rule import ( "fmt" "go/ast" + "regexp" "strconv" "strings" "sync" @@ -32,8 +33,9 @@ func (wl whiteList) add(kind, list string) { // AddConstantRule lints unused params in functions. type AddConstantRule struct { - whiteList whiteList - strLitLimit int + whiteList whiteList + ignoreFunctions []*regexp.Regexp + strLitLimit int sync.Mutex } @@ -47,7 +49,13 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin failures = append(failures, failure) } - w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int), strLitLimit: r.strLitLimit, whiteLst: r.whiteList} + w := lintAddConstantRule{ + onFailure: onFailure, + strLits: make(map[string]int), + strLitLimit: r.strLitLimit, + whiteLst: r.whiteList, + ignoreFunctions: r.ignoreFunctions, + } ast.Walk(w, file.AST) @@ -60,28 +68,76 @@ func (*AddConstantRule) Name() string { } type lintAddConstantRule struct { - onFailure func(lint.Failure) - strLits map[string]int - strLitLimit int - whiteLst whiteList + onFailure func(lint.Failure) + strLits map[string]int + strLitLimit int + whiteLst whiteList + ignoreFunctions []*regexp.Regexp } func (w lintAddConstantRule) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { + case *ast.CallExpr: + w.checkFunc(n) + return nil case *ast.GenDecl: return nil // skip declarations case *ast.BasicLit: - switch kind := n.Kind.String(); kind { - case kindFLOAT, kindINT: - w.checkNumLit(kind, n) - case kindSTRING: - w.checkStrLit(n) - } + w.checkLit(n) } return w } +func (w lintAddConstantRule) checkFunc(expr *ast.CallExpr) { + fName := w.getFuncName(expr) + + for _, arg := range expr.Args { + switch t := arg.(type) { + case *ast.CallExpr: + w.checkFunc(t) + case *ast.BasicLit: + if w.isIgnoredFunc(fName) { + continue + } + w.checkLit(t) + } + } +} + +func (w lintAddConstantRule) getFuncName(expr *ast.CallExpr) string { + switch f := expr.Fun.(type) { + case *ast.SelectorExpr: + switch prefix := f.X.(type) { + case *ast.Ident: + return prefix.Name + "." + f.Sel.Name + } + case *ast.Ident: + return f.Name + } + + return "" +} + +func (w lintAddConstantRule) checkLit(n *ast.BasicLit) { + switch kind := n.Kind.String(); kind { + case kindFLOAT, kindINT: + w.checkNumLit(kind, n) + case kindSTRING: + w.checkStrLit(n) + } +} + +func (w lintAddConstantRule) isIgnoredFunc(fName string) bool { + for _, pattern := range w.ignoreFunctions { + if pattern.MatchString(fName) { + return true + } + } + + return false +} + func (w lintAddConstantRule) checkStrLit(n *ast.BasicLit) { if w.whiteLst[kindSTRING][n.Value] { return @@ -158,6 +214,25 @@ func (r *AddConstantRule) configure(arguments lint.Arguments) { panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v)) } r.strLitLimit = limit + case "ignoreFuncs": + excludes, ok := v.(string) + if !ok { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule, string expected. Got '%v' (%T)", v, v)) + } + + for _, exclude := range strings.Split(excludes, ",") { + exclude = strings.Trim(exclude, " ") + if exclude == "" { + panic("Invalid argument to the ignoreFuncs parameter of add-constant rule, expected regular expression must not be empty.") + } + + exp, err := regexp.Compile(exclude) + if err != nil { + panic(fmt.Sprintf("Invalid argument to the ignoreFuncs parameter of add-constant rule: regexp %q does not compile: %v", exclude, err)) + } + + r.ignoreFunctions = append(r.ignoreFunctions, exp) + } } } } diff --git a/test/add-constant_test.go b/test/add-constant_test.go index e4e4aec..16eb173 100644 --- a/test/add-constant_test.go +++ b/test/add-constant_test.go @@ -13,6 +13,7 @@ func TestAddConstant(t *testing.T) { "allowStrs": "\"\"", "allowInts": "0,1,2", "allowFloats": "0.0,1.0", + "ignoreFuncs": "os\\.(CreateFile|WriteFile|Chmod|FindProcess),.Println,ignoredFunc", }} testRule(t, "add-constant", &rule.AddConstantRule{}, &lint.RuleConfig{ diff --git a/testdata/add-constant.go b/testdata/add-constant.go index d240f40..4017ccd 100644 --- a/testdata/add-constant.go +++ b/testdata/add-constant.go @@ -1,5 +1,10 @@ package fixtures +import ( + "fmt" + "os" +) + func foo(a, b, c, d int) { a = 1.0 // ignore b = "ignore" @@ -13,4 +18,34 @@ func foo(a, b, c, d int) { println("lit") } } + + println(0666) // MATCH /avoid magic numbers like '0666', create a named constant for it/ + os.Chmod("test", 0666) // ignore + os.FindProcess(102100) // ignore + fmt.Println("test", 12) // ignore + fmt.Printf("%d", 100) // MATCH /avoid magic numbers like '100', create a named constant for it/ + ignoredFunc(1000) // ignore + + println("The result of calling myFunc is: ", ignoredFunc(100)) // ignore + println("result is: ", ignoredFunc(notIgnoredFunc(ignoredFunc(100)))) // ignore + println("result of calling myFunc is: ", notIgnoredFunc(ignoredFunc(100))) // ignore + + println("result myFunc is: ", notIgnoredFunc(100)) // MATCH /avoid magic numbers like '100', create a named constant for it/ + println("The result is: ", ignoredFunc(notIgnoredFunc(100))) // MATCH /avoid magic numbers like '100', create a named constant for it/ +} + +func myPrintln(s string, num int) { + +} + +func not2ignoredFunc(num int) int { + return num +} + +func ignoredFunc(num int) int { + return num +} + +func notIgnoredFunc(num int) int { + return num }