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 enabled by default, but abide by the same rules as other linters.
If the disable all option is specified either on command line or in .golang.yml
files linters.disable-all: true
, custom linters will be disabled;
they can be re-enabled by adding them to 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
.