1 | // Copyright 2014 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Package shift defines an Analyzer that checks for shifts that exceed |
6 | // the width of an integer. |
7 | package shift |
8 | |
9 | // TODO(adonovan): integrate with ctrflow (CFG-based) dead code analysis. May |
10 | // have impedance mismatch due to its (non-)treatment of constant |
11 | // expressions (such as runtime.GOARCH=="386"). |
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 | // Do a complete pass to compute dead nodes. |
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 | // TODO(adonovan): move updateDead into this file. |
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 | // Skip shift checks on unreachable nodes. |
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 | // checkLongShift checks if shift or shift-assign operations shift by more than |
78 | // the length of the underlying variable. |
79 | func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) { |
80 | if pass.TypesInfo.Types[x].Value != nil { |
81 | // Ignore shifts of constants. |
82 | // These are frequently used for bit-twiddling tricks |
83 | // like ^uint(0) >> 63 for 32/64 bit detection and compatibility. |
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 // invalid type |
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 |
Members