github.com/songshiyun/revive@v1.1.5-0.20220323112655-f8433a19b3c5/rule/var-naming.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/token" 7 "strings" 8 9 "github.com/songshiyun/revive/lint" 10 ) 11 12 // VarNamingRule lints given else constructs. 13 type VarNamingRule struct { 14 configured bool 15 whitelist []string 16 blacklist []string 17 } 18 19 // Apply applies the rule to given file. 20 func (r *VarNamingRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { 21 var failures []lint.Failure 22 23 if !r.configured { 24 if len(arguments) >= 1 { 25 r.whitelist = getList(arguments[0], "whitelist") 26 } 27 28 if len(arguments) >= 2 { 29 r.blacklist = getList(arguments[1], "blacklist") 30 } 31 r.configured = true 32 } 33 34 fileAst := file.AST 35 walker := lintNames{ 36 file: file, 37 fileAst: fileAst, 38 whitelist: r.whitelist, 39 blacklist: r.blacklist, 40 onFailure: func(failure lint.Failure) { 41 failures = append(failures, failure) 42 }, 43 } 44 45 // Package names need slightly different handling than other names. 46 if strings.Contains(walker.fileAst.Name.Name, "_") && !strings.HasSuffix(walker.fileAst.Name.Name, "_test") { 47 walker.onFailure(lint.Failure{ 48 Failure: "don't use an underscore in package name", 49 Confidence: 1, 50 Node: walker.fileAst, 51 Category: "naming", 52 }) 53 } 54 55 ast.Walk(&walker, fileAst) 56 57 return failures 58 } 59 60 // Name returns the rule name. 61 func (r *VarNamingRule) Name() string { 62 return "var-naming" 63 } 64 65 func checkList(fl *ast.FieldList, thing string, w *lintNames) { 66 if fl == nil { 67 return 68 } 69 for _, f := range fl.List { 70 for _, id := range f.Names { 71 check(id, thing, w) 72 } 73 } 74 } 75 76 func check(id *ast.Ident, thing string, w *lintNames) { 77 if id.Name == "_" { 78 return 79 } 80 if knownNameExceptions[id.Name] { 81 return 82 } 83 84 // Handle two common styles from other languages that don't belong in Go. 85 if len(id.Name) >= 5 && allCapsRE.MatchString(id.Name) && strings.Contains(id.Name, "_") { 86 w.onFailure(lint.Failure{ 87 Failure: "don't use ALL_CAPS in Go names; use CamelCase", 88 Confidence: 0.8, 89 Node: id, 90 Category: "naming", 91 }) 92 return 93 } 94 if len(id.Name) > 2 && id.Name[0] == 'k' && id.Name[1] >= 'A' && id.Name[1] <= 'Z' { 95 should := string(id.Name[1]+'a'-'A') + id.Name[2:] 96 w.onFailure(lint.Failure{ 97 Failure: fmt.Sprintf("don't use leading k in Go names; %s %s should be %s", thing, id.Name, should), 98 Confidence: 0.8, 99 Node: id, 100 Category: "naming", 101 }) 102 } 103 104 should := lint.Name(id.Name, w.whitelist, w.blacklist) 105 if id.Name == should { 106 return 107 } 108 109 if len(id.Name) > 2 && strings.Contains(id.Name[1:], "_") { 110 w.onFailure(lint.Failure{ 111 Failure: fmt.Sprintf("don't use underscores in Go names; %s %s should be %s", thing, id.Name, should), 112 Confidence: 0.9, 113 Node: id, 114 Category: "naming", 115 }) 116 return 117 } 118 w.onFailure(lint.Failure{ 119 Failure: fmt.Sprintf("%s %s should be %s", thing, id.Name, should), 120 Confidence: 0.8, 121 Node: id, 122 Category: "naming", 123 }) 124 } 125 126 type lintNames struct { 127 file *lint.File 128 fileAst *ast.File 129 lastGen *ast.GenDecl 130 genDeclMissingComments map[*ast.GenDecl]bool 131 onFailure func(lint.Failure) 132 whitelist []string 133 blacklist []string 134 } 135 136 func (w *lintNames) Visit(n ast.Node) ast.Visitor { 137 switch v := n.(type) { 138 case *ast.AssignStmt: 139 if v.Tok == token.ASSIGN { 140 return w 141 } 142 for _, exp := range v.Lhs { 143 if id, ok := exp.(*ast.Ident); ok { 144 check(id, "var", w) 145 } 146 } 147 case *ast.FuncDecl: 148 funcName := v.Name.Name 149 if w.file.IsTest() && 150 (strings.HasPrefix(funcName, "Example") || 151 strings.HasPrefix(funcName, "Test") || 152 strings.HasPrefix(funcName, "Benchmark") || 153 strings.HasPrefix(funcName, "Fuzz")) { 154 return w 155 } 156 157 thing := "func" 158 if v.Recv != nil { 159 thing = "method" 160 } 161 162 // Exclude naming warnings for functions that are exported to C but 163 // not exported in the Go API. 164 // See https://github.com/golang/lint/issues/144. 165 if ast.IsExported(v.Name.Name) || !isCgoExported(v) { 166 check(v.Name, thing, w) 167 } 168 169 checkList(v.Type.Params, thing+" parameter", w) 170 checkList(v.Type.Results, thing+" result", w) 171 case *ast.GenDecl: 172 if v.Tok == token.IMPORT { 173 return w 174 } 175 var thing string 176 switch v.Tok { 177 case token.CONST: 178 thing = "const" 179 case token.TYPE: 180 thing = "type" 181 case token.VAR: 182 thing = "var" 183 } 184 for _, spec := range v.Specs { 185 switch s := spec.(type) { 186 case *ast.TypeSpec: 187 check(s.Name, thing, w) 188 case *ast.ValueSpec: 189 for _, id := range s.Names { 190 check(id, thing, w) 191 } 192 } 193 } 194 case *ast.InterfaceType: 195 // Do not check interface method names. 196 // They are often constrained by the method names of concrete types. 197 for _, x := range v.Methods.List { 198 ft, ok := x.Type.(*ast.FuncType) 199 if !ok { // might be an embedded interface name 200 continue 201 } 202 checkList(ft.Params, "interface method parameter", w) 203 checkList(ft.Results, "interface method result", w) 204 } 205 case *ast.RangeStmt: 206 if v.Tok == token.ASSIGN { 207 return w 208 } 209 if id, ok := v.Key.(*ast.Ident); ok { 210 check(id, "range var", w) 211 } 212 if id, ok := v.Value.(*ast.Ident); ok { 213 check(id, "range var", w) 214 } 215 case *ast.StructType: 216 for _, f := range v.Fields.List { 217 for _, id := range f.Names { 218 check(id, "struct field", w) 219 } 220 } 221 } 222 return w 223 } 224 225 func getList(arg interface{}, argName string) []string { 226 temp, ok := arg.([]interface{}) 227 if !ok { 228 panic(fmt.Sprintf("Invalid argument to the var-naming rule. Expecting a %s of type slice with initialisms, got %T", argName, arg)) 229 } 230 var list []string 231 for _, v := range temp { 232 if val, ok := v.(string); ok { 233 list = append(list, val) 234 } else { 235 panic(fmt.Sprintf("Invalid %s values of the var-naming rule. Expecting slice of strings but got element of type %T", val, arg)) 236 } 237 } 238 return list 239 }