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 }