Linter. Everything's covered:
- Structure fields.
- Enum values in
switch - Enum values in
map - Enum values in a slice
Run me:
$ go install github.com/kolypto/go-exhaustive@latest
$ go-exhaustive ./...Define a type:
type User struct {
Id int
FirstName string
LastName string
}Now make sure all fields are initialized:
user := User{
Id: 1,
FirstName: "Mark",
//exhaustive:structinit
// 🔥 This simple tag will verify that every field is initialized.
// Result: //exhaustive:structinit incomplete. Missing fields: LastName"
}Why bother?
As your codebase grows, fields get added, remain un-initialized,
and you get bugs because of default values, and panics because of nils.
This opt-in linter will warn you where approapriate.
Go does not natively have enums, but consts of a certain type are considered to be enums:
type Faith string
const (
ATHEIST Faith = "atheist"
AGNOSTIC Faith = "agnostic"
MONOTHEIST Faith = "monotheist"
POLYTHEIST Faith = "polytheist"
)The linter can now check whether a switch stament covers them all:
func IsTheist(faith Faith) bool {
switch faith {
case AHEIST:
return false
case MONOTHEIST, POLYTHEIST:
return true
//exhaustive:enum
// 🔥 This simple tag will verify that every enum option is covered.
// Result: //exhaustive:enum incomplete. Missing values: AGNOSTIC
}
}It can also check your maps and slices: literally, every place where you'd expect full coverage:
var IsTheist = map[Faith]bool{
ATHEIST: false,
MONOTHEIST: true,
POLYTHEIST: true,
//exhaustive:enum
// 🔥 This simple tag will verify that every enum option is covered.
// Result: //exhaustive:enum incomplete. Missing values: AGNOSTIC
}
var AllFaiths = []Faith{
ATHEIST, MONOTHEIST, POLYTHEIST,
//exhaustive:enum
// 🔥 Result: //exhaustive:enum incomplete. Missing values: AGNOSTIC
}The //exhaustive:structinit linter makes sure that every struct field is initialized.
It's opt-in: it does not check your entire codebase. Only the structs you've decorated.
When the tag is placed within a struct literal:
return OpenAPIJsonResponse{
User: user,
//exhaustive:structinit
}, nilIt will warn you if a field is not initialized:
//exhaustive:structinit incomplete. Missing fields: IsOnline
Think of it this way: you declare your intention to have the structure completely initialized. Then you're safe when new fields get added: by someone, or especially by OpenAPI generators.
The //exhaustive:enum works with enum values: new types with series of defined const values.
Like this one:
type Faith string
const (
ATHEIST Faith = "atheist"
AGNOSTIC Faith = "agnostic"
MONOTHEIST Faith = "monotheist"
POLYTHEIST Faith = "polytheist"
)The linter's got your back: the linter will make sure that you've covered every possible option. It works in:
- In
switchcases: makes sure that every enum member is covered, even whendefaultis present - In
mapkeys: make sure every value is covered - In
mapvalues too - In slices and *arrays`
Kudos to: