## File: README.md

---

`golangci-lint` is a fast Go linters runner.

It runs linters in parallel, uses caching, supports YAML configuration,
integrates with all major IDEs, and includes over a hundred linters.

## Install `golangci-lint`

- [On my machine](https://golangci-lint.run/docs/welcome/install/local);
- [On CI/CD systems](https://golangci-lint.run/docs/welcome/install/ci).

## Documentation

Documentation is hosted at https://golangci-lint.run.

## Social Networks

[](https://gophers.slack.com/archives/CS0TBRKPC)
[](https://fosstodon.org/@golangcilint)
[](https://bsky.app/profile/golangci-lint.run)
[](https://twitter.com/golangci)

## Support Us

`golangci-lint` is a free and open-source project built by volunteers.

If you value it, consider supporting us, we appreciate it! :heart:

[](https://donate.golangci.org)
[](https://golangci-lint.run/docs/product/thanks/)

## Badges


[](/LICENSE)
[](https://github.com/golangci/golangci-lint/releases/latest)
[](https://hub.docker.com/r/golangci/golangci-lint)
[](https://somsubhra.github.io/github-release-stats/?username=golangci&repository=golangci-lint)

## Contributors

This project exists thanks to all the people who contribute. [How to contribute](https://golangci-lint.run/docs/contributing/).

<a href="https://github.com/golangci/golangci-lint/graphs/contributors">
  
</a>

## Sponsors

&nbsp;

  <a href="https://www.jetbrains.com/go/?utm_source=OSS&utm_medium=referral&utm_campaign=golangci" target="_blank">
    
      
      
      
    
  </a>

---

## File: docs/src/docs/contributing/architecture.mdx

---
title: Architecture
---

import ResponsiveContainer from "components/ResponsiveContainer";

There are the following `golangci-lint` execution steps:

<ResponsiveContainer>

```mermaid
graph LR
    init[Init]
    loadPackages[Load packages]
    runLinters[Run linters]
    postprocess[Postprocess issues]
    print[Print issues]

    init --> loadPackages --> runLinters --> postprocess --> print

```

</ResponsiveContainer>

## Init

The configuration is loaded from file and flags by `config.Loader` inside `PersistentPreRun` (or `PreRun`) of the commands that require configuration.

The linter database (`linterdb.Manager`) is fill based on the configuration:
- The linters ("internals" and plugins) are built by `linterdb.LinterBuilder` and `linterdb.PluginBuilder` builders.
- The configuration is validated by `linterdb.Validator`.

## Load Packages

Loading packages is listing all packages and their recursive dependencies for analysis.
Also, depending on the enabled linters set some parsing of the source code can be performed at this step.

Packages loading starts here:

```go title=pkg/lint/load.go
func (cl *ContextLoader) Load(ctx context.Context, linters []*linter.Config) (*linter.Context, error) {
	loadMode := cl.findLoadMode(linters)
	pkgs, err := cl.loadPackages(ctx, loadMode)
	if err != nil {
		return nil, fmt.Errorf("failed to load packages: %w", err)
	}

	// ...
	ret := &linter.Context{
		// ...
	}
	return ret, nil
}
```

First, we find a load mode as union of load modes for all enabled linters.
We use [go/packages](https://pkg.go.dev/golang.org/x/tools/go/packages) for packages loading and use it's enum `packages.Need*` for load modes.
Load mode sets which data does a linter needs for execution.

A linter that works only with AST need minimum of information: only filenames and AST. There is no need for
packages dependencies or type information. AST is built during `go/analysis` execution to reduce memory usage.
Such AST-based linters are configured with the following code:

```go title=pkg/lint/linter/config.go
func (lc *Config) WithLoadFiles() *Config {
	lc.LoadMode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
	return lc
}
```

If a linter uses `go/analysis` and needs type information, we need to extract more data by `go/packages`:

```go title=pkg/lint/linter/config.go
func (lc *Config) WithLoadForGoAnalysis() *Config {
	lc = lc.WithLoadFiles()
	lc.LoadMode |= packages.NeedImports | packages.NeedDeps | packages.NeedExportFile | packages.NeedTypesSizes
	lc.IsSlow = true
	return lc
}
```

After finding a load mode, we run `go/packages`: the library get list of dirs (or `./...` as the default value) as input
and outputs list of packages and requested information about them: filenames, type information, AST, etc.

## Run Linters

First, we need to find all enabled linters. All linters are registered here:

```go title=pkg/lint/lintersdb/builder_linter.go
func (b LinterBuilder) Build(cfg *config.Config) []*linter.Config {
	// ...
	return []*linter.Config{
		// ...
		linter.NewConfig(golinters.NewBodyclose()).
			WithSince("v1.18.0").
			WithLoadForGoAnalysis().
			WithPresets(linter.PresetPerformance, linter.PresetBugs).
			WithURL("https://github.com/timakin/bodyclose"),
		// ...
		linter.NewConfig(golinters.NewGovet(govetCfg)).
			WithEnabledByDefault().
			WithSince("v1.0.0").
			WithLoadForGoAnalysis().
			WithPresets(linter.PresetBugs, linter.PresetMetaLinter).
			WithAlternativeNames("vet", "vetshadow").
			WithURL("https://pkg.go.dev/cmd/vet"),
		// ...
	}
}
```

We filter requested in config and command-line linters in `EnabledSet`:

```go title=pkg/lint/lintersdb/manager.go
func (m *Manager) GetEnabledLintersMap() (map[string]*linter.Config, error)
```

We merge enabled linters into one `MetaLinter` to improve execution time if we can:

```go titlepkg/lint/lintersdb/manager.go
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters into a fewer number of linters.
// E.g. some go/analysis linters can be optimized into one metalinter for data reuse and speed up.
func (m *Manager) GetOptimizedLinters() ([]*linter.Config, error) {
	// ...
	m.combineGoAnalysisLinters(resultLintersSet)
	// ...
}
```

The `MetaLinter` just stores all merged linters inside to run them at once:

```go title=pkg/golinters/goanalysis/metalinter.go
type MetaLinter struct {
	linters              []*Linter
	analyzerToLinterName map[*analysis.Analyzer]string
}
```

Currently, all linters except `unused` can be merged into this meta linter.
The `unused` isn't merged because it has high memory usage.

Linters execution starts in `runAnalyzers`.
It's the most complex part of the `golangci-lint`.
We use custom [go/analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) runner there.
It runs as much as it can in parallel. It lazy-loads as much as it can to reduce memory usage.
Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory.

We don't use existing [multichecker](https://pkg.go.dev/golang.org/x/tools/go/analysis/multichecker) because
it doesn't use caching and doesn't have some important performance optimizations.

All found by linters issues are represented with `result.Issue` struct:

```go title=pkg/result/issue.go
type Issue struct {
	FromLinter string
	Text       string

	Severity string

	// Source lines of a code with the issue to show
	SourceLines []string

	// If we know how to fix the issue we can provide replacement lines
	Replacement *Replacement

	// Pkg is needed for proper caching of linting results
	Pkg *packages.Package `json:"-"`

	LineRange *Range `json:",omitempty"`

	Pos token.Position

	// HunkPos is used only when golangci-lint is run over a diff
	HunkPos int `json:",omitempty"`

	// If we are expecting a nolint (because this is from nolintlint), record the expected linter
	ExpectNoLint         bool
	ExpectedNoLintLinter string
}
```

## Postprocess Issues

We have an abstraction of `result.Processor` to postprocess found issues:

```sh
$ tree -L 1 ./pkg/result/processors/
./pkg/result/processors/
./pkg/result/processors/
├── autogenerated_exclude.go
├── autogenerated_exclude_test.go
├── base_rule.go
├── cgo.go
├── diff.go
├── exclude.go
├── exclude_rules.go
├── exclude_rules_test.go
├── exclude_test.go
├── filename_unadjuster.go
├── fixer.go
├── identifier_marker.go
├── identifier_marker_test.go
├── issues.go
├── max_from_linter.go
├── max_from_linter_test.go
├── max_per_file_from_linter.go
├── max_per_file_from_linter_test.go
├── max_same_issues.go
├── max_same_issues_test.go
├── nolint.go
├── nolint_test.go
├── path_prefixer.go
├── path_prefixer_test.go
├── path_prettifier.go
├── path_shortener.go
├── processor.go
├── processor_test.go
├── severity_rules.go
├── severity_rules_test.go
├── skip_dirs.go
├── skip_files.go
├── skip_files_test.go
├── sort_results.go
├── sort_results_test.go
├── source_code.go
├── testdata
├── uniq_by_line.go
└── uniq_by_line_test.go
```

The abstraction is simple:

```go title=pkg/result/processors/processor.go
type Processor interface {
	Process(issues []result.Issue) ([]result.Issue, error)
	Name() string
	Finish()
}
```

A processor can hide issues (`nolint`, `exclude`) or change issues (`path_shortener`).

## Print Issues

We have an abstraction for printing found issues.

```sh
$ tree -L 1 ./pkg/printers/
./pkg/printers/
├── checkstyle.go
├── checkstyle_test.go
├── codeclimate.go
├── codeclimate_test.go
├── github.go
├── github_test.go
├── html.go
├── html_test.go
├── json.go
├── json_test.go
├── junitxml.go
├── junitxml_test.go
├── printer.go
├── tab.go
├── tab_test.go
├── teamcity.go
├── teamcity_test.go
├── text.go
└── text_test.go
```

Needed printer is selected by command line option `--out-format`.

---

## File: docs/src/docs/contributing/debug.mdx

---
title: Debugging
---

You can see a verbose output of linter by using `-v` option.

```bash
golangci-lint run -v
```

If you would like to see more detailed logs you can use the environment variable `GL_DEBUG`.
Its value is a list of debug tags.

The existing debug tags are documented in the following file: https://github.com/golangci/golangci-lint/blob/HEAD/pkg/logutils/logutils.go

For example:

```bash
GL_DEBUG="loader,gocritic" golangci-lint run
```

```bash
GL_DEBUG="loader,env" golangci-lint run
```

---

## File: docs/src/docs/contributing/faq.mdx

---
title: Contributing FAQ
---

## How to add a new open-source linter to `golangci-lint`

See [there](/contributing/new-linters#how-to-add-a-public-linter-to-golangci-lint).

## How to add a new private linter to `golangci-lint`

See [there](/contributing/new-linters#how-to-add-a-private-linter-to-golangci-lint).

## How to update an existing linter

We use [Dependabot](https://github.com/golangci/golangci-lint/blob/HEAD/.github/dependabot.yml) to update dependencies, including linters.
The updates happen at least automatically once a week (Sunday 11am UTC).

No pull requests to update a linter will be accepted unless you are the author of the linter and specific changes are required.

## How to add a configuration option to an existing linter

Add a new field to the [linter settings struct](https://github.com/golangci/golangci-lint/blob/HEAD/pkg/config/linters_settings.go).
Document it in [.golangci.next.reference.yml](https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.next.reference.yml).
Pass it to the linter.

## How to see `INFO` or `DEBUG` logs

See [tutorial about debugging](/contributing/debug/).

---

## File: docs/src/docs/contributing/new-linters.mdx

---
title: New linters
---

## How to write a linter

Use `go/analysis` and take a look at [this tutorial](https://disaev.me/p/writing-useful-go-analysis-linter/):
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:

1. Implement functional tests for the linter:
    - Add one file into directory `pkg/golinters/{yourlintername}/testdata/`.
2. Add a new file `pkg/golinters/{yourlintername}/{yourlintername}.go`.
   Other linters implementation can help you.
3. Add the new struct for the linter (which you've implemented in `pkg/golinters/{yourlintername}/{yourlintername}.go`) to the
   list of all supported linters in [`pkg/lint/lintersdb/builder_linter.go`](https://github.com/golangci/golangci-lint/blob/HEAD/pkg/lint/lintersdb/builder_linter.go)
   to the method `LinterBuilder.Build`.
    - Add `WithSince("next_version")`, where `next_version` must be replaced by the next minor version. (ex: v1.2.0 if the current version is v1.1.0)
4. Find out what options do you need to configure for the linter.
   For example, `nakedret` has only 1 option: [`max-func-lines`](https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.reference.yml).
   Choose default values to not being annoying for users of golangci-lint. Add configuration options to:
    - [.golangci.next.reference.yml](https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.next.reference.yml): the example of a configuration file.
      You can also add them to [.golangci.yml](https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.yml)
      if you think that this project needs not default values.
    - [config struct](https://github.com/golangci/golangci-lint/blob/HEAD/pkg/config/config.go):
      don't forget about `mapstructure` tags for proper configuration files parsing.
5. Take a look at the example of [pull requests with new linter support](https://github.com/golangci/golangci-lint/pulls?q=is%3Apr+is%3Amerged+label%3A%22linter%3A+new%22).
6. Run the tests:
    ```bash
    go run ./cmd/golangci-lint/ run --no-config --disable-all --enable={yourlintername} ./pkg/golinters/{yourlintername}/testdata/{yourlintername}.go
    ```

## 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 2 plugin systems:

1. [Module Plugin System](/plugins/module-plugins)
2. [Go Plugin System](/plugins/go-plugins)

---

## File: docs/src/docs/contributing/quick-start.mdx

---
title: Contributing Quick Start
---

1. [Contributing workflow](/contributing/workflow/): about our workflow and environment setup.
2. [Architecture of golangci-lint](/contributing/architecture/)
3. [How to add new linters](/contributing/new-linters/)
4. [Debugging golangci-lint](/contributing/debug/)
5. [Contributing FAQ](/contributing/faq/)
6. [Contributing to the website](/contributing/website/)

---

## File: docs/src/docs/contributing/website.mdx

---
title: Website architecture
---

## Technology

We use [Gatsby](https://www.gatsbyjs.org/) for static site generation because sites built with it are very fast.

This framework uses React and JavaScript/TypeScript.

## Source Code

The website lives in `docs/` directory of [golangci-lint repository](https://github.com/golangci/golangci-lint).

## Theme

Initially the site is based on [@rocketseat](https://github.com/jpedroschmitz/rocketdocs) theme.
Later we've merged its code into `src/@rocketseat` because we needed too much changes
and [gatsby shadowing](https://www.gatsbyjs.org/docs/themes/shadowing/) doesn't allow shadowing `gatsby-node.js` or `gatsby-config.js`.

## Navigation

Left menu is configured in `src/config/sidebar.yml`.

## Articles

Articles are located in `src/docs/` in `*.mdx` files. [MDX](https://mdxjs.com/getting-started/gatsby) is Markdown
allowing to use `React` components.

## Templating

We use templates like `{.SomeField}` inside our `mdx` files.
There templates are expanded by running `make website_expand_templates` in the root of the repository.
It runs script `scripts/website/expand_templates/` that rewrites `mdx` files with replaced templates.

## Hosting

We use GitHub Pages as static website hosting and CD.

GitHub deploys the website to production after merging anything to a `master` branch.

## Local Testing

Install Node.js (v20 or newer).

Run:

```sh
npm install --legacy-peer-deps
npm run start
```

And navigate to `http://localhost:8000` after successful Gatsby build.
There is no need to restart Gatsby server almost for all changes: it supports hot reload.
Also, there is no need to refresh a webpage: hot reload updates changed content on the open page.

## Website Build

To do it run:

```sh
go run ./scripts/website/expand_templates/
```

---

## File: docs/src/docs/contributing/workflow.mdx

---
title: Contributing Workflow
---

# Contributing

By participating in this project, you agree to abide our [code of conduct](https://github.com/golangci/golangci-lint?tab=coc-ov-file).

## Set up your machine

`golangci-lint` is written in [Go](https://go.dev).

Prerequisites:

- `make`
- [Go](https://go.dev/doc/install)

Fork and clone [golangci-lint](https://github.com/golangci/golangci-lint) repository.

A good way of making sure everything is all right is running the following:

```sh
make build
./golangci-lint run -v
```

## Test your change

When you are satisfied with the changes, we suggest you run:

```sh
$ make test
```

Which runs all the linters and tests.

## Create or update parameters for docs

Add your new or updated parameters to `.golangci.next.reference.yml` so they will be shown in the docs

## Submit a pull request

Push your branch to your `golangci-lint` fork and open a pull request against the
`master` branch.

## Pull request checks

First, please, accept [CLA](https://gist.github.com/jirfag/26a39fd375da84b2d5ad4296fecb0668) - [CLA assistant](https://cla-assistant.io/) will make a comment on the pull request about it.

Also, we run a few checks in CI by using GitHub Actions, you can see them [here](https://github.com/golangci/golangci-lint/blob/HEAD/.github/workflows/pr.yml).

## New releases

First, see [our versioning policy](/product/roadmap/#versioning-policy).

To make a new release create a tag `vx.y.z`. Don't forget to add zero patch version for a new minor release, e.g. `v1.99.0`.
A GitHub Action [workflow](https://github.com/golangci/golangci-lint/blob/HEAD/.github/workflows/tag.yml) will start building and publishing release after that.

After making a release you need to update
GitHub [Action config](https://github.com/golangci/golangci-lint/blob/HEAD/assets/github-action-config.json) by running:
```sh
make assets/github-action-config.json
```

---

## File: docs/src/docs/plugins/go-plugins.mdx

---
title: Go Plugin System
---

Private linters can be added through [Go's plugin system](https://pkg.go.dev/plugin).

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

## 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:
```go
func New(conf any) ([]*analysis.Analyzer, error) {
	// ...
}
```

See [plugin/example.go](https://github.com/golangci/example-plugin-linter/blob/HEAD/plugin/example.go) for more info.

To build the plugin, from the root project directory, run:
```bash
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`.

## 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](https://github.com/golangci/example-plugin-linter).

If you're looking for instructions on how to configure your own custom linter, they can be found further down.

1. If the project you want to lint does not have one already, copy the [.golangci.yml](https://github.com/golangci/golangci-lint/blob/HEAD/.golangci.yml) to the root directory.
2. Adjust the YAML to appropriate `linters-settings.custom` entries as so:
  ```yaml title=.golangci.yml
  linters-settings:
    custom:
      example:
        path: /example.so
        original-url: github.com/golangci/example-linter
        settings: # Settings are optional.
          one: Foo
          two:
            - name: Bar
          three:
            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.

---

## File: docs/src/docs/plugins/module-plugins.mdx

---
title: Module Plugin System
---

An example linter can be found at [here](https://github.com/golangci/example-plugin-module-linter).

## The Automatic Way

- Define your building configuration into `.custom-gcl.yml`.
- Run the command `golangci-lint custom` (or `golangci-lint custom -v` to have logs).
- Define the plugin inside the `linters-settings.custom` section with the type `module`.
- Run your custom version of golangci-lint.

Requirements:
- Go
- git

### Configuration Example

```yaml title=.custom-gcl.yml
version: {.LatestVersion}
plugins:
  # a plugin from a Go proxy
  - module: 'github.com/golangci/plugin1'
    import: 'github.com/golangci/plugin1/foo'
    version: v1.0.0

  # a plugin from local source
  - module: 'github.com/golangci/plugin2'
    path: /my/local/path/plugin2
```

```yaml title=.golangci.yml
linters-settings:
  custom:
    foo:
      type: "module"
      settings:
        message: hello

linters:
  disable-all: true
  enable:
    - foo
```

## The Manual Way

- Add a blank-import of your module inside `cmd/golangci-lint/plugins.go`.
- Run `go mod tidy` (the module containing the plugin will be imported).
- Run `make build`.
- Define the plugin inside the configuration `linters-settings.custom` section with the type `module`.
- Run your custom version of golangci-lint.

### Configuration Example

```yaml title=.golangci.yml
linters-settings:
  custom:
    foo:
      type: "module"
      settings:
        message: hello

linters:
  disable-all: true
  enable:
    - foo
```

## Reference

The configuration file can be validated with the JSON Schema: https://golangci-lint.run/jsonschema/custom-gcl.jsonschema.json

```yml
{ .CustomGCLReference }
```