go.tools/cmd/vet: detect suspicious shifts

LGTM=josharian, dsymonds, r
R=golang-codereviews, josharian, minux, dsymonds, dave, axwalk, adg, r
CC=golang-codereviews
https://golang.org/cl/134780043
This commit is contained in:
Matt Jibson 2014-08-29 11:17:01 -07:00 committed by Rob Pike
parent a4d1505cfc
commit 59c8647562
3 changed files with 167 additions and 0 deletions

View File

@ -151,6 +151,12 @@ there is a uintptr-typed word in memory that holds a pointer value,
because that word will be invisible to stack copying and to the garbage because that word will be invisible to stack copying and to the garbage
collector. collector.
Shifts
Flag: -shift
Shifts equal to or longer than the variable's length.
Other flags Other flags
These flags configure the behavior of vet: These flags configure the behavior of vet:

83
cmd/vet/shift.go Normal file
View File

@ -0,0 +1,83 @@
// 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.
/*
This file contains the code to check for suspicious shifts.
*/
package main
import (
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/types"
)
func init() {
register("shift",
"check for useless shifts",
checkShift,
binaryExpr, assignStmt)
}
func checkShift(f *File, node ast.Node) {
switch node := node.(type) {
case *ast.BinaryExpr:
if node.Op == token.SHL || node.Op == token.SHR {
checkLongShift(f, 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(f, node, node.Lhs[0], node.Rhs[0])
}
}
}
// checkLongShift checks if shift or shift-assign operations shift by more than
// the length of the underlying variable.
func checkLongShift(f *File, node ast.Node, x, y ast.Expr) {
v := f.pkg.types[y].Value
if v == nil {
return
}
amt, ok := exact.Int64Val(v)
if !ok {
return
}
t := f.pkg.types[x].Type
if t == nil {
return
}
b, ok := t.Underlying().(*types.Basic)
if !ok {
return
}
var size int64
var msg string
switch b.Kind() {
case types.Uint8, types.Int8:
size = 8
case types.Uint16, types.Int16:
size = 16
case types.Uint32, types.Int32:
size = 32
case types.Uint64, types.Int64:
size = 64
case types.Int, types.Uint, types.Uintptr:
// These types may be as small as 32 bits, but no smaller.
size = 32
msg = "might be "
default:
return
}
if amt >= size {
ident := f.gofmt(x)
f.Badf(node.Pos(), "%s %stoo small for shift of %d", ident, msg, amt)
}
}

78
cmd/vet/testdata/shift.go vendored Normal file
View File

@ -0,0 +1,78 @@
// 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.
// This file contains tests for the suspicious shift checker.
package testdata
func ShiftTest() {
var i8 int8
_ = i8 << 7
_ = (i8 + 1) << 8 // ERROR "\(i8 \+ 1\) too small for shift of 8"
_ = i8 << (7 + 1) // ERROR "i8 too small for shift of 8"
_ = i8 >> 8 // ERROR "i8 too small for shift of 8"
i8 <<= 8 // ERROR "i8 too small for shift of 8"
i8 >>= 8 // ERROR "i8 too small for shift of 8"
var i16 int16
_ = i16 << 15
_ = i16 << 16 // ERROR "i16 too small for shift of 16"
_ = i16 >> 16 // ERROR "i16 too small for shift of 16"
i16 <<= 16 // ERROR "i16 too small for shift of 16"
i16 >>= 16 // ERROR "i16 too small for shift of 16"
var i32 int32
_ = i32 << 31
_ = i32 << 32 // ERROR "i32 too small for shift of 32"
_ = i32 >> 32 // ERROR "i32 too small for shift of 32"
i32 <<= 32 // ERROR "i32 too small for shift of 32"
i32 >>= 32 // ERROR "i32 too small for shift of 32"
var i64 int64
_ = i64 << 63
_ = i64 << 64 // ERROR "i64 too small for shift of 64"
_ = i64 >> 64 // ERROR "i64 too small for shift of 64"
i64 <<= 64 // ERROR "i64 too small for shift of 64"
i64 >>= 64 // ERROR "i64 too small for shift of 64"
var u8 uint8
_ = u8 << 7
_ = u8 << 8 // ERROR "u8 too small for shift of 8"
_ = u8 >> 8 // ERROR "u8 too small for shift of 8"
u8 <<= 8 // ERROR "u8 too small for shift of 8"
u8 >>= 8 // ERROR "u8 too small for shift of 8"
var u16 uint16
_ = u16 << 15
_ = u16 << 16 // ERROR "u16 too small for shift of 16"
_ = u16 >> 16 // ERROR "u16 too small for shift of 16"
u16 <<= 16 // ERROR "u16 too small for shift of 16"
u16 >>= 16 // ERROR "u16 too small for shift of 16"
var u32 uint32
_ = u32 << 31
_ = u32 << 32 // ERROR "u32 too small for shift of 32"
_ = u32 >> 32 // ERROR "u32 too small for shift of 32"
u32 <<= 32 // ERROR "u32 too small for shift of 32"
u32 >>= 32 // ERROR "u32 too small for shift of 32"
var u64 uint64
_ = u64 << 63
_ = u64 << 64 // ERROR "u64 too small for shift of 64"
_ = u64 >> 64 // ERROR "u64 too small for shift of 64"
u64 <<= 64 // ERROR "u64 too small for shift of 64"
u64 >>= 64 // ERROR "u64 too small for shift of 64"
_ = u64 << u64 // Non-constant shifts should succeed.
var i int
_ = i << 31
_ = i << 32 // ERROR "i might be too small for shift of 32"
_ = i >> 32 // ERROR "i might be too small for shift of 32"
i <<= 32 // ERROR "i might be too small for shift of 32"
i >>= 32 // ERROR "i might be too small for shift of 32"
var u uint
_ = u << 31
_ = u << 32 // ERROR "u might be too small for shift of 32"
_ = u >> 32 // ERROR "u might be too small for shift of 32"
u <<= 32 // ERROR "u might be too small for shift of 32"
u >>= 32 // ERROR "u might be too small for shift of 32"
var p uintptr
_ = p << 31
_ = p << 32 // ERROR "p might be too small for shift of 32"
_ = p >> 32 // ERROR "p might be too small for shift of 32"
p <<= 32 // ERROR "p might be too small for shift of 32"
p >>= 32 // ERROR "p might be too small for shift of 32"
}