github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa6002/sa6002.go (about)

     1  package sa6002
     2  
     3  import (
     4  	"go/types"
     5  
     6  	"github.com/amarpal/go-tools/analysis/callcheck"
     7  	"github.com/amarpal/go-tools/analysis/lint"
     8  	"github.com/amarpal/go-tools/go/types/typeutil"
     9  	"github.com/amarpal/go-tools/internal/passes/buildir"
    10  	"github.com/amarpal/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.Documentation{
    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  }