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"
21 "golang.org/x/tools/go/analysis"
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"
32 Instr ir.CallInstruction
40 func (c *Call) Invalid(msg string) {
41 c.invalids = append(c.invalids, msg)
44 type Argument struct {
53 func (arg *Argument) Invalid(msg string) {
54 arg.invalids = append(arg.invalids, msg)
57 type CallCheck func(call *Call)
59 func extractConstExpectKind(v ir.Value, kind constant.Kind) *ir.Const {
61 if k == nil || k.Value == nil || k.Value.Kind() != kind {
67 func extractConst(v ir.Value) *ir.Const {
69 switch v := v.(type) {
72 case *ir.MakeInterface:
73 return extractConst(v.X)
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 {
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)
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)
107 return fmt.Errorf("%q is not a valid URL: %s", s, err)
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) {
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.
128 if ct, ok := val.(*ir.ChangeType); ok {
131 mk, ok := val.(*ir.MakeChan)
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 {
143 func Pointer(v Value) bool {
144 switch v.Value.Type().Underlying().(type) {
145 case *types.Pointer, *types.Interface:
151 func ConvertedFromInt(v Value) bool {
152 conv, ok := v.Value.(*ir.Convert)
156 b, ok := conv.X.Type().Underlying().(*types.Basic)
160 if (b.Info() & types.IsInteger) == 0 {
166 func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool {
167 typ = typ.Underlying()
168 switch typ := typ.(type) {
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:
176 return code.IsGoVersion(pass, 8)
181 for i := 0; i < n; i++ {
182 if !validEncodingBinaryType(pass, typ.Field(i).Type()) {
188 return validEncodingBinaryType(pass, typ.Elem())
189 case *types.Interface:
190 // we can't determine if it's a valid type or not
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()
201 if ttyp, ok := typ.(interface {
204 if _, ok := ttyp.(*types.Pointer); !ok {
209 return validEncodingBinaryType(pass, typ)
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))
223 func validateServiceName(s string) bool {
224 if len(s) < 1 || len(s) > 15 {
227 if s[0] == '-' || s[len(s)-1] == '-' {
230 if strings.Contains(s, "--") {
234 for _, r := range s {
235 if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') {
239 if r >= '0' && r <= '9' {
247 func validatePort(s string) bool {
248 n, err := strconv.ParseInt(s, 10, 64)
250 return validateServiceName(s)
252 return n >= 0 && n <= 65535
255 func ValidHostPort(v Value) bool {
256 if k := extractConstExpectKind(v.Value, constant.String); k != nil {
257 s := constant.StringVal(k.Value)
258 _, port, err := net.SplitHostPort(s)
262 // TODO(dh): check hostname
263 if !validatePort(port) {
270 // ConvertedFrom reports whether value v was converted from type typ.
271 func ConvertedFrom(v Value, typ string) bool {
272 change, ok := v.Value.(*ir.ChangeType)
273 return ok && typeutil.IsType(change.X.Type(), typ)
276 func UniqueStringCutset(v Value) bool {
277 if c := extractConstExpectKind(v.Value, constant.String); c != nil {
278 s := constant.StringVal(c.Value)
284 for i, r := range rs[1:] {