diff --git a/README.md b/README.md
index 253f8aa9..5646976c 100644
--- a/README.md
+++ b/README.md
@@ -77,16 +77,17 @@ GolangCI-Lint can be used with zero configuration. By default next linters are e
```
$ golangci-lint linters
Enabled by default linters:
-govet: Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
-errcheck: Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
-staticcheck: Staticcheck is go vet on steroids, applying a ton of static analysis checks
-unused: Checks Go code for unused constants, variables, functions and types
-gosimple: Linter for Go source code that specialises on simplifying code
-gas: Inspects source code for security problems
-structcheck: Finds unused struct fields
-varcheck: Finds unused global variables and constants
-ineffassign: Detects when assignments to existing variables are not used
-deadcode: Finds unused code
+govet: Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: true]
+errcheck: Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: false]
+staticcheck: Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false]
+unused: Checks Go code for unused constants, variables, functions and types [fast: false]
+gosimple: Linter for Go source code that specializes in simplifying a code [fast: false]
+gas: Inspects source code for security problems [fast: false]
+structcheck: Finds an unused struct fields [fast: false]
+varcheck: Finds unused global variables and constants [fast: false]
+ineffassign: Detects when assignments to existing variables are not used [fast: true]
+deadcode: Finds unused code [fast: false]
+typecheck: Like the front-end of a Go compiler, parses and type-checks Go code [fast: false]
```
and next linters are disabled by default:
@@ -94,17 +95,17 @@ and next linters are disabled by default:
$ golangci-lint linters
...
Disabled by default linters:
-golint: Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
-interfacer: Linter that suggests narrower interface types
-unconvert: Remove unnecessary type conversions
-dupl: Tool for code clone detection
-goconst: Finds repeated strings that could be replaced by a constant
-gocyclo: Computes and checks the cyclomatic complexity of functions
-gofmt: Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
-goimports: Goimports does everything that gofmt does. Additionally it checks unused imports
-maligned: Tool to detect Go structs that would take less memory if their fields were sorted
-megacheck: 3 sub-linters in one: unused, gosimple and staticcheck
-depguard: Go linter that checks if package imports are in a list of acceptable packages
+golint: Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true]
+interfacer: Linter that suggests narrower interface types [fast: false]
+unconvert: Remove unnecessary type conversions [fast: false]
+dupl: Tool for code clone detection [fast: true]
+goconst: Finds repeated strings that could be replaced by a constant [fast: true]
+gocyclo: Computes and checks the cyclomatic complexity of functions [fast: true]
+gofmt: Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true]
+goimports: Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true]
+maligned: Tool to detect Go structs that would take less memory if their fields were sorted [fast: false]
+megacheck: 3 sub-linters in one: unused, gosimple and staticcheck [fast: false]
+depguard: Go linter that checks if package imports are in a list of acceptable packages [fast: false]
```
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
@@ -174,30 +175,30 @@ golangci-lint linters
```
## Enabled By Default Linters
-- [go vet](https://golang.org/cmd/vet/) - Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
-- [errcheck](https://github.com/kisielk/errcheck): Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
-- [staticcheck](https://staticcheck.io/): Staticcheck is a go vet on steroids, applying a ton of static analysis checks
-- [unused](https://github.com/dominikh/go-tools/tree/master/cmd/unused): Checks Go code for unused constants, variables, functions, and types
-- [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple): Linter for Go source code that specializes in simplifying a code
-- [gas](https://github.com/GoASTScanner/gas): Inspects source code for security problems
-- [structcheck](https://github.com/opennota/check): Finds an unused struct fields
-- [varcheck](https://github.com/opennota/check): Finds unused global variables and constants
-- [ineffassign](https://github.com/gordonklaus/ineffassign): Detects when assignments to existing variables are not used
-- [deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode): Finds unused code
-- typecheck: Like the front-end of a Go compiler, parses and type-checks Go code. Similar to [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype).
+- [govet](https://golang.org/cmd/vet/) - Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+- [errcheck](https://github.com/kisielk/errcheck) - Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+- [staticcheck](https://staticcheck.io/) - Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+- [unused](https://github.com/dominikh/go-tools/tree/master/cmd/unused) - Checks Go code for unused constants, variables, functions and types
+- [gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple) - Linter for Go source code that specializes in simplifying a code
+- [gas](https://github.com/GoASTScanner/gas) - Inspects source code for security problems
+- [structcheck](https://github.com/opennota/check) - Finds an unused struct fields
+- [varcheck](https://github.com/opennota/check) - Finds unused global variables and constants
+- [ineffassign](https://github.com/gordonklaus/ineffassign) - Detects when assignments to existing variables are not used
+- [deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode) - Finds unused code
+- typecheck - Like the front-end of a Go compiler, parses and type-checks Go code
## Disabled By Default Linters (`-E/--enable`)
-- [golint](https://github.com/golang/lint): Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
-- [interfacer](https://github.com/mvdan/interfacer): Linter that suggests narrower interface types
-- [unconvert](https://github.com/mdempsky/unconvert): Remove unnecessary type conversions
-- [dupl](https://github.com/mibk/dupl): Tool for code clone detection
-- [goconst](https://github.com/jgautheron/goconst): Finds repeated strings that could be replaced by a constant
-- [gocyclo](https://github.com/alecthomas/gocyclo): Computes and checks the cyclomatic complexity of functions
-- [gofmt](https://golang.org/cmd/gofmt/): Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
-- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports): Goimports does everything that gofmt does. Additionally it checks unused imports
-- [maligned](https://github.com/mdempsky/maligned): Tool to detect Go structs that would take less memory if their fields were sorted
-- [megacheck](https://github.com/dominikh/go-tools/tree/master/cmd/megacheck): 3 sub-linters in one: unused, gosimple and staticcheck
-- [depguard](https://github.com/OpenPeeDeeP/depguard): Go linter that checks if package imports are in a list of acceptable packages
+- [golint](https://github.com/golang/lint) - Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+- [interfacer](https://github.com/mvdan/interfacer) - Linter that suggests narrower interface types
+- [unconvert](https://github.com/mdempsky/unconvert) - Remove unnecessary type conversions
+- [dupl](https://github.com/mibk/dupl) - Tool for code clone detection
+- [goconst](https://github.com/jgautheron/goconst) - Finds repeated strings that could be replaced by a constant
+- [gocyclo](https://github.com/alecthomas/gocyclo) - Computes and checks the cyclomatic complexity of functions
+- [gofmt](https://golang.org/cmd/gofmt/) - Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) - Goimports does everything that gofmt does. Additionally it checks unused imports
+- [maligned](https://github.com/mdempsky/maligned) - Tool to detect Go structs that would take less memory if their fields were sorted
+- [megacheck](https://github.com/dominikh/go-tools/tree/master/cmd/megacheck) - 3 sub-linters in one: unused, gosimple and staticcheck
+- [depguard](https://github.com/OpenPeeDeeP/depguard) - Go linter that checks if package imports are in a list of acceptable packages
# Configuration
## Command-Line Options
@@ -306,7 +307,6 @@ There is a [`.golangci.yml`](https://github.com/golangci/golangci-lint/blob/mast
It's a [.golangci.yml](https://github.com/golangci/golangci-lint/blob/master/.golangci.yml) of this repo: we enable more linters than by default and make their settings more strict:
```yaml
run:
- deadline: 30s
tests: true
linters-settings:
@@ -410,26 +410,19 @@ Thanks to [alecthomas/gometalinter](https://github.com/alecthomas/gometalinter)
Thanks to [bradleyfalzon/revgrep](https://github.com/bradleyfalzon/revgrep) for cool diff tool.
Thanks to developers and authors of used linters:
-- [golang/vet](https://golang.org/cmd/vet/)
-- [kisielk/errcheck](https://github.com/kisielk/errcheck)
-- [staticcheck](https://staticcheck.io/)
-- [dominikh/go-tools/unused](https://github.com/dominikh/go-tools/tree/master/cmd/unused)
-- [dominikh/go-tools/gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple)
-- [GoASTScanner/gas](https://github.com/GoASTScanner/gas)
-- [opennota/check](https://github.com/opennota/check)
-- [gordonklaus/ineffassign](https://github.com/gordonklaus/ineffassign)
-- [remyoudompheng/go-misc/deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode)
-- [golang/lint](https://github.com/golang/lint)
-- [mvdan/interfacer](https://github.com/mvdan/interfacer)
-- [mdempsky/unconvert](https://github.com/mdempsky/unconvert)
-- [mibk/dupl](https://github.com/mibk/dupl)
-- [jgautheron/goconst](https://github.com/jgautheron/goconst)
-- [alecthomas/gocyclo](https://github.com/alecthomas/gocyclo)
-- [golang/gofmt](https://golang.org/cmd/gofmt/)
-- [golang/x/tools/goimports](https://godoc.org/golang.org/x/tools/cmd/goimports)
-- [mdempsky/maligned](https://github.com/mdempsky/maligned)
-- [dominikh/go-tools/megacheck](https://github.com/dominikh/go-tools/tree/master/cmd/megacheck)
-- [OpenPeeDeeP/depguard](https://github.com/OpenPeeDeeP/depguard)
+- [kisielk](https://github.com/kisielk)
+- [golang](https://github.com/golang)
+- [dominikh](https://github.com/dominikh)
+- [GoASTScanner](https://github.com/GoASTScanner)
+- [opennota](https://github.com/opennota)
+- [mvdan](https://github.com/mvdan)
+- [mdempsky](https://github.com/mdempsky)
+- [gordonklaus](https://github.com/gordonklaus)
+- [mibk](https://github.com/mibk)
+- [jgautheron](https://github.com/jgautheron)
+- [remyoudompheng](https://github.com/remyoudompheng)
+- [alecthomas](https://github.com/alecthomas)
+- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
# Future Plans
1. Upstream all changes of forked linters.
diff --git a/README.md.tmpl b/README.md.tmpl
new file mode 100644
index 00000000..5666bb74
--- /dev/null
+++ b/README.md.tmpl
@@ -0,0 +1,360 @@
+# GolangCI-Lint
+[](https://travis-ci.com/golangci/golangci-lint)
+
+GolangCI-Lint is a linters aggregator. It's fast: on average [5 times faster](#performance) than gometalinter. It's [easy to integrate and use](#issues-options), has [nice output](#quick-start) and has a minimum number of false positives.
+
+GolangCI-Lint has [integrations](#ide-integrations) with VS Code, GNU Emacs, Sublime Text.
+
+Sponsored by [GolangCI.com](https://golangci.com): SaaS service for running linters on Github pull requests. Free for Open Source.
+
+
+
+ * [Install](#install)
+ * [Demo](#demo)
+ * [Quick Start](#quick-start)
+ * [Comparison](#comparison)
+ * [golangci-lint
vs gometalinter
](#golangci-lint-vs-gometalinter)
+ * [golangci-lint
vs Run Needed Linters Manually](#golangci-lint-vs-run-needed-linters-manually)
+ * [Performance](#performance)
+ * [Comparison with gometalinter](#comparison-with-gometalinter)
+ * [Supported Linters](#supported-linters)
+ * [Enabled By Default Linters](#enabled-by-default-linters)
+ * [Disabled By Default Linters (-E/--enable)](#disabled-by-default-linters--e--enable)
+ * [Configuration](#configuration)
+ * [Command-Line Options](#command-line-options)
+ * [Configuration File](#configuration-file)
+ * [False Positives](#false-positives)
+ * [IDE integrations](#ide-integrations)
+ * [Internals](#internals)
+ * [FAQ](#faq)
+ * [Thanks](#thanks)
+ * [Future Plans](#future-plans)
+ * [Contact Information](#contact-information)
+
+# Install
+Recommended way to install is:
+```bash
+go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
+```
+
+You can also install it by brew:
+```bash
+brew install golangci/tap/golangci-lint
+```
+
+For CI you can use fast local installation:
+```bash
+curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh
+```
+
+Check the [releases page](https://github.com/golangci/golangci-lint/releases) to fix the version.
+
+# Demo
+Example of output:
+
+
+Short 1.5 min video demo of analyzing [beego](https://github.com/astaxie/beego).
+[](https://asciinema.org/a/1a1qaEXMlOSeRyvASbnuFomah)
+
+# Quick Start
+To run golangci-lint execute:
+```bash
+golangci-lint run
+```
+
+It's an equivalent of executing:
+```bash
+golangci-lint run ./...
+```
+
+You can choose which directories and files to analyze:
+```bash
+golangci-lint run dir1 dir2/... dir3/file1.go
+```
+Directories are analyzed NOT recursively, to analyze them recursively append `/...` to their path.
+
+GolangCI-Lint can be used with zero configuration. By default next linters are enabled:
+```
+$ golangci-lint linters
+{{.LintersCommandOutputEnabledOnly}}
+```
+
+and next linters are disabled by default:
+```
+$ golangci-lint linters
+...
+{{.LintersCommandOutputDisabledOnly}}
+```
+
+Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
+```bash
+$ golangci-lint run --disable-all -E errcheck
+```
+
+# Comparison
+## `golangci-lint` vs `gometalinter`
+GolangCI-Lint was created to fix next issues with `gometalinter`:
+1. Slow work: `gometalinter` usually works for minutes in average projects. **GolangCI-Lint works [2-7x times faster](#performance)** by [reusing work](#internals).
+2. Huge memory consumption: parallel linters don't share the same program representation and can eat `n` times more memory (`n` - concurrency). GolangCI-Lint fixes it by sharing representation and **eats 1.35x less memory**.
+3. Can't set honest concurrency: if you set it to `n` it can take up to `n*n` threads because of forced threads in specific linters. `gometalinter` can't do anything about it, because it runs linters as black-boxes in forked processes. In GolangCI-Lint we run all linters in one process and fully control them. Configured concurrency will be honest.
+This issue is important because often you'd like to set concurrency to CPUs count minus one to **not freeze your PC** and be able to work on it while analyzing code.
+4. Lack of nice output. We like how compilers `gcc` and `clang` format their warnings: **using colors, printing of warned line and showing position in line**.
+5. Too many issues. GolangCI-Lint cuts a lot of issues by using default exclude list of common false-positives. Also, it has enabled by default **smart issues processing**: merge multiple issues for one line, merge issues with the same text or from the same linter. All of these smart processors can be configured by the user.
+6. Integration to large codebases. A good way to start using linters in a large project is not to fix all hundreds on existing issues, but setup CI and **fix only issues in new commits**. You can use `revgrep` for it, but it's yet another utility to install and configure. With `golangci-lint` it's much easier: `revgrep` is already built into `golangci-lint` and you can use it with one option (`-n, --new` or `--new-from-rev`).
+7. Installation. With `gometalinter`, you need to run linters installation step. It's easy to forget this step and have stale linters. It also complicates CI setup. GolangCI-Lint requires **no installation of linters**.
+8. **Yaml or toml config**. Gometalinter's JSON isn't convenient for configuration files.
+
+## `golangci-lint` vs Run Needed Linters Manually
+1. It will be much slower because `golangci-lint` runs all linters in parallel and shares 50-80% of linters work.
+2. It will have less control and more false-positives: some linters can't be properly configured without hacks.
+3. It will take more time because of different usages and need of tracking of versions of `n` linters.
+
+# Performance
+Benchmarks were executed on MacBook Pro (Retina, 13-inch, Late 2013), 2,4 GHz Intel Core i5, 8 GB 1600 MHz DDR3. It has 4 cores and concurrency for linters was default: number of cores. Benchmark runs and measures timings automatically, it's code is [here](https://github.com/golangci/golangci-lint/blob/master/test/bench.go) (`BenchmarkWithGometalinter`).
+
+We measure peak memory usage (RSS) by tracking of processes RSS every 5 ms.
+
+## Comparison with gometalinter
+We compare golangci-lint and gometalinter in default mode, but explicitly specify all linters to enable because of small differences in the default configuration.
+```bash
+$ golangci-lint run --no-config --issues-exit-code=0 --deadline=30m \
+ --disable-all --enable=deadcode --enable=gocyclo --enable=golint --enable=varcheck \
+ --enable=structcheck --enable=maligned --enable=errcheck --enable=dupl --enable=ineffassign \
+ --enable=interfacer --enable=unconvert --enable=goconst --enable=gas --enable=megacheck
+$ gometalinter --deadline=30m --vendor --cyclo-over=30 --dupl-threshold=150 \
+ --exclude= --skip=testdata --skip=builtin \
+ --disable-all --enable=deadcode --enable=gocyclo --enable=golint --enable=varcheck \
+ --enable=structcheck --enable=maligned --enable=errcheck --enable=dupl --enable=ineffassign \
+ --enable=interfacer --enable=unconvert --enable=goconst --enable=gas --enable=megacheck
+ ./...
+```
+
+| Repository | GolangCI Time | GolangCI Is Faster than Gometalinter | GolangCI Memory | GolangCI eats less memory than Gometalinter |
+| ---------- | ------------- | ------------------------------------ | --------------- | ------------------------------------------- |
+| gometalinter repo, 4 kLoC | 6s | **6.4x** | 0.7GB | 1.5x |
+| self-repo, 4 kLoC | 12s | **7.5x** | 1.2GB | 1.7x |
+| beego, 50 kLoC | 10s | **4.2x** | 1.4GB | 1.1x |
+| hugo, 70 kLoC | 15s | **6.1x** | 1.6GB | 1.8x |
+| consul, 127 kLoC | 58s | **4x** | 2.7GB | 1.7x |
+| terraform, 190 kLoC | 2m13s | **1.6x** | 4.8GB | 1x |
+| go-ethereum, 250 kLoC | 33s | **5x** | 3.6GB | 1x |
+| go source, 1300 kLoC | 2m45s | **2x** | 4.7GB | 1x |
+
+
+**On average golangci-lint is 4.6 times faster** than gometalinter. Maximum difference is in the
+self-repo: **7.5 times faster**, minimum difference is in terraform source code repo: 1.8 times faster.
+
+On average golangci-lint consumes 1.35 times less memory.
+
+# Supported Linters
+To see a list of supported linters and which linters are enabled/disabled by default execute a command
+```bash
+golangci-lint linters
+```
+
+## Enabled By Default Linters
+{{.EnabledByDefaultLinters}}
+
+## Disabled By Default Linters (`-E/--enable`)
+{{.DisabledByDefaultLinters}}
+
+# Configuration
+## Command-Line Options
+Run next command to see their description and defaults.
+```bash
+golangci-lint run -h
+```
+
+### Run Options
+- `-c, --config` - path to [config file](#configuration-file) if you don't like using default config path `.golangci.(yml|toml|json)`.
+- `-j, --concurrency` - the number of threads used. By default, it's a number of CPUs. Unlike `gometalinter`, it's an honest value, since we do not fork linter processes.
+- `--build-tags` - build tags to take into account.
+- `--issues-exit-code` - exit code if issues were found. The default is `1`.
+- `--deadline` - timeout for running golangci-lint, `1m` by default.
+- `--tests` - analyze `*_test.go` files. It's `false` by default.
+- `-v, --verbose` - enable verbose output. Use this options to see which linters were enabled, to see timings of steps and another helpful information.
+- `--print-resources-usage` - print memory usage and total time elapsed.
+
+### Linters
+- `-E, --enable` - enable specific linter. You can pass option multiple times or use a comma:
+```bash
+golangci-lint run --disable-all -E golint -E govet -E errcheck
+golangci-lint run --disable-all --enable golint,govet,errcheck
+```
+- `-D, --disable` - disable specific linter. Similar to enabling option.
+- `--enable-all` - enable all supported linters.
+- `--disable-all` - disable all supported linters.
+- `-p, --presets` - enable specific presets. To list all presets run
+```bash
+$ golangci-lint linters
+...
+Linters presets:
+bugs: govet, errcheck, staticcheck, gas, megacheck
+unused: unused, structcheck, varcheck, ineffassign, deadcode, megacheck
+format: gofmt, goimports
+style: golint, gosimple, interfacer, unconvert, dupl, goconst, megacheck, depguard
+complexity: gocyclo
+performance: maligned
+```
+Usage example:
+```bash
+$ golangci-lint run -v --disable-all -p bugs,style,complexity,format
+INFO[0000] Active linters: [govet goconst gocyclo gofmt gas dupl goimports megacheck interfacer unconvert errcheck golint]
+```
+- `--fast` - run only fast linters from the enabled set of linters. To find out which linters are fast run `golangci-lint linters`.
+
+### Linters Options
+- `--errcheck.check-type-assertions` - errcheck: check for ignored type assertion results. Disabled by default.
+- `--errcheck.check-blank` - errcheck: check for errors assigned to blank identifier: `_ = errFunc()`. Disabled by default
+- `--govet.check-shadowing` - govet: check for shadowed variables. Disabled by default.
+- `--golint.min-confidence` - golint: minimum confidence of a problem to print it. The default is `0.8`.
+- `--gofmt.simplify` - gofmt: simplify code (`gofmt -s`), enabled by default.
+- `--gocyclo.min-complexity` - gocyclo: a minimal complexity of function to report it. The default is `30` (it's very high limit).
+- `--maligned.suggest-new` - Maligned: print suggested more optimal struct fields ordering. Disabled by default. Example:
+```
+crypto/tls/ticket.go:20: struct of size 64 bytes could be of size 56 bytes:
+struct{
+ masterSecret []byte,
+ certificates [][]byte,
+ vers uint16,
+ cipherSuite uint16,
+ usedOldKey bool,
+}
+```
+- `--dupl.threshold` - dupl: Minimal threshold to detect copy-paste, `150` by default.
+- `--goconst.min-len` - goconst: minimum constant string length, `3` by default.
+- `--goconst.min-occurrences` - goconst: minimum occurences of constant string count to trigger issue. Default is `3`.
+
+### Issues Options
+- `-n, --new` - show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed. It's a super-useful option for integration `golangci-lint` into existing large codebase. It's not practical to fix all existing issues at the moment of integration: much better don't allow issues in new code. Disabled by default.
+- `--new-from-rev` - show only new issues created after specified git revision.
+- `--new-from-patch` - show only new issues created in git patch with the specified file path.
+- `-e, --exclude` - exclude issue by regexp on issue text.
+- `--exclude-use-default` - use or not use default excludes. We tested our linter on large codebases and marked common false positives. By default we ignore common false positives by next regexps:
+ - `Error return value of .((os\.)?std(out|err)\..*|.*Close|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked` - ercheck: almost all programs ignore errors on these functions and in most cases it's ok.
+ - `(should have comment|comment on exported method)` - golint: annoying issues about not having a comment. The rare codebase has such comments.
+ - `G103:` - gas: `Use of unsafe calls should be audited`
+ - `G104:` - gas: `disable what errcheck does: it reports on Close etc`
+ - `G204:` - gas: `Subprocess launching should be audited: too lot false - positives`
+ - `G301:` - gas: `Expect directory permissions to be 0750 or less`
+ - `G302:` - gas: `Expect file permissions to be 0600 or less`
+ - `G304:` - gas: ``Potential file inclusion via variable: `src, err := ioutil.ReadFile(filename)`.``
+
+ - `(possible misuse of unsafe.Pointer|should have signature)` - common false positives by govet.
+ - `ineffective break statement. Did you mean to break out of the outer loop` - megacheck: developers tend to write in C-style with an explicit `break` in a switch, so it's ok to ignore.
+
+ Use option `--exclude-use-default=false` to disable these default exclude regexps.
+- `--max-issues-per-linter` - maximum issues count per one linter. Set to `0` to disable. The default value is `50` to not being annoying.
+- `--max-same-issues` - maximum count of issues with the same text. Set to 0 to disable. The default value is `3` to not being annoying.
+
+### Output Options
+- `--out-format` - format of output: `colored-line-number|line-number|json`, default is `colored-line-number`.
+- `--print-issued-lines` - print line of source code where the issue occurred. Enabled by default.
+- `--print-linter-name` - print linter name in issue line. Enabled by default.
+- `--print-welcome` - print welcome message. Enabled by default.
+
+## Configuration File
+GolangCI-Lint looks for next config paths in the current directory:
+- `.golangci.yml`
+- `.golangci.toml`
+- `.golangci.json`
+
+Configuration options inside the file are identical to command-line options.
+There is a [`.golangci.yml`](https://github.com/golangci/golangci-lint/blob/master/.golangci.example.yml) with all supported options.
+
+It's a [.golangci.yml](https://github.com/golangci/golangci-lint/blob/master/.golangci.yml) of this repo: we enable more linters than by default and make their settings more strict:
+```yaml
+{{.GolangciYaml}}
+```
+
+# False Positives
+False positives are inevitable, but we did our best to reduce their count. For example, we have an enabled by default set of [exclude patterns](#issues-options). If false positive occurred you have next choices:
+1. Exclude issue by text using command-line option `-e` or config option `issues.exclude`. It's helpful when you decided to ignore all issues of this type.
+2. Exclude this one issue by using special comment `// nolint[:linter1,linter2,...]` on issued line.
+Comment `// nolint` disables all issues reporting on this line. Comment e.g. `// nolint:govet` disables only govet issues for this line.
+
+Please create [GitHub Issues here](https://github.com/golangci/golangci-lint/issues/new) about found false positives. We will add it to default exclude list if it's common or we will fix underlying linter.
+
+# IDE integrations
+1. VS Code - [pull request](https://github.com/Microsoft/vscode-go/pull/1693) to vscode-go (thanks to [pdf](https://github.com/pdf)).
+2. Vim - [issue](https://github.com/fatih/vim-go/issues/1841) for vim-go.
+3. GNU Emacs - [flycheck checker](https://github.com/weijiangan/flycheck-golangci-lint) (thanks to [weijiangan](https://github.com/weijiangan)).
+4. Sublime Text - [plugin](https://github.com/alecthomas/SublimeLinter-contrib-golang-cilint) for SublimeLinter (thanks to [alecthomas](https://github.com/alecthomas)).
+
+# Internals
+The key difference with gometalinter is that golangci-lint shares work between specific linters (golint, govet, ...).
+For small and medium projects 50-80% of work between linters can be reused.
+Now we share `loader.Program` and `SSA` representation building. `SSA` representation is used from
+a [fork of go-tools](https://github.com/dominikh/go-tools), not the official one. Also, we are going to
+reuse `AST` parsing and traversal.
+
+We don't fork to call specific linter but use its API. We forked GitHub repos of almost all linters
+to make API. It also allows us to be more performant and control actual count of used threads.
+
+All linters are vendored in `/vendor` folder: their version is fixed, they are builtin
+and you don't need to install them separately.
+
+We use chains for issues and independent processors to post-process them: exclude issues by limits,
+nolint comment, diff, regexps; prettify paths etc.
+
+We use `cobra` for command-line action.
+
+# FAQ
+**Q: How to add custom linter?**
+
+A: You can integrate it yourself, see this [wiki page](https://github.com/golangci/golangci-lint/wiki/How-to-add-a-custom-linter) with documentation. Or you can create [GitHub Issue](https://github.com/golangci/golangci-lint/issues/new) and we will integrate it soon.
+
+**Q: It's cool to use `golangci-lint` when starting a project, but what about existing projects with large codebase? It will take days to fix all found issues**
+
+A: We are sure that every project can easily integrate `golangci-lint`, even the large one. The idea is to not fix all existing issues. Fix only newly added issue: issues in new code. To do this setup CI (or better use [GolangCI](https://golangci.com) to run `golangci-lint` with option `--new-from-rev=origin/master`. Also, take a look at option `-n`.
+By doing this you won't create new issues in code and can smoothly fix existing issues (or not).
+
+**Q: How to use `golangci-lint` in CI (Continuous Integration)?**
+
+A: You have 2 choices:
+1. Use [GolangCI](https://golangci.com): this service is highly integrated with GitHub (issues are commented in the pull request) and uses a `golangci-lint` tool. For configuration use `.golangci.yml` (or toml/json).
+2. Use custom CI: just run `golangci-lint` in CI and check the exit code. If it's non-zero - fail the build. The main disadvantage is that you can't see found issues in pull request code and should view build log, then open needed source file to see a context.
+If you'd like to vendor `golangci-lint` in your repo, run:
+```bash
+go get -u github.com/golang/dep/cmd/dep
+dep init
+dep ensure -v -add github.com/golangci/golangci-lint/cmd/golangci-lint
+```
+Then add these lines to your `Gopkg.toml` file, so `dep ensure -update` won't delete the vendored `golangci-lint` code.
+```toml
+required = [
+ "github.com/golangci/golangci-lint/cmd/golangci-lint",
+]
+```
+In your CI scripts, install the vendored `golangci-lint` like this:
+```bash
+go install ./vendor/github.com/golangci/golangci-lint/cmd/golangci-lint/`
+```
+Vendoring `golangci-lint` saves a network request, potentially making your CI system a little more reliable.
+
+**Q: `golangci-lint` doesn't work**
+1. Update it: `go get -u github.com/golangci/golangci-lint/cmd/golangci-lint`
+2. Run it with `-v` option and check the output.
+3. If it doesn't help create [GitHub issue](https://github.com/golangci/golangci-lint/issues/new) with the output.
+
+# Thanks
+Thanks to [alecthomas/gometalinter](https://github.com/alecthomas/gometalinter) for inspiration and amazing work.
+Thanks to [bradleyfalzon/revgrep](https://github.com/bradleyfalzon/revgrep) for cool diff tool.
+
+Thanks to developers and authors of used linters:
+{{.ThanksList}}
+
+# Future Plans
+1. Upstream all changes of forked linters.
+2. Fully integrate all used linters: make a common interface and reuse 100% of what can be reused: AST traversal, packages preparation etc.
+3. Make it easy to write own linter/checker: it should take a minimum code, have perfect documentation, debugging and testing tooling.
+4. Speedup packages loading (dig into [loader](golang.org/x/tools/go/loader)): on-disk cache and existing code profiling-optimizing.
+5. Analyze (don't only filter) only new code: analyze only changed files and dependencies, make incremental analysis, caches.
+6. Smart new issues detector: don't print existing issues on changed lines.
+7. Integration with Text Editors. On-the-fly code analysis for text editors: it should be super-fast.
+8. Minimize false-positives by fixing linters and improving testing tooling.
+9. Automatic issues fixing (code rewrite, refactoring) where it's possible.
+10. Documentation for every issue type.
+
+# Contact Information
+You can contact the author of GolangCI-Lint by [denis@golangci.com](mailto:denis@golangci.com).
diff --git a/pkg/commands/linters.go b/pkg/commands/linters.go
index 7adc4d9a..032db3c4 100644
--- a/pkg/commands/linters.go
+++ b/pkg/commands/linters.go
@@ -6,7 +6,8 @@ import (
"strings"
"github.com/fatih/color"
- "github.com/golangci/golangci-lint/pkg"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
+ "github.com/golangci/golangci-lint/pkg/lint/lintersdb"
"github.com/golangci/golangci-lint/pkg/printers"
"github.com/spf13/cobra"
)
@@ -20,7 +21,7 @@ func (e *Executor) initLinters() {
e.rootCmd.AddCommand(lintersCmd)
}
-func printLinterConfigs(lcs []pkg.LinterConfig) {
+func printLinterConfigs(lcs []linter.Config) {
for _, lc := range lcs {
fmt.Fprintf(printers.StdOut, "%s: %s [fast: %t]\n", color.YellowString(lc.Linter.Name()),
lc.Linter.Desc(), !lc.DoesFullImport)
@@ -28,8 +29,8 @@ func printLinterConfigs(lcs []pkg.LinterConfig) {
}
func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
- var enabledLCs, disabledLCs []pkg.LinterConfig
- for _, lc := range pkg.GetAllSupportedLinterConfigs() {
+ var enabledLCs, disabledLCs []linter.Config
+ for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
if lc.EnabledByDefault {
enabledLCs = append(enabledLCs, lc)
} else {
@@ -43,8 +44,8 @@ func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
printLinterConfigs(disabledLCs)
color.Green("\nLinters presets:")
- for _, p := range pkg.AllPresets() {
- linters := pkg.GetAllLinterConfigsForPreset(p)
+ for _, p := range lintersdb.AllPresets() {
+ linters := lintersdb.GetAllLinterConfigsForPreset(p)
linterNames := []string{}
for _, lc := range linters {
linterNames = append(linterNames, lc.Linter.Name())
diff --git a/pkg/commands/run.go b/pkg/commands/run.go
index 151e2dca..c3f3a895 100644
--- a/pkg/commands/run.go
+++ b/pkg/commands/run.go
@@ -12,9 +12,9 @@ import (
"strings"
"time"
- "github.com/golangci/golangci-lint/pkg"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/lintersdb"
"github.com/golangci/golangci-lint/pkg/printers"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/golangci/golangci-lint/pkg/result/processors"
@@ -100,7 +100,7 @@ func (e *Executor) initRun() {
runCmd.Flags().BoolVar(&lc.EnableAll, "enable-all", false, "Enable all linters")
runCmd.Flags().BoolVar(&lc.DisableAll, "disable-all", false, "Disable all linters")
runCmd.Flags().StringSliceVarP(&lc.Presets, "presets", "p", []string{},
- fmt.Sprintf("Enable presets (%s) of linters. Run 'golangci-lint linters' to see them. This option implies option --disable-all", strings.Join(pkg.AllPresets(), "|")))
+ fmt.Sprintf("Enable presets (%s) of linters. Run 'golangci-lint linters' to see them. This option implies option --disable-all", strings.Join(lintersdb.AllPresets(), "|")))
runCmd.Flags().BoolVar(&lc.Fast, "fast", false, "Run only fast linters from enabled linters set")
// Issues config
@@ -122,16 +122,12 @@ func (e *Executor) initRun() {
func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan result.Issue, error) {
e.cfg.Run.Args = args
- linters, err := pkg.GetEnabledLinters(e.cfg)
+ linters, err := lintersdb.GetEnabledLinters(e.cfg)
if err != nil {
return nil, err
}
- ctxLinters := make([]lint.LinterConfig, 0, len(linters))
- for _, lc := range linters {
- ctxLinters = append(ctxLinters, lint.LinterConfig(lc))
- }
- lintCtx, err := lint.BuildContext(ctx, ctxLinters, e.cfg)
+ lintCtx, err := lint.LoadContext(ctx, linters, e.cfg)
if err != nil {
return nil, err
}
@@ -162,11 +158,7 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan resul
},
}
- runLinters := make([]lint.RunnerLinterConfig, 0, len(linters))
- for _, lc := range linters {
- runLinters = append(runLinters, lint.RunnerLinterConfig(lc))
- }
- return runner.Run(ctx, runLinters, lintCtx), nil
+ return runner.Run(ctx, linters, lintCtx), nil
}
func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
diff --git a/pkg/golinters/deadcode.go b/pkg/golinters/deadcode.go
index bb6e89c2..4f7d74be 100644
--- a/pkg/golinters/deadcode.go
+++ b/pkg/golinters/deadcode.go
@@ -5,7 +5,7 @@ import (
"fmt"
deadcodeAPI "github.com/golangci/go-misc/deadcode"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -19,7 +19,7 @@ func (Deadcode) Desc() string {
return "Finds unused code"
}
-func (d Deadcode) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (d Deadcode) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues, err := deadcodeAPI.Run(lintCtx.Program)
if err != nil {
return nil, err
diff --git a/pkg/golinters/depguard.go b/pkg/golinters/depguard.go
index 3c3987ea..72f5ed02 100644
--- a/pkg/golinters/depguard.go
+++ b/pkg/golinters/depguard.go
@@ -6,7 +6,7 @@ import (
"strings"
depguardAPI "github.com/OpenPeeDeeP/depguard"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -20,7 +20,7 @@ func (Depguard) Desc() string {
return "Go linter that checks if package imports are in a list of acceptable packages"
}
-func (d Depguard) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (d Depguard) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
dg := &depguardAPI.Depguard{
Packages: lintCtx.Settings().Depguard.Packages,
IncludeGoRoot: lintCtx.Settings().Depguard.IncludeGoRoot,
diff --git a/pkg/golinters/dupl.go b/pkg/golinters/dupl.go
index d9963620..2784f344 100644
--- a/pkg/golinters/dupl.go
+++ b/pkg/golinters/dupl.go
@@ -5,7 +5,7 @@ import (
"fmt"
"go/token"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
duplAPI "github.com/mibk/dupl"
)
@@ -20,7 +20,7 @@ func (Dupl) Desc() string {
return "Tool for code clone detection"
}
-func (d Dupl) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (d Dupl) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues, err := duplAPI.Run(lintCtx.Paths.Files, lintCtx.Settings().Dupl.Threshold)
if err != nil {
return nil, err
diff --git a/pkg/golinters/errcheck.go b/pkg/golinters/errcheck.go
index fbd59afd..5c5bd00e 100644
--- a/pkg/golinters/errcheck.go
+++ b/pkg/golinters/errcheck.go
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
errcheckAPI "github.com/kisielk/errcheck/golangci"
)
@@ -19,7 +19,7 @@ func (Errcheck) Desc() string {
return "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases"
}
-func (e Errcheck) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (e Errcheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
errCfg := &lintCtx.Settings().Errcheck
issues, err := errcheckAPI.Run(lintCtx.Program, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
if err != nil {
diff --git a/pkg/golinters/gas.go b/pkg/golinters/gas.go
index 58c5c2ee..c5ecd033 100644
--- a/pkg/golinters/gas.go
+++ b/pkg/golinters/gas.go
@@ -10,7 +10,7 @@ import (
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/rules"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/sirupsen/logrus"
)
@@ -25,7 +25,7 @@ func (Gas) Desc() string {
return "Inspects source code for security problems"
}
-func (lint Gas) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint Gas) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
gasConfig := gas.NewConfig()
enabledRules := rules.Generate()
logger := log.New(ioutil.Discard, "", 0)
diff --git a/pkg/golinters/goconst.go b/pkg/golinters/goconst.go
index 09ee3628..653561eb 100644
--- a/pkg/golinters/goconst.go
+++ b/pkg/golinters/goconst.go
@@ -5,7 +5,7 @@ import (
"fmt"
goconstAPI "github.com/golangci/goconst"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -19,7 +19,7 @@ func (Goconst) Desc() string {
return "Finds repeated strings that could be replaced by a constant"
}
-func (lint Goconst) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint Goconst) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
var goconstIssues []goconstAPI.Issue
// TODO: make it cross-package: pass package names inside goconst
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
diff --git a/pkg/golinters/gocyclo.go b/pkg/golinters/gocyclo.go
index 8945abea..d9b43d87 100644
--- a/pkg/golinters/gocyclo.go
+++ b/pkg/golinters/gocyclo.go
@@ -6,7 +6,7 @@ import (
"sort"
gocycloAPI "github.com/golangci/gocyclo/pkg/gocyclo"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -20,7 +20,7 @@ func (Gocyclo) Desc() string {
return "Computes and checks the cyclomatic complexity of functions"
}
-func (g Gocyclo) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (g Gocyclo) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
var stats []gocycloAPI.Stat
for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
stats = gocycloAPI.BuildStats(f.F, f.Fset, stats)
diff --git a/pkg/golinters/gofmt.go b/pkg/golinters/gofmt.go
index d4cc9cae..773405dc 100644
--- a/pkg/golinters/gofmt.go
+++ b/pkg/golinters/gofmt.go
@@ -8,7 +8,7 @@ import (
gofmtAPI "github.com/golangci/gofmt/gofmt"
goimportsAPI "github.com/golangci/gofmt/goimports"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/sirupsen/logrus"
"sourcegraph.com/sourcegraph/go-diff/diff"
@@ -102,7 +102,7 @@ func (g Gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
return issues, nil
}
-func (g Gofmt) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (g Gofmt) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
var issues []result.Issue
for _, f := range lintCtx.Paths.Files {
diff --git a/pkg/golinters/golint.go b/pkg/golinters/golint.go
index c26f9fc4..1d5f5476 100644
--- a/pkg/golinters/golint.go
+++ b/pkg/golinters/golint.go
@@ -6,7 +6,7 @@ import (
"io/ioutil"
lintAPI "github.com/golang/lint"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/sirupsen/logrus"
)
@@ -21,7 +21,7 @@ func (Golint) Desc() string {
return "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes"
}
-func (g Golint) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (g Golint) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
var issues []result.Issue
var lintErr error
for _, pkgFiles := range lintCtx.Paths.FilesGrouppedByDirs() {
diff --git a/pkg/golinters/govet.go b/pkg/golinters/govet.go
index c50639c6..5dad22ac 100644
--- a/pkg/golinters/govet.go
+++ b/pkg/golinters/govet.go
@@ -3,7 +3,7 @@ package golinters
import (
"context"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
govetAPI "github.com/golangci/govet"
)
@@ -18,7 +18,7 @@ func (Govet) Desc() string {
return "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string"
}
-func (g Govet) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (g Govet) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
// TODO: check .S asm files: govet can do it if pass dirs
var govetIssues []govetAPI.Issue
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
diff --git a/pkg/golinters/ineffassign.go b/pkg/golinters/ineffassign.go
index cb7a75f1..3d13ddf3 100644
--- a/pkg/golinters/ineffassign.go
+++ b/pkg/golinters/ineffassign.go
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
ineffassignAPI "github.com/golangci/ineffassign"
)
@@ -19,7 +19,7 @@ func (Ineffassign) Desc() string {
return "Detects when assignments to existing variables are not used"
}
-func (lint Ineffassign) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint Ineffassign) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues := ineffassignAPI.Run(lintCtx.Paths.Files)
if len(issues) == 0 {
return nil, nil
diff --git a/pkg/golinters/interfacer.go b/pkg/golinters/interfacer.go
index becc50ce..f3652e49 100644
--- a/pkg/golinters/interfacer.go
+++ b/pkg/golinters/interfacer.go
@@ -5,7 +5,7 @@ import (
"mvdan.cc/interfacer/check"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -19,7 +19,7 @@ func (Interfacer) Desc() string {
return "Linter that suggests narrower interface types"
}
-func (lint Interfacer) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint Interfacer) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
c := new(check.Checker)
c.Program(lintCtx.Program)
c.ProgramSSA(lintCtx.SSAProgram)
diff --git a/pkg/golinters/maligned.go b/pkg/golinters/maligned.go
index a79d633e..e90bc9a4 100644
--- a/pkg/golinters/maligned.go
+++ b/pkg/golinters/maligned.go
@@ -4,7 +4,7 @@ import (
"context"
"fmt"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
malignedAPI "github.com/golangci/maligned"
)
@@ -19,7 +19,7 @@ func (Maligned) Desc() string {
return "Tool to detect Go structs that would take less memory if their fields were sorted"
}
-func (m Maligned) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (m Maligned) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues := malignedAPI.Run(lintCtx.Program)
if len(issues) == 0 {
return nil, nil
diff --git a/pkg/golinters/megacheck.go b/pkg/golinters/megacheck.go
index 45f0392d..f853d1b0 100644
--- a/pkg/golinters/megacheck.go
+++ b/pkg/golinters/megacheck.go
@@ -6,7 +6,7 @@ import (
"strings"
megacheckAPI "github.com/golangci/go-tools/cmd/megacheck"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -42,15 +42,15 @@ func (m Megacheck) Name() string {
func (m Megacheck) Desc() string {
descs := map[string]string{
"unused": "Checks Go code for unused constants, variables, functions and types",
- "gosimple": "Linter for Go source code that specialises on simplifying code",
- "staticcheck": "Staticcheck is go vet on steroids, applying a ton of static analysis checks",
+ "gosimple": "Linter for Go source code that specializes in simplifying a code",
+ "staticcheck": "Staticcheck is a go vet on steroids, applying a ton of static analysis checks",
"megacheck": "3 sub-linters in one: unused, gosimple and staticcheck",
}
return descs[m.Name()]
}
-func (m Megacheck) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (m Megacheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues := megacheckAPI.Run(lintCtx.Program, lintCtx.LoaderConfig, lintCtx.SSAProgram,
m.StaticcheckEnabled, m.GosimpleEnabled, m.UnusedEnabled)
if len(issues) == 0 {
diff --git a/pkg/golinters/structcheck.go b/pkg/golinters/structcheck.go
index dd87a50c..9fd431fa 100644
--- a/pkg/golinters/structcheck.go
+++ b/pkg/golinters/structcheck.go
@@ -5,7 +5,7 @@ import (
"fmt"
structcheckAPI "github.com/golangci/check/cmd/structcheck"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -16,10 +16,10 @@ func (Structcheck) Name() string {
}
func (Structcheck) Desc() string {
- return "Finds unused struct fields"
+ return "Finds an unused struct fields"
}
-func (s Structcheck) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (s Structcheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues := structcheckAPI.Run(lintCtx.Program, lintCtx.Settings().Structcheck.CheckExportedFields)
if len(issues) == 0 {
return nil, nil
diff --git a/pkg/golinters/typecheck.go b/pkg/golinters/typecheck.go
index 01ad9862..e76108a5 100644
--- a/pkg/golinters/typecheck.go
+++ b/pkg/golinters/typecheck.go
@@ -6,7 +6,7 @@ import (
"strconv"
"strings"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -62,7 +62,7 @@ func (lint TypeCheck) parseError(err error) *result.Issue {
}
}
-func (lint TypeCheck) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint TypeCheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
if lintCtx.NotCompilingPackages == nil {
return nil, nil
}
diff --git a/pkg/golinters/unconvert.go b/pkg/golinters/unconvert.go
index 7f38f8c4..22ca8082 100644
--- a/pkg/golinters/unconvert.go
+++ b/pkg/golinters/unconvert.go
@@ -3,7 +3,7 @@ package golinters
import (
"context"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
unconvertAPI "github.com/golangci/unconvert"
)
@@ -18,7 +18,7 @@ func (Unconvert) Desc() string {
return "Remove unnecessary type conversions"
}
-func (lint Unconvert) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (lint Unconvert) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
positions := unconvertAPI.Run(lintCtx.Program)
if len(positions) == 0 {
return nil, nil
diff --git a/pkg/golinters/varcheck.go b/pkg/golinters/varcheck.go
index 05aa9c1d..8e506e98 100644
--- a/pkg/golinters/varcheck.go
+++ b/pkg/golinters/varcheck.go
@@ -5,7 +5,7 @@ import (
"fmt"
varcheckAPI "github.com/golangci/check/cmd/varcheck"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
@@ -19,7 +19,7 @@ func (Varcheck) Desc() string {
return "Finds unused global variables and constants"
}
-func (v Varcheck) Run(ctx context.Context, lintCtx *lint.Context) ([]result.Issue, error) {
+func (v Varcheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
issues := varcheckAPI.Run(lintCtx.Program, lintCtx.Settings().Varcheck.CheckExportedFields)
if len(issues) == 0 {
return nil, nil
diff --git a/pkg/lint/linter/config.go b/pkg/lint/linter/config.go
new file mode 100644
index 00000000..b50c266b
--- /dev/null
+++ b/pkg/lint/linter/config.go
@@ -0,0 +1,69 @@
+package linter
+
+const (
+ PresetFormatting = "format"
+ PresetComplexity = "complexity"
+ PresetStyle = "style"
+ PresetBugs = "bugs"
+ PresetUnused = "unused"
+ PresetPerformance = "performance"
+)
+
+type Config struct {
+ Linter Linter
+ EnabledByDefault bool
+ DoesFullImport bool
+ NeedsSSARepr bool
+ InPresets []string
+ Speed int // more value means faster execution of linter
+
+ OriginalURL string // URL of original (not forked) repo, needed for autogenerated README
+}
+
+func (lc Config) WithFullImport() Config {
+ lc.DoesFullImport = true
+ return lc
+}
+
+func (lc Config) WithSSA() Config {
+ lc.DoesFullImport = true
+ lc.NeedsSSARepr = true
+ return lc
+}
+
+func (lc Config) WithPresets(presets ...string) Config {
+ lc.InPresets = presets
+ return lc
+}
+
+func (lc Config) WithSpeed(speed int) Config {
+ lc.Speed = speed
+ return lc
+}
+
+func (lc Config) WithURL(url string) Config {
+ lc.OriginalURL = url
+ return lc
+}
+
+func (lc Config) NeedsProgramLoading() bool {
+ return lc.DoesFullImport
+}
+
+func (lc Config) NeedsSSARepresentation() bool {
+ return lc.NeedsSSARepr
+}
+
+func (lc Config) GetSpeed() int {
+ return lc.Speed
+}
+
+func (lc Config) GetLinter() Linter {
+ return lc.Linter
+}
+
+func NewConfig(linter Linter) *Config {
+ return &Config{
+ Linter: linter,
+ }
+}
diff --git a/pkg/lint/linter/context.go b/pkg/lint/linter/context.go
new file mode 100644
index 00000000..173b2536
--- /dev/null
+++ b/pkg/lint/linter/context.go
@@ -0,0 +1,23 @@
+package linter
+
+import (
+ "github.com/golangci/go-tools/ssa"
+ "github.com/golangci/golangci-lint/pkg/config"
+ "github.com/golangci/golangci-lint/pkg/fsutils"
+ "github.com/golangci/golangci-lint/pkg/lint/astcache"
+ "golang.org/x/tools/go/loader"
+)
+
+type Context struct {
+ Paths *fsutils.ProjectPaths
+ Cfg *config.Config
+ Program *loader.Program
+ SSAProgram *ssa.Program
+ LoaderConfig *loader.Config
+ ASTCache *astcache.Cache
+ NotCompilingPackages []*loader.PackageInfo
+}
+
+func (c *Context) Settings() *config.LintersSettings {
+ return &c.Cfg.LintersSettings
+}
diff --git a/pkg/lint/linter.go b/pkg/lint/linter/linter.go
similarity index 92%
rename from pkg/lint/linter.go
rename to pkg/lint/linter/linter.go
index 1dd58017..cfe9ec02 100644
--- a/pkg/lint/linter.go
+++ b/pkg/lint/linter/linter.go
@@ -1,4 +1,4 @@
-package lint
+package linter
import (
"context"
diff --git a/pkg/enabled_linters.go b/pkg/lint/lintersdb/lintersdb.go
similarity index 59%
rename from pkg/enabled_linters.go
rename to pkg/lint/lintersdb/lintersdb.go
index 814d3279..fbc37253 100644
--- a/pkg/enabled_linters.go
+++ b/pkg/lint/lintersdb/lintersdb.go
@@ -1,4 +1,4 @@
-package pkg
+package lintersdb
import (
"fmt"
@@ -8,21 +8,12 @@ import (
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters"
- "github.com/golangci/golangci-lint/pkg/lint"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/sirupsen/logrus"
)
-const (
- PresetFormatting = "format"
- PresetComplexity = "complexity"
- PresetStyle = "style"
- PresetBugs = "bugs"
- PresetUnused = "unused"
- PresetPerformance = "performance"
-)
-
func AllPresets() []string {
- return []string{PresetBugs, PresetUnused, PresetFormatting, PresetStyle, PresetComplexity, PresetPerformance}
+ return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting, linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance}
}
func allPresetsSet() map[string]bool {
@@ -33,64 +24,12 @@ func allPresetsSet() map[string]bool {
return ret
}
-type LinterConfig struct {
- Linter lint.Linter
- EnabledByDefault bool
- DoesFullImport bool
- NeedsSSARepr bool
- InPresets []string
- Speed int // more value means faster execution of linter
-}
-
-func (lc LinterConfig) WithFullImport() LinterConfig {
- lc.DoesFullImport = true
- return lc
-}
-
-func (lc LinterConfig) WithSSA() LinterConfig {
- lc.DoesFullImport = true
- lc.NeedsSSARepr = true
- return lc
-}
-
-func (lc LinterConfig) WithPresets(presets ...string) LinterConfig {
- lc.InPresets = presets
- return lc
-}
-
-func (lc LinterConfig) WithSpeed(speed int) LinterConfig {
- lc.Speed = speed
- return lc
-}
-
-func (lc LinterConfig) NeedsProgramLoading() bool {
- return lc.DoesFullImport
-}
-
-func (lc LinterConfig) NeedsSSARepresentation() bool {
- return lc.NeedsSSARepr
-}
-
-func (lc LinterConfig) GetSpeed() int {
- return lc.Speed
-}
-
-func (lc LinterConfig) GetLinter() lint.Linter {
- return lc.Linter
-}
-
-func newLinterConfig(linter lint.Linter) LinterConfig {
- return LinterConfig{
- Linter: linter,
- }
-}
-
-var nameToLC map[string]LinterConfig
+var nameToLC map[string]linter.Config
var nameToLCOnce sync.Once
-func getLinterConfig(name string) *LinterConfig {
+func getLinterConfig(name string) *linter.Config {
nameToLCOnce.Do(func() {
- nameToLC = make(map[string]LinterConfig)
+ nameToLC = make(map[string]linter.Config)
for _, lc := range GetAllSupportedLinterConfigs() {
nameToLC[lc.Linter.Name()] = lc
}
@@ -104,8 +43,8 @@ func getLinterConfig(name string) *LinterConfig {
return &lc
}
-func enableLinterConfigs(lcs []LinterConfig, isEnabled func(lc *LinterConfig) bool) []LinterConfig {
- var ret []LinterConfig
+func enableLinterConfigs(lcs []linter.Config, isEnabled func(lc *linter.Config) bool) []linter.Config {
+ var ret []linter.Config
for _, lc := range lcs {
lc.EnabledByDefault = isEnabled(&lc)
ret = append(ret, lc)
@@ -114,35 +53,113 @@ func enableLinterConfigs(lcs []LinterConfig, isEnabled func(lc *LinterConfig) bo
return ret
}
-func GetAllSupportedLinterConfigs() []LinterConfig {
- lcs := []LinterConfig{
- newLinterConfig(golinters.Govet{}).WithPresets(PresetBugs).WithSpeed(4),
- newLinterConfig(golinters.Errcheck{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(10),
- newLinterConfig(golinters.Golint{}).WithPresets(PresetStyle).WithSpeed(3),
+func GetAllSupportedLinterConfigs() []linter.Config {
+ lcs := []linter.Config{
+ linter.NewConfig(golinters.Govet{}).
+ WithPresets(linter.PresetBugs).
+ WithSpeed(4).
+ WithURL("https://golang.org/cmd/vet/"),
+ linter.NewConfig(golinters.Errcheck{}).
+ WithFullImport().
+ WithPresets(linter.PresetBugs).
+ WithSpeed(10).
+ WithURL("https://github.com/kisielk/errcheck"),
+ linter.NewConfig(golinters.Golint{}).
+ WithPresets(linter.PresetStyle).
+ WithSpeed(3).
+ WithURL("https://github.com/golang/lint"),
- newLinterConfig(golinters.Megacheck{StaticcheckEnabled: true}).WithSSA().
- WithPresets(PresetBugs).WithSpeed(2),
- newLinterConfig(golinters.Megacheck{UnusedEnabled: true}).WithSSA().WithPresets(PresetUnused).WithSpeed(5),
- newLinterConfig(golinters.Megacheck{GosimpleEnabled: true}).WithSSA().WithPresets(PresetStyle).WithSpeed(5),
+ linter.NewConfig(golinters.Megacheck{StaticcheckEnabled: true}).
+ WithSSA().
+ WithPresets(linter.PresetBugs).
+ WithSpeed(2).
+ WithURL("https://staticcheck.io/"),
+ linter.NewConfig(golinters.Megacheck{UnusedEnabled: true}).
+ WithSSA().
+ WithPresets(linter.PresetUnused).
+ WithSpeed(5).
+ WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/unused"),
+ linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true}).
+ WithSSA().
+ WithPresets(linter.PresetStyle).
+ WithSpeed(5).
+ WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/gosimple"),
- newLinterConfig(golinters.Gas{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(8),
- newLinterConfig(golinters.Structcheck{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
- newLinterConfig(golinters.Varcheck{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
- newLinterConfig(golinters.Interfacer{}).WithSSA().WithPresets(PresetStyle).WithSpeed(6),
- newLinterConfig(golinters.Unconvert{}).WithFullImport().WithPresets(PresetStyle).WithSpeed(10),
- newLinterConfig(golinters.Ineffassign{}).WithPresets(PresetUnused).WithSpeed(9),
- newLinterConfig(golinters.Dupl{}).WithPresets(PresetStyle).WithSpeed(7),
- newLinterConfig(golinters.Goconst{}).WithPresets(PresetStyle).WithSpeed(9),
- newLinterConfig(golinters.Deadcode{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
- newLinterConfig(golinters.Gocyclo{}).WithPresets(PresetComplexity).WithSpeed(8),
- newLinterConfig(golinters.TypeCheck{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(10),
+ linter.NewConfig(golinters.Gas{}).
+ WithFullImport().
+ WithPresets(linter.PresetBugs).
+ WithSpeed(8).
+ WithURL("https://github.com/GoASTScanner/gas"),
+ linter.NewConfig(golinters.Structcheck{}).
+ WithFullImport().
+ WithPresets(linter.PresetUnused).
+ WithSpeed(10).
+ WithURL("https://github.com/opennota/check"),
+ linter.NewConfig(golinters.Varcheck{}).
+ WithFullImport().
+ WithPresets(linter.PresetUnused).
+ WithSpeed(10).
+ WithURL("https://github.com/opennota/check"),
+ linter.NewConfig(golinters.Interfacer{}).
+ WithSSA().
+ WithPresets(linter.PresetStyle).
+ WithSpeed(6).
+ WithURL("https://github.com/mvdan/interfacer"),
+ linter.NewConfig(golinters.Unconvert{}).
+ WithFullImport().
+ WithPresets(linter.PresetStyle).
+ WithSpeed(10).
+ WithURL("https://github.com/mdempsky/unconvert"),
+ linter.NewConfig(golinters.Ineffassign{}).
+ WithPresets(linter.PresetUnused).
+ WithSpeed(9).
+ WithURL("https://github.com/gordonklaus/ineffassign"),
+ linter.NewConfig(golinters.Dupl{}).
+ WithPresets(linter.PresetStyle).
+ WithSpeed(7).
+ WithURL("https://github.com/mibk/dupl"),
+ linter.NewConfig(golinters.Goconst{}).
+ WithPresets(linter.PresetStyle).
+ WithSpeed(9).
+ WithURL("https://github.com/jgautheron/goconst"),
+ linter.NewConfig(golinters.Deadcode{}).
+ WithFullImport().
+ WithPresets(linter.PresetUnused).
+ WithSpeed(10).
+ WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"),
+ linter.NewConfig(golinters.Gocyclo{}).
+ WithPresets(linter.PresetComplexity).
+ WithSpeed(8).
+ WithURL("https://github.com/alecthomas/gocyclo"),
+ linter.NewConfig(golinters.TypeCheck{}).
+ WithFullImport().
+ WithPresets(linter.PresetBugs).
+ WithSpeed(10).
+ WithURL(""),
- newLinterConfig(golinters.Gofmt{}).WithPresets(PresetFormatting).WithSpeed(7),
- newLinterConfig(golinters.Gofmt{UseGoimports: true}).WithPresets(PresetFormatting).WithSpeed(5),
- newLinterConfig(golinters.Maligned{}).WithFullImport().WithPresets(PresetPerformance).WithSpeed(10),
- newLinterConfig(golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}).
- WithSSA().WithPresets(PresetStyle, PresetBugs, PresetUnused).WithSpeed(1),
- newLinterConfig(golinters.Depguard{}).WithFullImport().WithPresets(PresetStyle).WithSpeed(6),
+ linter.NewConfig(golinters.Gofmt{}).
+ WithPresets(linter.PresetFormatting).
+ WithSpeed(7).
+ WithURL("https://golang.org/cmd/gofmt/"),
+ linter.NewConfig(golinters.Gofmt{UseGoimports: true}).
+ WithPresets(linter.PresetFormatting).
+ WithSpeed(5).
+ WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"),
+ linter.NewConfig(golinters.Maligned{}).
+ WithFullImport().
+ WithPresets(linter.PresetPerformance).
+ WithSpeed(10).
+ WithURL("https://github.com/mdempsky/maligned"),
+ linter.NewConfig(golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}).
+ WithSSA().
+ WithPresets(linter.PresetStyle, linter.PresetBugs, linter.PresetUnused).
+ WithSpeed(1).
+ WithURL("https://github.com/dominikh/go-tools/tree/master/cmd/megacheck"),
+ linter.NewConfig(golinters.Depguard{}).
+ WithFullImport().
+ WithPresets(linter.PresetStyle).
+ WithSpeed(6).
+ WithURL("https://github.com/OpenPeeDeeP/depguard"),
}
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
@@ -152,7 +169,7 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
golinters.Maligned{}.Name(): true, // rarely usable
golinters.TypeCheck{}.Name(): true, // annoying because of different building envs
}
- return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
+ return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
return !disabled[lc.Linter.Name()]
})
}
@@ -170,13 +187,13 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
golinters.Deadcode{}.Name(): true,
golinters.TypeCheck{}.Name(): true,
}
- return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
+ return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
return enabled[lc.Linter.Name()]
})
}
-func getAllEnabledByDefaultLinters() []LinterConfig {
- var ret []LinterConfig
+func getAllEnabledByDefaultLinters() []linter.Config {
+ var ret []linter.Config
for _, lc := range GetAllSupportedLinterConfigs() {
if lc.EnabledByDefault {
ret = append(ret, lc)
@@ -186,8 +203,8 @@ func getAllEnabledByDefaultLinters() []LinterConfig {
return ret
}
-func linterConfigsToMap(lcs []LinterConfig) map[string]*LinterConfig {
- ret := map[string]*LinterConfig{}
+func linterConfigsToMap(lcs []linter.Config) map[string]*linter.Config {
+ ret := map[string]*linter.Config{}
for _, lc := range lcs {
lc := lc // local copy
ret[lc.Linter.Name()] = &lc
@@ -276,8 +293,8 @@ func validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
return nil
}
-func GetAllLinterConfigsForPreset(p string) []LinterConfig {
- ret := []LinterConfig{}
+func GetAllLinterConfigsForPreset(p string) []linter.Config {
+ ret := []linter.Config{}
for _, lc := range GetAllSupportedLinterConfigs() {
for _, ip := range lc.InPresets {
if p == ip {
@@ -290,8 +307,8 @@ func GetAllLinterConfigsForPreset(p string) []LinterConfig {
return ret
}
-func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []LinterConfig) map[string]*LinterConfig { // nolint:gocyclo
- resultLintersSet := map[string]*LinterConfig{}
+func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []linter.Config) map[string]*linter.Config { // nolint:gocyclo
+ resultLintersSet := map[string]*linter.Config{}
switch {
case len(lcfg.Presets) != 0:
break // imply --disable-all
@@ -345,7 +362,7 @@ func getAllMegacheckSubLinterNames() []string {
return []string{unusedName, gosimpleName, staticcheckName}
}
-func optimizeLintersSet(linters map[string]*LinterConfig) {
+func optimizeLintersSet(linters map[string]*linter.Config) {
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
@@ -379,14 +396,14 @@ func optimizeLintersSet(linters map[string]*LinterConfig) {
linters[m.Name()] = &lc
}
-func GetEnabledLinters(cfg *config.Config) ([]LinterConfig, error) {
+func GetEnabledLinters(cfg *config.Config) ([]linter.Config, error) {
if err := validateEnabledDisabledLintersConfig(&cfg.Linters); err != nil {
return nil, err
}
resultLintersSet := getEnabledLintersSet(&cfg.Linters, getAllEnabledByDefaultLinters())
- var resultLinters []LinterConfig
+ var resultLinters []linter.Config
for _, lc := range resultLintersSet {
resultLinters = append(resultLinters, *lc)
}
@@ -410,7 +427,7 @@ func uniqStrings(ss []string) []string {
return ret
}
-func verbosePrintLintersStatus(cfg *config.Config, lcs []LinterConfig) {
+func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config) {
var linterNames []string
for _, lc := range lcs {
linterNames = append(linterNames, lc.Linter.Name())
diff --git a/pkg/enabled_linters_test.go b/pkg/lint/lintersdb/lintersdb_test.go
similarity index 92%
rename from pkg/enabled_linters_test.go
rename to pkg/lint/lintersdb/lintersdb_test.go
index cf795c60..7a1205aa 100644
--- a/pkg/enabled_linters_test.go
+++ b/pkg/lint/lintersdb/lintersdb_test.go
@@ -1,10 +1,11 @@
-package pkg
+package lintersdb
import (
"sort"
"testing"
"github.com/golangci/golangci-lint/pkg/config"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/stretchr/testify/assert"
)
@@ -45,7 +46,7 @@ func TestGetEnabledLintersSet(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
- defaultLinters := []LinterConfig{}
+ defaultLinters := []linter.Config{}
for _, ln := range c.def {
defaultLinters = append(defaultLinters, *getLinterConfig(ln))
}
diff --git a/pkg/lint/context.go b/pkg/lint/load.go
similarity index 89%
rename from pkg/lint/context.go
rename to pkg/lint/load.go
index b75369b0..8ca3da13 100644
--- a/pkg/lint/context.go
+++ b/pkg/lint/load.go
@@ -14,6 +14,7 @@ import (
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/fsutils"
"github.com/golangci/golangci-lint/pkg/lint/astcache"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/sirupsen/logrus"
"golang.org/x/tools/go/loader"
)
@@ -32,12 +33,7 @@ func (c *Context) Settings() *config.LintersSettings {
return &c.Cfg.LintersSettings
}
-type LinterConfig interface {
- NeedsProgramLoading() bool
- NeedsSSARepresentation() bool
-}
-
-func isFullImportNeeded(linters []LinterConfig) bool {
+func isFullImportNeeded(linters []linter.Config) bool {
for _, linter := range linters {
if linter.NeedsProgramLoading() {
return true
@@ -47,7 +43,7 @@ func isFullImportNeeded(linters []LinterConfig) bool {
return false
}
-func isSSAReprNeeded(linters []LinterConfig) bool {
+func isSSAReprNeeded(linters []linter.Config) bool {
for _, linter := range linters {
if linter.NeedsSSARepresentation() {
return true
@@ -57,7 +53,7 @@ func isSSAReprNeeded(linters []LinterConfig) bool {
return false
}
-func loadWholeAppIfNeeded(ctx context.Context, linters []LinterConfig, cfg *config.Run, paths *fsutils.ProjectPaths) (*loader.Program, *loader.Config, error) {
+func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *config.Run, paths *fsutils.ProjectPaths) (*loader.Program, *loader.Config, error) {
if !isFullImportNeeded(linters) {
return nil, nil, nil
}
@@ -117,7 +113,7 @@ func discoverGoRoot() (string, error) {
// separateNotCompilingPackages moves not compiling packages into separate slices:
// a lot of linters crash on such packages. Leave them only for those linters
// which can work with them.
-func separateNotCompilingPackages(lintCtx *Context) {
+func separateNotCompilingPackages(lintCtx *linter.Context) {
prog := lintCtx.Program
if prog.Created != nil {
@@ -142,7 +138,7 @@ func separateNotCompilingPackages(lintCtx *Context) {
}
}
-func BuildContext(ctx context.Context, linters []LinterConfig, cfg *config.Config) (*Context, error) {
+func LoadContext(ctx context.Context, linters []linter.Config, cfg *config.Config) (*linter.Context, error) {
// Set GOROOT to have working cross-compilation: cross-compiled binaries
// have invalid GOROOT. XXX: can't use runtime.GOROOT().
goroot, err := discoverGoRoot()
@@ -183,7 +179,7 @@ func BuildContext(ctx context.Context, linters []LinterConfig, cfg *config.Confi
astCache = astcache.LoadFromFiles(paths.Files)
}
- ret := &Context{
+ ret := &linter.Context{
Paths: paths,
Cfg: cfg,
Program: prog,
diff --git a/pkg/lint/context_test.go b/pkg/lint/load_test.go
similarity index 81%
rename from pkg/lint/context_test.go
rename to pkg/lint/load_test.go
index a236c744..edd721d1 100644
--- a/pkg/lint/context_test.go
+++ b/pkg/lint/load_test.go
@@ -7,24 +7,17 @@ import (
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/fsutils"
"github.com/golangci/golangci-lint/pkg/lint/astcache"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/stretchr/testify/assert"
)
-type testLinterConfig struct{}
-
-func (t testLinterConfig) NeedsProgramLoading() bool {
- return true
-}
-
-func (t testLinterConfig) NeedsSSARepresentation() bool {
- return true
-}
-
func TestASTCacheLoading(t *testing.T) {
ctx := context.Background()
- linters := []LinterConfig{testLinterConfig{}}
+ linters := []linter.Config{
+ linter.NewConfig(nil).WithFullImport(),
+ }
- inputPaths := []string{"./...", "./", "./context.go", "context.go"}
+ inputPaths := []string{"./...", "./", "./load.go", "load.go"}
for _, inputPath := range inputPaths {
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true)
assert.NoError(t, err)
diff --git a/pkg/lint/runner.go b/pkg/lint/runner.go
index 09038027..594cff4b 100644
--- a/pkg/lint/runner.go
+++ b/pkg/lint/runner.go
@@ -9,6 +9,7 @@ import (
"sync"
"time"
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
"github.com/golangci/golangci-lint/pkg/result/processors"
"github.com/golangci/golangci-lint/pkg/timeutils"
@@ -20,12 +21,12 @@ type SimpleRunner struct {
}
type lintRes struct {
- linter Linter
+ linter linter.Config
err error
issues []result.Issue
}
-func runLinterSafe(ctx context.Context, lintCtx *Context, linter Linter) (ret []result.Issue, err error) {
+func runLinterSafe(ctx context.Context, lintCtx *linter.Context, lc linter.Config) (ret []result.Issue, err error) {
defer func() {
if panicData := recover(); panicData != nil {
err = fmt.Errorf("panic occured: %s", panicData)
@@ -33,10 +34,19 @@ func runLinterSafe(ctx context.Context, lintCtx *Context, linter Linter) (ret []
}
}()
- return linter.Run(ctx, lintCtx)
+ issues, err := lc.Linter.Run(ctx, lintCtx)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, i := range issues {
+ i.FromLinter = lc.Linter.Name()
+ }
+
+ return issues, nil
}
-func runWorker(ctx context.Context, lintCtx *Context, tasksCh <-chan Linter, lintResultsCh chan<- lintRes, name string) {
+func runWorker(ctx context.Context, lintCtx *linter.Context, tasksCh <-chan linter.Config, lintResultsCh chan<- lintRes, name string) {
sw := timeutils.NewStopwatch(name)
defer sw.Print()
@@ -44,7 +54,7 @@ func runWorker(ctx context.Context, lintCtx *Context, tasksCh <-chan Linter, lin
select {
case <-ctx.Done():
return
- case linter, ok := <-tasksCh:
+ case lc, ok := <-tasksCh:
if !ok {
return
}
@@ -55,11 +65,11 @@ func runWorker(ctx context.Context, lintCtx *Context, tasksCh <-chan Linter, lin
}
var issues []result.Issue
var err error
- sw.TrackStage(linter.Name(), func() {
- issues, err = runLinterSafe(ctx, lintCtx, linter)
+ sw.TrackStage(lc.Linter.Name(), func() {
+ issues, err = runLinterSafe(ctx, lintCtx, lc)
})
lintResultsCh <- lintRes{
- linter: linter,
+ linter: lc,
err: err,
issues: issues,
}
@@ -87,13 +97,8 @@ func logWorkersStat(workersFinishTimes []time.Time) {
logrus.Infof("Workers idle times: %s", strings.Join(logStrings, ", "))
}
-type RunnerLinterConfig interface {
- GetSpeed() int
- GetLinter() Linter
-}
-
-func getSortedLintersConfigs(linters []RunnerLinterConfig) []RunnerLinterConfig {
- ret := make([]RunnerLinterConfig, len(linters))
+func getSortedLintersConfigs(linters []linter.Config) []linter.Config {
+ ret := make([]linter.Config, len(linters))
copy(ret, linters)
sort.Slice(ret, func(i, j int) bool {
@@ -103,8 +108,8 @@ func getSortedLintersConfigs(linters []RunnerLinterConfig) []RunnerLinterConfig
return ret
}
-func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *Context, linters []RunnerLinterConfig) <-chan lintRes {
- tasksCh := make(chan Linter, len(linters))
+func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *linter.Context, linters []linter.Config) <-chan lintRes {
+ tasksCh := make(chan linter.Config, len(linters))
lintResultsCh := make(chan lintRes, len(linters))
var wg sync.WaitGroup
@@ -122,7 +127,7 @@ func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *Context, linters
lcs := getSortedLintersConfigs(linters)
for _, lc := range lcs {
- tasksCh <- lc.GetLinter()
+ tasksCh <- lc
}
close(tasksCh)
@@ -146,7 +151,7 @@ func (r SimpleRunner) processLintResults(ctx context.Context, inCh <-chan lintRe
for res := range inCh {
if res.err != nil {
- logrus.Infof("Can't run linter %s: %s", res.linter.Name(), res.err)
+ logrus.Infof("Can't run linter %s: %s", res.linter.Linter.Name(), res.err)
continue
}
@@ -189,7 +194,7 @@ func collectIssues(ctx context.Context, resCh <-chan lintRes) <-chan result.Issu
return retIssues
}
-func (r SimpleRunner) Run(ctx context.Context, linters []RunnerLinterConfig, lintCtx *Context) <-chan result.Issue {
+func (r SimpleRunner) Run(ctx context.Context, linters []linter.Config, lintCtx *linter.Context) <-chan result.Issue {
lintResultsCh := r.runWorkers(ctx, lintCtx, linters)
processedLintResultsCh := r.processLintResults(ctx, lintResultsCh)
if ctx.Err() != nil {
diff --git a/scripts/gen_readme/main.go b/scripts/gen_readme/main.go
new file mode 100644
index 00000000..90c89d69
--- /dev/null
+++ b/scripts/gen_readme/main.go
@@ -0,0 +1,122 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "strings"
+ "text/template"
+
+ "github.com/golangci/golangci-lint/pkg/lint/linter"
+ "github.com/golangci/golangci-lint/pkg/lint/lintersdb"
+)
+
+func main() {
+ const (
+ tmplPath = "README.md.tmpl"
+ outPath = "README.md"
+ )
+
+ if err := genReadme(tmplPath, outPath); err != nil {
+ log.Fatalf("failed: %s", err)
+ }
+ log.Printf("Successfully generated %s", outPath)
+}
+
+func genReadme(tmplPath, outPath string) error {
+ ctx, err := buildTemplateContext()
+ if err != nil {
+ return err
+ }
+
+ out, err := os.Create(outPath)
+ if err != nil {
+ return err
+ }
+ defer out.Close()
+
+ tmpl := template.Must(template.ParseFiles(tmplPath))
+ return tmpl.Execute(out, ctx)
+}
+
+func buildTemplateContext() (map[string]interface{}, error) {
+ golangciYaml, err := ioutil.ReadFile(".golangci.yml")
+ if err != nil {
+ return nil, fmt.Errorf("can't read .golangci.yml: %s", err)
+ }
+
+ if err = exec.Command("go", "install", "./cmd/...").Run(); err != nil {
+ return nil, fmt.Errorf("can't run go install: %s", err)
+ }
+
+ lintersOut, err := exec.Command("golangci-lint", "linters").Output()
+ if err != nil {
+ return nil, fmt.Errorf("can't run linters cmd: %s", err)
+ }
+
+ lintersOutParts := bytes.Split(lintersOut, []byte("\n\n"))
+
+ return map[string]interface{}{
+ "GolangciYaml": string(golangciYaml),
+ "LintersCommandOutputEnabledOnly": string(lintersOutParts[0]),
+ "LintersCommandOutputDisabledOnly": string(lintersOutParts[1]),
+ "EnabledByDefaultLinters": getLintersListMarkdown(true),
+ "DisabledByDefaultLinters": getLintersListMarkdown(false),
+ "ThanksList": getThanksList(),
+ }, nil
+}
+
+func getLintersListMarkdown(enabled bool) string {
+ var neededLcs []linter.Config
+ lcs := lintersdb.GetAllSupportedLinterConfigs()
+ for _, lc := range lcs {
+ if lc.EnabledByDefault == enabled {
+ neededLcs = append(neededLcs, lc)
+ }
+ }
+
+ var lines []string
+ for _, lc := range neededLcs {
+ var link string
+ if lc.OriginalURL != "" {
+ link = fmt.Sprintf("[%s](%s)", lc.Linter.Name(), lc.OriginalURL)
+ } else {
+ link = lc.Linter.Name()
+ }
+ line := fmt.Sprintf("- %s - %s", link, lc.Linter.Desc())
+ lines = append(lines, line)
+ }
+
+ return strings.Join(lines, "\n")
+}
+
+func getThanksList() string {
+ var lines []string
+ addedAuthors := map[string]bool{}
+ for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
+ if lc.OriginalURL == "" {
+ continue
+ }
+
+ const githubPrefix = "https://github.com/"
+ if !strings.HasPrefix(lc.OriginalURL, githubPrefix) {
+ continue
+ }
+
+ githubSuffix := strings.TrimPrefix(lc.OriginalURL, githubPrefix)
+ githubAuthor := strings.Split(githubSuffix, "/")[0]
+ if addedAuthors[githubAuthor] {
+ continue
+ }
+ addedAuthors[githubAuthor] = true
+
+ line := fmt.Sprintf("- [%s](https://github.com/%s)",
+ githubAuthor, githubAuthor)
+ lines = append(lines, line)
+ }
+
+ return strings.Join(lines, "\n")
+}