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. 7package 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 13import ( 14 "go/ast" 15 "go/build" 16 "go/constant" 17 "go/token" 18 "go/types" 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) 25 26var Analyzer = &analysis.Analyzer{ 27 Name: "shift", 28 Doc: "check for shifts that equal or exceed the width of the integer", 29 Requires: []*analysis.Analyzer{inspect.Analyzer}, 30 Run: run, 31} 32 33func run(pass *analysis.Pass) (interface{}, error) { 34 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 35 36 // Do a complete pass to compute dead nodes. 37 dead := make(map[ast.Node]bool) 38 nodeFilter := []ast.Node{ 39 (*ast.IfStmt)(nil), 40 (*ast.SwitchStmt)(nil), 41 } 42 inspect.Preorder(nodeFilter, func(n ast.Node) { 43 // TODO(adonovan): move updateDead into this file. 44 updateDead(pass.TypesInfo, dead, n) 45 }) 46 47 nodeFilter = []ast.Node{ 48 (*ast.AssignStmt)(nil), 49 (*ast.BinaryExpr)(nil), 50 } 51 inspect.Preorder(nodeFilter, func(node ast.Node) { 52 if dead[node] { 53 // Skip shift checks on unreachable nodes. 54 return 55 } 56 57 switch node := node.(type) { 58 case *ast.BinaryExpr: 59 if node.Op == token.SHL || node.Op == token.SHR { 60 checkLongShift(pass, node, node.X, node.Y) 61 } 62 case *ast.AssignStmt: 63 if len(node.Lhs) != 1 || len(node.Rhs) != 1 { 64 return 65 } 66 if node.Tok == token.SHL_ASSIGN || node.Tok == token.SHR_ASSIGN { 67 checkLongShift(pass, node, node.Lhs[0], node.Rhs[0]) 68 } 69 } 70 }) 71 return nil, nil 72} 73 74// checkLongShift checks if shift or shift-assign operations shift by more than 75// the length of the underlying variable. 76func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) { 77 if pass.TypesInfo.Types[x].Value != nil { 78 // Ignore shifts of constants. 79 // These are frequently used for bit-twiddling tricks 80 // like ^uint(0) >> 63 for 32/64 bit detection and compatibility. 81 return 82 } 83 84 v := pass.TypesInfo.Types[y].Value 85 if v == nil { 86 return 87 } 88 amt, ok := constant.Int64Val(v) 89 if !ok { 90 return 91 } 92 t := pass.TypesInfo.Types[x].Type 93 if t == nil { 94 return 95 } 96 b, ok := t.Underlying().(*types.Basic) 97 if !ok { 98 return 99 } 100 var size int64 101 switch b.Kind() { 102 case types.Uint8, types.Int8: 103 size = 8 104 case types.Uint16, types.Int16: 105 size = 16 106 case types.Uint32, types.Int32: 107 size = 32 108 case types.Uint64, types.Int64: 109 size = 64 110 case types.Int, types.Uint: 111 size = uintBitSize 112 case types.Uintptr: 113 size = uintptrBitSize 114 default: 115 return 116 } 117 if amt >= size { 118 ident := analysisutil.Format(pass.Fset, x) 119 pass.Reportf(node.Pos(), "%s (%d bits) too small for shift of %d", ident, size, amt) 120 } 121} 122 123var ( 124 uintBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uint]) 125 uintptrBitSize = 8 * archSizes.Sizeof(types.Typ[types.Uintptr]) 126) 127 128var archSizes = types.SizesFor("gccgo", build.Default.GOARCH) 129