Table of Contents
How to write a linter
Use go/analysis
and take a look at this tutorial:
it shows how to write go/analysis
linter from scratch and integrate it into golangci-lint
.
How to add a public linter to golangci-lint
You need to implement a new linter using go/analysis
API.
We don't accept non go/analysis
linters.
After that:
- Implement functional tests for the linter:
- Add one file into directory
test/testdata
. - Run the test to ensure that test fails:T=yourlintername.go make test_linters
- Run:go run ./cmd/golangci-lint/ run --no-config --disable-all --enable=yourlintername ./test/testdata/yourlintername.go
- Add one file into directory
- Add a new file
pkg/golinters/{yourlintername}.go
. Look at other linters in this directory. Implement linter integration and check that test passes. - Add the new struct for the linter (which you've implemented in
pkg/golinters/{yourlintername}.go
) to the list of all supported linters inpkg/lint/lintersdb/manager.go
to the functionGetAllSupportedLinterConfigs
.- Add
WithSince("next_version")
, wherenext_version
must be replaced by the next minor version. (ex: v1.2.0 if the current version is v1.1.0)
- Add
- Find out what options do you need to configure for the linter.
For example,
nakedret
has only 1 option:max-func-lines
. Choose default values to not being annoying for users of golangci-lint. Add configuration options to:- .golangci.reference.yml - the example of a configuration file. You can also add them to .golangci.yml if you think that this project needs not default values.
- config struct -
don't forget about
mapstructure
tag for proper configuration files parsing by pflag.
- Take a look at the example of pull requests with new linter support.
How to add a private linter to golangci-lint
Some people and organizations may choose to have custom-made linters run as a part of golangci-lint
.
Typically, these linters can't be open-sourced or too specific.
Such linters can be added through Go's plugin library.
For a private linter (which acts as a plugin) to work properly, the plugin as well as the golangci-lint binary needs to be built for the same environment.
CGO_ENABLED
is another requirement.
This means that golangci-lint
needs to be built for whatever machine you intend to run it on
(cloning the golangci-lint repository and running a CGO_ENABLED=1 make build
should do the trick for your machine).
Configure a Plugin
If you already have a linter plugin available, you can follow these steps to define its usage in a projects .golangci.yml
file.
An example linter can be found at here.
If you're looking for instructions on how to configure your own custom linter, they can be found further down.
- If the project you want to lint does not have one already, copy the .golangci.yml to the root directory.
- Adjust the yaml to appropriate
linters-settings:custom
entries as so:linters-settings:custom:example:path: /example.sodescription: The description of the linteroriginal-url: github.com/golangci/example-lintersettings: # Settings are optional.one: Footwo:- name: Barthree:name: Bar
That is all the configuration that is required to run a custom linter in your project.
Custom linters are disabled by default, and are not enabled when linters.enable-all
is specified.
They can be enabled by adding them the linters.enable
list, or providing the enabled option on the command line (golangci-lint run -Eexample
).
The configuration inside the settings
field of linter have some limitations (there are NOT related to the plugin system itself):
we use Viper to handle the configuration but Viper put all the keys in lowercase, and .
cannot be used inside a key.
Create a Plugin
Your linter must provide one or more golang.org/x/tools/go/analysis.Analyzer
structs.
Your project should also use go.mod
.
All versions of libraries that overlap golangci-lint
(including replaced libraries) MUST be set to the same version as golangci-lint
.
You can see the versions by running go version -m golangci-lint
.
You'll also need to create a Go file like plugin/example.go
.
This file MUST be in the package main
, and MUST define an exposed function called New
with the following signature:
func New(conf any) ([]*analysis.Analyzer, error) {// ...}
See plugin/example.go for more info.
To build the plugin, from the root project directory, run:
go build -buildmode=plugin plugin/example.go
This will create a plugin *.so
file that can be copied into your project or another well known location for usage in golangci-lint
.