2019-04-22 12:59:42 +02:00
package staticcheck
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
differ on different architectures . Furthermore , it doesn ' t support
serializing maps , channels , strings , or functions .
2020-05-09 15:44:17 +02:00
Before Go 1.8 , bool wasn ' t supported , either . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA1004" : & lint . Documentation {
Title : ` Suspiciously small untyped constant in time.Sleep ` ,
Text : ` The time . Sleep function takes a time . Duration as its only argument .
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
' 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
2019-04-22 12:59:42 +02:00
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 :
2020-05-09 15:44:17 +02:00
exec . Command ( "/bin/sh" , "-c" , "ls | grep Awesome" ) ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
fmt . Printf ( s )
2019-04-22 12:59:42 +02:00
it would lead to the following output :
2020-05-09 15:44:17 +02:00
Interest rate : 5 % ! ( NOVERB ) .
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
UNIX - like systems , the syscall . SIGKILL and syscall . SIGSTOP signals are
never passed to the process , but instead handled directly by the
2020-05-09 15:44:17 +02:00
kernel . It is therefore pointless to try and handle these signals . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
os . Exit ( m . Run ( ) ) . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
benchmarks and uses it in computations to determine the duration of a
single operation . Benchmark code must not alter b . N as this would
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
case without matching the first case .
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
block . In a function that never returns , i . e . one containing an
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
should be used instead . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA5008" : & lint . Documentation {
Title : ` Invalid struct tag ` ,
Since : "2019.2" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA5009" : & lint . Documentation {
Title : ` Invalid Printf call ` ,
Since : "2019.2" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA6000" : & lint . Documentation {
Title : ` Using regexp.Match or related in a loop, should use regexp.Compile ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA6001" : & lint . Documentation {
Title : ` Missing an optimization opportunity when indexing maps by byte slices ` ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
Text : ` Map keys must be comparable , which precludes the use of byte slices .
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
that discuss this problem . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
the slice of runes . ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
https : //blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`,
Since : "2019.2" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA9001" : & lint . Documentation {
Title : ` Defers in range loops may not run when you expect them to ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA9002" : & lint . Documentation {
Title : ` Using a non-octal os.FileMode that looks like it was meant to be in octal. ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA9003" : & lint . Documentation {
Title : ` Empty body in an if or else branch ` ,
Since : "2017.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"SA9004" : & lint . Documentation {
Title : ` Only the first constant has an explicit type ` ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
Text : ` In a constant declaration such as the following :
2019-04-22 12:59:42 +02:00
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 :
2020-05-09 15:44:17 +02:00
package pkg
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
as EnumSecond has no explicit type , and thus defaults to int . ` ,
Since : "2019.1" ,
} ,
2019-04-22 12:59:42 +02:00
2020-05-09 15:44:17 +02:00
"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
2019-04-22 12:59:42 +02:00
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
2020-05-09 15:44:17 +02:00
flag empty structs . ` ,
Since : "2019.2" ,
} ,
}