go/analysis/passes/shift: update for generics

Warn about shifts that exceed the width of a type in the type parameter
type set.

For golang/go#48704

Change-Id: If16650b0c7214415f650884014eda9690d8da11a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/357758
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Tim King <taking@google.com>
This commit is contained in:
Robert Findley 2021-10-21 20:09:29 -04:00
parent 08f2e89cd5
commit 1b02aa2bfe
3 changed files with 61 additions and 4 deletions

View File

@ -14,11 +14,13 @@ import (
"go/ast"
"go/constant"
"go/token"
"math"
"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"
"golang.org/x/tools/internal/typeparams"
)
const Doc = "check for shifts that equal or exceed the width of the integer"
@ -93,9 +95,27 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
if t == nil {
return
}
size := 8 * pass.TypesSizes.Sizeof(t)
if amt >= size {
terms, err := typeparams.StructuralTerms(t)
if err != nil {
return // invalid type
}
sizes := make(map[int64]struct{})
for _, term := range terms {
size := 8 * pass.TypesSizes.Sizeof(term.Type())
sizes[size] = struct{}{}
}
minSize := int64(math.MaxInt64)
for size := range sizes {
if size < minSize {
minSize = size
}
}
if amt >= minSize {
ident := analysisutil.Format(pass.Fset, x)
pass.ReportRangef(node, "%s (%d bits) too small for shift of %d", ident, size, amt)
qualifier := ""
if len(sizes) > 1 {
qualifier = "may be "
}
pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
}
}

View File

@ -9,9 +9,14 @@ import (
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/shift"
"golang.org/x/tools/internal/typeparams"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, shift.Analyzer, "a")
pkgs := []string{"a"}
if typeparams.Enabled {
pkgs = append(pkgs, "typeparams")
}
analysistest.Run(t, testdata, shift.Analyzer, pkgs...)
}

View File

@ -0,0 +1,32 @@
// Copyright 2021 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 typeparams
import "unsafe"
func GenericShiftTest[DifferentSize ~int8|int16|int64, SameSize int8|byte]() {
var d DifferentSize
_ = d << 7
_ = d << 8 // want "d .may be 8 bits. too small for shift of 8"
_ = d << 15 // want "d .may be 8 bits. too small for shift of 15"
_ = (d + 1) << 8 // want ".d . 1. .may be 8 bits. too small for shift of 8"
_ = (d + 1) << 16 // want ".d . 1. .may be 8 bits. too small for shift of 16"
_ = d << (7 + 1) // want "d .may be 8 bits. too small for shift of 8"
_ = d >> 8 // want "d .may be 8 bits. too small for shift of 8"
d <<= 8 // want "d .may be 8 bits. too small for shift of 8"
d >>= 8 // want "d .may be 8 bits. too small for shift of 8"
// go/types does not compute constant sizes for type parameters, so we do not
// report a diagnostic here.
_ = d << (8 * DifferentSize(unsafe.Sizeof(d)))
var s SameSize
_ = s << 7
_ = s << 8 // want "s .8 bits. too small for shift of 8"
_ = s << (7 + 1) // want "s .8 bits. too small for shift of 8"
_ = s >> 8 // want "s .8 bits. too small for shift of 8"
s <<= 8 // want "s .8 bits. too small for shift of 8"
s >>= 8 // want "s .8 bits. too small for shift of 8"
}