aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/nishanths
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/nishanths
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/github.com/nishanths')
-rw-r--r--vendor/github.com/nishanths/exhaustive/.gitignore3
-rw-r--r--vendor/github.com/nishanths/exhaustive/.travis.yml12
-rw-r--r--vendor/github.com/nishanths/exhaustive/Makefile28
-rw-r--r--vendor/github.com/nishanths/exhaustive/README.md81
-rw-r--r--vendor/github.com/nishanths/exhaustive/comment.go74
-rw-r--r--vendor/github.com/nishanths/exhaustive/enum.go249
-rw-r--r--vendor/github.com/nishanths/exhaustive/exhaustive.go405
-rw-r--r--vendor/github.com/nishanths/exhaustive/fact.go28
-rw-r--r--vendor/github.com/nishanths/exhaustive/generated.go34
-rw-r--r--vendor/github.com/nishanths/exhaustive/go.mod2
-rw-r--r--vendor/github.com/nishanths/exhaustive/go.sum47
-rw-r--r--vendor/github.com/nishanths/exhaustive/switch.go601
12 files changed, 834 insertions, 730 deletions
diff --git a/vendor/github.com/nishanths/exhaustive/.gitignore b/vendor/github.com/nishanths/exhaustive/.gitignore
index 24bde5301..10acec6e1 100644
--- a/vendor/github.com/nishanths/exhaustive/.gitignore
+++ b/vendor/github.com/nishanths/exhaustive/.gitignore
@@ -5,3 +5,6 @@ tags
# binary
cmd/exhaustive/exhaustive
exhaustive
+
+# testing artifacts
+coverage.out
diff --git a/vendor/github.com/nishanths/exhaustive/.travis.yml b/vendor/github.com/nishanths/exhaustive/.travis.yml
deleted file mode 100644
index bd342f558..000000000
--- a/vendor/github.com/nishanths/exhaustive/.travis.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-language: go
-
-go:
- - 1.x
- - master
-
-# Only clone the most recent commit.
-git:
- depth: 1
-
-notifications:
- email: false
diff --git a/vendor/github.com/nishanths/exhaustive/Makefile b/vendor/github.com/nishanths/exhaustive/Makefile
new file mode 100644
index 000000000..981a7ebe9
--- /dev/null
+++ b/vendor/github.com/nishanths/exhaustive/Makefile
@@ -0,0 +1,28 @@
+.PHONY: default
+default: build
+
+.PHONY: build
+build:
+ go build ./...
+
+.PHONY: test
+test:
+ go test -cover ./...
+
+.PHONY: install-vet
+install-vet:
+ go install github.com/nishanths/exhaustive/cmd/exhaustive@latest
+ go install github.com/gordonklaus/ineffassign@latest
+ go install github.com/kisielk/errcheck@latest
+
+.PHONY: vet
+vet:
+ go vet ./...
+ exhaustive ./...
+ ineffassign ./...
+ errcheck ./...
+
+.PHONY: upgrade-deps
+upgrade-deps:
+ go get golang.org/x/tools
+ go mod tidy
diff --git a/vendor/github.com/nishanths/exhaustive/README.md b/vendor/github.com/nishanths/exhaustive/README.md
index 90afc87fe..3992704a5 100644
--- a/vendor/github.com/nishanths/exhaustive/README.md
+++ b/vendor/github.com/nishanths/exhaustive/README.md
@@ -1,54 +1,31 @@
-# exhaustive
+## exhaustive [![Godoc][2]][1]
-[![Godoc](https://godoc.org/github.com/nishanths/exhaustive?status.svg)](https://godoc.org/github.com/nishanths/exhaustive)
-
-[![Build Status](https://travis-ci.org/nishanths/exhaustive.svg?branch=master)](https://travis-ci.org/nishanths/exhaustive)
-
-The `exhaustive` package and command line program can be used to detect
-enum switch statements that are not exhaustive.
-
-An enum switch statement is exhaustive if it has cases for each of the enum's members. See godoc for the definition of enum used by the program.
-
-The `exhaustive` package provides an `Analyzer` that follows the guidelines
-described in the [go/analysis](https://godoc.org/golang.org/x/tools/go/analysis) package; this makes
-it possible to integrate into existing analysis driver programs.
-
-## Install
+Check exhaustiveness of enum switch statements in Go source code.
```
-go get github.com/nishanths/exhaustive/...
+go install github.com/nishanths/exhaustive/cmd/exhaustive@latest
```
-## Docs
+For docs on the flags, the definition of enum, and the definition of
+exhaustiveness, see [godocs.io][4].
-https://godoc.org/github.com/nishanths/exhaustive
+For the changelog, see [CHANGELOG][changelog] in the wiki.
-## Usage
+The package provides an `Analyzer` that follows the guidelines in the
+[`go/analysis`][3] package; this should make it possible to integrate
+exhaustive with your own analysis driver program.
-The command line usage is:
+## Bugs
-```
-Usage: exhaustive [-flags] [packages...]
-
-Flags:
- -check-generated
- check switch statements in generated files also
- -default-signifies-exhaustive
- indicates that switch statements are to be considered exhaustive if a 'default' case
- is present, even if all enum members aren't listed in the switch (default false)
- -fix
- apply all suggested fixes (default false)
-
-Examples:
- exhaustive github.com/foo/bar/...
- exhaustive github.com/a/b github.com/x/y
-```
+`exhaustive` does not report missing cases if the switch statement
+switches on a type parameterized type. See [this
+issue](https://github.com/nishanths/exhaustive/issues/31) for details.
## Example
-Given the code:
+Given the enum
-```diff
+```go
package token
type Token int
@@ -57,35 +34,41 @@ const (
Add Token = iota
Subtract
Multiply
-+ Quotient
-+ Remainder
+ Quotient
+ Remainder
)
```
-```
+
+and the switch statement
+
+```go
package calc
import "token"
-func processToken(t token.Token) {
+func f(t token.Token) {
switch t {
case token.Add:
- ...
case token.Subtract:
- ...
case token.Multiply:
- ...
+ default:
}
}
```
-Running the `exhaustive` command will print:
+running exhaustive will print
```
calc.go:6:2: missing cases in switch of type token.Token: Quotient, Remainder
```
-Enums can also be defined using explicit constant values instead of `iota`.
+## Contributing
-## License
+Issues and pull requests are welcome. Before making a substantial
+change, please discuss it in an issue.
-BSD 2-Clause
+[1]: https://godocs.io/github.com/nishanths/exhaustive
+[2]: https://godocs.io/github.com/nishanths/exhaustive?status.svg
+[3]: https://pkg.go.dev/golang.org/x/tools/go/analysis
+[4]: https://godocs.io/github.com/nishanths/exhaustive
+[changelog]: https://github.com/nishanths/exhaustive/wiki/CHANGELOG
diff --git a/vendor/github.com/nishanths/exhaustive/comment.go b/vendor/github.com/nishanths/exhaustive/comment.go
new file mode 100644
index 000000000..cae8c64d1
--- /dev/null
+++ b/vendor/github.com/nishanths/exhaustive/comment.go
@@ -0,0 +1,74 @@
+package exhaustive
+
+import (
+ "go/ast"
+ "regexp"
+ "strings"
+)
+
+// Generated file definition
+// http://golang.org/s/generatedcode
+//
+// To convey to humans and machine tools that code is generated, generated
+// source should have a line that matches the following regular expression (in
+// Go syntax):
+//
+// ^// Code generated .* DO NOT EDIT\.$
+//
+// This line must appear before the first non-comment, non-blank
+// text in the file.
+
+func isGeneratedFile(file *ast.File) bool {
+ // NOTE: file.Comments includes file.Doc as well, so no need
+ // to separately check file.Doc.
+
+ for _, c := range file.Comments {
+ for _, cc := range c.List {
+ // This check is intended to handle "must appear before the
+ // first non-comment, non-blank text in the file".
+ // TODO: Is this check fully correct? Seems correct based
+ // on https://golang.org/ref/spec#Source_file_organization.
+ if c.Pos() >= file.Package {
+ return false
+ }
+ // According to the docs:
+ // '\r' has been removed.
+ // '\n' has been removed for //-style comments, which is what we care about.
+ // Also manually verified.
+ if isGeneratedFileComment(cc.Text) {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+var generatedCodeRe = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
+
+func isGeneratedFileComment(s string) bool {
+ return generatedCodeRe.MatchString(s)
+}
+
+// ignoreDirective is used to exclude checking of specific switch statements.
+const ignoreDirective = "//exhaustive:ignore"
+const enforceDirective = "//exhaustive:enforce"
+
+func containsDirective(comments []*ast.CommentGroup, directive string) bool {
+ for _, c := range comments {
+ for _, cc := range c.List {
+ if strings.HasPrefix(cc.Text, directive) {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func containsEnforceDirective(comments []*ast.CommentGroup) bool {
+ return containsDirective(comments, enforceDirective)
+}
+
+func containsIgnoreDirective(comments []*ast.CommentGroup) bool {
+ return containsDirective(comments, ignoreDirective)
+}
diff --git a/vendor/github.com/nishanths/exhaustive/enum.go b/vendor/github.com/nishanths/exhaustive/enum.go
index ed0df642b..2b287e39a 100644
--- a/vendor/github.com/nishanths/exhaustive/enum.go
+++ b/vendor/github.com/nishanths/exhaustive/enum.go
@@ -1,146 +1,171 @@
package exhaustive
import (
+ "fmt"
"go/ast"
"go/token"
"go/types"
+ "strings"
- "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/ast/inspector"
)
-type enums map[string]*enumMembers // enum type name -> enum members
+// constantValue is a constant.Value.ExactString().
+type constantValue string
+// Represents an enum type (or a potential enum type).
+// It is a defined (named) type's name.
+type enumType struct{ *types.TypeName }
+
+func (et enumType) String() string { return et.TypeName.String() } // for debugging
+func (et enumType) scope() *types.Scope { return et.TypeName.Parent() } // scope that the type is declared in
+func (et enumType) factObject() types.Object { return et.TypeName } // types.Object for fact export
+
+// enumMembers is the members for a single enum type.
+// The zero value is ready to use.
type enumMembers struct {
- // Names in the order encountered in the AST.
- OrderedNames []string
-
- // Maps name -> (constant.Value).ExactString().
- // If a name is missing in the map, it means that it does not have a
- // corresponding constant.Value defined in the AST.
- NameToValue map[string]string
-
- // Maps (constant.Value).ExactString() -> names.
- // Names that don't have a constant.Value defined in the AST (e.g., some
- // iota constants) will not have a corresponding entry in this map.
- ValueToNames map[string][]string
+ Names []string // enum member names, AST order
+ NameToValue map[string]constantValue // enum member name -> constant value
+ ValueToNames map[constantValue][]string // constant value -> enum member names
}
-func (em *enumMembers) add(name string, constVal *string) {
- em.OrderedNames = append(em.OrderedNames, name)
+func (em *enumMembers) add(name string, val constantValue) {
+ if em.NameToValue == nil {
+ em.NameToValue = make(map[string]constantValue)
+ }
+ if em.ValueToNames == nil {
+ em.ValueToNames = make(map[constantValue][]string)
+ }
- if constVal != nil {
- if em.NameToValue == nil {
- em.NameToValue = make(map[string]string)
- }
- em.NameToValue[name] = *constVal
+ em.Names = append(em.Names, name)
+ em.NameToValue[name] = val
+ em.ValueToNames[val] = append(em.ValueToNames[val], name)
+}
- if em.ValueToNames == nil {
- em.ValueToNames = make(map[string][]string)
+func (em enumMembers) String() string { return em.factString() } // for debugging
+
+func (em enumMembers) factString() string {
+ var buf strings.Builder
+ for j, vv := range em.Names {
+ buf.WriteString(vv)
+ // add comma separator between each enum member
+ if j != len(em.Names)-1 {
+ buf.WriteString(",")
}
- em.ValueToNames[*constVal] = append(em.ValueToNames[*constVal], name)
}
+ return buf.String()
}
-func (em *enumMembers) numMembers() int {
- return len(em.OrderedNames)
-}
-
-func findEnums(pass *analysis.Pass) enums {
- pkgEnums := make(enums)
-
- // Gather enum types.
- for _, f := range pass.Files {
- for _, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if !ok {
- continue
- }
- if gen.Tok != token.TYPE {
- continue
- }
- for _, s := range gen.Specs {
- // Must be TypeSpec since we've filtered on token.TYPE.
- t, ok := s.(*ast.TypeSpec)
- obj := pass.TypesInfo.Defs[t.Name]
- if obj == nil {
- continue
- }
+func findEnums(pkgScopeOnly bool, pkg *types.Package, inspect *inspector.Inspector, info *types.Info) map[enumType]enumMembers {
+ result := make(map[enumType]enumMembers)
- named, ok := obj.Type().(*types.Named)
+ inspect.Preorder([]ast.Node{&ast.GenDecl{}}, func(n ast.Node) {
+ gen := n.(*ast.GenDecl)
+ if gen.Tok != token.CONST {
+ return
+ }
+ for _, s := range gen.Specs {
+ for _, name := range s.(*ast.ValueSpec).Names {
+ enumTyp, memberName, val, ok := possibleEnumMember(name, info)
if !ok {
continue
}
- basic, ok := named.Underlying().(*types.Basic)
- if !ok {
+ if pkgScopeOnly && enumTyp.scope() != pkg.Scope() {
continue
}
-
- switch i := basic.Info(); {
- case i&types.IsInteger != 0:
- pkgEnums[named.Obj().Name()] = &enumMembers{}
- case i&types.IsFloat != 0:
- pkgEnums[named.Obj().Name()] = &enumMembers{}
- case i&types.IsString != 0:
- pkgEnums[named.Obj().Name()] = &enumMembers{}
- }
+ v := result[enumTyp]
+ v.add(memberName, val)
+ result[enumTyp] = v
}
}
+ })
+
+ return result
+}
+
+func possibleEnumMember(constName *ast.Ident, info *types.Info) (et enumType, name string, val constantValue, ok bool) {
+ obj := info.Defs[constName]
+ if obj == nil {
+ panic(fmt.Sprintf("info.Defs[%s] == nil", constName))
+ }
+ if _, ok = obj.(*types.Const); !ok {
+ panic(fmt.Sprintf("obj must be *types.Const, got %T", obj))
+ }
+ if isBlankIdentifier(obj) {
+ // These objects have a nil parent scope.
+ // Also, we have no real purpose to record them.
+ return enumType{}, "", "", false
}
- // Gather enum members.
- for _, f := range pass.Files {
- for _, decl := range f.Decls {
- gen, ok := decl.(*ast.GenDecl)
- if !ok {
- continue
- }
- if gen.Tok != token.CONST && gen.Tok != token.VAR {
- continue
- }
- for _, s := range gen.Specs {
- // Must be ValueSpec since we've filtered on token.CONST, token.VAR.
- v := s.(*ast.ValueSpec)
- for i, name := range v.Names {
- obj := pass.TypesInfo.Defs[name]
- if obj == nil {
- continue
- }
-
- named, ok := obj.Type().(*types.Named)
- if !ok {
- continue
- }
-
- // Get the constant.Value representation, if any.
- var constVal *string
- if len(v.Values) > i {
- value := v.Values[i]
- if con, ok := pass.TypesInfo.Types[value]; ok && con.Value != nil {
- str := con.Value.ExactString() // temp var to be able to take address
- constVal = &str
- }
- }
-
- em, ok := pkgEnums[named.Obj().Name()]
- if !ok {
- continue
- }
- em.add(obj.Name(), constVal)
- pkgEnums[named.Obj().Name()] = em
- }
- }
- }
+ /*
+ NOTE:
+
+ type T int
+ const A T = iota // obj.Type() is T
+
+ type R T
+ const B R = iota // obj.Type() is R
+
+ type T2 int
+ type T1 = T2
+ const C T1 = iota // obj.Type() is T2
+
+ type T3 = T4
+ type T4 int
+ type T5 = T3
+ const D T5 = iota // obj.Type() is T4
+
+ // And, in all these cases, validNamedBasic(obj.Type()) == true.
+ */
+
+ if !validNamedBasic(obj.Type()) {
+ return enumType{}, "", "", false
}
- // Delete member-less enum types.
- // We can't call these enums, since we can't be sure without
- // the existence of members. (The type may just be a named type,
- // for instance.)
- for k, v := range pkgEnums {
- if v.numMembers() == 0 {
- delete(pkgEnums, k)
- }
+ named := obj.Type().(*types.Named) // guaranteed by validNamedBasic()
+ tn := named.Obj()
+
+ // Enum type's scope and enum member's scope must be the same. If they're
+ // not, don't consider the const a member. Additionally, the enum type and
+ // the enum member must be in the same package (the scope check accounts for
+ // this, too).
+ if tn.Parent() != obj.Parent() {
+ return enumType{}, "", "", false
}
- return pkgEnums
+ return enumType{tn}, obj.Name(), determineConstVal(constName, info), true
+}
+
+func determineConstVal(name *ast.Ident, info *types.Info) constantValue {
+ c := info.ObjectOf(name).(*types.Const)
+ return constantValue(c.Val().ExactString())
+}
+
+func isBlankIdentifier(obj types.Object) bool {
+ return obj.Name() == "_" // NOTE: go/types/decl.go does a direct comparison like this
+}
+
+func validBasic(basic *types.Basic) bool {
+ switch i := basic.Info(); {
+ case i&types.IsInteger != 0, i&types.IsFloat != 0, i&types.IsString != 0:
+ return true
+ }
+ return false
+}
+
+// validNamedBasic returns whether the type t is a named type whose underlying
+// type is a valid basic type to form an enum.
+// A type that passes this check meets the definition of an enum type.
+// Note that
+// validNamedBasic(t) == true => t.(*types.Named)
+func validNamedBasic(t types.Type) bool {
+ named, ok := t.(*types.Named)
+ if !ok {
+ return false
+ }
+ basic, ok := named.Underlying().(*types.Basic)
+ if !ok || !validBasic(basic) {
+ return false
+ }
+ return true
}
diff --git a/vendor/github.com/nishanths/exhaustive/exhaustive.go b/vendor/github.com/nishanths/exhaustive/exhaustive.go
index 73815a626..e852df757 100644
--- a/vendor/github.com/nishanths/exhaustive/exhaustive.go
+++ b/vendor/github.com/nishanths/exhaustive/exhaustive.go
@@ -1,113 +1,259 @@
-// Package exhaustive provides an analyzer that checks exhaustiveness of enum
-// switch statements. The analyzer also provides fixes to make the offending
-// switch statements exhaustive (see "Fixes" section).
-//
-// See "cmd/exhaustive" subpackage for the related command line program.
-//
-// Definition of enum
-//
-// The Go language spec does not provide an explicit definition for enums.
-// For the purpose of this program, an enum type is a package-level named type
-// whose underlying type is an integer (includes byte and rune), a float, or
-// a string type. An enum type must have associated with it one or more
-// package-level variables of the named type in the package. These variables
-// constitute the enum's members.
-//
-// In the code snippet below, Biome is an enum type with 3 members. (You may
-// also use iota instead of explicitly specifying values.)
-//
-// type Biome int
-//
-// const (
-// Tundra Biome = 1
-// Savanna Biome = 2
-// Desert Biome = 3
-// )
-//
-// Switch statement exhaustiveness
-//
-// An enum switch statement is exhaustive if it has cases for each of the enum's members.
-//
-// For an enum type defined in the same package as the switch statement, both
-// exported and unexported enum members must be present in order to consider
-// the switch exhaustive. On the other hand, for an enum type defined
-// in an external package it is sufficient for just exported enum members
-// to be present in order to consider the switch exhaustive.
-//
-// Flags
-//
-// The analyzer accepts a boolean flag: -default-signifies-exhaustive.
-// The flag, if enabled, indicates to the analyzer that switch statements
-// are to be considered exhaustive as long as a 'default' case is present, even
-// if all enum members aren't listed in the switch statements cases.
-//
-// The -check-generated boolean flag, disabled by default, indicates whether
-// to check switch statements in generated Go source files.
-//
-// The other relevant flag is the -fix flag; its behavior is described
-// in the next section.
-//
-// Fixes
-//
-// The analyzer suggests fixes for a switch statement if it is not exhaustive
-// and does not have a 'default' case. The suggested fix always adds a single
-// case clause for the missing enum members.
-//
-// case MissingA, MissingB, MissingC:
-// panic(fmt.Sprintf("unhandled value: %v", v))
-//
-// where v is the expression in the switch statement's tag (in other words, the
-// value being switched upon). If the switch statement's tag is a function or a
-// method call the analyzer does not suggest a fix, as reusing the call expression
-// in the panic/fmt.Sprintf call could be mutative.
-//
-// The rationale for the fix using panic is that it might be better to fail loudly on
-// existing unhandled or impossible cases than to let them slip by quietly unnoticed.
-// An even better fix may, of course, be to manually inspect the sites reported
-// by the package and handle the missing cases if necessary.
-//
-// Imports will be adjusted automatically to account for the "fmt" dependency.
-//
-// Skipping analysis
-//
-// If the following directive comment:
-//
-// //exhaustive:ignore
-//
-// is associated with a switch statement, the analyzer skips
-// checking of the switch statement and no diagnostics are reported.
-//
-// Additionally, no diagnostics are reported for switch statements in
-// generated files (see https://golang.org/s/generatedcode for definition of
-// generated file), unless the -check-generated flag is enabled.
+/*
+Package exhaustive provides an analyzer that checks exhaustiveness of enum
+switch statements in Go source code.
+
+Definition of enum
+
+The Go language spec does not provide an explicit definition for an enum. For
+the purpose of this analyzer, an enum type is any named type (a.k.a. defined
+type) whose underlying type is an integer (includes byte and rune), a float, or
+a string type. An enum type has associated with it constants of this named type;
+these constants constitute the enum members.
+
+In the example below, Biome is an enum type with 3 members.
+
+ type Biome int
+
+ const (
+ Tundra Biome = 1
+ Savanna Biome = 2
+ Desert Biome = 3
+ )
+
+For a constant to be an enum member for an enum type, the constant must be
+declared in the same scope as the enum type. Note that the scope requirement
+implies that only constants declared in the same package as the enum type's
+package can constitute the enum members for the enum type.
+
+Enum member constants for a given enum type don't necessarily have to all be
+declared in the same const block. Constant values may be specified using iota,
+using explicit values, or by any means of declaring a valid Go const. It is
+allowed for multiple enum member constants for a given 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
+of the enum type's members are listed in the switch statement's cases. If
+multiple enum member constants 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 identifiers denoting constants (e.g. Tundra) and qualified identifiers
+denoting constants (e.g. somepkg.Grassland) listed in a switch statement's cases
+can contribute towards satisfying exhaustiveness. Literal values, struct fields,
+re-assignable variables, etc. will not.
+
+The analyzer will produce a diagnostic about unhandled enum members if the
+required memebers are not listed in a switch statement's cases (this applies
+even if the switch statement has a 'default' case).
+
+Type aliases
+
+The analyzer handles type aliases for an enum type in the following manner.
+Consider the example below. T2 is a enum type, and T1 is an alias for T2. Note
+that we don't term T1 itself an enum type; it is only an alias for an enum
+type.
+
+ package pkg
+ type T1 = newpkg.T2
+ const (
+ A = newpkg.A
+ B = newpkg.B
+ )
+
+ package newpkg
+ type T2 int
+ const (
+ A T2 = 1
+ B T2 = 2
+ )
+
+Then a switch statement that switches on a value of type T1 (which, in reality,
+is just an alternate spelling for type T2) is exhaustive if all of T2's enum
+members are listed in the switch statement's cases. The same conditions
+described in the previous section for same-valued enum members and for
+exported/unexported enum members apply here too.
+
+It is worth noting that, though T1 and T2 are identical types, only constants
+declared in the same scope as type T2's scope can be T2's enum members. In the
+example, newpkg.A and newpkg.B are T2's enum members.
+
+The analyzer guarantees that introducing a type alias (such as type T1 =
+newpkg.T2) will never result in new diagnostics from the analyzer, as long as
+the set of enum member constant values of the new RHS type (newpkg.T2) is a
+subset of the set of enum member constant values of the old LHS type (T1).
+
+Advanced notes
+
+Non-enum member constants in a switch statement's cases: Recall from an earlier
+section that a constant must be declared in the same scope as the enum type to
+be an enum member. It is valid, however, both to the Go type checker and to this
+analyzer, for any constant of the right type to be listed in the cases of an
+enum switch statement (it does not necessarily have to be an enum member
+constant declared in the same scope/package as the enum type's scope/package).
+This is particularly useful when a type alias is involved: A forwarding constant
+declaration (such as pkg.A, in type T1's package) can take the place of the
+actual enum member constant (newpkg.A, in type T2's package) in the switch
+statement's cases to satisfy exhaustiveness.
+
+ var v pkg.T1 = pkg.ReturnsT1() // v is effectively of type newpkg.T2 due to alias
+ switch v {
+ case pkg.A: // valid substitute for newpkg.A (same constant value)
+ case pkg.B: // valid substitute for newpkg.B (same constant value)
+ }
+
+Flags
+
+Notable flags supported by the analyzer are described below.
+All of these flags are optional.
+
+ flag type default value
+
+ -explicit-exhaustive-switch bool false
+ -check-generated bool false
+ -default-signifies-exhaustive bool false
+ -ignore-enum-members string (none)
+ -package-scope-only bool false
+
+
+If the -explicit-exhaustive-switch flag is enabled, the analyzer only runs on
+switch statements explicitly marked with the comment text
+("exhaustive:enforce"). Otherwise, it runs on every enum switch statement not
+marked with the comment text ("exhaustive:ignore").
+
+If the -check-generated flag is enabled, switch statements in generated Go
+source files are also checked. Otherwise, by default, switch statements in
+generated files are not checked. See https://golang.org/s/generatedcode for the
+definition of generated file.
+
+If the -default-signifies-exhaustive flag is enabled, the presence of a
+'default' case in a switch statement always satisfies exhaustiveness, even if
+all enum members are not listed. It is not recommended that you enable this
+flag; enabling it generally defeats the purpose of exhaustiveness checking.
+
+The -ignore-enum-members flag specifies a regular expression in Go syntax. Enum
+members matching the regular expression don't have to be listed in switch
+statement cases to satisfy exhaustiveness. The specified regular expression is
+matched against an enum member name inclusive of the enum package import path:
+for example, if the enum package import path is "example.com/pkg" and the member
+name is "Tundra", the specified regular expression will be matched against the
+string "example.com/pkg.Tundra".
+
+If the -package-scope-only flag is enabled, the analyzer only finds enums
+defined in package scopes, and consequently only switch statements that switch
+on package-scoped enums will be checked for exhaustiveness. By default, the
+analyzer finds enums defined in all scopes, and checks switch statements that
+switch on all these enums.
+
+Skip analysis
+
+In implicitly exhaustive switch mode (-explicit-exhaustive-switch=false), skip
+checking of a specific switch statement by associating the comment shown in
+the example below with the switch statement. Note the lack of whitespace
+between the comment marker ("//") and the comment text ("exhaustive:ignore").
+
+ //exhaustive:ignore
+ switch v { ... }
+
+In explicitly exhaustive switch mode (-explicit-exhaustive-switch=true), run
+exhaustiveness checks on a specific switch statement by associating the
+comment shown in the example below with the switch statement.
+
+ //exhaustive:enforce
+ switch v { ... }
+
+To ignore specific enum members, see the -ignore-enum-members flag.
+
+Switch statements in generated Go source files are not checked by default.
+Use the -check-generated flag to change this behavior.
+*/
package exhaustive
import (
- "go/ast"
- "go/types"
- "sort"
- "strings"
+ "flag"
+ "regexp"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
)
+var _ flag.Value = (*regexpFlag)(nil)
+
+// regexpFlag implements the flag.Value interface for parsing
+// regular expression flag values.
+type regexpFlag struct{ r *regexp.Regexp }
+
+func (v *regexpFlag) String() string {
+ if v == nil || v.r == nil {
+ return ""
+ }
+ return v.r.String()
+}
+
+func (v *regexpFlag) Set(expr string) error {
+ if expr == "" {
+ v.r = nil
+ return nil
+ }
+
+ r, err := regexp.Compile(expr)
+ if err != nil {
+ return err
+ }
+
+ v.r = r
+ return nil
+}
+
+func (v *regexpFlag) value() *regexp.Regexp { return v.r }
+
+func init() {
+ Analyzer.Flags.BoolVar(&fExplicitExhaustiveSwitch, ExplicitExhaustiveSwitchFlag, false, "only run exhaustive check on switches with \"//exhaustive:enforce\" comment")
+ Analyzer.Flags.BoolVar(&fCheckGenerated, CheckGeneratedFlag, false, "check switch statements in generated files")
+ Analyzer.Flags.BoolVar(&fDefaultSignifiesExhaustive, DefaultSignifiesExhaustiveFlag, false, "presence of \"default\" case in switch statements satisfies exhaustiveness, even if all enum members are not listed")
+ Analyzer.Flags.Var(&fIgnoreEnumMembers, IgnoreEnumMembersFlag, "enum members matching `regex` do not have to be listed in switch statements to satisfy exhaustiveness")
+ Analyzer.Flags.BoolVar(&fPackageScopeOnly, PackageScopeOnlyFlag, false, "consider enums only in package scopes, not in inner scopes")
+
+ var unused string
+ Analyzer.Flags.StringVar(&unused, IgnorePatternFlag, "", "no effect (deprecated); see -"+IgnoreEnumMembersFlag+" instead")
+ Analyzer.Flags.StringVar(&unused, CheckingStrategyFlag, "", "no effect (deprecated)")
+}
+
// Flag names used by the analyzer. They are exported for use by analyzer
// driver programs.
const (
- DefaultSignifiesExhaustiveFlag = "default-signifies-exhaustive"
+ ExplicitExhaustiveSwitchFlag = "explicit-exhaustive-switch"
CheckGeneratedFlag = "check-generated"
+ DefaultSignifiesExhaustiveFlag = "default-signifies-exhaustive"
+ IgnoreEnumMembersFlag = "ignore-enum-members"
+ PackageScopeOnlyFlag = "package-scope-only"
+
+ IgnorePatternFlag = "ignore-pattern" // Deprecated: see IgnoreEnumMembersFlag instead.
+ CheckingStrategyFlag = "checking-strategy" // Deprecated.
)
var (
+ fExplicitExhaustiveSwitch bool
+ fCheckGenerated bool
fDefaultSignifiesExhaustive bool
- fCheckGeneratedFiles bool
+ fIgnoreEnumMembers regexpFlag
+ fPackageScopeOnly bool
)
-func init() {
- Analyzer.Flags.BoolVar(&fDefaultSignifiesExhaustive, DefaultSignifiesExhaustiveFlag, false, "indicates that switch statements are to be considered exhaustive if a 'default' case is present, even if all enum members aren't listed in the switch")
- Analyzer.Flags.BoolVar(&fCheckGeneratedFiles, CheckGeneratedFlag, false, "check switch statements in generated files also")
+// resetFlags resets the flag variables to their default values.
+// Useful in tests.
+func resetFlags() {
+ fExplicitExhaustiveSwitch = false
+ fCheckGenerated = false
+ fDefaultSignifiesExhaustive = false
+ fIgnoreEnumMembers = regexpFlag{}
+ fPackageScopeOnly = false
}
var Analyzer = &analysis.Analyzer{
@@ -115,76 +261,21 @@ var Analyzer = &analysis.Analyzer{
Doc: "check exhaustiveness of enum switch statements",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
- FactTypes: []analysis.Fact{&enumsFact{}},
-}
-
-// IgnoreDirectivePrefix is used to exclude checking of specific switch statements.
-// See package comment for details.
-const IgnoreDirectivePrefix = "//exhaustive:ignore"
-
-func containsIgnoreDirective(comments []*ast.Comment) bool {
- for _, c := range comments {
- if strings.HasPrefix(c.Text, IgnoreDirectivePrefix) {
- return true
- }
- }
- return false
-}
-
-type enumsFact struct {
- Enums enums
-}
-
-var _ analysis.Fact = (*enumsFact)(nil)
-
-func (e *enumsFact) AFact() {}
-
-func (e *enumsFact) String() string {
- // sort for stability (required for testing)
- var sortedKeys []string
- for k := range e.Enums {
- sortedKeys = append(sortedKeys, k)
- }
- sort.Strings(sortedKeys)
-
- var buf strings.Builder
- for i, k := range sortedKeys {
- v := e.Enums[k]
- buf.WriteString(k)
- buf.WriteString(":")
-
- for j, vv := range v.OrderedNames {
- buf.WriteString(vv)
- // add comma separator between each enum member in an enum type
- if j != len(v.OrderedNames)-1 {
- buf.WriteString(",")
- }
- }
- // add semicolon separator between each enum type
- if i != len(sortedKeys)-1 {
- buf.WriteString("; ")
- }
- }
- return buf.String()
+ FactTypes: []analysis.Fact{&enumMembersFact{}},
}
func run(pass *analysis.Pass) (interface{}, error) {
- e := findEnums(pass)
- if len(e) != 0 {
- pass.ExportPackageFact(&enumsFact{Enums: e})
- }
-
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
- comments := make(map[*ast.File]ast.CommentMap) // CommentMap per package file, lazily populated by reference
- generated := make(map[*ast.File]bool)
- checkSwitchStatements(pass, inspect, comments, generated)
- return nil, nil
-}
-
-func enumTypeName(e *types.Named, samePkg bool) string {
- if samePkg {
- return e.Obj().Name()
+ for typ, members := range findEnums(fPackageScopeOnly, pass.Pkg, inspect, pass.TypesInfo) {
+ exportFact(pass, typ, members)
}
- return e.Obj().Pkg().Name() + "." + e.Obj().Name()
+
+ checkSwitchStatements(pass, inspect, config{
+ explicitExhaustiveSwitch: fExplicitExhaustiveSwitch,
+ defaultSignifiesExhaustive: fDefaultSignifiesExhaustive,
+ checkGeneratedFiles: fCheckGenerated,
+ ignoreEnumMembers: fIgnoreEnumMembers.value(),
+ })
+ return nil, nil
}
diff --git a/vendor/github.com/nishanths/exhaustive/fact.go b/vendor/github.com/nishanths/exhaustive/fact.go
new file mode 100644
index 000000000..ecf0907b2
--- /dev/null
+++ b/vendor/github.com/nishanths/exhaustive/fact.go
@@ -0,0 +1,28 @@
+package exhaustive
+
+import "golang.org/x/tools/go/analysis"
+
+// NOTE: Fact types must remain gob-coding compatible.
+// See TestFactsGob.
+
+var _ analysis.Fact = (*enumMembersFact)(nil)
+
+type enumMembersFact struct{ Members enumMembers }
+
+func (f *enumMembersFact) AFact() {}
+func (f *enumMembersFact) String() string { return f.Members.factString() }
+
+// exportFact exports the enum members for the given enum type.
+func exportFact(pass *analysis.Pass, enumTyp enumType, members enumMembers) {
+ pass.ExportObjectFact(enumTyp.factObject(), &enumMembersFact{members})
+}
+
+// importFact imports the enum members for the given possible enum type.
+// An (_, false) return indicates that the enum type is not a known one.
+func importFact(pass *analysis.Pass, possibleEnumType enumType) (enumMembers, bool) {
+ var f enumMembersFact
+ if !pass.ImportObjectFact(possibleEnumType.factObject(), &f) {
+ return enumMembers{}, false
+ }
+ return f.Members, true
+}
diff --git a/vendor/github.com/nishanths/exhaustive/generated.go b/vendor/github.com/nishanths/exhaustive/generated.go
deleted file mode 100644
index 19b4fb12b..000000000
--- a/vendor/github.com/nishanths/exhaustive/generated.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package exhaustive
-
-import (
- "go/ast"
- "strings"
-)
-
-// Adapated from https://gotools.org/dmitri.shuralyov.com/go/generated
-
-func isGeneratedFile(file *ast.File) bool {
- for _, c := range file.Comments {
- for _, cc := range c.List {
- s := cc.Text // "\n" already removed (see doc comment)
- if len(s) >= 1 && s[len(s)-1] == '\r' {
- s = s[:len(s)-1] // Trim "\r".
- }
- if containsGeneratedComment(s) {
- return true
- }
- }
- }
-
- return false
-}
-
-func containsGeneratedComment(s string) bool {
- return strings.HasPrefix(s, genCommentPrefix) &&
- strings.HasSuffix(s, genCommentSuffix)
-}
-
-const (
- genCommentPrefix = "// Code generated "
- genCommentSuffix = " DO NOT EDIT."
-)
diff --git a/vendor/github.com/nishanths/exhaustive/go.mod b/vendor/github.com/nishanths/exhaustive/go.mod
index 9a75e5152..e50629727 100644
--- a/vendor/github.com/nishanths/exhaustive/go.mod
+++ b/vendor/github.com/nishanths/exhaustive/go.mod
@@ -2,4 +2,4 @@ module github.com/nishanths/exhaustive
go 1.14
-require golang.org/x/tools v0.0.0-20201011145850-ed2f50202694
+require golang.org/x/tools v0.1.10
diff --git a/vendor/github.com/nishanths/exhaustive/go.sum b/vendor/github.com/nishanths/exhaustive/go.sum
index 4f00a79cc..3f735d1c2 100644
--- a/vendor/github.com/nishanths/exhaustive/go.sum
+++ b/vendor/github.com/nishanths/exhaustive/go.sum
@@ -1,38 +1,29 @@
-github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
+golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a h1:gILuVKC+ZPD6g/tj6zBOdnOH1ZHI0zZ86+KLMogc6/s=
-golang.org/x/tools v0.0.0-20200519015757-0d0afa43d58a/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200519142718-10921354bc51 h1:GtYAC9y+dpwWCXBwbcZgxcFfiqW4SI93yvQqpF+9+P8=
-golang.org/x/tools v0.0.0-20201011145850-ed2f50202694 h1:BANdcOVw3KTuUiyfDp7wrzCpkCe8UP3lowugJngxBTg=
-golang.org/x/tools v0.0.0-20201011145850-ed2f50202694/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/vendor/github.com/nishanths/exhaustive/switch.go b/vendor/github.com/nishanths/exhaustive/switch.go
index 2cec7f9cb..dcf3b6d0a 100644
--- a/vendor/github.com/nishanths/exhaustive/switch.go
+++ b/vendor/github.com/nishanths/exhaustive/switch.go
@@ -1,14 +1,11 @@
package exhaustive
import (
- "bytes"
"fmt"
"go/ast"
- "go/printer"
- "go/token"
"go/types"
+ "regexp"
"sort"
- "strconv"
"strings"
"golang.org/x/tools/go/analysis"
@@ -16,416 +13,346 @@ import (
"golang.org/x/tools/go/ast/inspector"
)
-func isDefaultCase(c *ast.CaseClause) bool {
- return c.List == nil // see doc comment on field
-}
+// nodeVisitor is like the visitor function used by Inspector.WithStack,
+// except that it returns an additional value: a short description of
+// the result of this node visit.
+//
+// The result is typically useful in debugging or in unit tests to check
+// that the nodeVisitor function took the expected code path.
+type nodeVisitor func(n ast.Node, push bool, stack []ast.Node) (proceed bool, result string)
+
+// Result values returned by a node visitor constructed via switchStmtChecker.
+const (
+ resultNotPush = "not push"
+ resultGeneratedFile = "generated file"
+ resultNoSwitchTag = "no switch tag"
+ resultTagNotValue = "switch tag not value type"
+ resultTagNotNamed = "switch tag not named type"
+ resultTagNoPkg = "switch tag does not belong to regular package"
+ resultTagNotEnum = "switch tag not known enum type"
+ resultSwitchIgnoreComment = "switch statement has ignore comment"
+ resultSwitchNoEnforceComment = "switch statement has no enforce comment"
+ resultEnumMembersAccounted = "requisite enum members accounted for"
+ resultDefaultCaseSuffices = "default case presence satisfies exhaustiveness"
+ resultReportedDiagnostic = "reported diagnostic"
+)
-func checkSwitchStatements(
- pass *analysis.Pass,
- inspect *inspector.Inspector,
- comments map[*ast.File]ast.CommentMap,
- generated map[*ast.File]bool,
-) {
- inspect.WithStack([]ast.Node{&ast.SwitchStmt{}}, func(n ast.Node, push bool, stack []ast.Node) bool {
+// switchStmtChecker returns a node visitor that checks exhaustiveness
+// of enum switch statements for the supplied pass, and reports diagnostics for
+// switch statements that are non-exhaustive.
+// It expects to only see *ast.SwitchStmt nodes.
+func switchStmtChecker(pass *analysis.Pass, cfg config) nodeVisitor {
+ generated := make(map[*ast.File]bool) // cached results
+ comments := make(map[*ast.File]ast.CommentMap) // cached results
+
+ return func(n ast.Node, push bool, stack []ast.Node) (bool, string) {
if !push {
- return true
+ // The proceed return value should not matter; it is ignored by
+ // inspector package for pop calls.
+ // Nevertheless, return true to be on the safe side for the future.
+ return true, resultNotPush
}
file := stack[0].(*ast.File)
- // Determine if file is a generated file, based on https://golang.org/s/generatedcode.
- // If generated, don't check this file.
- var isGenerated bool
- if gen, ok := generated[file]; ok {
- isGenerated = gen
- } else {
- isGenerated = isGeneratedFile(file)
- generated[file] = isGenerated
+ // Determine if the file is a generated file, and save the result.
+ // If it is a generated file, don't check the file.
+ if _, ok := generated[file]; !ok {
+ generated[file] = isGeneratedFile(file)
}
- if isGenerated && !fCheckGeneratedFiles {
- // don't check
- return true
+ if generated[file] && !cfg.checkGeneratedFiles {
+ // Don't check this file.
+ // Return false because the children nodes of node `n` don't have to be checked.
+ return false, resultGeneratedFile
}
sw := n.(*ast.SwitchStmt)
+
+ if _, ok := comments[file]; !ok {
+ comments[file] = ast.NewCommentMap(pass.Fset, file, file.Comments)
+ }
+ switchComments := comments[file][sw]
+ if !cfg.explicitExhaustiveSwitch && containsIgnoreDirective(switchComments) {
+ // Skip checking of this switch statement due to ignore directive comment.
+ // Still return true because there may be nested switch statements
+ // that are not to be ignored.
+ return true, resultSwitchIgnoreComment
+ }
+ if cfg.explicitExhaustiveSwitch && !containsEnforceDirective(switchComments) {
+ // Skip checking of this switch statement due to missing enforce directive comment.
+ return true, resultSwitchNoEnforceComment
+ }
+
if sw.Tag == nil {
- return true
+ return true, resultNoSwitchTag
}
+
t := pass.TypesInfo.Types[sw.Tag]
if !t.IsValue() {
- return true
+ return true, resultTagNotValue
}
+
tagType, ok := t.Type.(*types.Named)
if !ok {
- return true
+ return true, resultTagNotNamed
}
tagPkg := tagType.Obj().Pkg()
if tagPkg == nil {
- // Doc comment: nil for labels and objects in the Universe scope.
+ // The Go documentation says: nil for labels and objects in the Universe scope.
// This happens for the `error` type, for example.
- // Continuing would mean that ImportPackageFact panics.
- return true
+ return true, resultTagNoPkg
}
- var enums enumsFact
- if !pass.ImportPackageFact(tagPkg, &enums) {
- // Can't do anything further.
- return true
+ enumTyp := enumType{tagType.Obj()}
+ members, ok := importFact(pass, enumTyp)
+ if !ok {
+ // switch tag's type is not a known enum type.
+ return true, resultTagNotEnum
}
- em, isEnum := enums.Enums[tagType.Obj().Name()]
- if !isEnum {
- // Tag's type is not a known enum.
- return true
- }
+ samePkg := tagPkg == pass.Pkg // do the switch statement and the switch tag type (i.e. enum type) live in the same package?
+ checkUnexported := samePkg // we want to include unexported members in the exhaustiveness check only if we're in the same package
+ checklist := makeChecklist(members, tagPkg, checkUnexported, cfg.ignoreEnumMembers)
- // Get comment map.
- var allComments ast.CommentMap
- if cm, ok := comments[file]; ok {
- allComments = cm
- } else {
- allComments = ast.NewCommentMap(pass.Fset, file, file.Comments)
- comments[file] = allComments
- }
+ hasDefaultCase := analyzeSwitchClauses(sw, pass.TypesInfo, func(val constantValue) {
+ checklist.found(val)
+ })
- specificComments := allComments.Filter(sw)
- for _, group := range specificComments.Comments() {
- if containsIgnoreDirective(group.List) {
- return true // skip checking due to ignore directive
- }
+ if len(checklist.remaining()) == 0 {
+ // All enum members accounted for.
+ // Nothing to report.
+ return true, resultEnumMembersAccounted
}
-
- samePkg := tagPkg == pass.Pkg
- checkUnexported := samePkg
-
- hitlist := hitlistFromEnumMembers(em, checkUnexported)
- if len(hitlist) == 0 {
- // can happen if external package and enum consists only of
- // unexported members
- return true
+ if hasDefaultCase && cfg.defaultSignifiesExhaustive {
+ // Though enum members are not accounted for,
+ // the existence of the default case signifies exhaustiveness.
+ // So don't report.
+ return true, resultDefaultCaseSuffices
}
+ pass.Report(makeDiagnostic(sw, samePkg, enumTyp, members, checklist.remaining()))
+ return true, resultReportedDiagnostic
+ }
+}
- defaultCaseExists := false
- for _, stmt := range sw.Body.List {
- caseCl := stmt.(*ast.CaseClause)
- if isDefaultCase(caseCl) {
- defaultCaseExists = true
- continue // nothing more to do if it's the default case
- }
- for _, e := range caseCl.List {
- e = astutil.Unparen(e)
- if samePkg {
- ident, ok := e.(*ast.Ident)
- if !ok {
- continue
- }
- updateHitlist(hitlist, em, ident.Name)
- } else {
- selExpr, ok := e.(*ast.SelectorExpr)
- if !ok {
- continue
- }
-
- // ensure X is package identifier
- ident, ok := selExpr.X.(*ast.Ident)
- if !ok {
- continue
- }
- if !isPackageNameIdentifier(pass, ident) {
- continue
- }
-
- updateHitlist(hitlist, em, selExpr.Sel.Name)
- }
- }
- }
+// config is configuration for checkSwitchStatements.
+type config struct {
+ explicitExhaustiveSwitch bool
+ defaultSignifiesExhaustive bool
+ checkGeneratedFiles bool
+ ignoreEnumMembers *regexp.Regexp // can be nil
+}
- defaultSuffices := fDefaultSignifiesExhaustive && defaultCaseExists
- shouldReport := len(hitlist) > 0 && !defaultSuffices
+// checkSwitchStatements checks exhaustiveness of enum switch statements for the supplied
+// pass. It reports switch statements that are not exhaustive via pass.Report.
+func checkSwitchStatements(pass *analysis.Pass, inspect *inspector.Inspector, cfg config) {
+ f := switchStmtChecker(pass, cfg)
- if shouldReport {
- reportSwitch(pass, sw, samePkg, tagType, em, hitlist, defaultCaseExists, file)
- }
- return true
+ inspect.WithStack([]ast.Node{&ast.SwitchStmt{}}, func(n ast.Node, push bool, stack []ast.Node) bool {
+ proceed, _ := f(n, push, stack)
+ return proceed
})
}
-func updateHitlist(hitlist map[string]struct{}, em *enumMembers, foundName string) {
- constVal, ok := em.NameToValue[foundName]
- if !ok {
- // only delete the name alone from hitlist
- delete(hitlist, foundName)
- return
- }
-
- // delete all of the same-valued names from hitlist
- namesToDelete := em.ValueToNames[constVal]
- for _, n := range namesToDelete {
- delete(hitlist, n)
- }
+func isDefaultCase(c *ast.CaseClause) bool {
+ return c.List == nil // see doc comment on List field
}
-func isPackageNameIdentifier(pass *analysis.Pass, ident *ast.Ident) bool {
- obj := pass.TypesInfo.ObjectOf(ident)
+func denotesPackage(ident *ast.Ident, info *types.Info) (*types.Package, bool) {
+ obj := info.ObjectOf(ident)
if obj == nil {
- return false
+ return nil, false
}
- _, ok := obj.(*types.PkgName)
- return ok
+ n, ok := obj.(*types.PkgName)
+ if !ok {
+ return nil, false
+ }
+ return n.Imported(), true
}
-func hitlistFromEnumMembers(em *enumMembers, checkUnexported bool) map[string]struct{} {
- hitlist := make(map[string]struct{})
- for _, m := range em.OrderedNames {
- if m == "_" {
- // blank identifier is often used to skip entries in iota lists
- continue
+// analyzeSwitchClauses analyzes the clauses in the supplied switch statement.
+// The info param should typically be pass.TypesInfo. The found function is
+// called for each enum member name found in the switch statement.
+// The hasDefaultCase return value indicates whether the switch statement has a
+// default clause.
+func analyzeSwitchClauses(sw *ast.SwitchStmt, info *types.Info, found func(val constantValue)) (hasDefaultCase bool) {
+ for _, stmt := range sw.Body.List {
+ caseCl := stmt.(*ast.CaseClause)
+ if isDefaultCase(caseCl) {
+ hasDefaultCase = true
+ continue // nothing more to do if it's the default case
}
- if !ast.IsExported(m) && !checkUnexported {
- continue
+ for _, expr := range caseCl.List {
+ analyzeCaseClauseExpr(expr, info, found)
}
- hitlist[m] = struct{}{}
}
- return hitlist
+ return hasDefaultCase
}
-func determineMissingOutput(missingMembers map[string]struct{}, em *enumMembers) []string {
- constValMembers := make(map[string][]string) // value -> names
- var otherMembers []string // non-constant value names
-
- for m := range missingMembers {
- if constVal, ok := em.NameToValue[m]; ok {
- constValMembers[constVal] = append(constValMembers[constVal], m)
- } else {
- otherMembers = append(otherMembers, m)
+func analyzeCaseClauseExpr(e ast.Expr, info *types.Info, found func(val constantValue)) {
+ handleIdent := func(ident *ast.Ident) {
+ obj := info.Uses[ident]
+ if obj == nil {
+ return
+ }
+ if _, ok := obj.(*types.Const); !ok {
+ return
}
- }
- missingOutput := make([]string, 0, len(constValMembers)+len(otherMembers))
- for _, names := range constValMembers {
- sort.Strings(names)
- missingOutput = append(missingOutput, strings.Join(names, "|"))
+ // There are two scenarios.
+ // See related test cases in typealias/quux/quux.go.
+ //
+ // ### Scenario 1
+ //
+ // Tag package and constant package are the same.
+ //
+ // For example:
+ // var mode fs.FileMode
+ // switch mode {
+ // case fs.ModeDir:
+ // }
+ //
+ // This is simple: we just use fs.ModeDir's value.
+ //
+ // ### Scenario 2
+ //
+ // Tag package and constant package are different.
+ //
+ // For example:
+ // var mode fs.FileMode
+ // switch mode {
+ // case os.ModeDir:
+ // }
+ //
+ // Or equivalently:
+ // var mode os.FileMode // in effect, fs.FileMode because of type alias in package os
+ // switch mode {
+ // case os.ModeDir:
+ // }
+ //
+ // In this scenario, too, we accept the case clause expr constant
+ // value, as is. If the Go type checker is okay with the
+ // name being listed in the case clause, we don't care much further.
+ //
+ found(determineConstVal(ident, info))
}
- missingOutput = append(missingOutput, otherMembers...)
- sort.Strings(missingOutput)
- return missingOutput
-}
-func reportSwitch(
- pass *analysis.Pass,
- sw *ast.SwitchStmt,
- samePkg bool,
- enumType *types.Named,
- em *enumMembers,
- missingMembers map[string]struct{},
- defaultCaseExists bool,
- f *ast.File,
-) {
- missingOutput := determineMissingOutput(missingMembers, em)
-
- var fixes []analysis.SuggestedFix
- if !defaultCaseExists {
- if fix, ok := computeFix(pass, pass.Fset, f, sw, enumType, samePkg, missingMembers); ok {
- fixes = append(fixes, fix)
+ e = astutil.Unparen(e)
+ switch e := e.(type) {
+ case *ast.Ident:
+ handleIdent(e)
+
+ case *ast.SelectorExpr:
+ x := astutil.Unparen(e.X)
+ // Ensure we only see the form `pkg.Const`, and not e.g. `structVal.f`
+ // or `structVal.inner.f`.
+ // Check that X, which is everything except the rightmost *ast.Ident (or
+ // Sel), is also an *ast.Ident.
+ xIdent, ok := x.(*ast.Ident)
+ if !ok {
+ return
+ }
+ // Doesn't matter which package, just that it denotes a package.
+ if _, ok := denotesPackage(xIdent, info); !ok {
+ return
}
+ handleIdent(e.Sel)
}
-
- pass.Report(analysis.Diagnostic{
- Pos: sw.Pos(),
- End: sw.End(),
- Message: fmt.Sprintf("missing cases in switch of type %s: %s", enumTypeName(enumType, samePkg), strings.Join(missingOutput, ", ")),
- SuggestedFixes: fixes,
- })
}
-func computeFix(pass *analysis.Pass, fset *token.FileSet, f *ast.File, sw *ast.SwitchStmt, enumType *types.Named, samePkg bool, missingMembers map[string]struct{}) (analysis.SuggestedFix, bool) {
- // Function and method calls may be mutative, so we don't want to reuse the
- // call expression in the about-to-be-inserted case clause body. So we just
- // don't suggest a fix in such situations.
- //
- // However, we need to make an exception for type conversions, which are
- // also call expressions in the AST.
- //
- // We'll need to lookup type information for this, and can't rely solely
- // on the AST.
- if containsFuncCall(pass, sw.Tag) {
- return analysis.SuggestedFix{}, false
- }
-
- textEdits := []analysis.TextEdit{
- missingCasesTextEdit(fset, f, samePkg, sw, enumType, missingMembers),
- }
-
- // need to add "fmt" import if "fmt" import doesn't already exist
- if !hasImportWithPath(fset, f, `"fmt"`) {
- textEdits = append(textEdits, fmtImportTextEdit(fset, f))
- }
-
- missing := make([]string, 0, len(missingMembers))
+// diagnosticMissingMembers constructs the list of missing enum members,
+// suitable for use in a reported diagnostic message.
+func diagnosticMissingMembers(missingMembers map[string]struct{}, em enumMembers) []string {
+ missingByConstVal := make(map[constantValue][]string) // missing members, keyed by constant value.
for m := range missingMembers {
- missing = append(missing, m)
+ val := em.NameToValue[m]
+ missingByConstVal[val] = append(missingByConstVal[val], m)
}
- sort.Strings(missing)
-
- return analysis.SuggestedFix{
- Message: fmt.Sprintf("add case clause for: %s?", strings.Join(missing, ", ")),
- TextEdits: textEdits,
- }, true
-}
-func containsFuncCall(pass *analysis.Pass, e ast.Expr) bool {
- e = astutil.Unparen(e)
- c, ok := e.(*ast.CallExpr)
- if !ok {
- return false
- }
- if _, isFunc := pass.TypesInfo.TypeOf(c.Fun).Underlying().(*types.Signature); isFunc {
- return true
- }
- for _, a := range c.Args {
- if containsFuncCall(pass, a) {
- return true
- }
+ var out []string
+ for _, names := range missingByConstVal {
+ sort.Strings(names)
+ out = append(out, strings.Join(names, "|"))
}
- return false
+ sort.Strings(out)
+ return out
}
-func firstImportDecl(fset *token.FileSet, f *ast.File) *ast.GenDecl {
- for _, decl := range f.Decls {
- genDecl, ok := decl.(*ast.GenDecl)
- if ok && genDecl.Tok == token.IMPORT {
- // first IMPORT GenDecl
- return genDecl
- }
+// diagnosticEnumTypeName returns a string representation of an enum type for
+// use in reported diagnostics.
+func diagnosticEnumTypeName(enumType *types.TypeName, samePkg bool) string {
+ if samePkg {
+ return enumType.Name()
}
- return nil
+ return enumType.Pkg().Name() + "." + enumType.Name()
}
-// copies an GenDecl in a manner such that appending to the returned GenDecl's Specs field
-// doesn't mutate the original GenDecl
-func copyGenDecl(im *ast.GenDecl) *ast.GenDecl {
- imCopy := *im
- imCopy.Specs = make([]ast.Spec, len(im.Specs))
- for i := range im.Specs {
- imCopy.Specs[i] = im.Specs[i]
+// Makes a "missing cases in switch" diagnostic.
+// samePkg should be true if the enum type and the switch statement are defined
+// in the same package.
+func makeDiagnostic(sw *ast.SwitchStmt, samePkg bool, enumTyp enumType, allMembers enumMembers, missingMembers map[string]struct{}) analysis.Diagnostic {
+ message := fmt.Sprintf("missing cases in switch of type %s: %s",
+ diagnosticEnumTypeName(enumTyp.TypeName, samePkg),
+ strings.Join(diagnosticMissingMembers(missingMembers, allMembers), ", "))
+
+ return analysis.Diagnostic{
+ Pos: sw.Pos(),
+ End: sw.End(),
+ Message: message,
}
- return &imCopy
}
-func hasImportWithPath(fset *token.FileSet, f *ast.File, pathLiteral string) bool {
- igroups := astutil.Imports(fset, f)
- for _, igroup := range igroups {
- for _, importSpec := range igroup {
- if importSpec.Path.Value == pathLiteral {
- return true
- }
- }
- }
- return false
+// A checklist holds a set of enum member names that have to be
+// accounted for to satisfy exhaustiveness in an enum switch statement.
+//
+// The found method checks off member names from the set, based on
+// constant value, when a constant value is encoutered in the switch
+// statement's cases.
+//
+// The remaining method returns the member names not accounted for.
+//
+type checklist struct {
+ em enumMembers
+ checkl map[string]struct{}
}
-func fmtImportTextEdit(fset *token.FileSet, f *ast.File) analysis.TextEdit {
- firstDecl := firstImportDecl(fset, f)
-
- if firstDecl == nil {
- // file has no import declarations
- // insert "fmt" import spec after package statement
- return analysis.TextEdit{
- Pos: f.Name.End() + 1, // end of package name + 1
- End: f.Name.End() + 1,
- NewText: []byte(`import (
- "fmt"
- )`),
- }
- }
+func makeChecklist(em enumMembers, enumPkg *types.Package, includeUnexported bool, ignore *regexp.Regexp) *checklist {
+ checkl := make(map[string]struct{})
- // copy because we'll be mutating its Specs field
- firstDeclCopy := copyGenDecl(firstDecl)
-
- // find insertion index for "fmt" import spec
- var i int
- for ; i < len(firstDeclCopy.Specs); i++ {
- im := firstDeclCopy.Specs[i].(*ast.ImportSpec)
- if v, _ := strconv.Unquote(im.Path.Value); v > "fmt" {
- break
+ add := func(memberName string) {
+ if memberName == "_" {
+ // Blank identifier is often used to skip entries in iota lists.
+ // Also, it can't be referenced anywhere (including in a switch
+ // statement's cases), so it doesn't make sense to include it
+ // as required member to satisfy exhaustiveness.
+ return
+ }
+ if !ast.IsExported(memberName) && !includeUnexported {
+ return
}
+ if ignore != nil && ignore.MatchString(enumPkg.Path()+"."+memberName) {
+ return
+ }
+ checkl[memberName] = struct{}{}
}
- // insert "fmt" import spec at the index
- fmtSpec := &ast.ImportSpec{
- Path: &ast.BasicLit{
- // NOTE: Pos field doesn't seem to be required for our
- // purposes here.
- Kind: token.STRING,
- Value: `"fmt"`,
- },
- }
- s := firstDeclCopy.Specs // local var for easier comprehension of next line
- s = append(s[:i], append([]ast.Spec{fmtSpec}, s[i:]...)...)
- firstDeclCopy.Specs = s
-
- // create the text edit
- var buf bytes.Buffer
- printer.Fprint(&buf, fset, firstDeclCopy)
-
- return analysis.TextEdit{
- Pos: firstDecl.Pos(),
- End: firstDecl.End(),
- NewText: buf.Bytes(),
+ for _, name := range em.Names {
+ add(name)
}
-}
-func missingCasesTextEdit(fset *token.FileSet, f *ast.File, samePkg bool, sw *ast.SwitchStmt, enumType *types.Named, missingMembers map[string]struct{}) analysis.TextEdit {
- // ... Construct insertion text for case clause and its body ...
-
- var tag bytes.Buffer
- printer.Fprint(&tag, fset, sw.Tag)
-
- // If possible and if necessary, determine the package identifier based on the AST of other `case` clauses.
- var pkgIdent *ast.Ident
- if !samePkg {
- for _, stmt := range sw.Body.List {
- caseCl := stmt.(*ast.CaseClause)
- // At least one expression must exist in List at this point.
- // List cannot be nil because we only arrive here if the "default" clause
- // does not exist. Additionally, a syntactically valid case clause must
- // have at least one expression.
- if sel, ok := caseCl.List[0].(*ast.SelectorExpr); ok {
- pkgIdent = sel.X.(*ast.Ident)
- break
- }
- }
+ return &checklist{
+ em: em,
+ checkl: checkl,
}
+}
- missing := make([]string, 0, len(missingMembers))
- for m := range missingMembers {
- if !samePkg {
- if pkgIdent != nil {
- // we were able to determine package identifier
- missing = append(missing, pkgIdent.Name+"."+m)
- } else {
- // use the package name (may not be correct always)
- //
- // TODO: May need to also add import if the package isn't imported
- // elsewhere. This (ie, a switch with zero case clauses) should
- // happen rarely, so don't implement this for now.
- missing = append(missing, enumType.Obj().Pkg().Name()+"."+m)
- }
- } else {
- missing = append(missing, m)
- }
+func (c *checklist) found(val constantValue) {
+ // Delete all of the same-valued names.
+ for _, name := range c.em.ValueToNames[val] {
+ delete(c.checkl, name)
}
- sort.Strings(missing)
-
- insert := `case ` + strings.Join(missing, ", ") + `:
- panic(fmt.Sprintf("unhandled value: %v",` + tag.String() + `))`
-
- // ... Create the text edit ...
+}
- return analysis.TextEdit{
- Pos: sw.Body.Rbrace - 1,
- End: sw.Body.Rbrace - 1,
- NewText: []byte(insert),
- }
+func (c *checklist) remaining() map[string]struct{} {
+ return c.checkl
}