This repository has been archived by the owner on Dec 30, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrangeExprCopy_checker.go
80 lines (71 loc) · 1.96 KB
/
rangeExprCopy_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package checkers
import (
"go/ast"
"go/types"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "rangeExprCopy"
info.Tags = []string{"performance"}
info.Params = lintpack.CheckerParams{
"sizeThreshold": {
Value: 512,
Usage: "size in bytes that makes the warning trigger",
},
"skipTestFuncs": {
Value: true,
Usage: "whether to check test functions",
},
}
info.Summary = "Detects expensive copies of `for` loop range expressions"
info.Details = "Suggests to use pointer to array to avoid the copy using `&` on range expression."
info.Before = `
var xs [2048]byte
for _, x := range xs { // Copies 2048 bytes
// Loop body.
}`
info.After = `
var xs [2048]byte
for _, x := range &xs { // No copy
// Loop body.
}`
info.Note = "See Go issue for details: https://github.com/golang/go/issues/15812."
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
c := &rangeExprCopyChecker{ctx: ctx}
c.sizeThreshold = int64(info.Params.Int("sizeThreshold"))
c.skipTestFuncs = info.Params.Bool("skipTestFuncs")
return astwalk.WalkerForStmt(c)
})
}
type rangeExprCopyChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
sizeThreshold int64
skipTestFuncs bool
}
func (c *rangeExprCopyChecker) EnterFunc(fn *ast.FuncDecl) bool {
return fn.Body != nil &&
!(c.skipTestFuncs && isUnitTestFunc(c.ctx, fn))
}
func (c *rangeExprCopyChecker) VisitStmt(stmt ast.Stmt) {
rng, ok := stmt.(*ast.RangeStmt)
if !ok || rng.Key == nil || rng.Value == nil {
return
}
tv := c.ctx.TypesInfo.Types[rng.X]
if !tv.Addressable() {
return
}
if _, ok := tv.Type.(*types.Array); !ok {
return
}
if size := c.ctx.SizesInfo.Sizeof(tv.Type); size >= c.sizeThreshold {
c.warn(rng, size)
}
}
func (c *rangeExprCopyChecker) warn(rng *ast.RangeStmt, size int64) {
c.ctx.Warn(rng, "copy of %s (%d bytes) can be avoided with &%s",
rng.X, size, rng.X)
}