--- /dev/null
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package shift defines an Analyzer that checks for shifts that exceed
+// the width of an integer.
+package shift
+
+// TODO(adonovan): integrate with ctrflow (CFG-based) dead code analysis. May
+// have impedance mismatch due to its (non-)treatment of constant
+// expressions (such as runtime.GOARCH=="386").
+
+import (
+ "go/ast"
+ "go/constant"
+ "go/token"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+ "golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = "check for shifts that equal or exceed the width of the integer"
+
+var Analyzer = &analysis.Analyzer{
+ Name: "shift",
+ Doc: Doc,
+ Requires: []*analysis.Analyzer{inspect.Analyzer},
+ Run: run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+ inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+ // Do a complete pass to compute dead nodes.
+ dead := make(map[ast.Node]bool)
+ nodeFilter := []ast.Node{
+ (*ast.IfStmt)(nil),
+ (*ast.SwitchStmt)(nil),
+ }
+ inspect.Preorder(nodeFilter, func(n ast.Node) {
+ // TODO(adonovan): move updateDead into this file.
+ updateDead(pass.TypesInfo, dead, n)
+ })
+
+ nodeFilter = []ast.Node{
+ (*ast.AssignStmt)(nil),
+ (*ast.BinaryExpr)(nil),
+ }
+ inspect.Preorder(nodeFilter, func(node ast.Node) {
+ if dead[node] {
+ // Skip shift checks on unreachable nodes.
+ return
+ }
+
+ switch node := node.(type) {
+ case *ast.BinaryExpr:
+ if node.Op == token.SHL || node.Op == token.SHR {
+ checkLongShift(pass, node, node.X, node.Y)
+ }
+ case *ast.AssignStmt:
+ if len(node.Lhs) != 1 || len(node.Rhs) != 1 {
+ return
+ }
+ if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN {
+ checkLongShift(pass, node, node.Lhs[0], node.Rhs[0])
+ }
+ }
+ })
+ return nil, nil
+}
+
+// checkLongShift checks if shift or shift-assign operations shift by more than
+// the length of the underlying variable.
+func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
+ if pass.TypesInfo.Types[x].Value != nil {
+ // Ignore shifts of constants.
+ // These are frequently used for bit-twiddling tricks
+ // like ^uint(0) >> 63 for 32/64 bit detection and compatibility.
+ return
+ }
+
+ v := pass.TypesInfo.Types[y].Value
+ if v == nil {
+ return
+ }
+ amt, ok := constant.Int64Val(v)
+ if !ok {
+ return
+ }
+ t := pass.TypesInfo.Types[x].Type
+ if t == nil {
+ return
+ }
+ size := 8 * pass.TypesSizes.Sizeof(t)
+ if amt >= size {
+ ident := analysisutil.Format(pass.Fset, x)
+ pass.ReportRangef(node, "%s (%d bits) too small for shift of %d", ident, size, amt)
+ }
+}