aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/staticcheck/doc.go
blob: 4a87d4a24cea4f2d0694d9822abfb4b7be50aaff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
package staticcheck

import "honnef.co/go/tools/lint"

var Docs = map[string]*lint.Documentation{
	"SA1000": &lint.Documentation{
		Title: `Invalid regular expression`,
		Since: "2017.1",
	},

	"SA1001": &lint.Documentation{
		Title: `Invalid template`,
		Since: "2017.1",
	},

	"SA1002": &lint.Documentation{
		Title: `Invalid format in time.Parse`,
		Since: "2017.1",
	},

	"SA1003": &lint.Documentation{
		Title: `Unsupported argument to functions in encoding/binary`,
		Text: `The encoding/binary package can only serialize types with known sizes.
This precludes the use of the int and uint types, as their sizes
differ on different architectures. Furthermore, it doesn't support
serializing maps, channels, strings, or functions.

Before Go 1.8, bool wasn't supported, either.`,
		Since: "2017.1",
	},

	"SA1004": &lint.Documentation{
		Title: `Suspiciously small untyped constant in time.Sleep`,
		Text: `The time.Sleep function takes a time.Duration as its only argument.
Durations are expressed in nanoseconds. Thus, calling time.Sleep(1)
will sleep for 1 nanosecond. This is a common source of bugs, as sleep
functions in other languages often accept seconds or milliseconds.

The time package provides constants such as time.Second to express
large durations. These can be combined with arithmetic to express
arbitrary durations, for example '5 * time.Second' for 5 seconds.

If you truly meant to sleep for a tiny amount of time, use
'n * time.Nanosecond' to signal to staticcheck that you did mean to sleep
for some amount of nanoseconds.`,
		Since: "2017.1",
	},

	"SA1005": &lint.Documentation{
		Title: `Invalid first argument to exec.Command`,
		Text: `os/exec runs programs directly (using variants of the fork and exec
system calls on Unix systems). This shouldn't be confused with running
a command in a shell. The shell will allow for features such as input
redirection, pipes, and general scripting. The shell is also
responsible for splitting the user's input into a program name and its
arguments. For example, the equivalent to

    ls / /tmp

would be

    exec.Command("ls", "/", "/tmp")

If you want to run a command in a shell, consider using something like
the following – but be aware that not all systems, particularly
Windows, will have a /bin/sh program:

    exec.Command("/bin/sh", "-c", "ls | grep Awesome")`,
		Since: "2017.1",
	},

	"SA1006": &lint.Documentation{
		Title: `Printf with dynamic first argument and no further arguments`,
		Text: `Using fmt.Printf with a dynamic first argument can lead to unexpected
output. The first argument is a format string, where certain character
combinations have special meaning. If, for example, a user were to
enter a string such as

    Interest rate: 5%

and you printed it with

    fmt.Printf(s)

it would lead to the following output:

    Interest rate: 5%!(NOVERB).

Similarly, forming the first parameter via string concatenation with
user input should be avoided for the same reason. When printing user
input, either use a variant of fmt.Print, or use the %s Printf verb
and pass the string as an argument.`,
		Since: "2017.1",
	},

	"SA1007": &lint.Documentation{
		Title: `Invalid URL in net/url.Parse`,
		Since: "2017.1",
	},

	"SA1008": &lint.Documentation{
		Title: `Non-canonical key in http.Header map`,
		Text: `Keys in http.Header maps are canonical, meaning they follow a specific
combination of uppercase and lowercase letters. Methods such as
http.Header.Add and http.Header.Del convert inputs into this canonical
form before manipulating the map.

When manipulating http.Header maps directly, as opposed to using the
provided methods, care should be taken to stick to canonical form in
order to avoid inconsistencies. The following piece of code
demonstrates one such inconsistency:

    h := http.Header{}
    h["etag"] = []string{"1234"}
    h.Add("etag", "5678")
    fmt.Println(h)

    // Output:
    // map[Etag:[5678] etag:[1234]]

The easiest way of obtaining the canonical form of a key is to use
http.CanonicalHeaderKey.`,
		Since: "2017.1",
	},

	"SA1010": &lint.Documentation{
		Title: `(*regexp.Regexp).FindAll called with n == 0, which will always return zero results`,
		Text: `If n >= 0, the function returns at most n matches/submatches. To
return all results, specify a negative number.`,
		Since: "2017.1",
	},

	"SA1011": &lint.Documentation{
		Title: `Various methods in the strings package expect valid UTF-8, but invalid input is provided`,
		Since: "2017.1",
	},

	"SA1012": &lint.Documentation{
		Title: `A nil context.Context is being passed to a function, consider using context.TODO instead`,
		Since: "2017.1",
	},

	"SA1013": &lint.Documentation{
		Title: `io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second`,
		Since: "2017.1",
	},

	"SA1014": &lint.Documentation{
		Title: `Non-pointer value passed to Unmarshal or Decode`,
		Since: "2017.1",
	},

	"SA1015": &lint.Documentation{
		Title: `Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions`,
		Since: "2017.1",
	},

	"SA1016": &lint.Documentation{
		Title: `Trapping a signal that cannot be trapped`,
		Text: `Not all signals can be intercepted by a process. Speficially, on
UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are
never passed to the process, but instead handled directly by the
kernel. It is therefore pointless to try and handle these signals.`,
		Since: "2017.1",
	},

	"SA1017": &lint.Documentation{
		Title: `Channels used with os/signal.Notify should be buffered`,
		Text: `The os/signal package uses non-blocking channel sends when delivering
signals. If the receiving end of the channel isn't ready and the
channel is either unbuffered or full, the signal will be dropped. To
avoid missing signals, the channel should be buffered and of the
appropriate size. For a channel used for notification of just one
signal value, a buffer of size 1 is sufficient.`,
		Since: "2017.1",
	},

	"SA1018": &lint.Documentation{
		Title: `strings.Replace called with n == 0, which does nothing`,
		Text: `With n == 0, zero instances will be replaced. To replace all
instances, use a negative number, or use strings.ReplaceAll.`,
		Since: "2017.1",
	},

	"SA1019": &lint.Documentation{
		Title: `Using a deprecated function, variable, constant or field`,
		Since: "2017.1",
	},

	"SA1020": &lint.Documentation{
		Title: `Using an invalid host:port pair with a net.Listen-related function`,
		Since: "2017.1",
	},

	"SA1021": &lint.Documentation{
		Title: `Using bytes.Equal to compare two net.IP`,
		Text: `A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The
length of the slice for an IPv4 address, however, can be either 4 or
16 bytes long, using different ways of representing IPv4 addresses. In
order to correctly compare two net.IPs, the net.IP.Equal method should
be used, as it takes both representations into account.`,
		Since: "2017.1",
	},

	"SA1023": &lint.Documentation{
		Title: `Modifying the buffer in an io.Writer implementation`,
		Text:  `Write must not modify the slice data, even temporarily.`,
		Since: "2017.1",
	},

	"SA1024": &lint.Documentation{
		Title: `A string cutset contains duplicate characters`,
		Text: `The strings.TrimLeft and strings.TrimRight functions take cutsets, not
prefixes. A cutset is treated as a set of characters to remove from a
string. For example,

    strings.TrimLeft("42133word", "1234"))

will result in the string "word" – any characters that are 1, 2, 3 or
4 are cut from the left of the string.

In order to remove one string from another, use strings.TrimPrefix instead.`,
		Since: "2017.1",
	},

	"SA1025": &lint.Documentation{
		Title: `It is not possible to use (*time.Timer).Reset's return value correctly`,
		Since: "2019.1",
	},

	"SA1026": &lint.Documentation{
		Title: `Cannot marshal channels or functions`,
		Since: "2019.2",
	},

	"SA1027": &lint.Documentation{
		Title: `Atomic access to 64-bit variable must be 64-bit aligned`,
		Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to
arrange for 64-bit alignment of 64-bit words accessed atomically. The
first word in a variable or in an allocated struct, array, or slice
can be relied upon to be 64-bit aligned.

You can use the structlayout tool to inspect the alignment of fields
in a struct.`,
		Since: "2019.2",
	},

	"SA2000": &lint.Documentation{
		Title: `sync.WaitGroup.Add called inside the goroutine, leading to a race condition`,
		Since: "2017.1",
	},

	"SA2001": &lint.Documentation{
		Title: `Empty critical section, did you mean to defer the unlock?`,
		Text: `Empty critical sections of the kind

    mu.Lock()
    mu.Unlock()

are very often a typo, and the following was intended instead:

    mu.Lock()
    defer mu.Unlock()

Do note that sometimes empty critical sections can be useful, as a
form of signaling to wait on another goroutine. Many times, there are
simpler ways of achieving the same effect. When that isn't the case,
the code should be amply commented to avoid confusion. Combining such
comments with a //lint:ignore directive can be used to suppress this
rare false positive.`,
		Since: "2017.1",
	},

	"SA2002": &lint.Documentation{
		Title: `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed`,
		Since: "2017.1",
	},

	"SA2003": &lint.Documentation{
		Title: `Deferred Lock right after locking, likely meant to defer Unlock instead`,
		Since: "2017.1",
	},

	"SA3000": &lint.Documentation{
		Title: `TestMain doesn't call os.Exit, hiding test failures`,
		Text: `Test executables (and in turn 'go test') exit with a non-zero status
code if any tests failed. When specifying your own TestMain function,
it is your responsibility to arrange for this, by calling os.Exit with
the correct code. The correct code is returned by (*testing.M).Run, so
the usual way of implementing TestMain is to end it with
os.Exit(m.Run()).`,
		Since: "2017.1",
	},

	"SA3001": &lint.Documentation{
		Title: `Assigning to b.N in benchmarks distorts the results`,
		Text: `The testing package dynamically sets b.N to improve the reliability of
benchmarks and uses it in computations to determine the duration of a
single operation. Benchmark code must not alter b.N as this would
falsify results.`,
		Since: "2017.1",
	},

	"SA4000": &lint.Documentation{
		Title: `Boolean expression has identical expressions on both sides`,
		Since: "2017.1",
	},

	"SA4001": &lint.Documentation{
		Title: `&*x gets simplified to x, it does not copy x`,
		Since: "2017.1",
	},

	"SA4002": &lint.Documentation{
		Title: `Comparing strings with known different sizes has predictable results`,
		Since: "2017.1",
	},

	"SA4003": &lint.Documentation{
		Title: `Comparing unsigned values against negative values is pointless`,
		Since: "2017.1",
	},

	"SA4004": &lint.Documentation{
		Title: `The loop exits unconditionally after one iteration`,
		Since: "2017.1",
	},

	"SA4005": &lint.Documentation{
		Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`,
		Since: "2017.1",
	},

	"SA4006": &lint.Documentation{
		Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`,
		Since: "2017.1",
	},

	"SA4008": &lint.Documentation{
		Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`,
		Since: "2017.1",
	},

	"SA4009": &lint.Documentation{
		Title: `A function argument is overwritten before its first use`,
		Since: "2017.1",
	},

	"SA4010": &lint.Documentation{
		Title: `The result of append will never be observed anywhere`,
		Since: "2017.1",
	},

	"SA4011": &lint.Documentation{
		Title: `Break statement with no effect. Did you mean to break out of an outer loop?`,
		Since: "2017.1",
	},

	"SA4012": &lint.Documentation{
		Title: `Comparing a value against NaN even though no value is equal to NaN`,
		Since: "2017.1",
	},

	"SA4013": &lint.Documentation{
		Title: `Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.`,
		Since: "2017.1",
	},

	"SA4014": &lint.Documentation{
		Title: `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`,
		Since: "2017.1",
	},

	"SA4015": &lint.Documentation{
		Title: `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful`,
		Since: "2017.1",
	},

	"SA4016": &lint.Documentation{
		Title: `Certain bitwise operations, such as x ^ 0, do not do anything useful`,
		Since: "2017.1",
	},

	"SA4017": &lint.Documentation{
		Title: `A pure function's return value is discarded, making the call pointless`,
		Since: "2017.1",
	},

	"SA4018": &lint.Documentation{
		Title: `Self-assignment of variables`,
		Since: "2017.1",
	},

	"SA4019": &lint.Documentation{
		Title: `Multiple, identical build constraints in the same file`,
		Since: "2017.1",
	},

	"SA4020": &lint.Documentation{
		Title: `Unreachable case clause in a type switch`,
		Text: `In a type switch like the following

    type T struct{}
    func (T) Read(b []byte) (int, error) { return 0, nil }

    var v interface{} = T{}

    switch v.(type) {
    case io.Reader:
        // ...
    case T:
        // unreachable
    }

the second case clause can never be reached because T implements
io.Reader and case clauses are evaluated in source order.

Another example:

    type T struct{}
    func (T) Read(b []byte) (int, error) { return 0, nil }
    func (T) Close() error { return nil }

    var v interface{} = T{}

    switch v.(type) {
    case io.Reader:
        // ...
    case io.ReadCloser:
        // unreachable
    }

Even though T has a Close method and thus implements io.ReadCloser,
io.Reader will always match first. The method set of io.Reader is a
subset of io.ReadCloser. Thus it is impossible to match the second
case without matching the first case.


Structurally equivalent interfaces

A special case of the previous example are structurally identical
interfaces. Given these declarations

    type T error
    type V error

    func doSomething() error {
        err, ok := doAnotherThing()
        if ok {
            return T(err)
        }

        return U(err)
    }

the following type switch will have an unreachable case clause:

    switch doSomething().(type) {
    case T:
        // ...
    case V:
        // unreachable
    }

T will always match before V because they are structurally equivalent
and therefore doSomething()'s return value implements both.`,
		Since: "2019.2",
	},

	"SA4021": &lint.Documentation{
		Title: `x = append(y) is equivalent to x = y`,
		Since: "2019.2",
	},

	"SA5000": &lint.Documentation{
		Title: `Assignment to nil map`,
		Since: "2017.1",
	},

	"SA5001": &lint.Documentation{
		Title: `Defering Close before checking for a possible error`,
		Since: "2017.1",
	},

	"SA5002": &lint.Documentation{
		Title: `The empty for loop (for {}) spins and can block the scheduler`,
		Since: "2017.1",
	},

	"SA5003": &lint.Documentation{
		Title: `Defers in infinite loops will never execute`,
		Text: `Defers are scoped to the surrounding function, not the surrounding
block. In a function that never returns, i.e. one containing an
infinite loop, defers will never execute.`,
		Since: "2017.1",
	},

	"SA5004": &lint.Documentation{
		Title: `for { select { ... with an empty default branch spins`,
		Since: "2017.1",
	},

	"SA5005": &lint.Documentation{
		Title: `The finalizer references the finalized object, preventing garbage collection`,
		Text: `A finalizer is a function associated with an object that runs when the
garbage collector is ready to collect said object, that is when the
object is no longer referenced by anything.

If the finalizer references the object, however, it will always remain
as the final reference to that object, preventing the garbage
collector from collecting the object. The finalizer will never run,
and the object will never be collected, leading to a memory leak. That
is why the finalizer should instead use its first argument to operate
on the object. That way, the number of references can temporarily go
to zero before the object is being passed to the finalizer.`,
		Since: "2017.1",
	},

	"SA5006": &lint.Documentation{
		Title: `Slice index out of bounds`,
		Since: "2017.1",
	},

	"SA5007": &lint.Documentation{
		Title: `Infinite recursive call`,
		Text: `A function that calls itself recursively needs to have an exit
condition. Otherwise it will recurse forever, until the system runs
out of memory.

This issue can be caused by simple bugs such as forgetting to add an
exit condition. It can also happen "on purpose". Some languages have
tail call optimization which makes certain infinite recursive calls
safe to use. Go, however, does not implement TCO, and as such a loop
should be used instead.`,
		Since: "2017.1",
	},

	"SA5008": &lint.Documentation{
		Title: `Invalid struct tag`,
		Since: "2019.2",
	},

	"SA5009": &lint.Documentation{
		Title: `Invalid Printf call`,
		Since: "2019.2",
	},

	"SA6000": &lint.Documentation{
		Title: `Using regexp.Match or related in a loop, should use regexp.Compile`,
		Since: "2017.1",
	},

	"SA6001": &lint.Documentation{
		Title: `Missing an optimization opportunity when indexing maps by byte slices`,

		Text: `Map keys must be comparable, which precludes the use of byte slices.
This usually leads to using string keys and converting byte slices to
strings.

Normally, a conversion of a byte slice to a string needs to copy the data and
causes allocations. The compiler, however, recognizes m[string(b)] and
uses the data of b directly, without copying it, because it knows that
the data can't change during the map lookup. This leads to the
counter-intuitive situation that

    k := string(b)
    println(m[k])
    println(m[k])

will be less efficient than

    println(m[string(b)])
    println(m[string(b)])

because the first version needs to copy and allocate, while the second
one does not.

For some history on this optimization, check out commit
f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`,
		Since: "2017.1",
	},

	"SA6002": &lint.Documentation{
		Title: `Storing non-pointer values in sync.Pool allocates memory`,
		Text: `A sync.Pool is used to avoid unnecessary allocations and reduce the
amount of work the garbage collector has to do.

When passing a value that is not a pointer to a function that accepts
an interface, the value needs to be placed on the heap, which means an
additional allocation. Slices are a common thing to put in sync.Pools,
and they're structs with 3 fields (length, capacity, and a pointer to
an array). In order to avoid the extra allocation, one should store a
pointer to the slice instead.

See the comments on https://go-review.googlesource.com/c/go/+/24371
that discuss this problem.`,
		Since: "2017.1",
	},

	"SA6003": &lint.Documentation{
		Title: `Converting a string to a slice of runes before ranging over it`,
		Text: `You may want to loop over the runes in a string. Instead of converting
the string to a slice of runes and looping over that, you can loop
over the string itself. That is,

    for _, r := range s {}

and

    for _, r := range []rune(s) {}

will yield the same values. The first version, however, will be faster
and avoid unnecessary memory allocations.

Do note that if you are interested in the indices, ranging over a
string and over a slice of runes will yield different indices. The
first one yields byte offsets, while the second one yields indices in
the slice of runes.`,
		Since: "2017.1",
	},

	"SA6005": &lint.Documentation{
		Title: `Inefficient string comparison with strings.ToLower or strings.ToUpper`,
		Text: `Converting two strings to the same case and comparing them like so

    if strings.ToLower(s1) == strings.ToLower(s2) {
        ...
    }

is significantly more expensive than comparing them with
strings.EqualFold(s1, s2). This is due to memory usage as well as
computational complexity.

strings.ToLower will have to allocate memory for the new strings, as
well as convert both strings fully, even if they differ on the very
first byte. strings.EqualFold, on the other hand, compares the strings
one character at a time. It doesn't need to create two intermediate
strings and can return as soon as the first non-matching character has
been found.

For a more in-depth explanation of this issue, see
https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`,
		Since: "2019.2",
	},

	"SA9001": &lint.Documentation{
		Title: `Defers in range loops may not run when you expect them to`,
		Since: "2017.1",
	},

	"SA9002": &lint.Documentation{
		Title: `Using a non-octal os.FileMode that looks like it was meant to be in octal.`,
		Since: "2017.1",
	},

	"SA9003": &lint.Documentation{
		Title: `Empty body in an if or else branch`,
		Since: "2017.1",
	},

	"SA9004": &lint.Documentation{
		Title: `Only the first constant has an explicit type`,

		Text: `In a constant declaration such as the following:

    const (
        First byte = 1
        Second     = 2
    )

the constant Second does not have the same type as the constant First.
This construct shouldn't be confused with

    const (
        First byte = iota
        Second
    )

where First and Second do indeed have the same type. The type is only
passed on when no explicit value is assigned to the constant.

When declaring enumerations with explicit values it is therefore
important not to write

    const (
          EnumFirst EnumType = 1
          EnumSecond         = 2
          EnumThird          = 3
    )

This discrepancy in types can cause various confusing behaviors and
bugs.


Wrong type in variable declarations

The most obvious issue with such incorrect enumerations expresses
itself as a compile error:

    package pkg

    const (
        EnumFirst  uint8 = 1
        EnumSecond       = 2
    )

    func fn(useFirst bool) {
        x := EnumSecond
        if useFirst {
            x = EnumFirst
        }
    }

fails to compile with

    ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment


Losing method sets

A more subtle issue occurs with types that have methods and optional
interfaces. Consider the following:

    package main

    import "fmt"

    type Enum int

    func (e Enum) String() string {
        return "an enum"
    }

    const (
        EnumFirst  Enum = 1
        EnumSecond      = 2
    )

    func main() {
        fmt.Println(EnumFirst)
        fmt.Println(EnumSecond)
    }

This code will output

    an enum
    2

as EnumSecond has no explicit type, and thus defaults to int.`,
		Since: "2019.1",
	},

	"SA9005": &lint.Documentation{
		Title: `Trying to marshal a struct with no public fields nor custom marshaling`,
		Text: `The encoding/json and encoding/xml packages only operate on exported
fields in structs, not unexported ones. It is usually an error to try
to (un)marshal structs that only consist of unexported fields.

This check will not flag calls involving types that define custom
marshaling behavior, e.g. via MarshalJSON methods. It will also not
flag empty structs.`,
		Since: "2019.2",
	},
}