...
1
2
3
4
5
6
7 package shift
8
9
10
11
12
13 import (
14 "go/ast"
15 "go/constant"
16 "go/token"
17 "go/types"
18 "math"
19
20 "golang.org/x/tools/go/analysis"
21 "golang.org/x/tools/go/analysis/passes/inspect"
22 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
23 "golang.org/x/tools/go/ast/inspector"
24 "golang.org/x/tools/internal/typeparams"
25 )
26
27 const Doc = "check for shifts that equal or exceed the width of the integer"
28
29 var Analyzer = &analysis.Analyzer{
30 Name: "shift",
31 Doc: Doc,
32 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shift",
33 Requires: []*analysis.Analyzer{inspect.Analyzer},
34 Run: run,
35 }
36
37 func run(pass *analysis.Pass) (interface{}, error) {
38 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
39
40
41 dead := make(map[ast.Node]bool)
42 nodeFilter := []ast.Node{
43 (*ast.IfStmt)(nil),
44 (*ast.SwitchStmt)(nil),
45 }
46 inspect.Preorder(nodeFilter, func(n ast.Node) {
47
48 updateDead(pass.TypesInfo, dead, n)
49 })
50
51 nodeFilter = []ast.Node{
52 (*ast.AssignStmt)(nil),
53 (*ast.BinaryExpr)(nil),
54 }
55 inspect.Preorder(nodeFilter, func(node ast.Node) {
56 if dead[node] {
57
58 return
59 }
60
61 switch node := node.(type) {
62 case *ast.BinaryExpr:
63 if node.Op == token.SHL || node.Op == token.SHR {
64 checkLongShift(pass, node, node.X, node.Y)
65 }
66 case *ast.AssignStmt:
67 if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
68 return
69 }
70 if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
71 checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
72 }
73 }
74 })
75 return nil, nil
76 }
77
78
79
80 func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
81 if pass.TypesInfo.Types[x].Value != nil {
82
83
84
85 return
86 }
87
88 v := pass.TypesInfo.Types[y].Value
89 if v == nil {
90 return
91 }
92 amt, ok := constant.Int64Val(v)
93 if !ok {
94 return
95 }
96 t := pass.TypesInfo.Types[x].Type
97 if t == nil {
98 return
99 }
100 var structuralTypes []types.Type
101 switch t := t.(type) {
102 case *types.TypeParam:
103 terms, err := typeparams.StructuralTerms(t)
104 if err != nil {
105 return
106 }
107 for _, term := range terms {
108 structuralTypes = append(structuralTypes, term.Type())
109 }
110 default:
111 structuralTypes = append(structuralTypes, t)
112 }
113 sizes := make(map[int64]struct{})
114 for _, t := range structuralTypes {
115 size := 8 * pass.TypesSizes.Sizeof(t)
116 sizes[size] = struct{}{}
117 }
118 minSize := int64(math.MaxInt64)
119 for size := range sizes {
120 if size < minSize {
121 minSize = size
122 }
123 }
124 if amt >= minSize {
125 ident := analysisutil.Format(pass.Fset, x)
126 qualifier := ""
127 if len(sizes) > 1 {
128 qualifier = "may be "
129 }
130 pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
131 }
132 }
133
View as plain text