github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/honnef.co/go/tools/staticcheck/rules.go (about)

     1  package staticcheck
     2  
     3  import (
     4  	"fmt"
     5  	"go/constant"
     6  	"go/types"
     7  	"net"
     8  	"net/url"
     9  	"regexp"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  	"unicode/utf8"
    15  
    16  	"honnef.co/go/tools/lint"
    17  	"honnef.co/go/tools/ssa"
    18  	"honnef.co/go/tools/staticcheck/vrp"
    19  )
    20  
    21  const (
    22  	MsgInvalidHostPort = "invalid port or service name in host:port pair"
    23  	MsgInvalidUTF8     = "argument is not a valid UTF-8 encoded string"
    24  	MsgNonUniqueCutset = "cutset contains duplicate characters"
    25  )
    26  
    27  type Call struct {
    28  	Job   *lint.Job
    29  	Instr ssa.CallInstruction
    30  	Args  []*Argument
    31  
    32  	Checker *Checker
    33  	Parent  *ssa.Function
    34  
    35  	invalids []string
    36  }
    37  
    38  func (c *Call) Invalid(msg string) {
    39  	c.invalids = append(c.invalids, msg)
    40  }
    41  
    42  type Argument struct {
    43  	Value    Value
    44  	invalids []string
    45  }
    46  
    47  func (arg *Argument) Invalid(msg string) {
    48  	arg.invalids = append(arg.invalids, msg)
    49  }
    50  
    51  type Value struct {
    52  	Value ssa.Value
    53  	Range vrp.Range
    54  }
    55  
    56  type CallCheck func(call *Call)
    57  
    58  func extractConsts(v ssa.Value) []*ssa.Const {
    59  	switch v := v.(type) {
    60  	case *ssa.Const:
    61  		return []*ssa.Const{v}
    62  	case *ssa.MakeInterface:
    63  		return extractConsts(v.X)
    64  	default:
    65  		return nil
    66  	}
    67  }
    68  
    69  func ValidateRegexp(v Value) error {
    70  	for _, c := range extractConsts(v.Value) {
    71  		if c.Value == nil {
    72  			continue
    73  		}
    74  		if c.Value.Kind() != constant.String {
    75  			continue
    76  		}
    77  		s := constant.StringVal(c.Value)
    78  		if _, err := regexp.Compile(s); err != nil {
    79  			return err
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func ValidateTimeLayout(v Value) error {
    86  	for _, c := range extractConsts(v.Value) {
    87  		if c.Value == nil {
    88  			continue
    89  		}
    90  		if c.Value.Kind() != constant.String {
    91  			continue
    92  		}
    93  		s := constant.StringVal(c.Value)
    94  		s = strings.Replace(s, "_", " ", -1)
    95  		s = strings.Replace(s, "Z", "-", -1)
    96  		_, err := time.Parse(s, s)
    97  		if err != nil {
    98  			return err
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  func ValidateURL(v Value) error {
   105  	for _, c := range extractConsts(v.Value) {
   106  		if c.Value == nil {
   107  			continue
   108  		}
   109  		if c.Value.Kind() != constant.String {
   110  			continue
   111  		}
   112  		s := constant.StringVal(c.Value)
   113  		_, err := url.Parse(s)
   114  		if err != nil {
   115  			return fmt.Errorf("%q is not a valid URL: %s", s, err)
   116  		}
   117  	}
   118  	return nil
   119  }
   120  
   121  func IntValue(v Value, z vrp.Z) bool {
   122  	r, ok := v.Range.(vrp.IntInterval)
   123  	if !ok || !r.IsKnown() {
   124  		return false
   125  	}
   126  	if r.Lower != r.Upper {
   127  		return false
   128  	}
   129  	if r.Lower.Cmp(z) == 0 {
   130  		return true
   131  	}
   132  	return false
   133  }
   134  
   135  func InvalidUTF8(v Value) bool {
   136  	for _, c := range extractConsts(v.Value) {
   137  		if c.Value == nil {
   138  			continue
   139  		}
   140  		if c.Value.Kind() != constant.String {
   141  			continue
   142  		}
   143  		s := constant.StringVal(c.Value)
   144  		if !utf8.ValidString(s) {
   145  			return true
   146  		}
   147  	}
   148  	return false
   149  }
   150  
   151  func UnbufferedChannel(v Value) bool {
   152  	r, ok := v.Range.(vrp.ChannelInterval)
   153  	if !ok || !r.IsKnown() {
   154  		return false
   155  	}
   156  	if r.Size.Lower.Cmp(vrp.NewZ(0)) == 0 &&
   157  		r.Size.Upper.Cmp(vrp.NewZ(0)) == 0 {
   158  		return true
   159  	}
   160  	return false
   161  }
   162  
   163  func Pointer(v Value) bool {
   164  	switch v.Value.Type().Underlying().(type) {
   165  	case *types.Pointer, *types.Interface:
   166  		return true
   167  	}
   168  	return false
   169  }
   170  
   171  func ConvertedFromInt(v Value) bool {
   172  	conv, ok := v.Value.(*ssa.Convert)
   173  	if !ok {
   174  		return false
   175  	}
   176  	b, ok := conv.X.Type().Underlying().(*types.Basic)
   177  	if !ok {
   178  		return false
   179  	}
   180  	if (b.Info() & types.IsInteger) == 0 {
   181  		return false
   182  	}
   183  	return true
   184  }
   185  
   186  func validEncodingBinaryType(j *lint.Job, typ types.Type) bool {
   187  	typ = typ.Underlying()
   188  	switch typ := typ.(type) {
   189  	case *types.Basic:
   190  		switch typ.Kind() {
   191  		case types.Uint8, types.Uint16, types.Uint32, types.Uint64,
   192  			types.Int8, types.Int16, types.Int32, types.Int64,
   193  			types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid:
   194  			return true
   195  		case types.Bool:
   196  			return j.IsGoVersion(8)
   197  		}
   198  		return false
   199  	case *types.Struct:
   200  		n := typ.NumFields()
   201  		for i := 0; i < n; i++ {
   202  			if !validEncodingBinaryType(j, typ.Field(i).Type()) {
   203  				return false
   204  			}
   205  		}
   206  		return true
   207  	case *types.Array:
   208  		return validEncodingBinaryType(j, typ.Elem())
   209  	case *types.Interface:
   210  		// we can't determine if it's a valid type or not
   211  		return true
   212  	}
   213  	return false
   214  }
   215  
   216  func CanBinaryMarshal(j *lint.Job, v Value) bool {
   217  	typ := v.Value.Type().Underlying()
   218  	if ttyp, ok := typ.(*types.Pointer); ok {
   219  		typ = ttyp.Elem().Underlying()
   220  	}
   221  	if ttyp, ok := typ.(interface {
   222  		Elem() types.Type
   223  	}); ok {
   224  		if _, ok := ttyp.(*types.Pointer); !ok {
   225  			typ = ttyp.Elem()
   226  		}
   227  	}
   228  
   229  	return validEncodingBinaryType(j, typ)
   230  }
   231  
   232  func RepeatZeroTimes(name string, arg int) CallCheck {
   233  	return func(call *Call) {
   234  		arg := call.Args[arg]
   235  		if IntValue(arg.Value, vrp.NewZ(0)) {
   236  			arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name))
   237  		}
   238  	}
   239  }
   240  
   241  func validateServiceName(s string) bool {
   242  	if len(s) < 1 || len(s) > 15 {
   243  		return false
   244  	}
   245  	if s[0] == '-' || s[len(s)-1] == '-' {
   246  		return false
   247  	}
   248  	if strings.Contains(s, "--") {
   249  		return false
   250  	}
   251  	hasLetter := false
   252  	for _, r := range s {
   253  		if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
   254  			hasLetter = true
   255  			continue
   256  		}
   257  		if r >= '0' && r <= '9' {
   258  			continue
   259  		}
   260  		return false
   261  	}
   262  	return hasLetter
   263  }
   264  
   265  func validatePort(s string) bool {
   266  	n, err := strconv.ParseInt(s, 10, 64)
   267  	if err != nil {
   268  		return validateServiceName(s)
   269  	}
   270  	return n >= 0 && n <= 65535
   271  }
   272  
   273  func ValidHostPort(v Value) bool {
   274  	for _, k := range extractConsts(v.Value) {
   275  		if k.Value == nil {
   276  			continue
   277  		}
   278  		if k.Value.Kind() != constant.String {
   279  			continue
   280  		}
   281  		s := constant.StringVal(k.Value)
   282  		_, port, err := net.SplitHostPort(s)
   283  		if err != nil {
   284  			return false
   285  		}
   286  		// TODO(dh): check hostname
   287  		if !validatePort(port) {
   288  			return false
   289  		}
   290  	}
   291  	return true
   292  }
   293  
   294  // ConvertedFrom reports whether value v was converted from type typ.
   295  func ConvertedFrom(v Value, typ string) bool {
   296  	change, ok := v.Value.(*ssa.ChangeType)
   297  	return ok && types.TypeString(change.X.Type(), nil) == typ
   298  }
   299  
   300  func UniqueStringCutset(v Value) bool {
   301  	for _, c := range extractConsts(v.Value) {
   302  		if c.Value == nil {
   303  			continue
   304  		}
   305  		if c.Value.Kind() != constant.String {
   306  			continue
   307  		}
   308  		s := constant.StringVal(c.Value)
   309  		rs := runeSlice(s)
   310  		if len(rs) < 2 {
   311  			continue
   312  		}
   313  		sort.Sort(rs)
   314  		for i, r := range rs[1:] {
   315  			if rs[i] == r {
   316  				return false
   317  			}
   318  		}
   319  	}
   320  	return true
   321  }