Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Exhaustive

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 ./...

Example: Struct Init

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.

Example: Enum

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
}

In Detail

Exhaustive: Struct

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
}, nil

It 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.

Exhaustive: Enum

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 switch cases: makes sure that every enum member is covered, even when default is present
  • In map keys: make sure every value is covered
  • In map values too
  • In slices and *arrays`

Inspired By

Kudos to:

About

Exhaustive: everything's covered!

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages