Merge pull request #55 from golangci/feature/generate-parts-of-readme
generate parts of README automatically
This commit is contained in:
commit
efc97a374a
121
README.md
121
README.md
@ -77,16 +77,17 @@ GolangCI-Lint can be used with zero configuration. By default next linters are e
|
|||||||
```
|
```
|
||||||
$ golangci-lint linters
|
$ golangci-lint linters
|
||||||
Enabled by default 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
|
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
|
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 go vet on steroids, applying a ton of static analysis checks
|
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
|
unused: Checks Go code for unused constants, variables, functions and types [fast: false]
|
||||||
gosimple: Linter for Go source code that specialises on simplifying code
|
gosimple: Linter for Go source code that specializes in simplifying a code [fast: false]
|
||||||
gas: Inspects source code for security problems
|
gas: Inspects source code for security problems [fast: false]
|
||||||
structcheck: Finds unused struct fields
|
structcheck: Finds an unused struct fields [fast: false]
|
||||||
varcheck: Finds unused global variables and constants
|
varcheck: Finds unused global variables and constants [fast: false]
|
||||||
ineffassign: Detects when assignments to existing variables are not used
|
ineffassign: Detects when assignments to existing variables are not used [fast: true]
|
||||||
deadcode: Finds unused code
|
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:
|
and next linters are disabled by default:
|
||||||
@ -94,17 +95,17 @@ and next linters are disabled by default:
|
|||||||
$ golangci-lint linters
|
$ golangci-lint linters
|
||||||
...
|
...
|
||||||
Disabled by default linters:
|
Disabled by default linters:
|
||||||
golint: Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
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
|
interfacer: Linter that suggests narrower interface types [fast: false]
|
||||||
unconvert: Remove unnecessary type conversions
|
unconvert: Remove unnecessary type conversions [fast: false]
|
||||||
dupl: Tool for code clone detection
|
dupl: Tool for code clone detection [fast: true]
|
||||||
goconst: Finds repeated strings that could be replaced by a constant
|
goconst: Finds repeated strings that could be replaced by a constant [fast: true]
|
||||||
gocyclo: Computes and checks the cyclomatic complexity of functions
|
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
|
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
|
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
|
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
|
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
|
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:
|
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
|
||||||
@ -174,30 +175,30 @@ golangci-lint linters
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Enabled By Default 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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [gas](https://github.com/GoASTScanner/gas) - Inspects source code for security problems
|
||||||
- [structcheck](https://github.com/opennota/check): Finds an unused struct fields
|
- [structcheck](https://github.com/opennota/check) - Finds an unused struct fields
|
||||||
- [varcheck](https://github.com/opennota/check): Finds unused global variables and constants
|
- [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
|
- [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
|
- [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).
|
- typecheck - Like the front-end of a Go compiler, parses and type-checks Go code
|
||||||
|
|
||||||
## Disabled By Default Linters (`-E/--enable`)
|
## 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
|
- [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
|
- [interfacer](https://github.com/mvdan/interfacer) - Linter that suggests narrower interface types
|
||||||
- [unconvert](https://github.com/mdempsky/unconvert): Remove unnecessary type conversions
|
- [unconvert](https://github.com/mdempsky/unconvert) - Remove unnecessary type conversions
|
||||||
- [dupl](https://github.com/mibk/dupl): Tool for code clone detection
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [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
|
- [depguard](https://github.com/OpenPeeDeeP/depguard) - Go linter that checks if package imports are in a list of acceptable packages
|
||||||
|
|
||||||
# Configuration
|
# Configuration
|
||||||
## Command-Line Options
|
## 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:
|
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
|
```yaml
|
||||||
run:
|
run:
|
||||||
deadline: 30s
|
|
||||||
tests: true
|
tests: true
|
||||||
|
|
||||||
linters-settings:
|
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 [bradleyfalzon/revgrep](https://github.com/bradleyfalzon/revgrep) for cool diff tool.
|
||||||
|
|
||||||
Thanks to developers and authors of used linters:
|
Thanks to developers and authors of used linters:
|
||||||
- [golang/vet](https://golang.org/cmd/vet/)
|
- [kisielk](https://github.com/kisielk)
|
||||||
- [kisielk/errcheck](https://github.com/kisielk/errcheck)
|
- [golang](https://github.com/golang)
|
||||||
- [staticcheck](https://staticcheck.io/)
|
- [dominikh](https://github.com/dominikh)
|
||||||
- [dominikh/go-tools/unused](https://github.com/dominikh/go-tools/tree/master/cmd/unused)
|
- [GoASTScanner](https://github.com/GoASTScanner)
|
||||||
- [dominikh/go-tools/gosimple](https://github.com/dominikh/go-tools/tree/master/cmd/gosimple)
|
- [opennota](https://github.com/opennota)
|
||||||
- [GoASTScanner/gas](https://github.com/GoASTScanner/gas)
|
- [mvdan](https://github.com/mvdan)
|
||||||
- [opennota/check](https://github.com/opennota/check)
|
- [mdempsky](https://github.com/mdempsky)
|
||||||
- [gordonklaus/ineffassign](https://github.com/gordonklaus/ineffassign)
|
- [gordonklaus](https://github.com/gordonklaus)
|
||||||
- [remyoudompheng/go-misc/deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode)
|
- [mibk](https://github.com/mibk)
|
||||||
- [golang/lint](https://github.com/golang/lint)
|
- [jgautheron](https://github.com/jgautheron)
|
||||||
- [mvdan/interfacer](https://github.com/mvdan/interfacer)
|
- [remyoudompheng](https://github.com/remyoudompheng)
|
||||||
- [mdempsky/unconvert](https://github.com/mdempsky/unconvert)
|
- [alecthomas](https://github.com/alecthomas)
|
||||||
- [mibk/dupl](https://github.com/mibk/dupl)
|
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
|
||||||
- [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)
|
|
||||||
|
|
||||||
# Future Plans
|
# Future Plans
|
||||||
1. Upstream all changes of forked linters.
|
1. Upstream all changes of forked linters.
|
||||||
|
360
README.md.tmpl
Normal file
360
README.md.tmpl
Normal file
@ -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.
|
||||||
|
|
||||||
|
<a href="https://golangci.com/"><img src="docs/go.png" width="250px"></a>
|
||||||
|
|
||||||
|
* [Install](#install)
|
||||||
|
* [Demo](#demo)
|
||||||
|
* [Quick Start](#quick-start)
|
||||||
|
* [Comparison](#comparison)
|
||||||
|
* [<code>golangci-lint</code> vs <code>gometalinter</code>](#golangci-lint-vs-gometalinter)
|
||||||
|
* [<code>golangci-lint</code> 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=<defaul golangci-lint excludes> --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).
|
@ -6,7 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"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/golangci/golangci-lint/pkg/printers"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
@ -20,7 +21,7 @@ func (e *Executor) initLinters() {
|
|||||||
e.rootCmd.AddCommand(lintersCmd)
|
e.rootCmd.AddCommand(lintersCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLinterConfigs(lcs []pkg.LinterConfig) {
|
func printLinterConfigs(lcs []linter.Config) {
|
||||||
for _, lc := range lcs {
|
for _, lc := range lcs {
|
||||||
fmt.Fprintf(printers.StdOut, "%s: %s [fast: %t]\n", color.YellowString(lc.Linter.Name()),
|
fmt.Fprintf(printers.StdOut, "%s: %s [fast: %t]\n", color.YellowString(lc.Linter.Name()),
|
||||||
lc.Linter.Desc(), !lc.DoesFullImport)
|
lc.Linter.Desc(), !lc.DoesFullImport)
|
||||||
@ -28,8 +29,8 @@ func printLinterConfigs(lcs []pkg.LinterConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
|
func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
|
||||||
var enabledLCs, disabledLCs []pkg.LinterConfig
|
var enabledLCs, disabledLCs []linter.Config
|
||||||
for _, lc := range pkg.GetAllSupportedLinterConfigs() {
|
for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
|
||||||
if lc.EnabledByDefault {
|
if lc.EnabledByDefault {
|
||||||
enabledLCs = append(enabledLCs, lc)
|
enabledLCs = append(enabledLCs, lc)
|
||||||
} else {
|
} else {
|
||||||
@ -43,8 +44,8 @@ func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
|
|||||||
printLinterConfigs(disabledLCs)
|
printLinterConfigs(disabledLCs)
|
||||||
|
|
||||||
color.Green("\nLinters presets:")
|
color.Green("\nLinters presets:")
|
||||||
for _, p := range pkg.AllPresets() {
|
for _, p := range lintersdb.AllPresets() {
|
||||||
linters := pkg.GetAllLinterConfigsForPreset(p)
|
linters := lintersdb.GetAllLinterConfigsForPreset(p)
|
||||||
linterNames := []string{}
|
linterNames := []string{}
|
||||||
for _, lc := range linters {
|
for _, lc := range linters {
|
||||||
linterNames = append(linterNames, lc.Linter.Name())
|
linterNames = append(linterNames, lc.Linter.Name())
|
||||||
|
@ -12,9 +12,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg"
|
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"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/printers"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
"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.EnableAll, "enable-all", false, "Enable all linters")
|
||||||
runCmd.Flags().BoolVar(&lc.DisableAll, "disable-all", false, "Disable all linters")
|
runCmd.Flags().BoolVar(&lc.DisableAll, "disable-all", false, "Disable all linters")
|
||||||
runCmd.Flags().StringSliceVarP(&lc.Presets, "presets", "p", []string{},
|
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")
|
runCmd.Flags().BoolVar(&lc.Fast, "fast", false, "Run only fast linters from enabled linters set")
|
||||||
|
|
||||||
// Issues config
|
// Issues config
|
||||||
@ -122,16 +122,12 @@ func (e *Executor) initRun() {
|
|||||||
func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan result.Issue, error) {
|
func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan result.Issue, error) {
|
||||||
e.cfg.Run.Args = args
|
e.cfg.Run.Args = args
|
||||||
|
|
||||||
linters, err := pkg.GetEnabledLinters(e.cfg)
|
linters, err := lintersdb.GetEnabledLinters(e.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctxLinters := make([]lint.LinterConfig, 0, len(linters))
|
lintCtx, err := lint.LoadContext(ctx, linters, e.cfg)
|
||||||
for _, lc := range linters {
|
|
||||||
ctxLinters = append(ctxLinters, lint.LinterConfig(lc))
|
|
||||||
}
|
|
||||||
lintCtx, err := lint.BuildContext(ctx, ctxLinters, e.cfg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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))
|
return runner.Run(ctx, linters, lintCtx), nil
|
||||||
for _, lc := range linters {
|
|
||||||
runLinters = append(runLinters, lint.RunnerLinterConfig(lc))
|
|
||||||
}
|
|
||||||
return runner.Run(ctx, runLinters, lintCtx), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
|
func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
deadcodeAPI "github.com/golangci/go-misc/deadcode"
|
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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ func (Deadcode) Desc() string {
|
|||||||
return "Finds unused code"
|
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)
|
issues, err := deadcodeAPI.Run(lintCtx.Program)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
depguardAPI "github.com/OpenPeeDeeP/depguard"
|
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"
|
"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"
|
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{
|
dg := &depguardAPI.Depguard{
|
||||||
Packages: lintCtx.Settings().Depguard.Packages,
|
Packages: lintCtx.Settings().Depguard.Packages,
|
||||||
IncludeGoRoot: lintCtx.Settings().Depguard.IncludeGoRoot,
|
IncludeGoRoot: lintCtx.Settings().Depguard.IncludeGoRoot,
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
duplAPI "github.com/mibk/dupl"
|
duplAPI "github.com/mibk/dupl"
|
||||||
)
|
)
|
||||||
@ -20,7 +20,7 @@ func (Dupl) Desc() string {
|
|||||||
return "Tool for code clone detection"
|
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)
|
issues, err := duplAPI.Run(lintCtx.Paths.Files, lintCtx.Settings().Dupl.Threshold)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
errcheckAPI "github.com/kisielk/errcheck/golangci"
|
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"
|
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
|
errCfg := &lintCtx.Settings().Errcheck
|
||||||
issues, err := errcheckAPI.Run(lintCtx.Program, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
|
issues, err := errcheckAPI.Run(lintCtx.Program, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/GoASTScanner/gas"
|
"github.com/GoASTScanner/gas"
|
||||||
"github.com/GoASTScanner/gas/rules"
|
"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/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -25,7 +25,7 @@ func (Gas) Desc() string {
|
|||||||
return "Inspects source code for security problems"
|
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()
|
gasConfig := gas.NewConfig()
|
||||||
enabledRules := rules.Generate()
|
enabledRules := rules.Generate()
|
||||||
logger := log.New(ioutil.Discard, "", 0)
|
logger := log.New(ioutil.Discard, "", 0)
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
goconstAPI "github.com/golangci/goconst"
|
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"
|
"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"
|
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
|
var goconstIssues []goconstAPI.Issue
|
||||||
// TODO: make it cross-package: pass package names inside goconst
|
// TODO: make it cross-package: pass package names inside goconst
|
||||||
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
|
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
gocycloAPI "github.com/golangci/gocyclo/pkg/gocyclo"
|
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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ func (Gocyclo) Desc() string {
|
|||||||
return "Computes and checks the cyclomatic complexity of functions"
|
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
|
var stats []gocycloAPI.Stat
|
||||||
for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
|
for _, f := range lintCtx.ASTCache.GetAllValidFiles() {
|
||||||
stats = gocycloAPI.BuildStats(f.F, f.Fset, stats)
|
stats = gocycloAPI.BuildStats(f.F, f.Fset, stats)
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
gofmtAPI "github.com/golangci/gofmt/gofmt"
|
gofmtAPI "github.com/golangci/gofmt/gofmt"
|
||||||
goimportsAPI "github.com/golangci/gofmt/goimports"
|
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/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"sourcegraph.com/sourcegraph/go-diff/diff"
|
"sourcegraph.com/sourcegraph/go-diff/diff"
|
||||||
@ -102,7 +102,7 @@ func (g Gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
|||||||
return issues, nil
|
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
|
var issues []result.Issue
|
||||||
|
|
||||||
for _, f := range lintCtx.Paths.Files {
|
for _, f := range lintCtx.Paths.Files {
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
lintAPI "github.com/golang/lint"
|
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/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/sirupsen/logrus"
|
"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"
|
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 issues []result.Issue
|
||||||
var lintErr error
|
var lintErr error
|
||||||
for _, pkgFiles := range lintCtx.Paths.FilesGrouppedByDirs() {
|
for _, pkgFiles := range lintCtx.Paths.FilesGrouppedByDirs() {
|
||||||
|
@ -3,7 +3,7 @@ package golinters
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
govetAPI "github.com/golangci/govet"
|
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"
|
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
|
// TODO: check .S asm files: govet can do it if pass dirs
|
||||||
var govetIssues []govetAPI.Issue
|
var govetIssues []govetAPI.Issue
|
||||||
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
|
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
ineffassignAPI "github.com/golangci/ineffassign"
|
ineffassignAPI "github.com/golangci/ineffassign"
|
||||||
)
|
)
|
||||||
@ -19,7 +19,7 @@ func (Ineffassign) Desc() string {
|
|||||||
return "Detects when assignments to existing variables are not used"
|
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)
|
issues := ineffassignAPI.Run(lintCtx.Paths.Files)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"mvdan.cc/interfacer/check"
|
"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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ func (Interfacer) Desc() string {
|
|||||||
return "Linter that suggests narrower interface types"
|
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 := new(check.Checker)
|
||||||
c.Program(lintCtx.Program)
|
c.Program(lintCtx.Program)
|
||||||
c.ProgramSSA(lintCtx.SSAProgram)
|
c.ProgramSSA(lintCtx.SSAProgram)
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
malignedAPI "github.com/golangci/maligned"
|
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"
|
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)
|
issues := malignedAPI.Run(lintCtx.Program)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
megacheckAPI "github.com/golangci/go-tools/cmd/megacheck"
|
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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,15 +42,15 @@ func (m Megacheck) Name() string {
|
|||||||
func (m Megacheck) Desc() string {
|
func (m Megacheck) Desc() string {
|
||||||
descs := map[string]string{
|
descs := map[string]string{
|
||||||
"unused": "Checks Go code for unused constants, variables, functions and types",
|
"unused": "Checks Go code for unused constants, variables, functions and types",
|
||||||
"gosimple": "Linter for Go source code that specialises on simplifying code",
|
"gosimple": "Linter for Go source code that specializes in simplifying a code",
|
||||||
"staticcheck": "Staticcheck is go vet on steroids, applying a ton of static analysis checks",
|
"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",
|
"megacheck": "3 sub-linters in one: unused, gosimple and staticcheck",
|
||||||
}
|
}
|
||||||
|
|
||||||
return descs[m.Name()]
|
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,
|
issues := megacheckAPI.Run(lintCtx.Program, lintCtx.LoaderConfig, lintCtx.SSAProgram,
|
||||||
m.StaticcheckEnabled, m.GosimpleEnabled, m.UnusedEnabled)
|
m.StaticcheckEnabled, m.GosimpleEnabled, m.UnusedEnabled)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
structcheckAPI "github.com/golangci/check/cmd/structcheck"
|
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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,10 +16,10 @@ func (Structcheck) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (Structcheck) Desc() 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)
|
issues := structcheckAPI.Run(lintCtx.Program, lintCtx.Settings().Structcheck.CheckExportedFields)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"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 {
|
if lintCtx.NotCompilingPackages == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package golinters
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
unconvertAPI "github.com/golangci/unconvert"
|
unconvertAPI "github.com/golangci/unconvert"
|
||||||
)
|
)
|
||||||
@ -18,7 +18,7 @@ func (Unconvert) Desc() string {
|
|||||||
return "Remove unnecessary type conversions"
|
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)
|
positions := unconvertAPI.Run(lintCtx.Program)
|
||||||
if len(positions) == 0 {
|
if len(positions) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
varcheckAPI "github.com/golangci/check/cmd/varcheck"
|
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"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ func (Varcheck) Desc() string {
|
|||||||
return "Finds unused global variables and constants"
|
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)
|
issues := varcheckAPI.Run(lintCtx.Program, lintCtx.Settings().Varcheck.CheckExportedFields)
|
||||||
if len(issues) == 0 {
|
if len(issues) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
69
pkg/lint/linter/config.go
Normal file
69
pkg/lint/linter/config.go
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
}
|
23
pkg/lint/linter/context.go
Normal file
23
pkg/lint/linter/context.go
Normal file
@ -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
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package lint
|
package linter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
@ -1,4 +1,4 @@
|
|||||||
package pkg
|
package lintersdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -8,21 +8,12 @@ import (
|
|||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/golinters"
|
"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"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
PresetFormatting = "format"
|
|
||||||
PresetComplexity = "complexity"
|
|
||||||
PresetStyle = "style"
|
|
||||||
PresetBugs = "bugs"
|
|
||||||
PresetUnused = "unused"
|
|
||||||
PresetPerformance = "performance"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AllPresets() []string {
|
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 {
|
func allPresetsSet() map[string]bool {
|
||||||
@ -33,64 +24,12 @@ func allPresetsSet() map[string]bool {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinterConfig struct {
|
var nameToLC map[string]linter.Config
|
||||||
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 nameToLCOnce sync.Once
|
var nameToLCOnce sync.Once
|
||||||
|
|
||||||
func getLinterConfig(name string) *LinterConfig {
|
func getLinterConfig(name string) *linter.Config {
|
||||||
nameToLCOnce.Do(func() {
|
nameToLCOnce.Do(func() {
|
||||||
nameToLC = make(map[string]LinterConfig)
|
nameToLC = make(map[string]linter.Config)
|
||||||
for _, lc := range GetAllSupportedLinterConfigs() {
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
||||||
nameToLC[lc.Linter.Name()] = lc
|
nameToLC[lc.Linter.Name()] = lc
|
||||||
}
|
}
|
||||||
@ -104,8 +43,8 @@ func getLinterConfig(name string) *LinterConfig {
|
|||||||
return &lc
|
return &lc
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableLinterConfigs(lcs []LinterConfig, isEnabled func(lc *LinterConfig) bool) []LinterConfig {
|
func enableLinterConfigs(lcs []linter.Config, isEnabled func(lc *linter.Config) bool) []linter.Config {
|
||||||
var ret []LinterConfig
|
var ret []linter.Config
|
||||||
for _, lc := range lcs {
|
for _, lc := range lcs {
|
||||||
lc.EnabledByDefault = isEnabled(&lc)
|
lc.EnabledByDefault = isEnabled(&lc)
|
||||||
ret = append(ret, lc)
|
ret = append(ret, lc)
|
||||||
@ -114,35 +53,113 @@ func enableLinterConfigs(lcs []LinterConfig, isEnabled func(lc *LinterConfig) bo
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllSupportedLinterConfigs() []LinterConfig {
|
func GetAllSupportedLinterConfigs() []linter.Config {
|
||||||
lcs := []LinterConfig{
|
lcs := []linter.Config{
|
||||||
newLinterConfig(golinters.Govet{}).WithPresets(PresetBugs).WithSpeed(4),
|
linter.NewConfig(golinters.Govet{}).
|
||||||
newLinterConfig(golinters.Errcheck{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(10),
|
WithPresets(linter.PresetBugs).
|
||||||
newLinterConfig(golinters.Golint{}).WithPresets(PresetStyle).WithSpeed(3),
|
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().
|
linter.NewConfig(golinters.Megacheck{StaticcheckEnabled: true}).
|
||||||
WithPresets(PresetBugs).WithSpeed(2),
|
WithSSA().
|
||||||
newLinterConfig(golinters.Megacheck{UnusedEnabled: true}).WithSSA().WithPresets(PresetUnused).WithSpeed(5),
|
WithPresets(linter.PresetBugs).
|
||||||
newLinterConfig(golinters.Megacheck{GosimpleEnabled: true}).WithSSA().WithPresets(PresetStyle).WithSpeed(5),
|
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),
|
linter.NewConfig(golinters.Gas{}).
|
||||||
newLinterConfig(golinters.Structcheck{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
|
WithFullImport().
|
||||||
newLinterConfig(golinters.Varcheck{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
|
WithPresets(linter.PresetBugs).
|
||||||
newLinterConfig(golinters.Interfacer{}).WithSSA().WithPresets(PresetStyle).WithSpeed(6),
|
WithSpeed(8).
|
||||||
newLinterConfig(golinters.Unconvert{}).WithFullImport().WithPresets(PresetStyle).WithSpeed(10),
|
WithURL("https://github.com/GoASTScanner/gas"),
|
||||||
newLinterConfig(golinters.Ineffassign{}).WithPresets(PresetUnused).WithSpeed(9),
|
linter.NewConfig(golinters.Structcheck{}).
|
||||||
newLinterConfig(golinters.Dupl{}).WithPresets(PresetStyle).WithSpeed(7),
|
WithFullImport().
|
||||||
newLinterConfig(golinters.Goconst{}).WithPresets(PresetStyle).WithSpeed(9),
|
WithPresets(linter.PresetUnused).
|
||||||
newLinterConfig(golinters.Deadcode{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
|
WithSpeed(10).
|
||||||
newLinterConfig(golinters.Gocyclo{}).WithPresets(PresetComplexity).WithSpeed(8),
|
WithURL("https://github.com/opennota/check"),
|
||||||
newLinterConfig(golinters.TypeCheck{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(10),
|
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),
|
linter.NewConfig(golinters.Gofmt{}).
|
||||||
newLinterConfig(golinters.Gofmt{UseGoimports: true}).WithPresets(PresetFormatting).WithSpeed(5),
|
WithPresets(linter.PresetFormatting).
|
||||||
newLinterConfig(golinters.Maligned{}).WithFullImport().WithPresets(PresetPerformance).WithSpeed(10),
|
WithSpeed(7).
|
||||||
newLinterConfig(golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}).
|
WithURL("https://golang.org/cmd/gofmt/"),
|
||||||
WithSSA().WithPresets(PresetStyle, PresetBugs, PresetUnused).WithSpeed(1),
|
linter.NewConfig(golinters.Gofmt{UseGoimports: true}).
|
||||||
newLinterConfig(golinters.Depguard{}).WithFullImport().WithPresets(PresetStyle).WithSpeed(6),
|
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" {
|
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
|
||||||
@ -152,7 +169,7 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
|||||||
golinters.Maligned{}.Name(): true, // rarely usable
|
golinters.Maligned{}.Name(): true, // rarely usable
|
||||||
golinters.TypeCheck{}.Name(): true, // annoying because of different building envs
|
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()]
|
return !disabled[lc.Linter.Name()]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -170,13 +187,13 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
|||||||
golinters.Deadcode{}.Name(): true,
|
golinters.Deadcode{}.Name(): true,
|
||||||
golinters.TypeCheck{}.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()]
|
return enabled[lc.Linter.Name()]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllEnabledByDefaultLinters() []LinterConfig {
|
func getAllEnabledByDefaultLinters() []linter.Config {
|
||||||
var ret []LinterConfig
|
var ret []linter.Config
|
||||||
for _, lc := range GetAllSupportedLinterConfigs() {
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
||||||
if lc.EnabledByDefault {
|
if lc.EnabledByDefault {
|
||||||
ret = append(ret, lc)
|
ret = append(ret, lc)
|
||||||
@ -186,8 +203,8 @@ func getAllEnabledByDefaultLinters() []LinterConfig {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func linterConfigsToMap(lcs []LinterConfig) map[string]*LinterConfig {
|
func linterConfigsToMap(lcs []linter.Config) map[string]*linter.Config {
|
||||||
ret := map[string]*LinterConfig{}
|
ret := map[string]*linter.Config{}
|
||||||
for _, lc := range lcs {
|
for _, lc := range lcs {
|
||||||
lc := lc // local copy
|
lc := lc // local copy
|
||||||
ret[lc.Linter.Name()] = &lc
|
ret[lc.Linter.Name()] = &lc
|
||||||
@ -276,8 +293,8 @@ func validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAllLinterConfigsForPreset(p string) []LinterConfig {
|
func GetAllLinterConfigsForPreset(p string) []linter.Config {
|
||||||
ret := []LinterConfig{}
|
ret := []linter.Config{}
|
||||||
for _, lc := range GetAllSupportedLinterConfigs() {
|
for _, lc := range GetAllSupportedLinterConfigs() {
|
||||||
for _, ip := range lc.InPresets {
|
for _, ip := range lc.InPresets {
|
||||||
if p == ip {
|
if p == ip {
|
||||||
@ -290,8 +307,8 @@ func GetAllLinterConfigsForPreset(p string) []LinterConfig {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []LinterConfig) map[string]*LinterConfig { // nolint:gocyclo
|
func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []linter.Config) map[string]*linter.Config { // nolint:gocyclo
|
||||||
resultLintersSet := map[string]*LinterConfig{}
|
resultLintersSet := map[string]*linter.Config{}
|
||||||
switch {
|
switch {
|
||||||
case len(lcfg.Presets) != 0:
|
case len(lcfg.Presets) != 0:
|
||||||
break // imply --disable-all
|
break // imply --disable-all
|
||||||
@ -345,7 +362,7 @@ func getAllMegacheckSubLinterNames() []string {
|
|||||||
return []string{unusedName, gosimpleName, staticcheckName}
|
return []string{unusedName, gosimpleName, staticcheckName}
|
||||||
}
|
}
|
||||||
|
|
||||||
func optimizeLintersSet(linters map[string]*LinterConfig) {
|
func optimizeLintersSet(linters map[string]*linter.Config) {
|
||||||
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
|
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
|
||||||
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
|
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
|
||||||
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
|
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
|
||||||
@ -379,14 +396,14 @@ func optimizeLintersSet(linters map[string]*LinterConfig) {
|
|||||||
linters[m.Name()] = &lc
|
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 {
|
if err := validateEnabledDisabledLintersConfig(&cfg.Linters); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resultLintersSet := getEnabledLintersSet(&cfg.Linters, getAllEnabledByDefaultLinters())
|
resultLintersSet := getEnabledLintersSet(&cfg.Linters, getAllEnabledByDefaultLinters())
|
||||||
|
|
||||||
var resultLinters []LinterConfig
|
var resultLinters []linter.Config
|
||||||
for _, lc := range resultLintersSet {
|
for _, lc := range resultLintersSet {
|
||||||
resultLinters = append(resultLinters, *lc)
|
resultLinters = append(resultLinters, *lc)
|
||||||
}
|
}
|
||||||
@ -410,7 +427,7 @@ func uniqStrings(ss []string) []string {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func verbosePrintLintersStatus(cfg *config.Config, lcs []LinterConfig) {
|
func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config) {
|
||||||
var linterNames []string
|
var linterNames []string
|
||||||
for _, lc := range lcs {
|
for _, lc := range lcs {
|
||||||
linterNames = append(linterNames, lc.Linter.Name())
|
linterNames = append(linterNames, lc.Linter.Name())
|
@ -1,10 +1,11 @@
|
|||||||
package pkg
|
package lintersdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ func TestGetEnabledLintersSet(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
defaultLinters := []LinterConfig{}
|
defaultLinters := []linter.Config{}
|
||||||
for _, ln := range c.def {
|
for _, ln := range c.def {
|
||||||
defaultLinters = append(defaultLinters, *getLinterConfig(ln))
|
defaultLinters = append(defaultLinters, *getLinterConfig(ln))
|
||||||
}
|
}
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||||
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
@ -32,12 +33,7 @@ func (c *Context) Settings() *config.LintersSettings {
|
|||||||
return &c.Cfg.LintersSettings
|
return &c.Cfg.LintersSettings
|
||||||
}
|
}
|
||||||
|
|
||||||
type LinterConfig interface {
|
func isFullImportNeeded(linters []linter.Config) bool {
|
||||||
NeedsProgramLoading() bool
|
|
||||||
NeedsSSARepresentation() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFullImportNeeded(linters []LinterConfig) bool {
|
|
||||||
for _, linter := range linters {
|
for _, linter := range linters {
|
||||||
if linter.NeedsProgramLoading() {
|
if linter.NeedsProgramLoading() {
|
||||||
return true
|
return true
|
||||||
@ -47,7 +43,7 @@ func isFullImportNeeded(linters []LinterConfig) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isSSAReprNeeded(linters []LinterConfig) bool {
|
func isSSAReprNeeded(linters []linter.Config) bool {
|
||||||
for _, linter := range linters {
|
for _, linter := range linters {
|
||||||
if linter.NeedsSSARepresentation() {
|
if linter.NeedsSSARepresentation() {
|
||||||
return true
|
return true
|
||||||
@ -57,7 +53,7 @@ func isSSAReprNeeded(linters []LinterConfig) bool {
|
|||||||
return false
|
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) {
|
if !isFullImportNeeded(linters) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
@ -117,7 +113,7 @@ func discoverGoRoot() (string, error) {
|
|||||||
// separateNotCompilingPackages moves not compiling packages into separate slices:
|
// separateNotCompilingPackages moves not compiling packages into separate slices:
|
||||||
// a lot of linters crash on such packages. Leave them only for those linters
|
// a lot of linters crash on such packages. Leave them only for those linters
|
||||||
// which can work with them.
|
// which can work with them.
|
||||||
func separateNotCompilingPackages(lintCtx *Context) {
|
func separateNotCompilingPackages(lintCtx *linter.Context) {
|
||||||
prog := lintCtx.Program
|
prog := lintCtx.Program
|
||||||
|
|
||||||
if prog.Created != nil {
|
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
|
// Set GOROOT to have working cross-compilation: cross-compiled binaries
|
||||||
// have invalid GOROOT. XXX: can't use runtime.GOROOT().
|
// have invalid GOROOT. XXX: can't use runtime.GOROOT().
|
||||||
goroot, err := discoverGoRoot()
|
goroot, err := discoverGoRoot()
|
||||||
@ -183,7 +179,7 @@ func BuildContext(ctx context.Context, linters []LinterConfig, cfg *config.Confi
|
|||||||
astCache = astcache.LoadFromFiles(paths.Files)
|
astCache = astcache.LoadFromFiles(paths.Files)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := &Context{
|
ret := &linter.Context{
|
||||||
Paths: paths,
|
Paths: paths,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
Program: prog,
|
Program: prog,
|
@ -7,24 +7,17 @@ import (
|
|||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||||
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
||||||
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/stretchr/testify/assert"
|
"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) {
|
func TestASTCacheLoading(t *testing.T) {
|
||||||
ctx := context.Background()
|
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 {
|
for _, inputPath := range inputPaths {
|
||||||
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true)
|
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
@ -9,6 +9,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
"github.com/golangci/golangci-lint/pkg/result/processors"
|
||||||
"github.com/golangci/golangci-lint/pkg/timeutils"
|
"github.com/golangci/golangci-lint/pkg/timeutils"
|
||||||
@ -20,12 +21,12 @@ type SimpleRunner struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type lintRes struct {
|
type lintRes struct {
|
||||||
linter Linter
|
linter linter.Config
|
||||||
err error
|
err error
|
||||||
issues []result.Issue
|
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() {
|
defer func() {
|
||||||
if panicData := recover(); panicData != nil {
|
if panicData := recover(); panicData != nil {
|
||||||
err = fmt.Errorf("panic occured: %s", panicData)
|
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)
|
sw := timeutils.NewStopwatch(name)
|
||||||
defer sw.Print()
|
defer sw.Print()
|
||||||
|
|
||||||
@ -44,7 +54,7 @@ func runWorker(ctx context.Context, lintCtx *Context, tasksCh <-chan Linter, lin
|
|||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return
|
return
|
||||||
case linter, ok := <-tasksCh:
|
case lc, ok := <-tasksCh:
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,11 +65,11 @@ func runWorker(ctx context.Context, lintCtx *Context, tasksCh <-chan Linter, lin
|
|||||||
}
|
}
|
||||||
var issues []result.Issue
|
var issues []result.Issue
|
||||||
var err error
|
var err error
|
||||||
sw.TrackStage(linter.Name(), func() {
|
sw.TrackStage(lc.Linter.Name(), func() {
|
||||||
issues, err = runLinterSafe(ctx, lintCtx, linter)
|
issues, err = runLinterSafe(ctx, lintCtx, lc)
|
||||||
})
|
})
|
||||||
lintResultsCh <- lintRes{
|
lintResultsCh <- lintRes{
|
||||||
linter: linter,
|
linter: lc,
|
||||||
err: err,
|
err: err,
|
||||||
issues: issues,
|
issues: issues,
|
||||||
}
|
}
|
||||||
@ -87,13 +97,8 @@ func logWorkersStat(workersFinishTimes []time.Time) {
|
|||||||
logrus.Infof("Workers idle times: %s", strings.Join(logStrings, ", "))
|
logrus.Infof("Workers idle times: %s", strings.Join(logStrings, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunnerLinterConfig interface {
|
func getSortedLintersConfigs(linters []linter.Config) []linter.Config {
|
||||||
GetSpeed() int
|
ret := make([]linter.Config, len(linters))
|
||||||
GetLinter() Linter
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSortedLintersConfigs(linters []RunnerLinterConfig) []RunnerLinterConfig {
|
|
||||||
ret := make([]RunnerLinterConfig, len(linters))
|
|
||||||
copy(ret, linters)
|
copy(ret, linters)
|
||||||
|
|
||||||
sort.Slice(ret, func(i, j int) bool {
|
sort.Slice(ret, func(i, j int) bool {
|
||||||
@ -103,8 +108,8 @@ func getSortedLintersConfigs(linters []RunnerLinterConfig) []RunnerLinterConfig
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *Context, linters []RunnerLinterConfig) <-chan lintRes {
|
func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *linter.Context, linters []linter.Config) <-chan lintRes {
|
||||||
tasksCh := make(chan Linter, len(linters))
|
tasksCh := make(chan linter.Config, len(linters))
|
||||||
lintResultsCh := make(chan lintRes, len(linters))
|
lintResultsCh := make(chan lintRes, len(linters))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
@ -122,7 +127,7 @@ func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *Context, linters
|
|||||||
|
|
||||||
lcs := getSortedLintersConfigs(linters)
|
lcs := getSortedLintersConfigs(linters)
|
||||||
for _, lc := range lcs {
|
for _, lc := range lcs {
|
||||||
tasksCh <- lc.GetLinter()
|
tasksCh <- lc
|
||||||
}
|
}
|
||||||
close(tasksCh)
|
close(tasksCh)
|
||||||
|
|
||||||
@ -146,7 +151,7 @@ func (r SimpleRunner) processLintResults(ctx context.Context, inCh <-chan lintRe
|
|||||||
|
|
||||||
for res := range inCh {
|
for res := range inCh {
|
||||||
if res.err != nil {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +194,7 @@ func collectIssues(ctx context.Context, resCh <-chan lintRes) <-chan result.Issu
|
|||||||
return retIssues
|
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)
|
lintResultsCh := r.runWorkers(ctx, lintCtx, linters)
|
||||||
processedLintResultsCh := r.processLintResults(ctx, lintResultsCh)
|
processedLintResultsCh := r.processLintResults(ctx, lintResultsCh)
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
122
scripts/gen_readme/main.go
Normal file
122
scripts/gen_readme/main.go
Normal file
@ -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")
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user