-
Notifications
You must be signed in to change notification settings - Fork 11
/
doc.go
216 lines (169 loc) · 7.3 KB
/
doc.go
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
/*
Package exhaustive defines an analyzer that checks exhaustiveness of switch
statements of enum-like constants in Go source code. The analyzer can
optionally also check exhaustiveness of keys in map literals whose key type
is enum-like.
# Definition of enum
The Go [language spec] does not have an explicit definition for enums. For
the purpose of this analyzer, and by convention, an enum type is any named
type that:
- has an [underlying type] of float, string, or integer (includes byte
and rune); and
- has at least one constant of its type defined in the same [block].
In the example below, Biome is an enum type. The three constants are its
enum members.
package eco
type Biome int
const (
Tundra Biome = 1
Savanna Biome = 2
Desert Biome = 3
)
Enum member constants for an enum type must be declared in the same block as
the type. The constant values may be specified using iota, literal values, or
any valid means for declaring a Go constant. It is allowed for multiple enum
member constants for an enum type to have the same constant value.
# Definition of exhaustiveness
A switch statement that switches on a value of an enum type is exhaustive if
all enum members are listed in the switch statement's cases. If multiple enum
members have the same constant value, it is sufficient for any one of these
same-valued members to be listed.
For an enum type defined in the same package as the switch statement, both
exported and unexported enum members must be listed to satisfy exhaustiveness.
For an enum type defined in an external package, it is sufficient that only
exported enum members are listed. Only constant identifiers (e.g. Tundra,
eco.Desert) listed in a switch statement's case clause can contribute towards
satisfying exhaustiveness; other expressions, such as literal values and
function calls, listed in case clauses do not contribute towards satisfying
exhaustiveness.
By default, the existence of a default case in a switch statement does not
unconditionally make a switch statement exhaustive. Use the
-default-signifies-exhaustive flag to adjust this behavior.
For a map literal whose key type is an enum type, a similar definition of
exhaustiveness applies. The map literal is considered exhaustive if all enum
members are be listed in its keys. Empty map literals are never checked for
exhaustiveness.
# Type parameters
A switch statement that switches on a value whose type is a type parameter is
checked for exhaustiveness if and only if each type element in the type
constraint is an enum type and the type elements share the same underlying
[BasicKind].
For example, the switch statement below will be checked because each type
element (i.e. M and N) in the type constraint is an enum type and the type
elements share the same underlying BasicKind, namely int8. To satisfy
exhaustiveness, the enum members collectively belonging to the enum types M
and N (i.e. A, B, and C) must be listed in the switch statement's cases.
func bar[T M | I](v T) {
switch v {
case T(A):
case T(B):
case T(C):
}
}
type I interface{ N }
type M int8
const A M = 1
type N int8
const B N = 2
const C N = 3
# Type aliases
The analyzer handles type aliases as shown in the example below. newpkg.M is
an enum type. oldpkg.M is an alias for newpkg.M. Note that oldpkg.M isn't
itself an enum type; oldpkg.M is simply an alias for the actual enum type
newpkg.M.
package oldpkg
type M = newpkg.M
const (
A = newpkg.A
B = newpkg.B
)
package newpkg
type M int
const (
A M = 1
B M = 2
)
A switch statement that switches either on a value of type newpkg.M or of type
oldpkg.M (which, being an alias, is just an alternative spelling for newpkg.M)
is exhaustive if all of newpkg.M's enum members are listed in the switch
statement's cases. The following switch statement is exhaustive.
func f(v newpkg.M) {
switch v {
case newpkg.A: // or equivalently oldpkg.A
case newpkg.B: // or equivalently oldpkg.B
}
}
The analyzer guarantees that introducing a type alias (such as type M =
newpkg.M) will not result in new diagnostics if the set of enum member
constant values of the RHS type is a subset of the set of enum member constant
values of the LHS type.
# Flags
Summary:
flag type default value
---- ---- -------------
-check comma-separated strings switch
-explicit-exhaustive-switch bool false
-explicit-exhaustive-map bool false
-check-generated bool false
-default-signifies-exhaustive bool false
-ignore-enum-members regexp pattern (none)
-ignore-enum-types regexp pattern (none)
-package-scope-only bool false
Descriptions:
-check
Comma-separated list of program elements to check for
exhaustiveness. Supported program element values are
"switch" and "map". The default value is "switch", which
means that only switch statements are checked.
-explicit-exhaustive-switch
Check a switch statement only if it is associated with a
"//exhaustive:enforce" comment. By default the analyzer
checks every switch statement that isn't associated with a
"//exhaustive:ignore" comment.
-explicit-exhaustive-map
Similar to -explicit-exhaustive-switch but for map literals.
-check-generated
Check generated files. For the definition of a generated
file, see https://golang.org/s/generatedcode.
-default-signifies-exhaustive
Consider a switch statement to be exhaustive
unconditionally if it has a default case. (In other words,
all enum members do not have to be listed in its cases if a
default case is present.) Setting this flag usually is
counter to the purpose of exhaustiveness checks, so it is
not recommended to set this flag.
-ignore-enum-members
Constants that match the specified regular expression (in
package regexp syntax) are not considered enum members and
hence do not have to be listed to satisfy exhaustiveness.
The specified regular expression is matched against the
constant name inclusive of import path. For example, if the
import path for the constant is "example.org/eco" and the
constant name is "Tundra", then the specified regular
expression is matched against the string
"example.org/eco.Tundra".
-ignore-enum-types
Similar to -ignore-enum-members but for types.
-package-scope-only
Only discover enums declared in file-level blocks. By
default, the analyzer discovers enums defined in all
blocks.
# Skip analysis
To skip analysis of a switch statement or a map literal, associate it with a
comment that begins with "//exhaustive:ignore". For example:
//exhaustive:ignore ... an optional explanation goes here ...
switch v {
case A:
case B:
}
To ignore specific constants in exhaustiveness checks, specify the
-ignore-enum-members flag:
exhaustive -ignore-enum-members '^example\.org/eco\.Tundra$'
To ignore specific types, specify the -ignore-enum-types flag:
exhaustive -ignore-enum-types '^time\.Duration$|^example\.org/measure\.Unit$'
[language spec]: https://golang.org/ref/spec
[underlying type]: https://golang.org/ref/spec#Underlying_types
[block]: https://golang.org/ref/spec#Blocks
[BasicKind]: https://pkg.go.dev/go/types#BasicKind
*/
package exhaustive