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