sa6002.go raw

   1  package sa6002
   2  
   3  import (
   4  	"go/types"
   5  
   6  	"honnef.co/go/tools/analysis/callcheck"
   7  	"honnef.co/go/tools/analysis/lint"
   8  	"honnef.co/go/tools/go/types/typeutil"
   9  	"honnef.co/go/tools/internal/passes/buildir"
  10  	"honnef.co/go/tools/knowledge"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  )
  14  
  15  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  16  	Analyzer: &analysis.Analyzer{
  17  		Name:     "SA6002",
  18  		Requires: []*analysis.Analyzer{buildir.Analyzer},
  19  		Run:      callcheck.Analyzer(rules),
  20  	},
  21  	Doc: &lint.RawDocumentation{
  22  		Title: `Storing non-pointer values in \'sync.Pool\' allocates memory`,
  23  		Text: `A \'sync.Pool\' is used to avoid unnecessary allocations and reduce the
  24  amount of work the garbage collector has to do.
  25  
  26  When passing a value that is not a pointer to a function that accepts
  27  an interface, the value needs to be placed on the heap, which means an
  28  additional allocation. Slices are a common thing to put in sync.Pools,
  29  and they're structs with 3 fields (length, capacity, and a pointer to
  30  an array). In order to avoid the extra allocation, one should store a
  31  pointer to the slice instead.
  32  
  33  See the comments on https://go-review.googlesource.com/c/go/+/24371
  34  that discuss this problem.`,
  35  		Since:    "2017.1",
  36  		Severity: lint.SeverityWarning,
  37  		MergeIf:  lint.MergeIfAny,
  38  	},
  39  })
  40  
  41  var Analyzer = SCAnalyzer.Analyzer
  42  
  43  var rules = map[string]callcheck.Check{
  44  	"(*sync.Pool).Put": func(call *callcheck.Call) {
  45  		arg := call.Args[knowledge.Arg("(*sync.Pool).Put.x")]
  46  		typ := arg.Value.Value.Type()
  47  		_, isSlice := typ.Underlying().(*types.Slice)
  48  		if !typeutil.IsPointerLike(typ) || isSlice {
  49  			arg.Invalid("argument should be pointer-like to avoid allocations")
  50  		}
  51  	},
  52  }
  53