src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

st1016.go (1881B)


      1 package st1016
      2 
      3 import (
      4 	"fmt"
      5 	"go/types"
      6 	"sort"
      7 	"strings"
      8 
      9 	"honnef.co/go/tools/analysis/code"
     10 	"honnef.co/go/tools/analysis/facts/generated"
     11 	"honnef.co/go/tools/analysis/lint"
     12 	"honnef.co/go/tools/analysis/report"
     13 	"honnef.co/go/tools/go/types/typeutil"
     14 	"honnef.co/go/tools/internal/passes/buildir"
     15 
     16 	"golang.org/x/tools/go/analysis"
     17 )
     18 
     19 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     20 	Analyzer: &analysis.Analyzer{
     21 		Name:     "ST1016",
     22 		Run:      run,
     23 		Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer},
     24 	},
     25 	Doc: &lint.RawDocumentation{
     26 		Title:      `Use consistent method receiver names`,
     27 		Since:      "2019.1",
     28 		NonDefault: true,
     29 		MergeIf:    lint.MergeIfAny,
     30 	},
     31 })
     32 
     33 var Analyzer = SCAnalyzer.Analyzer
     34 
     35 func run(pass *analysis.Pass) (any, error) {
     36 	irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg
     37 	for _, m := range irpkg.Members {
     38 		names := map[string]int{}
     39 
     40 		var firstFn *types.Func
     41 		if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
     42 			ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
     43 			for _, sel := range ms {
     44 				fn := sel.Obj().(*types.Func)
     45 				recv := fn.Type().(*types.Signature).Recv()
     46 				if code.IsGenerated(pass, recv.Pos()) {
     47 					// Don't concern ourselves with methods in generated code
     48 					continue
     49 				}
     50 				if typeutil.Dereference(recv.Type()) != T.Type() {
     51 					// skip embedded methods
     52 					continue
     53 				}
     54 				if firstFn == nil {
     55 					firstFn = fn
     56 				}
     57 				if recv.Name() != "" && recv.Name() != "_" {
     58 					names[recv.Name()]++
     59 				}
     60 			}
     61 		}
     62 
     63 		if len(names) > 1 {
     64 			var seen []string
     65 			for name, count := range names {
     66 				seen = append(seen, fmt.Sprintf("%dx %q", count, name))
     67 			}
     68 			sort.Strings(seen)
     69 
     70 			report.Report(pass, firstFn, fmt.Sprintf("methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", ")))
     71 		}
     72 	}
     73 	return nil, nil
     74 }