requiredfield
Introduction
requiredfield is a linter for Go that verifies that required fields of a struct are filled when it is initialized. Whether a field is required is specified with a comment.
For example:
type BufWriter struct {
W io.Writer // required
Buffer *bytes.Buffer
}
The linter will return an error on the following snippet:
w := BufWriter{Buffer: b}
// ERROR: missing required fields: W
To read more about the motivation for this linter, see Motivation.
Installation
Install the binary from source by running:
go install go.abhg.dev/requiredfield/cmd/[email protected]
Usage
To use the linter, run the binary directly:
requiredfield ./...
Alternatively, use it with go vet
:
go vet -vettool=$(which requiredfield) ./...
Overview
To indicate that a field is required, add a // required
comment next to it.
type BufWriter struct {
W io.Writer // required
Buffer *bytes.Buffer
}
This indicates that the W
field is required.
All instantiations of BufWriter
using the T{...}
form will be required to set the W
field explicitly.
For example:
w := BufWriter{Buffer: b}
// ERROR: missing required fields: W
Syntax
Fields are marked as required by adding a comment in one of the following forms next to them:
// required
// required<sep><description>
Where <sep>
is a non-alphanumeric character, and <description>
is an optional description.
For example:
type User struct {
Name string // required: must be non-empty
Email string
}
The description is for the benefit of other readers only. requiredfield will ignore it.
Positioning
The // required
comment must be on the line where the field is defined.
GOOD | BAD
-----------------------------+-------------------
type User struct { | type User struct {
Name string // required | // required
} | Name string
| }
If the field definition is spread across multiple lines, the comment must be on the last of these. For example,
type Watcher struct {
Callback func(
ctx context.Context,
req *Request,
) // required
}
Behavior
Any time a struct is initialized in the form T{..}
, requiredfield will ensure that all its required fields are set explicitly.
u := User{
Email: email,
}
// ERROR: missing required fields: Name
Required fields can be set to the zero value of their type, but that choice must be made explicitly.
u := User{
Name: "", // computed below
Email: email,
}
// ...
u.Name = name
FAQ
Why a comment instead of a struct tag?
The reasons for this choice are both, philosophical and cosmetic.
First, the philosophical reason: requiredfield is a linter that runs at compile-time, and therefore wants its footprint limited to compile-time only. Struct tags get compiled into your binary and are available at runtime via reflection. It would become possible for someone to change how the program behaves based on the value of those struct tags. requiredfield considers that a violation of the linter's boundaries, and aims to prevent that by using comments instead.
The cosmetic reason is much easier to explain: Struct tags are uglier than line comments.
Author ID `required:"true"`
// versus
Author ID // required
Motivation
A common pattern in Go is to use a struct to pass several parameters to a function. This is often referred to as a "parameter object" or a "parameter struct". If you're unfamiliar with the concept, you can read more about it in Designing Go Libraries > Parameter objects.
In short, the pattern provides some advantages:
- readability: names of fields are visible at call sites, allowing them to act as a form of documentation similar to named parameters in other languages
- flexibility: new fields can be added without updating all existing call sites
These are both desirable properties for libraries: users of the library get a readable API and maintainers of the library can add new optional fields without a major version bump.
For applications, however, the flexibility afforded by the pattern can turn into a problem. Application-internal packages rarely cares about API backwards compatibility and are prone to adding new required parameters to functions. If they use parameter objects, they lose the ability to safely add these required parameters: they can no longer have the compiler tell them that they missed a spot.
So application developers are left to choose between:
- parameter objects: get readability, lose safety
- functions with tens of parameters: lose readability, get safety
requiredfield aims to fill this gap with parameter objects so that applications can still get the readability benefits of using them without sacrificing safety.
License
This software is made available under the MIT license.
Bump golang.org/x/tools from 0.7.0 to 0.8.0
Bumps golang.org/x/tools from 0.7.0 to 0.8.0.
Release notes
Sourced from golang.org/x/tools's releases.
... (truncated)
Commits
5ef3193
gopls/internal/lsp/source/typerefs: reexpress tests wrt ExternalRefsc5f768a
go.mod: update golang.org/x dependencies7c33a56
gopls/internal/lsp/source: show both the original declaration and the value o...4d205d8
gopls/doc: add instructions for using go.work with the Go distributiond5076cc
gopls/internal/lsp/cache: don't trace a region for MetadataForFilef796361
gopls/internal/lsp: add tracing instrumentation for all Server methodse104501
gopls/internal/astutil: TestPurgeFuncBodies requires source code for std6520870
gopls/internal/lsp/source/typerefs: allow for duplicate decls902fdca
gopls/internal/lsp/source/typerefs: purge func bodies before parsing58c9a63
go/packages/internal/nodecount: count ast.Node frequencyDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase
.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebase
will rebase this PR@dependabot recreate
will recreate this PR, overwriting any edits that have been made to it@dependabot merge
will merge this PR after your CI passes on it@dependabot squash and merge
will squash and merge this PR after your CI passes on it@dependabot cancel merge
will cancel a previously requested merge and block automerging@dependabot reopen
will reopen this PR if it is closed@dependabot close
will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot ignore this major version
will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor version
will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependency
will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)var form tracking
The linter only supports the
x := T{..}
form of structs. It may be possible to also trackvar
form:This will likely need to account for control flow:
From cursory research, this will likely need to inspect the SSA IR.
doc: How to group fields, how to document fields
The documentation doesn't make it clear how to group multiple required fields. For the same type, it's currently:
It also isn't clear that you can still document your fields like so:
Required by default, granular opt-in/out
There are frequent cases where "field is required" is the default, and optional is the exception. e.g. there are a fair number of instances of
// optional [desc]
in the Go source itself.There should be a way to opt into "required by default" at a package level. This wouldn't be a global default for this because it would have far too many false positives.
With package-level opt-in, we'll inevitably need struct-level opt-out, so this issue is also for granular required/optional opt-in for structs or packages.
flag: -strict mode
In strict mode, the linter will disallow
var
-based instantiations of structs with required fields. (Pointers would be allowed.) The argument for that being that withvar x Foo
is equivalent tox := Foo{}
, and that's not allowed if there are any required fields.This would be opt-in.