Add labels to tasks (#45)
This commit is contained in:
15
vendor/honnef.co/go/tools/staticcheck/CONTRIBUTING.md
vendored
Normal file
15
vendor/honnef.co/go/tools/staticcheck/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Contributing to staticcheck
|
||||
|
||||
## Before filing an issue:
|
||||
|
||||
### Are you having trouble building staticcheck?
|
||||
|
||||
Check you have the latest version of its dependencies. Run
|
||||
```
|
||||
go get -u honnef.co/go/tools/staticcheck
|
||||
```
|
||||
If you still have problems, consider searching for existing issues before filing a new issue.
|
||||
|
||||
## Before sending a pull request:
|
||||
|
||||
Have you understood the purpose of staticcheck? Make sure to carefully read `README`.
|
21
vendor/honnef.co/go/tools/staticcheck/buildtag.go
vendored
Normal file
21
vendor/honnef.co/go/tools/staticcheck/buildtag.go
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
. "honnef.co/go/tools/lint/lintdsl"
|
||||
)
|
||||
|
||||
func buildTags(f *ast.File) [][]string {
|
||||
var out [][]string
|
||||
for _, line := range strings.Split(Preamble(f), "\n") {
|
||||
if !strings.HasPrefix(line, "+build ") {
|
||||
continue
|
||||
}
|
||||
line = strings.TrimSpace(strings.TrimPrefix(line, "+build "))
|
||||
fields := strings.Fields(line)
|
||||
out = append(out, fields)
|
||||
}
|
||||
return out
|
||||
}
|
2790
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
Normal file
2790
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
322
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
Normal file
322
vendor/honnef.co/go/tools/staticcheck/rules.go
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
package staticcheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
. "honnef.co/go/tools/lint/lintdsl"
|
||||
"honnef.co/go/tools/ssa"
|
||||
"honnef.co/go/tools/staticcheck/vrp"
|
||||
)
|
||||
|
||||
const (
|
||||
MsgInvalidHostPort = "invalid port or service name in host:port pair"
|
||||
MsgInvalidUTF8 = "argument is not a valid UTF-8 encoded string"
|
||||
MsgNonUniqueCutset = "cutset contains duplicate characters"
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Job *lint.Job
|
||||
Instr ssa.CallInstruction
|
||||
Args []*Argument
|
||||
|
||||
Checker *Checker
|
||||
Parent *ssa.Function
|
||||
|
||||
invalids []string
|
||||
}
|
||||
|
||||
func (c *Call) Invalid(msg string) {
|
||||
c.invalids = append(c.invalids, msg)
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Value Value
|
||||
invalids []string
|
||||
}
|
||||
|
||||
func (arg *Argument) Invalid(msg string) {
|
||||
arg.invalids = append(arg.invalids, msg)
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Value ssa.Value
|
||||
Range vrp.Range
|
||||
}
|
||||
|
||||
type CallCheck func(call *Call)
|
||||
|
||||
func extractConsts(v ssa.Value) []*ssa.Const {
|
||||
switch v := v.(type) {
|
||||
case *ssa.Const:
|
||||
return []*ssa.Const{v}
|
||||
case *ssa.MakeInterface:
|
||||
return extractConsts(v.X)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateRegexp(v Value) error {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
continue
|
||||
}
|
||||
if c.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(c.Value)
|
||||
if _, err := regexp.Compile(s); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateTimeLayout(v Value) error {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
continue
|
||||
}
|
||||
if c.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(c.Value)
|
||||
s = strings.Replace(s, "_", " ", -1)
|
||||
s = strings.Replace(s, "Z", "-", -1)
|
||||
_, err := time.Parse(s, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateURL(v Value) error {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
continue
|
||||
}
|
||||
if c.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(c.Value)
|
||||
_, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q is not a valid URL: %s", s, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IntValue(v Value, z vrp.Z) bool {
|
||||
r, ok := v.Range.(vrp.IntInterval)
|
||||
if !ok || !r.IsKnown() {
|
||||
return false
|
||||
}
|
||||
if r.Lower != r.Upper {
|
||||
return false
|
||||
}
|
||||
if r.Lower.Cmp(z) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func InvalidUTF8(v Value) bool {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
continue
|
||||
}
|
||||
if c.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(c.Value)
|
||||
if !utf8.ValidString(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func UnbufferedChannel(v Value) bool {
|
||||
r, ok := v.Range.(vrp.ChannelInterval)
|
||||
if !ok || !r.IsKnown() {
|
||||
return false
|
||||
}
|
||||
if r.Size.Lower.Cmp(vrp.NewZ(0)) == 0 &&
|
||||
r.Size.Upper.Cmp(vrp.NewZ(0)) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Pointer(v Value) bool {
|
||||
switch v.Value.Type().Underlying().(type) {
|
||||
case *types.Pointer, *types.Interface:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ConvertedFromInt(v Value) bool {
|
||||
conv, ok := v.Value.(*ssa.Convert)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
b, ok := conv.X.Type().Underlying().(*types.Basic)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if (b.Info() & types.IsInteger) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validEncodingBinaryType(j *lint.Job, typ types.Type) bool {
|
||||
typ = typ.Underlying()
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
case types.Uint8, types.Uint16, types.Uint32, types.Uint64,
|
||||
types.Int8, types.Int16, types.Int32, types.Int64,
|
||||
types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
|
||||
return true
|
||||
case types.Bool:
|
||||
return IsGoVersion(j, 8)
|
||||
}
|
||||
return false
|
||||
case *types.Struct:
|
||||
n := typ.NumFields()
|
||||
for i := 0; i < n; i++ {
|
||||
if !validEncodingBinaryType(j, typ.Field(i).Type()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case *types.Array:
|
||||
return validEncodingBinaryType(j, typ.Elem())
|
||||
case *types.Interface:
|
||||
// we can't determine if it's a valid type or not
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CanBinaryMarshal(j *lint.Job, v Value) bool {
|
||||
typ := v.Value.Type().Underlying()
|
||||
if ttyp, ok := typ.(*types.Pointer); ok {
|
||||
typ = ttyp.Elem().Underlying()
|
||||
}
|
||||
if ttyp, ok := typ.(interface {
|
||||
Elem() types.Type
|
||||
}); ok {
|
||||
if _, ok := ttyp.(*types.Pointer); !ok {
|
||||
typ = ttyp.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
return validEncodingBinaryType(j, typ)
|
||||
}
|
||||
|
||||
func RepeatZeroTimes(name string, arg int) CallCheck {
|
||||
return func(call *Call) {
|
||||
arg := call.Args[arg]
|
||||
if IntValue(arg.Value, vrp.NewZ(0)) {
|
||||
arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateServiceName(s string) bool {
|
||||
if len(s) < 1 || len(s) > 15 {
|
||||
return false
|
||||
}
|
||||
if s[0] == '-' || s[len(s)-1] == '-' {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(s, "--") {
|
||||
return false
|
||||
}
|
||||
hasLetter := false
|
||||
for _, r := range s {
|
||||
if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
|
||||
hasLetter = true
|
||||
continue
|
||||
}
|
||||
if r >= '0' && r <= '9' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return hasLetter
|
||||
}
|
||||
|
||||
func validatePort(s string) bool {
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return validateServiceName(s)
|
||||
}
|
||||
return n >= 0 && n <= 65535
|
||||
}
|
||||
|
||||
func ValidHostPort(v Value) bool {
|
||||
for _, k := range extractConsts(v.Value) {
|
||||
if k.Value == nil {
|
||||
continue
|
||||
}
|
||||
if k.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(k.Value)
|
||||
_, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// TODO(dh): check hostname
|
||||
if !validatePort(port) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ConvertedFrom reports whether value v was converted from type typ.
|
||||
func ConvertedFrom(v Value, typ string) bool {
|
||||
change, ok := v.Value.(*ssa.ChangeType)
|
||||
return ok && IsType(change.X.Type(), typ)
|
||||
}
|
||||
|
||||
func UniqueStringCutset(v Value) bool {
|
||||
for _, c := range extractConsts(v.Value) {
|
||||
if c.Value == nil {
|
||||
continue
|
||||
}
|
||||
if c.Value.Kind() != constant.String {
|
||||
continue
|
||||
}
|
||||
s := constant.StringVal(c.Value)
|
||||
rs := runeSlice(s)
|
||||
if len(rs) < 2 {
|
||||
continue
|
||||
}
|
||||
sort.Sort(rs)
|
||||
for i, r := range rs[1:] {
|
||||
if rs[i] == r {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
73
vendor/honnef.co/go/tools/staticcheck/vrp/channel.go
vendored
Normal file
73
vendor/honnef.co/go/tools/staticcheck/vrp/channel.go
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type ChannelInterval struct {
|
||||
Size IntInterval
|
||||
}
|
||||
|
||||
func (c ChannelInterval) Union(other Range) Range {
|
||||
i, ok := other.(ChannelInterval)
|
||||
if !ok {
|
||||
i = ChannelInterval{EmptyIntInterval}
|
||||
}
|
||||
if c.Size.Empty() || !c.Size.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Size.Empty() || !i.Size.IsKnown() {
|
||||
return c
|
||||
}
|
||||
return ChannelInterval{
|
||||
Size: c.Size.Union(i.Size).(IntInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (c ChannelInterval) String() string {
|
||||
return c.Size.String()
|
||||
}
|
||||
|
||||
func (c ChannelInterval) IsKnown() bool {
|
||||
return c.Size.IsKnown()
|
||||
}
|
||||
|
||||
type MakeChannelConstraint struct {
|
||||
aConstraint
|
||||
Buffer ssa.Value
|
||||
}
|
||||
type ChannelChangeTypeConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
func NewMakeChannelConstraint(buffer, y ssa.Value) Constraint {
|
||||
return &MakeChannelConstraint{NewConstraint(y), buffer}
|
||||
}
|
||||
func NewChannelChangeTypeConstraint(x, y ssa.Value) Constraint {
|
||||
return &ChannelChangeTypeConstraint{NewConstraint(y), x}
|
||||
}
|
||||
|
||||
func (c *MakeChannelConstraint) Operands() []ssa.Value { return []ssa.Value{c.Buffer} }
|
||||
func (c *ChannelChangeTypeConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
|
||||
func (c *MakeChannelConstraint) String() string {
|
||||
return fmt.Sprintf("%s = make(chan, %s)", c.Y().Name(), c.Buffer.Name())
|
||||
}
|
||||
func (c *ChannelChangeTypeConstraint) String() string {
|
||||
return fmt.Sprintf("%s = changetype(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
|
||||
func (c *MakeChannelConstraint) Eval(g *Graph) Range {
|
||||
i, ok := g.Range(c.Buffer).(IntInterval)
|
||||
if !ok {
|
||||
return ChannelInterval{NewIntInterval(NewZ(0), PInfinity)}
|
||||
}
|
||||
if i.Lower.Sign() == -1 {
|
||||
i.Lower = NewZ(0)
|
||||
}
|
||||
return ChannelInterval{i}
|
||||
}
|
||||
func (c *ChannelChangeTypeConstraint) Eval(g *Graph) Range { return g.Range(c.X) }
|
476
vendor/honnef.co/go/tools/staticcheck/vrp/int.go
vendored
Normal file
476
vendor/honnef.co/go/tools/staticcheck/vrp/int.go
vendored
Normal file
@ -0,0 +1,476 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math/big"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type Zs []Z
|
||||
|
||||
func (zs Zs) Len() int {
|
||||
return len(zs)
|
||||
}
|
||||
|
||||
func (zs Zs) Less(i int, j int) bool {
|
||||
return zs[i].Cmp(zs[j]) == -1
|
||||
}
|
||||
|
||||
func (zs Zs) Swap(i int, j int) {
|
||||
zs[i], zs[j] = zs[j], zs[i]
|
||||
}
|
||||
|
||||
type Z struct {
|
||||
infinity int8
|
||||
integer *big.Int
|
||||
}
|
||||
|
||||
func NewZ(n int64) Z {
|
||||
return NewBigZ(big.NewInt(n))
|
||||
}
|
||||
|
||||
func NewBigZ(n *big.Int) Z {
|
||||
return Z{integer: n}
|
||||
}
|
||||
|
||||
func (z1 Z) Infinite() bool {
|
||||
return z1.infinity != 0
|
||||
}
|
||||
|
||||
func (z1 Z) Add(z2 Z) Z {
|
||||
if z2.Sign() == -1 {
|
||||
return z1.Sub(z2.Negate())
|
||||
}
|
||||
if z1 == NInfinity {
|
||||
return NInfinity
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
if z2 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
|
||||
if !z1.Infinite() && !z2.Infinite() {
|
||||
n := &big.Int{}
|
||||
n.Add(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%s + %s is not defined", z1, z2))
|
||||
}
|
||||
|
||||
func (z1 Z) Sub(z2 Z) Z {
|
||||
if z2.Sign() == -1 {
|
||||
return z1.Add(z2.Negate())
|
||||
}
|
||||
if !z1.Infinite() && !z2.Infinite() {
|
||||
n := &big.Int{}
|
||||
n.Sub(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
if z1 != PInfinity && z2 == PInfinity {
|
||||
return NInfinity
|
||||
}
|
||||
if z1.Infinite() && !z2.Infinite() {
|
||||
return Z{infinity: z1.infinity}
|
||||
}
|
||||
if z1 == PInfinity && z2 == PInfinity {
|
||||
return PInfinity
|
||||
}
|
||||
panic(fmt.Sprintf("%s - %s is not defined", z1, z2))
|
||||
}
|
||||
|
||||
func (z1 Z) Mul(z2 Z) Z {
|
||||
if (z1.integer != nil && z1.integer.Sign() == 0) ||
|
||||
(z2.integer != nil && z2.integer.Sign() == 0) {
|
||||
return NewBigZ(&big.Int{})
|
||||
}
|
||||
|
||||
if z1.infinity != 0 || z2.infinity != 0 {
|
||||
return Z{infinity: int8(z1.Sign() * z2.Sign())}
|
||||
}
|
||||
|
||||
n := &big.Int{}
|
||||
n.Mul(z1.integer, z2.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
func (z1 Z) Negate() Z {
|
||||
if z1.infinity == 1 {
|
||||
return NInfinity
|
||||
}
|
||||
if z1.infinity == -1 {
|
||||
return PInfinity
|
||||
}
|
||||
n := &big.Int{}
|
||||
n.Neg(z1.integer)
|
||||
return NewBigZ(n)
|
||||
}
|
||||
|
||||
func (z1 Z) Sign() int {
|
||||
if z1.infinity != 0 {
|
||||
return int(z1.infinity)
|
||||
}
|
||||
return z1.integer.Sign()
|
||||
}
|
||||
|
||||
func (z1 Z) String() string {
|
||||
if z1 == NInfinity {
|
||||
return "-∞"
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return "∞"
|
||||
}
|
||||
return fmt.Sprintf("%d", z1.integer)
|
||||
}
|
||||
|
||||
func (z1 Z) Cmp(z2 Z) int {
|
||||
if z1.infinity == z2.infinity && z1.infinity != 0 {
|
||||
return 0
|
||||
}
|
||||
if z1 == PInfinity {
|
||||
return 1
|
||||
}
|
||||
if z1 == NInfinity {
|
||||
return -1
|
||||
}
|
||||
if z2 == NInfinity {
|
||||
return 1
|
||||
}
|
||||
if z2 == PInfinity {
|
||||
return -1
|
||||
}
|
||||
return z1.integer.Cmp(z2.integer)
|
||||
}
|
||||
|
||||
func MaxZ(zs ...Z) Z {
|
||||
if len(zs) == 0 {
|
||||
panic("Max called with no arguments")
|
||||
}
|
||||
if len(zs) == 1 {
|
||||
return zs[0]
|
||||
}
|
||||
ret := zs[0]
|
||||
for _, z := range zs[1:] {
|
||||
if z.Cmp(ret) == 1 {
|
||||
ret = z
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func MinZ(zs ...Z) Z {
|
||||
if len(zs) == 0 {
|
||||
panic("Min called with no arguments")
|
||||
}
|
||||
if len(zs) == 1 {
|
||||
return zs[0]
|
||||
}
|
||||
ret := zs[0]
|
||||
for _, z := range zs[1:] {
|
||||
if z.Cmp(ret) == -1 {
|
||||
ret = z
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var NInfinity = Z{infinity: -1}
|
||||
var PInfinity = Z{infinity: 1}
|
||||
var EmptyIntInterval = IntInterval{true, PInfinity, NInfinity}
|
||||
|
||||
func InfinityFor(v ssa.Value) IntInterval {
|
||||
if b, ok := v.Type().Underlying().(*types.Basic); ok {
|
||||
if (b.Info() & types.IsUnsigned) != 0 {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
}
|
||||
return NewIntInterval(NInfinity, PInfinity)
|
||||
}
|
||||
|
||||
type IntInterval struct {
|
||||
known bool
|
||||
Lower Z
|
||||
Upper Z
|
||||
}
|
||||
|
||||
func NewIntInterval(l, u Z) IntInterval {
|
||||
if u.Cmp(l) == -1 {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
return IntInterval{known: true, Lower: l, Upper: u}
|
||||
}
|
||||
|
||||
func (i IntInterval) IsKnown() bool {
|
||||
return i.known
|
||||
}
|
||||
|
||||
func (i IntInterval) Empty() bool {
|
||||
return i.Lower == PInfinity && i.Upper == NInfinity
|
||||
}
|
||||
|
||||
func (i IntInterval) IsMaxRange() bool {
|
||||
return i.Lower == NInfinity && i.Upper == PInfinity
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Intersection(i2 IntInterval) IntInterval {
|
||||
if !i1.IsKnown() {
|
||||
return i2
|
||||
}
|
||||
if !i2.IsKnown() {
|
||||
return i1
|
||||
}
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
i3 := NewIntInterval(MaxZ(i1.Lower, i2.Lower), MinZ(i1.Upper, i2.Upper))
|
||||
if i3.Lower.Cmp(i3.Upper) == 1 {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
return i3
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Union(other Range) Range {
|
||||
i2, ok := other.(IntInterval)
|
||||
if !ok {
|
||||
i2 = EmptyIntInterval
|
||||
}
|
||||
if i1.Empty() || !i1.IsKnown() {
|
||||
return i2
|
||||
}
|
||||
if i2.Empty() || !i2.IsKnown() {
|
||||
return i1
|
||||
}
|
||||
return NewIntInterval(MinZ(i1.Lower, i2.Lower), MaxZ(i1.Upper, i2.Upper))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Add(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
|
||||
return NewIntInterval(l1.Add(l2), u1.Add(u2))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Sub(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
l1, u1, l2, u2 := i1.Lower, i1.Upper, i2.Lower, i2.Upper
|
||||
return NewIntInterval(l1.Sub(u2), u1.Sub(l2))
|
||||
}
|
||||
|
||||
func (i1 IntInterval) Mul(i2 IntInterval) IntInterval {
|
||||
if i1.Empty() || i2.Empty() {
|
||||
return EmptyIntInterval
|
||||
}
|
||||
x1, x2 := i1.Lower, i1.Upper
|
||||
y1, y2 := i2.Lower, i2.Upper
|
||||
return NewIntInterval(
|
||||
MinZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
|
||||
MaxZ(x1.Mul(y1), x1.Mul(y2), x2.Mul(y1), x2.Mul(y2)),
|
||||
)
|
||||
}
|
||||
|
||||
func (i1 IntInterval) String() string {
|
||||
if !i1.IsKnown() {
|
||||
return "[⊥, ⊥]"
|
||||
}
|
||||
if i1.Empty() {
|
||||
return "{}"
|
||||
}
|
||||
return fmt.Sprintf("[%s, %s]", i1.Lower, i1.Upper)
|
||||
}
|
||||
|
||||
type IntArithmeticConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
Fn func(IntInterval, IntInterval) IntInterval
|
||||
}
|
||||
|
||||
type IntAddConstraint struct{ *IntArithmeticConstraint }
|
||||
type IntSubConstraint struct{ *IntArithmeticConstraint }
|
||||
type IntMulConstraint struct{ *IntArithmeticConstraint }
|
||||
|
||||
type IntConversionConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type IntIntersectionConstraint struct {
|
||||
aConstraint
|
||||
ranges Ranges
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
I IntInterval
|
||||
resolved bool
|
||||
}
|
||||
|
||||
type IntIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewIntArithmeticConstraint(a, b, y ssa.Value, op token.Token, fn func(IntInterval, IntInterval) IntInterval) *IntArithmeticConstraint {
|
||||
return &IntArithmeticConstraint{NewConstraint(y), a, b, op, fn}
|
||||
}
|
||||
func NewIntAddConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntAddConstraint{NewIntArithmeticConstraint(a, b, y, token.ADD, IntInterval.Add)}
|
||||
}
|
||||
func NewIntSubConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntSubConstraint{NewIntArithmeticConstraint(a, b, y, token.SUB, IntInterval.Sub)}
|
||||
}
|
||||
func NewIntMulConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &IntMulConstraint{NewIntArithmeticConstraint(a, b, y, token.MUL, IntInterval.Mul)}
|
||||
}
|
||||
func NewIntConversionConstraint(x, y ssa.Value) Constraint {
|
||||
return &IntConversionConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewIntIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
|
||||
return &IntIntersectionConstraint{
|
||||
aConstraint: NewConstraint(y),
|
||||
ranges: ranges,
|
||||
A: a,
|
||||
B: b,
|
||||
Op: op,
|
||||
}
|
||||
}
|
||||
func NewIntIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &IntIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *IntArithmeticConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *IntConversionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *IntIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
|
||||
func (s *IntIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *IntArithmeticConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s", c.Y().Name(), c.A.Name(), c.Op, c.B.Name())
|
||||
}
|
||||
func (c *IntConversionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s(%s)", c.Y().Name(), c.Y().Type(), c.X.Name())
|
||||
}
|
||||
func (c *IntIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
|
||||
}
|
||||
func (c *IntIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *IntArithmeticConstraint) Eval(g *Graph) Range {
|
||||
i1, i2 := g.Range(c.A).(IntInterval), g.Range(c.B).(IntInterval)
|
||||
if !i1.IsKnown() || !i2.IsKnown() {
|
||||
return IntInterval{}
|
||||
}
|
||||
return c.Fn(i1, i2)
|
||||
}
|
||||
func (c *IntConversionConstraint) Eval(g *Graph) Range {
|
||||
s := &types.StdSizes{
|
||||
// XXX is it okay to assume the largest word size, or do we
|
||||
// need to be platform specific?
|
||||
WordSize: 8,
|
||||
MaxAlign: 1,
|
||||
}
|
||||
fromI := g.Range(c.X).(IntInterval)
|
||||
toI := g.Range(c.Y()).(IntInterval)
|
||||
fromT := c.X.Type().Underlying().(*types.Basic)
|
||||
toT := c.Y().Type().Underlying().(*types.Basic)
|
||||
fromB := s.Sizeof(c.X.Type())
|
||||
toB := s.Sizeof(c.Y().Type())
|
||||
|
||||
if !fromI.IsKnown() {
|
||||
return toI
|
||||
}
|
||||
if !toI.IsKnown() {
|
||||
return fromI
|
||||
}
|
||||
|
||||
// uint<N> -> sint/uint<M>, M > N: [max(0, l1), min(2**N-1, u2)]
|
||||
if (fromT.Info()&types.IsUnsigned != 0) &&
|
||||
toB > fromB {
|
||||
|
||||
n := big.NewInt(1)
|
||||
n.Lsh(n, uint(fromB*8))
|
||||
n.Sub(n, big.NewInt(1))
|
||||
return NewIntInterval(
|
||||
MaxZ(NewZ(0), fromI.Lower),
|
||||
MinZ(NewBigZ(n), toI.Upper),
|
||||
)
|
||||
}
|
||||
|
||||
// sint<N> -> sint<M>, M > N; [max(-∞, l1), min(2**N-1, u2)]
|
||||
if (fromT.Info()&types.IsUnsigned == 0) &&
|
||||
(toT.Info()&types.IsUnsigned == 0) &&
|
||||
toB > fromB {
|
||||
|
||||
n := big.NewInt(1)
|
||||
n.Lsh(n, uint(fromB*8))
|
||||
n.Sub(n, big.NewInt(1))
|
||||
return NewIntInterval(
|
||||
MaxZ(NInfinity, fromI.Lower),
|
||||
MinZ(NewBigZ(n), toI.Upper),
|
||||
)
|
||||
}
|
||||
|
||||
return fromI
|
||||
}
|
||||
func (c *IntIntersectionConstraint) Eval(g *Graph) Range {
|
||||
xi := g.Range(c.A).(IntInterval)
|
||||
if !xi.IsKnown() {
|
||||
return c.I
|
||||
}
|
||||
return xi.Intersection(c.I)
|
||||
}
|
||||
func (c *IntIntervalConstraint) Eval(*Graph) Range { return c.I }
|
||||
|
||||
func (c *IntIntersectionConstraint) Futures() []ssa.Value {
|
||||
return []ssa.Value{c.B}
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) Resolve() {
|
||||
r, ok := c.ranges[c.B].(IntInterval)
|
||||
if !ok {
|
||||
c.I = InfinityFor(c.Y())
|
||||
return
|
||||
}
|
||||
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r
|
||||
case token.GTR:
|
||||
c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
|
||||
case token.GEQ:
|
||||
c.I = NewIntInterval(r.Lower, PInfinity)
|
||||
case token.LSS:
|
||||
// TODO(dh): do we need 0 instead of NInfinity for uints?
|
||||
c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
|
||||
case token.LEQ:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper)
|
||||
case token.NEQ:
|
||||
c.I = InfinityFor(c.Y())
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) IsKnown() bool {
|
||||
return c.I.IsKnown()
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) MarkUnresolved() {
|
||||
c.resolved = false
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) MarkResolved() {
|
||||
c.resolved = true
|
||||
}
|
||||
|
||||
func (c *IntIntersectionConstraint) IsResolved() bool {
|
||||
return c.resolved
|
||||
}
|
273
vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
vendored
Normal file
273
vendor/honnef.co/go/tools/staticcheck/vrp/slice.go
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
package vrp
|
||||
|
||||
// TODO(dh): most of the constraints have implementations identical to
|
||||
// that of strings. Consider reusing them.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type SliceInterval struct {
|
||||
Length IntInterval
|
||||
}
|
||||
|
||||
func (s SliceInterval) Union(other Range) Range {
|
||||
i, ok := other.(SliceInterval)
|
||||
if !ok {
|
||||
i = SliceInterval{EmptyIntInterval}
|
||||
}
|
||||
if s.Length.Empty() || !s.Length.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Length.Empty() || !i.Length.IsKnown() {
|
||||
return s
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: s.Length.Union(i.Length).(IntInterval),
|
||||
}
|
||||
}
|
||||
func (s SliceInterval) String() string { return s.Length.String() }
|
||||
func (s SliceInterval) IsKnown() bool { return s.Length.IsKnown() }
|
||||
|
||||
type SliceAppendConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
}
|
||||
|
||||
type SliceSliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type ArraySliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type SliceIntersectionConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
type SliceLengthConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type MakeSliceConstraint struct {
|
||||
aConstraint
|
||||
Size ssa.Value
|
||||
}
|
||||
|
||||
type SliceIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewSliceAppendConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &SliceAppendConstraint{NewConstraint(y), a, b}
|
||||
}
|
||||
func NewSliceSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &SliceSliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewArraySliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &ArraySliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewSliceIntersectionConstraint(x ssa.Value, i IntInterval, y ssa.Value) Constraint {
|
||||
return &SliceIntersectionConstraint{NewConstraint(y), x, i}
|
||||
}
|
||||
func NewSliceLengthConstraint(x, y ssa.Value) Constraint {
|
||||
return &SliceLengthConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewMakeSliceConstraint(size, y ssa.Value) Constraint {
|
||||
return &MakeSliceConstraint{NewConstraint(y), size}
|
||||
}
|
||||
func NewSliceIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &SliceIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *SliceAppendConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *SliceSliceConstraint) Operands() []ssa.Value {
|
||||
ops := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
ops = append(ops, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
ops = append(ops, c.Upper)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
func (c *ArraySliceConstraint) Operands() []ssa.Value {
|
||||
ops := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
ops = append(ops, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
ops = append(ops, c.Upper)
|
||||
}
|
||||
return ops
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *SliceLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (c *MakeSliceConstraint) Operands() []ssa.Value { return []ssa.Value{c.Size} }
|
||||
func (s *SliceIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *SliceAppendConstraint) String() string {
|
||||
return fmt.Sprintf("%s = append(%s, %s)", c.Y().Name(), c.A.Name(), c.B.Name())
|
||||
}
|
||||
func (c *SliceSliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *ArraySliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s.%t ⊓ %s", c.Y().Name(), c.X.Name(), c.Y().(*ssa.Sigma).Branch, c.I)
|
||||
}
|
||||
func (c *SliceLengthConstraint) String() string {
|
||||
return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
func (c *MakeSliceConstraint) String() string {
|
||||
return fmt.Sprintf("%s = make(slice, %s)", c.Y().Name(), c.Size.Name())
|
||||
}
|
||||
func (c *SliceIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *SliceAppendConstraint) Eval(g *Graph) Range {
|
||||
l1 := g.Range(c.A).(SliceInterval).Length
|
||||
var l2 IntInterval
|
||||
switch r := g.Range(c.B).(type) {
|
||||
case SliceInterval:
|
||||
l2 = r.Length
|
||||
case StringInterval:
|
||||
l2 = r.Length
|
||||
default:
|
||||
return SliceInterval{}
|
||||
}
|
||||
if !l1.IsKnown() || !l2.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: l1.Add(l2),
|
||||
}
|
||||
}
|
||||
func (c *SliceSliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
ur := g.Range(c.X).(SliceInterval).Length
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return SliceInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *ArraySliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
var l int64
|
||||
switch typ := c.X.Type().(type) {
|
||||
case *types.Array:
|
||||
l = typ.Len()
|
||||
case *types.Pointer:
|
||||
l = typ.Elem().(*types.Array).Len()
|
||||
}
|
||||
ur := NewIntInterval(NewZ(l), NewZ(l))
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return SliceInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return SliceInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *SliceIntersectionConstraint) Eval(g *Graph) Range {
|
||||
xi := g.Range(c.X).(SliceInterval)
|
||||
if !xi.IsKnown() {
|
||||
return c.I
|
||||
}
|
||||
return SliceInterval{
|
||||
Length: xi.Length.Intersection(c.I),
|
||||
}
|
||||
}
|
||||
func (c *SliceLengthConstraint) Eval(g *Graph) Range {
|
||||
i := g.Range(c.X).(SliceInterval).Length
|
||||
if !i.IsKnown() {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
return i
|
||||
}
|
||||
func (c *MakeSliceConstraint) Eval(g *Graph) Range {
|
||||
i, ok := g.Range(c.Size).(IntInterval)
|
||||
if !ok {
|
||||
return SliceInterval{NewIntInterval(NewZ(0), PInfinity)}
|
||||
}
|
||||
if i.Lower.Sign() == -1 {
|
||||
i.Lower = NewZ(0)
|
||||
}
|
||||
return SliceInterval{i}
|
||||
}
|
||||
func (c *SliceIntervalConstraint) Eval(*Graph) Range { return SliceInterval{c.I} }
|
258
vendor/honnef.co/go/tools/staticcheck/vrp/string.go
vendored
Normal file
258
vendor/honnef.co/go/tools/staticcheck/vrp/string.go
vendored
Normal file
@ -0,0 +1,258 @@
|
||||
package vrp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
type StringInterval struct {
|
||||
Length IntInterval
|
||||
}
|
||||
|
||||
func (s StringInterval) Union(other Range) Range {
|
||||
i, ok := other.(StringInterval)
|
||||
if !ok {
|
||||
i = StringInterval{EmptyIntInterval}
|
||||
}
|
||||
if s.Length.Empty() || !s.Length.IsKnown() {
|
||||
return i
|
||||
}
|
||||
if i.Length.Empty() || !i.Length.IsKnown() {
|
||||
return s
|
||||
}
|
||||
return StringInterval{
|
||||
Length: s.Length.Union(i.Length).(IntInterval),
|
||||
}
|
||||
}
|
||||
|
||||
func (s StringInterval) String() string {
|
||||
return s.Length.String()
|
||||
}
|
||||
|
||||
func (s StringInterval) IsKnown() bool {
|
||||
return s.Length.IsKnown()
|
||||
}
|
||||
|
||||
type StringSliceConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
Lower ssa.Value
|
||||
Upper ssa.Value
|
||||
}
|
||||
|
||||
type StringIntersectionConstraint struct {
|
||||
aConstraint
|
||||
ranges Ranges
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
Op token.Token
|
||||
I IntInterval
|
||||
resolved bool
|
||||
}
|
||||
|
||||
type StringConcatConstraint struct {
|
||||
aConstraint
|
||||
A ssa.Value
|
||||
B ssa.Value
|
||||
}
|
||||
|
||||
type StringLengthConstraint struct {
|
||||
aConstraint
|
||||
X ssa.Value
|
||||
}
|
||||
|
||||
type StringIntervalConstraint struct {
|
||||
aConstraint
|
||||
I IntInterval
|
||||
}
|
||||
|
||||
func NewStringSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
|
||||
return &StringSliceConstraint{NewConstraint(y), x, lower, upper}
|
||||
}
|
||||
func NewStringIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
|
||||
return &StringIntersectionConstraint{
|
||||
aConstraint: NewConstraint(y),
|
||||
ranges: ranges,
|
||||
A: a,
|
||||
B: b,
|
||||
Op: op,
|
||||
}
|
||||
}
|
||||
func NewStringConcatConstraint(a, b, y ssa.Value) Constraint {
|
||||
return &StringConcatConstraint{NewConstraint(y), a, b}
|
||||
}
|
||||
func NewStringLengthConstraint(x ssa.Value, y ssa.Value) Constraint {
|
||||
return &StringLengthConstraint{NewConstraint(y), x}
|
||||
}
|
||||
func NewStringIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
|
||||
return &StringIntervalConstraint{NewConstraint(y), i}
|
||||
}
|
||||
|
||||
func (c *StringSliceConstraint) Operands() []ssa.Value {
|
||||
vs := []ssa.Value{c.X}
|
||||
if c.Lower != nil {
|
||||
vs = append(vs, c.Lower)
|
||||
}
|
||||
if c.Upper != nil {
|
||||
vs = append(vs, c.Upper)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
func (c *StringIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
|
||||
func (c StringConcatConstraint) Operands() []ssa.Value { return []ssa.Value{c.A, c.B} }
|
||||
func (c *StringLengthConstraint) Operands() []ssa.Value { return []ssa.Value{c.X} }
|
||||
func (s *StringIntervalConstraint) Operands() []ssa.Value { return nil }
|
||||
|
||||
func (c *StringSliceConstraint) String() string {
|
||||
var lname, uname string
|
||||
if c.Lower != nil {
|
||||
lname = c.Lower.Name()
|
||||
}
|
||||
if c.Upper != nil {
|
||||
uname = c.Upper.Name()
|
||||
}
|
||||
return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
|
||||
}
|
||||
func (c *StringIntersectionConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s %s %s (%t branch)", c.Y().Name(), c.A.Name(), c.Op, c.B.Name(), c.Y().(*ssa.Sigma).Branch)
|
||||
}
|
||||
func (c StringConcatConstraint) String() string {
|
||||
return fmt.Sprintf("%s = %s + %s", c.Y().Name(), c.A.Name(), c.B.Name())
|
||||
}
|
||||
func (c *StringLengthConstraint) String() string {
|
||||
return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
|
||||
}
|
||||
func (c *StringIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
|
||||
|
||||
func (c *StringSliceConstraint) Eval(g *Graph) Range {
|
||||
lr := NewIntInterval(NewZ(0), NewZ(0))
|
||||
if c.Lower != nil {
|
||||
lr = g.Range(c.Lower).(IntInterval)
|
||||
}
|
||||
ur := g.Range(c.X).(StringInterval).Length
|
||||
if c.Upper != nil {
|
||||
ur = g.Range(c.Upper).(IntInterval)
|
||||
}
|
||||
if !lr.IsKnown() || !ur.IsKnown() {
|
||||
return StringInterval{}
|
||||
}
|
||||
|
||||
ls := []Z{
|
||||
ur.Lower.Sub(lr.Lower),
|
||||
ur.Upper.Sub(lr.Lower),
|
||||
ur.Lower.Sub(lr.Upper),
|
||||
ur.Upper.Sub(lr.Upper),
|
||||
}
|
||||
// TODO(dh): if we don't truncate lengths to 0 we might be able to
|
||||
// easily detect slices with high < low. we'd need to treat -∞
|
||||
// specially, though.
|
||||
for i, l := range ls {
|
||||
if l.Sign() == -1 {
|
||||
ls[i] = NewZ(0)
|
||||
}
|
||||
}
|
||||
|
||||
return StringInterval{
|
||||
Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
|
||||
}
|
||||
}
|
||||
func (c *StringIntersectionConstraint) Eval(g *Graph) Range {
|
||||
var l IntInterval
|
||||
switch r := g.Range(c.A).(type) {
|
||||
case StringInterval:
|
||||
l = r.Length
|
||||
case IntInterval:
|
||||
l = r
|
||||
}
|
||||
|
||||
if !l.IsKnown() {
|
||||
return StringInterval{c.I}
|
||||
}
|
||||
return StringInterval{
|
||||
Length: l.Intersection(c.I),
|
||||
}
|
||||
}
|
||||
func (c StringConcatConstraint) Eval(g *Graph) Range {
|
||||
i1, i2 := g.Range(c.A).(StringInterval), g.Range(c.B).(StringInterval)
|
||||
if !i1.Length.IsKnown() || !i2.Length.IsKnown() {
|
||||
return StringInterval{}
|
||||
}
|
||||
return StringInterval{
|
||||
Length: i1.Length.Add(i2.Length),
|
||||
}
|
||||
}
|
||||
func (c *StringLengthConstraint) Eval(g *Graph) Range {
|
||||
i := g.Range(c.X).(StringInterval).Length
|
||||
if !i.IsKnown() {
|
||||
return NewIntInterval(NewZ(0), PInfinity)
|
||||
}
|
||||
return i
|
||||
}
|
||||
func (c *StringIntervalConstraint) Eval(*Graph) Range { return StringInterval{c.I} }
|
||||
|
||||
func (c *StringIntersectionConstraint) Futures() []ssa.Value {
|
||||
return []ssa.Value{c.B}
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) Resolve() {
|
||||
if (c.A.Type().Underlying().(*types.Basic).Info() & types.IsString) != 0 {
|
||||
// comparing two strings
|
||||
r, ok := c.ranges[c.B].(StringInterval)
|
||||
if !ok {
|
||||
c.I = NewIntInterval(NewZ(0), PInfinity)
|
||||
return
|
||||
}
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r.Length
|
||||
case token.GTR, token.GEQ:
|
||||
c.I = NewIntInterval(r.Length.Lower, PInfinity)
|
||||
case token.LSS, token.LEQ:
|
||||
c.I = NewIntInterval(NewZ(0), r.Length.Upper)
|
||||
case token.NEQ:
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
} else {
|
||||
r, ok := c.ranges[c.B].(IntInterval)
|
||||
if !ok {
|
||||
c.I = NewIntInterval(NewZ(0), PInfinity)
|
||||
return
|
||||
}
|
||||
// comparing two lengths
|
||||
switch c.Op {
|
||||
case token.EQL:
|
||||
c.I = r
|
||||
case token.GTR:
|
||||
c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
|
||||
case token.GEQ:
|
||||
c.I = NewIntInterval(r.Lower, PInfinity)
|
||||
case token.LSS:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
|
||||
case token.LEQ:
|
||||
c.I = NewIntInterval(NInfinity, r.Upper)
|
||||
case token.NEQ:
|
||||
default:
|
||||
panic("unsupported op " + c.Op.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) IsKnown() bool {
|
||||
return c.I.IsKnown()
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) MarkUnresolved() {
|
||||
c.resolved = false
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) MarkResolved() {
|
||||
c.resolved = true
|
||||
}
|
||||
|
||||
func (c *StringIntersectionConstraint) IsResolved() bool {
|
||||
return c.resolved
|
||||
}
|
1049
vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
vendored
Normal file
1049
vendor/honnef.co/go/tools/staticcheck/vrp/vrp.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user