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