.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / staticcheck / rules.go
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                 _, port, err := net.SplitHostPort(s)
259                 if err != nil {
260                         return false
261                 }
262                 // TODO(dh): check hostname
263                 if !validatePort(port) {
264                         return false
265                 }
266         }
267         return true
268 }
269
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)
274 }
275
276 func UniqueStringCutset(v Value) bool {
277         if c := extractConstExpectKind(v.Value, constant.String); c != nil {
278                 s := constant.StringVal(c.Value)
279                 rs := runeSlice(s)
280                 if len(rs) < 2 {
281                         return true
282                 }
283                 sort.Sort(rs)
284                 for i, r := range rs[1:] {
285                         if rs[i] == r {
286                                 return false
287                         }
288                 }
289         }
290         return true
291 }