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