mirror of
https://github.com/golang/go
synced 2024-11-12 12:50:28 -07:00
d99d5f7caa
The real world code that inspired this fix, from runtime/pprof/map.go: // Compute hash of (stk, tag). h := uintptr(0) for _, x := range stk { h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1))) h += uintptr(x) * 41 } h = h<<8 | (h >> (8 * (unsafe.Sizeof(h) - 1))) h += uintptr(tag) * 41 Change-Id: I99a95b97cba73811faedb0b9a1b9b54e9a1784a3 Reviewed-on: https://go-review.googlesource.com/37574 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
112 lines
2.3 KiB
Go
112 lines
2.3 KiB
Go
// 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/constant"
|
|
"go/token"
|
|
"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) {
|
|
if f.pkg.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
|
|
}
|
|
|
|
// Ignore shifts where the shift amount is calculated using unsafe.
|
|
// These are used for bit-twiddling tricks.
|
|
var hasUnsafe bool
|
|
ast.Inspect(y, func(n ast.Node) bool {
|
|
sel, ok := n.(*ast.SelectorExpr)
|
|
if !ok {
|
|
return true
|
|
}
|
|
pkg, ok := sel.X.(*ast.Ident)
|
|
if !ok {
|
|
return true
|
|
}
|
|
if pkg.Name == "unsafe" {
|
|
hasUnsafe = true
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
if hasUnsafe {
|
|
return
|
|
}
|
|
|
|
v := f.pkg.types[y].Value
|
|
if v == nil {
|
|
return
|
|
}
|
|
amt, ok := constant.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)
|
|
}
|
|
}
|