Skip to content

Commit faaebac

Browse files
committed
const val always present
1 parent 2e0255f commit faaebac

File tree

6 files changed

+93
-110
lines changed

6 files changed

+93
-110
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ Install the command:
1010
go install github.com/nishanths/exhaustive/cmd/exhaustive@latest
1111
```
1212

13-
For documentation, see [pkg.go.dev][6]. It describes the flags, the
14-
definition of enum, and the definition of exhaustiveness used by this
15-
package.
13+
For documentation, see the package comment at [pkg.go.dev][6]. It
14+
describes the flags, the definition of enum, and the definition of
15+
exhaustiveness used by this package.
1616

17-
For changelog, see [CHANGELOG][changelog] in the wiki.
17+
For the changelog, see [CHANGELOG][changelog] in the wiki.
1818

19-
The `exhaustive` package provides an `Analzyer` that follows the
20-
guidelines in the [`go/analysis`][3] package; this should make
21-
it possible to integrate with external analysis driver programs.
19+
The package provides an `Analyzer` that follows the guidelines in the
20+
[`go/analysis`][3] package; this should make it possible to integrate
21+
with external analysis driver programs.
2222

2323
### Known issues
2424

25-
The package's behavior is undefined for enum types that are [type
25+
The analyzer's behavior is undefined for enum types that are [type
2626
aliases][4]. See issue [#13][5].
2727

2828
### Example

hitlist.go renamed to checklist.go

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ import (
77
"regexp"
88
)
99

10-
// A hitlist is the set of enum member names that should be listed in a switch
10+
// A checklist is the set of enum member names that should be listed in a switch
1111
// statement's case clauses in order for the switch to be exhaustive. The found
12-
// method marks a member as being listed in the switch, so, in usage, a hitlist
12+
// method marks a member as being listed in the switch, so, in usage, a checklist
1313
// is the set of yet unsatisfied enum members.
1414
//
1515
// Only interact via its methods. It is not safe for concurrent use.
16-
type hitlist struct {
16+
type checklist struct {
1717
em *enumMembers
1818
m map[string]struct{} // remaining unsatisfied member names
1919
}
2020

21-
func makeHitlist(em *enumMembers, enumPkg *types.Package, includeUnexported bool, ignore *regexp.Regexp) *hitlist {
21+
func makeChecklist(em *enumMembers, enumPkg *types.Package, includeUnexported bool, ignore *regexp.Regexp) *checklist {
2222
m := make(map[string]struct{})
2323

2424
add := func(memberName string) {
@@ -39,34 +39,30 @@ func makeHitlist(em *enumMembers, enumPkg *types.Package, includeUnexported bool
3939
add(name)
4040
}
4141

42-
return &hitlist{
42+
return &checklist{
4343
em: em,
4444
m: m,
4545
}
4646
}
4747

48-
func (h *hitlist) found(memberName string, strategy checkingStrategy) {
48+
func (c *checklist) found(memberName string, strategy checkingStrategy) {
4949
switch strategy {
5050
case strategyValue:
51-
if constVal, ok := h.em.NameToValue[memberName]; ok {
52-
// delete all of the same-valued names
53-
for _, n := range h.em.ValueToNames[constVal] {
54-
delete(h.m, n)
55-
}
56-
} else {
57-
// delete the name given name alone
58-
delete(h.m, memberName)
51+
// delete all of the same-valued names
52+
constVal := c.em.NameToValue[memberName]
53+
for _, n := range c.em.ValueToNames[constVal] {
54+
delete(c.m, n)
5955
}
6056

6157
case strategyName:
6258
// delete the given name alone
63-
delete(h.m, memberName)
59+
delete(c.m, memberName)
6460

6561
default:
6662
panic(fmt.Sprintf("unknown strategy %v", strategy))
6763
}
6864
}
6965

70-
func (h *hitlist) remaining() map[string]struct{} {
71-
return h.m
66+
func (c *checklist) remaining() map[string]struct{} {
67+
return c.m
7268
}

hitlist_test.go renamed to checklist_test.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import (
88
"testing"
99
)
1010

11-
func TestHitlist(t *testing.T) {
11+
func TestChecklist(t *testing.T) {
1212
enumPkg := types.NewPackage("github.com/example/bar-go", "bar")
1313

1414
em := &enumMembers{
1515
Names: []string{"A", "B", "C", "D", "E", "F", "G"},
1616
NameToValue: map[string]string{
1717
"A": "1",
1818
"B": "2",
19-
// C has no constVal
19+
"C": "5",
2020
"D": "2",
2121
"E": "3",
2222
"F": "2",
@@ -27,10 +27,11 @@ func TestHitlist(t *testing.T) {
2727
"2": {"B", "D", "F"},
2828
"3": {"E"},
2929
"4": {"G"},
30+
"5": {"C"},
3031
},
3132
}
3233

33-
checkRemaining := func(t *testing.T, h *hitlist, want map[string]struct{}) {
34+
checkRemaining := func(t *testing.T, h *checklist, want map[string]struct{}) {
3435
t.Helper()
3536
rem := h.remaining()
3637
if !reflect.DeepEqual(want, rem) {
@@ -39,16 +40,16 @@ func TestHitlist(t *testing.T) {
3940
}
4041

4142
t.Run("panics on unknown strategy", func(t *testing.T) {
42-
hitlist := makeHitlist(em, enumPkg, false, nil)
43+
checklist := makeChecklist(em, enumPkg, false, nil)
4344
f := func() {
44-
hitlist.found("A", checkingStrategy(8238))
45+
checklist.found("A", checkingStrategy(8238))
4546
}
4647
assertPanic(t, f, fmt.Sprintf("unknown strategy %v", checkingStrategy(8238)))
4748
})
4849

4950
t.Run("main operations", func(t *testing.T) {
50-
hitlist := makeHitlist(em, enumPkg, false, nil)
51-
checkRemaining(t, hitlist, map[string]struct{}{
51+
checklist := makeChecklist(em, enumPkg, false, nil)
52+
checkRemaining(t, checklist, map[string]struct{}{
5253
"A": {},
5354
"B": {},
5455
"C": {},
@@ -58,8 +59,8 @@ func TestHitlist(t *testing.T) {
5859
"G": {},
5960
})
6061

61-
hitlist.found("A", strategyValue)
62-
checkRemaining(t, hitlist, map[string]struct{}{
62+
checklist.found("A", strategyValue)
63+
checkRemaining(t, checklist, map[string]struct{}{
6364
"B": {},
6465
"C": {},
6566
"D": {},
@@ -68,8 +69,8 @@ func TestHitlist(t *testing.T) {
6869
"G": {},
6970
})
7071

71-
hitlist.found("B", strategyName)
72-
checkRemaining(t, hitlist, map[string]struct{}{
72+
checklist.found("B", strategyName)
73+
checkRemaining(t, checklist, map[string]struct{}{
7374
"C": {},
7475
"D": {},
7576
"E": {},
@@ -78,38 +79,38 @@ func TestHitlist(t *testing.T) {
7879
})
7980

8081
// repeated call should be a no-op.
81-
hitlist.found("B", strategyName)
82-
checkRemaining(t, hitlist, map[string]struct{}{
82+
checklist.found("B", strategyName)
83+
checkRemaining(t, checklist, map[string]struct{}{
8384
"C": {},
8485
"D": {},
8586
"E": {},
8687
"F": {},
8788
"G": {},
8889
})
8990

90-
hitlist.found("F", strategyValue)
91-
checkRemaining(t, hitlist, map[string]struct{}{
91+
checklist.found("F", strategyValue)
92+
checkRemaining(t, checklist, map[string]struct{}{
9293
"C": {},
9394
"E": {},
9495
"G": {},
9596
})
9697

97-
hitlist.found("C", strategyValue)
98-
checkRemaining(t, hitlist, map[string]struct{}{
98+
checklist.found("C", strategyValue)
99+
checkRemaining(t, checklist, map[string]struct{}{
99100
"E": {},
100101
"G": {},
101102
})
102103

103-
hitlist.found("E", strategyName)
104-
checkRemaining(t, hitlist, map[string]struct{}{
104+
checklist.found("E", strategyName)
105+
checkRemaining(t, checklist, map[string]struct{}{
105106
"G": {},
106107
})
107108
})
108109

109110
t.Run("ignore regexp", func(t *testing.T) {
110111
t.Run("nil means no filtering", func(t *testing.T) {
111-
hitlist := makeHitlist(em, enumPkg, false, nil)
112-
checkRemaining(t, hitlist, map[string]struct{}{
112+
checklist := makeChecklist(em, enumPkg, false, nil)
113+
checkRemaining(t, checklist, map[string]struct{}{
113114
"A": {},
114115
"B": {},
115116
"C": {},
@@ -121,8 +122,8 @@ func TestHitlist(t *testing.T) {
121122
})
122123

123124
t.Run("basic", func(t *testing.T) {
124-
hitlist := makeHitlist(em, enumPkg, false, regexp.MustCompile(`^github.com/example/bar-go.G$`))
125-
checkRemaining(t, hitlist, map[string]struct{}{
125+
checklist := makeChecklist(em, enumPkg, false, regexp.MustCompile(`^github.com/example/bar-go.G$`))
126+
checkRemaining(t, checklist, map[string]struct{}{
126127
"A": {},
127128
"B": {},
128129
"C": {},
@@ -133,13 +134,13 @@ func TestHitlist(t *testing.T) {
133134
})
134135

135136
t.Run("matches multiple", func(t *testing.T) {
136-
hitlist := makeHitlist(em, enumPkg, false, regexp.MustCompile(`^github.com/example/bar-go`))
137-
checkRemaining(t, hitlist, map[string]struct{}{})
137+
checklist := makeChecklist(em, enumPkg, false, regexp.MustCompile(`^github.com/example/bar-go`))
138+
checkRemaining(t, checklist, map[string]struct{}{})
138139
})
139140

140141
t.Run("uses package path, not package name", func(t *testing.T) {
141-
hitlist := makeHitlist(em, enumPkg, false, regexp.MustCompile(`bar.G`))
142-
checkRemaining(t, hitlist, map[string]struct{}{
142+
checklist := makeChecklist(em, enumPkg, false, regexp.MustCompile(`bar.G`))
143+
checkRemaining(t, checklist, map[string]struct{}{
143144
"A": {},
144145
"B": {},
145146
"C": {},
@@ -156,8 +157,8 @@ func TestHitlist(t *testing.T) {
156157
em.Names = append([]string{}, em.Names...)
157158
em.Names = append(em.Names, "_")
158159

159-
hitlist := makeHitlist(&em, enumPkg, true, nil)
160-
checkRemaining(t, hitlist, map[string]struct{}{
160+
checklist := makeChecklist(&em, enumPkg, true, nil)
161+
checkRemaining(t, checklist, map[string]struct{}{
161162
"A": {},
162163
"B": {},
163164
"C": {},
@@ -174,8 +175,8 @@ func TestHitlist(t *testing.T) {
174175
em.Names = append(em.Names, "lowercase")
175176

176177
t.Run("include", func(t *testing.T) {
177-
hitlist := makeHitlist(&em, enumPkg, true, nil)
178-
checkRemaining(t, hitlist, map[string]struct{}{
178+
checklist := makeChecklist(&em, enumPkg, true, nil)
179+
checkRemaining(t, checklist, map[string]struct{}{
179180
"A": {},
180181
"B": {},
181182
"C": {},
@@ -188,8 +189,8 @@ func TestHitlist(t *testing.T) {
188189
})
189190

190191
t.Run("don't include", func(t *testing.T) {
191-
hitlist := makeHitlist(&em, enumPkg, false, nil)
192-
checkRemaining(t, hitlist, map[string]struct{}{
192+
checklist := makeChecklist(&em, enumPkg, false, nil)
193+
checkRemaining(t, checklist, map[string]struct{}{
193194
"A": {},
194195
"B": {},
195196
"C": {},

enum.go

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,9 @@ type enumMembers struct {
2525
ValueToNames map[string][]string
2626
}
2727

28-
func (em *enumMembers) add(name string, constVal string, constValOk bool) {
28+
func (em *enumMembers) add(name string, constVal string) {
2929
em.Names = append(em.Names, name)
3030

31-
if !constValOk {
32-
return
33-
}
34-
3531
if em.NameToValue == nil {
3632
em.NameToValue = make(map[string]string)
3733
}
@@ -56,11 +52,11 @@ func findEnums(files []*ast.File, typesInfo *types.Info) enums {
5652
pkgEnums := make(enums)
5753

5854
// Gather enum members.
59-
findEnumMembers(files, typesInfo, possibleEnumTypes, func(memberName, typeName string, constVal string, constValOk bool) {
55+
findEnumMembers(files, typesInfo, possibleEnumTypes, func(memberName, typeName string, constVal string) {
6056
if _, ok := pkgEnums[typeName]; !ok {
6157
pkgEnums[typeName] = &enumMembers{}
6258
}
63-
pkgEnums[typeName].add(memberName, constVal, constValOk)
59+
pkgEnums[typeName].add(memberName, constVal)
6460
})
6561

6662
return pkgEnums
@@ -102,7 +98,7 @@ func findPossibleEnumTypes(files []*ast.File, typesInfo *types.Info, found func(
10298
}
10399
}
104100

105-
func findEnumMembers(files []*ast.File, typesInfo *types.Info, knownEnumTypes map[string]struct{}, found func(memberName, typeName string, constVal string, constValOk bool)) {
101+
func findEnumMembers(files []*ast.File, typesInfo *types.Info, knownEnumTypes map[string]struct{}, found func(memberName, typeName string, constVal string)) {
106102
for _, f := range files {
107103
for _, decl := range f.Decls {
108104
gen, ok := decl.(*ast.GenDecl)
@@ -117,43 +113,21 @@ func findEnumMembers(files []*ast.File, typesInfo *types.Info, knownEnumTypes ma
117113
v := s.(*ast.ValueSpec)
118114
for _, name := range v.Names {
119115
obj := typesInfo.Defs[name]
120-
if obj == nil {
121-
continue
122-
}
123-
124-
named, ok := obj.Type().(*types.Named)
116+
namedType, ok := obj.Type().(*types.Named)
125117
if !ok {
126118
continue
127119
}
128-
129-
if _, ok := knownEnumTypes[named.Obj().Name()]; !ok {
120+
if _, ok := knownEnumTypes[namedType.Obj().Name()]; !ok {
130121
continue
131122
}
132-
133-
cv, ok := determineConstVal(name, typesInfo)
134-
found(obj.Name(), named.Obj().Name(), cv, ok)
123+
found(obj.Name(), namedType.Obj().Name(), determineConstVal(name, typesInfo))
135124
}
136125
}
137126
}
138127
}
139128
}
140129

141-
func determineConstVal(name *ast.Ident, typesInfo *types.Info) (string, bool) {
142-
return determineConstValFromName(name, typesInfo)
143-
}
144-
145-
func determineConstValFromName(name *ast.Ident, typesInfo *types.Info) (string, bool) {
146-
nameObj := typesInfo.Defs[name]
147-
if nameObj == nil {
148-
return "", false
149-
}
150-
151-
c, ok := nameObj.(*types.Const)
152-
if !ok {
153-
return "", false
154-
}
155-
if c.Val() == nil {
156-
return "", false
157-
}
158-
return c.Val().ExactString(), true
130+
func determineConstVal(name *ast.Ident, typesInfo *types.Info) string {
131+
c := typesInfo.Defs[name].(*types.Const)
132+
return c.Val().ExactString()
159133
}

0 commit comments

Comments
 (0)