You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

add-constant.go 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package rule
  2. import (
  3. "fmt"
  4. "github.com/mgechev/revive/lint"
  5. "go/ast"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. defaultStrLitLimit = 2
  11. kindFLOAT = "FLOAT"
  12. kindINT = "INT"
  13. kindSTRING = "STRING"
  14. )
  15. type whiteList map[string]map[string]bool
  16. func newWhiteList() whiteList {
  17. return map[string]map[string]bool{kindINT: map[string]bool{}, kindFLOAT: map[string]bool{}, kindSTRING: map[string]bool{}}
  18. }
  19. func (wl whiteList) add(kind string, list string) {
  20. elems := strings.Split(list, ",")
  21. for _, e := range elems {
  22. wl[kind][e] = true
  23. }
  24. }
  25. // AddConstantRule lints unused params in functions.
  26. type AddConstantRule struct{}
  27. // Apply applies the rule to given file.
  28. func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
  29. strLitLimit := defaultStrLitLimit
  30. var whiteList = newWhiteList()
  31. if len(arguments) > 0 {
  32. args, ok := arguments[0].(map[string]interface{})
  33. if !ok {
  34. panic(fmt.Sprintf("Invalid argument to the add-constant rule. Expecting a k,v map, got %T", arguments[0]))
  35. }
  36. for k, v := range args {
  37. kind := ""
  38. switch k {
  39. case "allowFloats":
  40. kind = kindFLOAT
  41. fallthrough
  42. case "allowInts":
  43. if kind == "" {
  44. kind = kindINT
  45. }
  46. fallthrough
  47. case "allowStrs":
  48. if kind == "" {
  49. kind = kindSTRING
  50. }
  51. list, ok := v.(string)
  52. if !ok {
  53. panic(fmt.Sprintf("Invalid argument to the add-constant rule, string expected. Got '%v' (%T)", v, v))
  54. }
  55. whiteList.add(kind, list)
  56. case "maxLitCount":
  57. sl, ok := v.(string)
  58. if !ok {
  59. panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v' (%T)", v, v))
  60. }
  61. limit, err := strconv.Atoi(sl)
  62. if err != nil {
  63. panic(fmt.Sprintf("Invalid argument to the add-constant rule, expecting string representation of an integer. Got '%v'", v))
  64. }
  65. strLitLimit = limit
  66. }
  67. }
  68. }
  69. var failures []lint.Failure
  70. onFailure := func(failure lint.Failure) {
  71. failures = append(failures, failure)
  72. }
  73. w := lintAddConstantRule{onFailure: onFailure, strLits: make(map[string]int, 0), strLitLimit: strLitLimit, whiteLst: whiteList}
  74. ast.Walk(w, file.AST)
  75. return failures
  76. }
  77. // Name returns the rule name.
  78. func (r *AddConstantRule) Name() string {
  79. return "add-constant"
  80. }
  81. type lintAddConstantRule struct {
  82. onFailure func(lint.Failure)
  83. strLits map[string]int
  84. strLitLimit int
  85. whiteLst whiteList
  86. }
  87. func (w lintAddConstantRule) Visit(node ast.Node) ast.Visitor {
  88. switch n := node.(type) {
  89. case *ast.GenDecl:
  90. return nil // skip declarations
  91. case *ast.BasicLit:
  92. switch kind := n.Kind.String(); kind {
  93. case kindFLOAT, kindINT:
  94. w.checkNumLit(kind, n)
  95. case kindSTRING:
  96. w.checkStrLit(n)
  97. }
  98. }
  99. return w
  100. }
  101. func (w lintAddConstantRule) checkStrLit(n *ast.BasicLit) {
  102. if w.whiteLst[kindSTRING][n.Value] {
  103. return
  104. }
  105. count := w.strLits[n.Value]
  106. if count >= 0 {
  107. w.strLits[n.Value] = count + 1
  108. if w.strLits[n.Value] > w.strLitLimit {
  109. w.onFailure(lint.Failure{
  110. Confidence: 1,
  111. Node: n,
  112. Category: "style",
  113. Failure: fmt.Sprintf("string literal %s appears, at least, %d times, create a named constant for it", n.Value, w.strLits[n.Value]),
  114. })
  115. w.strLits[n.Value] = -1 // mark it to avoid failing again on the same literal
  116. }
  117. }
  118. }
  119. func (w lintAddConstantRule) checkNumLit(kind string, n *ast.BasicLit) {
  120. if w.whiteLst[kind][n.Value] {
  121. return
  122. }
  123. w.onFailure(lint.Failure{
  124. Confidence: 1,
  125. Node: n,
  126. Category: "style",
  127. Failure: fmt.Sprintf("avoid magic numbers like '%s', create a named constant for it", n.Value),
  128. })
  129. }