github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/confusing-naming.go (about) 1 package rule 2 3 import ( 4 "fmt" 5 "go/ast" 6 7 "strings" 8 "sync" 9 10 "github.com/mgechev/revive/lint" 11 ) 12 13 type referenceMethod struct { 14 fileName string 15 id *ast.Ident 16 } 17 18 type pkgMethods struct { 19 pkg *lint.Package 20 methods map[string]map[string]*referenceMethod 21 mu *sync.Mutex 22 } 23 24 type packages struct { 25 pkgs []pkgMethods 26 mu sync.Mutex 27 } 28 29 func (ps *packages) methodNames(lp *lint.Package) pkgMethods { 30 ps.mu.Lock() 31 32 for _, pkg := range ps.pkgs { 33 if pkg.pkg == lp { 34 ps.mu.Unlock() 35 return pkg 36 } 37 } 38 39 pkgm := pkgMethods{pkg: lp, methods: make(map[string]map[string]*referenceMethod), mu: &sync.Mutex{}} 40 ps.pkgs = append(ps.pkgs, pkgm) 41 42 ps.mu.Unlock() 43 return pkgm 44 } 45 46 var allPkgs = packages{pkgs: make([]pkgMethods, 1)} 47 48 // ConfusingNamingRule lints method names that differ only by capitalization 49 type ConfusingNamingRule struct{} 50 51 // Apply applies the rule to given file. 52 func (r *ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { 53 var failures []lint.Failure 54 fileAst := file.AST 55 pkgm := allPkgs.methodNames(file.Pkg) 56 walker := lintConfusingNames{ 57 fileName: file.Name, 58 pkgm: pkgm, 59 onFailure: func(failure lint.Failure) { 60 failures = append(failures, failure) 61 }, 62 } 63 64 ast.Walk(&walker, fileAst) 65 66 return failures 67 } 68 69 // Name returns the rule name. 70 func (r *ConfusingNamingRule) Name() string { 71 return "confusing-naming" 72 } 73 74 //checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file. 75 func checkMethodName(holder string, id *ast.Ident, w *lintConfusingNames) { 76 if id.Name == "init" && holder == defaultStructName { 77 // ignore init functions 78 return 79 } 80 81 pkgm := w.pkgm 82 name := strings.ToUpper(id.Name) 83 84 pkgm.mu.Lock() 85 defer pkgm.mu.Unlock() 86 87 if pkgm.methods[holder] != nil { 88 if pkgm.methods[holder][name] != nil { 89 refMethod := pkgm.methods[holder][name] 90 // confusing names 91 var kind string 92 if holder == defaultStructName { 93 kind = "function" 94 } else { 95 kind = "method" 96 } 97 var fileName string 98 if w.fileName == refMethod.fileName { 99 fileName = "the same source file" 100 } else { 101 fileName = refMethod.fileName 102 } 103 w.onFailure(lint.Failure{ 104 Failure: fmt.Sprintf("Method '%s' differs only by capitalization to %s '%s' in %s", id.Name, kind, refMethod.id.Name, fileName), 105 Confidence: 1, 106 Node: id, 107 Category: "naming", 108 }) 109 110 return 111 } 112 } else { 113 pkgm.methods[holder] = make(map[string]*referenceMethod, 1) 114 } 115 116 // update the black list 117 if pkgm.methods[holder] == nil { 118 println("no entry for '", holder, "'") 119 } 120 pkgm.methods[holder][name] = &referenceMethod{fileName: w.fileName, id: id} 121 } 122 123 type lintConfusingNames struct { 124 fileName string 125 pkgm pkgMethods 126 onFailure func(lint.Failure) 127 } 128 129 const defaultStructName = "_" // used to map functions 130 131 //getStructName of a function receiver. Defaults to defaultStructName 132 func getStructName(r *ast.FieldList) string { 133 result := defaultStructName 134 135 if r == nil || len(r.List) < 1 { 136 return result 137 } 138 139 t := r.List[0].Type 140 141 if p, _ := t.(*ast.StarExpr); p != nil { // if a pointer receiver => dereference pointer receiver types 142 t = p.X 143 } 144 145 if p, _ := t.(*ast.Ident); p != nil { 146 result = p.Name 147 } 148 149 return result 150 } 151 152 func checkStructFields(fields *ast.FieldList, structName string, w *lintConfusingNames) { 153 bl := make(map[string]bool, len(fields.List)) 154 for _, f := range fields.List { 155 for _, id := range f.Names { 156 normName := strings.ToUpper(id.Name) 157 if bl[normName] { 158 w.onFailure(lint.Failure{ 159 Failure: fmt.Sprintf("Field '%s' differs only by capitalization to other field in the struct type %s", id.Name, structName), 160 Confidence: 1, 161 Node: id, 162 Category: "naming", 163 }) 164 } else { 165 bl[normName] = true 166 } 167 } 168 } 169 } 170 171 func (w *lintConfusingNames) Visit(n ast.Node) ast.Visitor { 172 switch v := n.(type) { 173 case *ast.FuncDecl: 174 // Exclude naming warnings for functions that are exported to C but 175 // not exported in the Go API. 176 // See https://github.com/golang/lint/issues/144. 177 if ast.IsExported(v.Name.Name) || !isCgoExported(v) { 178 checkMethodName(getStructName(v.Recv), v.Name, w) 179 } 180 case *ast.TypeSpec: 181 if s, ok := v.Type.(*ast.StructType); ok { 182 checkStructFields(s.Fields, v.Name.Name, w) 183 } 184 185 default: 186 // will add other checks like field names, struct names, etc. 187 } 188 189 return w 190 }