...
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 Requires: []*analysis.Analyzer{inspect.Analyzer},
33 Run: run,
34 }
35
36 func run(pass *analysis.Pass) (interface{}, error) {
37 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
38
39
40 dead := make(map[ast.Node]bool)
41 nodeFilter := []ast.Node{
42 (*ast.IfStmt)(nil),
43 (*ast.SwitchStmt)(nil),
44 }
45 inspect.Preorder(nodeFilter, func(n ast.Node) {
46
47 updateDead(pass.TypesInfo, dead, n)
48 })
49
50 nodeFilter = []ast.Node{
51 (*ast.AssignStmt)(nil),
52 (*ast.BinaryExpr)(nil),
53 }
54 inspect.Preorder(nodeFilter, func(node ast.Node) {
55 if dead[node] {
56
57 return
58 }
59
60 switch node := node.(type) {
61 case *ast.BinaryExpr:
62 if node.Op == token.SHL || node.Op == token.SHR {
63 checkLongShift(pass, node, node.X, node.Y)
64 }
65 case *ast.AssignStmt:
66 if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
67 return
68 }
69 if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
70 checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
71 }
72 }
73 })
74 return nil, nil
75 }
76
77
78
79 func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
80 if pass.TypesInfo.Types[x].Value != nil {
81
82
83
84 return
85 }
86
87 v := pass.TypesInfo.Types[y].Value
88 if v == nil {
89 return
90 }
91 amt, ok := constant.Int64Val(v)
92 if !ok {
93 return
94 }
95 t := pass.TypesInfo.Types[x].Type
96 if t == nil {
97 return
98 }
99 var structuralTypes []types.Type
100 switch t := t.(type) {
101 case *typeparams.TypeParam:
102 terms, err := typeparams.StructuralTerms(t)
103 if err != nil {
104 return
105 }
106 for _, term := range terms {
107 structuralTypes = append(structuralTypes, term.Type())
108 }
109 default:
110 structuralTypes = append(structuralTypes, t)
111 }
112 sizes := make(map[int64]struct{})
113 for _, t := range structuralTypes {
114 size := 8 * pass.TypesSizes.Sizeof(t)
115 sizes[size] = struct{}{}
116 }
117 minSize := int64(math.MaxInt64)
118 for size := range sizes {
119 if size < minSize {
120 minSize = size
121 }
122 }
123 if amt >= minSize {
124 ident := analysisutil.Format(pass.Fset, x)
125 qualifier := ""
126 if len(sizes) > 1 {
127 qualifier = "may be "
128 }
129 pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
130 }
131 }
132
View as plain text