api/vendor/honnef.co/go/tools/staticcheck/vrp/string.go

259 lines
6.2 KiB
Go

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
}