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