123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- package rule
-
- import (
- "fmt"
- "go/ast"
-
- "strings"
- "sync"
-
- "github.com/mgechev/revive/lint"
- )
-
- type referenceMethod struct {
- fileName string
- id *ast.Ident
- }
-
- type pkgMethods struct {
- pkg *lint.Package
- methods map[string]map[string]*referenceMethod
- mu *sync.Mutex
- }
-
- type packages struct {
- pkgs []pkgMethods
- mu sync.Mutex
- }
-
- func (ps *packages) methodNames(lp *lint.Package) pkgMethods {
- ps.mu.Lock()
-
- for _, pkg := range ps.pkgs {
- if pkg.pkg == lp {
- ps.mu.Unlock()
- return pkg
- }
- }
-
- pkgm := pkgMethods{pkg: lp, methods: make(map[string]map[string]*referenceMethod), mu: &sync.Mutex{}}
- ps.pkgs = append(ps.pkgs, pkgm)
-
- ps.mu.Unlock()
- return pkgm
- }
-
- var allPkgs = packages{pkgs: make([]pkgMethods, 1)}
-
- // ConfusingNamingRule lints method names that differ only by capitalization
- type ConfusingNamingRule struct{}
-
- // Apply applies the rule to given file.
- func (r *ConfusingNamingRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
- var failures []lint.Failure
- fileAst := file.AST
- pkgm := allPkgs.methodNames(file.Pkg)
- walker := lintConfusingNames{
- fileName: file.Name,
- pkgm: pkgm,
- onFailure: func(failure lint.Failure) {
- failures = append(failures, failure)
- },
- }
-
- ast.Walk(&walker, fileAst)
-
- return failures
- }
-
- // Name returns the rule name.
- func (r *ConfusingNamingRule) Name() string {
- return "confusing-naming"
- }
-
- //checkMethodName checks if a given method/function name is similar (just case differences) to other method/function of the same struct/file.
- func checkMethodName(holder string, id *ast.Ident, w *lintConfusingNames) {
- if id.Name == "init" && holder == defaultStructName {
- // ignore init functions
- return
- }
-
- pkgm := w.pkgm
- name := strings.ToUpper(id.Name)
-
- pkgm.mu.Lock()
- defer pkgm.mu.Unlock()
-
- if pkgm.methods[holder] != nil {
- if pkgm.methods[holder][name] != nil {
- refMethod := pkgm.methods[holder][name]
- // confusing names
- var kind string
- if holder == defaultStructName {
- kind = "function"
- } else {
- kind = "method"
- }
- var fileName string
- if w.fileName == refMethod.fileName {
- fileName = "the same source file"
- } else {
- fileName = refMethod.fileName
- }
- w.onFailure(lint.Failure{
- Failure: fmt.Sprintf("Method '%s' differs only by capitalization to %s '%s' in %s", id.Name, kind, refMethod.id.Name, fileName),
- Confidence: 1,
- Node: id,
- Category: "naming",
- })
-
- return
- }
- } else {
- pkgm.methods[holder] = make(map[string]*referenceMethod, 1)
- }
-
- // update the black list
- if pkgm.methods[holder] == nil {
- println("no entry for '", holder, "'")
- }
- pkgm.methods[holder][name] = &referenceMethod{fileName: w.fileName, id: id}
- }
-
- type lintConfusingNames struct {
- fileName string
- pkgm pkgMethods
- onFailure func(lint.Failure)
- }
-
- const defaultStructName = "_" // used to map functions
-
- //getStructName of a function receiver. Defaults to defaultStructName
- func getStructName(r *ast.FieldList) string {
- result := defaultStructName
-
- if r == nil || len(r.List) < 1 {
- return result
- }
-
- t := r.List[0].Type
-
- if p, _ := t.(*ast.StarExpr); p != nil { // if a pointer receiver => dereference pointer receiver types
- t = p.X
- }
-
- if p, _ := t.(*ast.Ident); p != nil {
- result = p.Name
- }
-
- return result
- }
-
- func checkStructFields(fields *ast.FieldList, structName string, w *lintConfusingNames) {
- bl := make(map[string]bool, len(fields.List))
- for _, f := range fields.List {
- for _, id := range f.Names {
- normName := strings.ToUpper(id.Name)
- if bl[normName] {
- w.onFailure(lint.Failure{
- Failure: fmt.Sprintf("Field '%s' differs only by capitalization to other field in the struct type %s", id.Name, structName),
- Confidence: 1,
- Node: id,
- Category: "naming",
- })
- } else {
- bl[normName] = true
- }
- }
- }
- }
-
- func (w *lintConfusingNames) Visit(n ast.Node) ast.Visitor {
- switch v := n.(type) {
- case *ast.FuncDecl:
- // Exclude naming warnings for functions that are exported to C but
- // not exported in the Go API.
- // See https://github.com/golang/lint/issues/144.
- if ast.IsExported(v.Name.Name) || !isCgoExported(v) {
- checkMethodName(getStructName(v.Recv), v.Name, w)
- }
- case *ast.TypeSpec:
- if s, ok := v.Type.(*ast.StructType); ok {
- checkStructFields(s.Fields, v.Name.Name, w)
- }
-
- default:
- // will add other checks like field names, struct names, etc.
- }
-
- return w
- }
|