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