github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/staticcheck/vrp/string.go (about)

     1  package vrp
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"go/types"
     7  
     8  	"github.com/golangci/go-tools/ssa"
     9  )
    10  
    11  type StringInterval struct {
    12  	Length IntInterval
    13  }
    14  
    15  func (s StringInterval) Union(other Range) Range {
    16  	i, ok := other.(StringInterval)
    17  	if !ok {
    18  		i = StringInterval{EmptyIntInterval}
    19  	}
    20  	if s.Length.Empty() || !s.Length.IsKnown() {
    21  		return i
    22  	}
    23  	if i.Length.Empty() || !i.Length.IsKnown() {
    24  		return s
    25  	}
    26  	return StringInterval{
    27  		Length: s.Length.Union(i.Length).(IntInterval),
    28  	}
    29  }
    30  
    31  func (s StringInterval) String() string {
    32  	return s.Length.String()
    33  }
    34  
    35  func (s StringInterval) IsKnown() bool {
    36  	return s.Length.IsKnown()
    37  }
    38  
    39  type StringSliceConstraint struct {
    40  	aConstraint
    41  	X     ssa.Value
    42  	Lower ssa.Value
    43  	Upper ssa.Value
    44  }
    45  
    46  type StringIntersectionConstraint struct {
    47  	aConstraint
    48  	ranges   Ranges
    49  	A        ssa.Value
    50  	B        ssa.Value
    51  	Op       token.Token
    52  	I        IntInterval
    53  	resolved bool
    54  }
    55  
    56  type StringConcatConstraint struct {
    57  	aConstraint
    58  	A ssa.Value
    59  	B ssa.Value
    60  }
    61  
    62  type StringLengthConstraint struct {
    63  	aConstraint
    64  	X ssa.Value
    65  }
    66  
    67  type StringIntervalConstraint struct {
    68  	aConstraint
    69  	I IntInterval
    70  }
    71  
    72  func NewStringSliceConstraint(x, lower, upper, y ssa.Value) Constraint {
    73  	return &StringSliceConstraint{NewConstraint(y), x, lower, upper}
    74  }
    75  func NewStringIntersectionConstraint(a, b ssa.Value, op token.Token, ranges Ranges, y ssa.Value) Constraint {
    76  	return &StringIntersectionConstraint{
    77  		aConstraint: NewConstraint(y),
    78  		ranges:      ranges,
    79  		A:           a,
    80  		B:           b,
    81  		Op:          op,
    82  	}
    83  }
    84  func NewStringConcatConstraint(a, b, y ssa.Value) Constraint {
    85  	return &StringConcatConstraint{NewConstraint(y), a, b}
    86  }
    87  func NewStringLengthConstraint(x ssa.Value, y ssa.Value) Constraint {
    88  	return &StringLengthConstraint{NewConstraint(y), x}
    89  }
    90  func NewStringIntervalConstraint(i IntInterval, y ssa.Value) Constraint {
    91  	return &StringIntervalConstraint{NewConstraint(y), i}
    92  }
    93  
    94  func (c *StringSliceConstraint) Operands() []ssa.Value {
    95  	vs := []ssa.Value{c.X}
    96  	if c.Lower != nil {
    97  		vs = append(vs, c.Lower)
    98  	}
    99  	if c.Upper != nil {
   100  		vs = append(vs, c.Upper)
   101  	}
   102  	return vs
   103  }
   104  func (c *StringIntersectionConstraint) Operands() []ssa.Value { return []ssa.Value{c.A} }
   105  func (c StringConcatConstraint) Operands() []ssa.Value        { return []ssa.Value{c.A, c.B} }
   106  func (c *StringLengthConstraint) Operands() []ssa.Value       { return []ssa.Value{c.X} }
   107  func (s *StringIntervalConstraint) Operands() []ssa.Value     { return nil }
   108  
   109  func (c *StringSliceConstraint) String() string {
   110  	var lname, uname string
   111  	if c.Lower != nil {
   112  		lname = c.Lower.Name()
   113  	}
   114  	if c.Upper != nil {
   115  		uname = c.Upper.Name()
   116  	}
   117  	return fmt.Sprintf("%s[%s:%s]", c.X.Name(), lname, uname)
   118  }
   119  func (c *StringIntersectionConstraint) String() string {
   120  	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)
   121  }
   122  func (c StringConcatConstraint) String() string {
   123  	return fmt.Sprintf("%s = %s + %s", c.Y().Name(), c.A.Name(), c.B.Name())
   124  }
   125  func (c *StringLengthConstraint) String() string {
   126  	return fmt.Sprintf("%s = len(%s)", c.Y().Name(), c.X.Name())
   127  }
   128  func (c *StringIntervalConstraint) String() string { return fmt.Sprintf("%s = %s", c.Y().Name(), c.I) }
   129  
   130  func (c *StringSliceConstraint) Eval(g *Graph) Range {
   131  	lr := NewIntInterval(NewZ(0), NewZ(0))
   132  	if c.Lower != nil {
   133  		lr = g.Range(c.Lower).(IntInterval)
   134  	}
   135  	ur := g.Range(c.X).(StringInterval).Length
   136  	if c.Upper != nil {
   137  		ur = g.Range(c.Upper).(IntInterval)
   138  	}
   139  	if !lr.IsKnown() || !ur.IsKnown() {
   140  		return StringInterval{}
   141  	}
   142  
   143  	ls := []Z{
   144  		ur.Lower.Sub(lr.Lower),
   145  		ur.Upper.Sub(lr.Lower),
   146  		ur.Lower.Sub(lr.Upper),
   147  		ur.Upper.Sub(lr.Upper),
   148  	}
   149  	// TODO(dh): if we don't truncate lengths to 0 we might be able to
   150  	// easily detect slices with high < low. we'd need to treat -∞
   151  	// specially, though.
   152  	for i, l := range ls {
   153  		if l.Sign() == -1 {
   154  			ls[i] = NewZ(0)
   155  		}
   156  	}
   157  
   158  	return StringInterval{
   159  		Length: NewIntInterval(MinZ(ls...), MaxZ(ls...)),
   160  	}
   161  }
   162  func (c *StringIntersectionConstraint) Eval(g *Graph) Range {
   163  	var l IntInterval
   164  	switch r := g.Range(c.A).(type) {
   165  	case StringInterval:
   166  		l = r.Length
   167  	case IntInterval:
   168  		l = r
   169  	}
   170  
   171  	if !l.IsKnown() {
   172  		return StringInterval{c.I}
   173  	}
   174  	return StringInterval{
   175  		Length: l.Intersection(c.I),
   176  	}
   177  }
   178  func (c StringConcatConstraint) Eval(g *Graph) Range {
   179  	i1, i2 := g.Range(c.A).(StringInterval), g.Range(c.B).(StringInterval)
   180  	if !i1.Length.IsKnown() || !i2.Length.IsKnown() {
   181  		return StringInterval{}
   182  	}
   183  	return StringInterval{
   184  		Length: i1.Length.Add(i2.Length),
   185  	}
   186  }
   187  func (c *StringLengthConstraint) Eval(g *Graph) Range {
   188  	i := g.Range(c.X).(StringInterval).Length
   189  	if !i.IsKnown() {
   190  		return NewIntInterval(NewZ(0), PInfinity)
   191  	}
   192  	return i
   193  }
   194  func (c *StringIntervalConstraint) Eval(*Graph) Range { return StringInterval{c.I} }
   195  
   196  func (c *StringIntersectionConstraint) Futures() []ssa.Value {
   197  	return []ssa.Value{c.B}
   198  }
   199  
   200  func (c *StringIntersectionConstraint) Resolve() {
   201  	if (c.A.Type().Underlying().(*types.Basic).Info() & types.IsString) != 0 {
   202  		// comparing two strings
   203  		r, ok := c.ranges[c.B].(StringInterval)
   204  		if !ok {
   205  			c.I = NewIntInterval(NewZ(0), PInfinity)
   206  			return
   207  		}
   208  		switch c.Op {
   209  		case token.EQL:
   210  			c.I = r.Length
   211  		case token.GTR, token.GEQ:
   212  			c.I = NewIntInterval(r.Length.Lower, PInfinity)
   213  		case token.LSS, token.LEQ:
   214  			c.I = NewIntInterval(NewZ(0), r.Length.Upper)
   215  		case token.NEQ:
   216  		default:
   217  			panic("unsupported op " + c.Op.String())
   218  		}
   219  	} else {
   220  		r, ok := c.ranges[c.B].(IntInterval)
   221  		if !ok {
   222  			c.I = NewIntInterval(NewZ(0), PInfinity)
   223  			return
   224  		}
   225  		// comparing two lengths
   226  		switch c.Op {
   227  		case token.EQL:
   228  			c.I = r
   229  		case token.GTR:
   230  			c.I = NewIntInterval(r.Lower.Add(NewZ(1)), PInfinity)
   231  		case token.GEQ:
   232  			c.I = NewIntInterval(r.Lower, PInfinity)
   233  		case token.LSS:
   234  			c.I = NewIntInterval(NInfinity, r.Upper.Sub(NewZ(1)))
   235  		case token.LEQ:
   236  			c.I = NewIntInterval(NInfinity, r.Upper)
   237  		case token.NEQ:
   238  		default:
   239  			panic("unsupported op " + c.Op.String())
   240  		}
   241  	}
   242  }
   243  
   244  func (c *StringIntersectionConstraint) IsKnown() bool {
   245  	return c.I.IsKnown()
   246  }
   247  
   248  func (c *StringIntersectionConstraint) MarkUnresolved() {
   249  	c.resolved = false
   250  }
   251  
   252  func (c *StringIntersectionConstraint) MarkResolved() {
   253  	c.resolved = true
   254  }
   255  
   256  func (c *StringIntersectionConstraint) IsResolved() bool {
   257  	return c.resolved
   258  }