mirror of
https://github.com/mgechev/revive.git
synced 2024-11-24 08:32:22 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
82b0df5338
@ -1,5 +1,6 @@
|
||||
language: go
|
||||
go: 1.11.x
|
||||
node_js: 11.15.0
|
||||
install: make install
|
||||
script:
|
||||
- make build
|
||||
|
54
README.md
54
README.md
@ -34,7 +34,13 @@ Here's how `revive` is different from `golint`:
|
||||
- [`excelize`](https://github.com/360EntSecGroup-Skylar/excelize) - Go library for reading and writing Microsoft Excel™ (XLSX) files
|
||||
- [`aurora`](https://github.com/xuri/aurora) - aurora is a web-based Beanstalk queue server console written in Go
|
||||
- [`soar`](https://github.com/XiaoMi/soar) - SQL Optimizer And Rewriter
|
||||
- [`gorush`](https://github.com/appleboy/gorush) - A push notification server written in Go (Golang)
|
||||
- [`gorush`](https://github.com/appleboy/gorush) - A push notification server written in Go (Golang)a
|
||||
- [`go-echarts`](https://github.com/chenjiandongx/go-echarts) - The adorable charts library for Golang
|
||||
- [`reviewdog`](https://github.com/reviewdog/reviewdog) - Automated code review tool integrated with any code analysis tools regardless of programming language
|
||||
- [`sklearn`](https://github.com/pa-m/sklearn) - A partial port of scikit-learn written in Go
|
||||
- [`lorawan-stack`](https://github.com/TheThingsNetwork/lorawan-stack) - The Things Network Stack for LoRaWAN V3
|
||||
- [`gofight`](https://github.com/appleboy/gofight) - Testing API Handler written in Golang.
|
||||
- [`ggz`](https://github.com/go-ggz/ggz) - An URL shortener service written in Golang
|
||||
|
||||
*Open a PR to add your project*.
|
||||
|
||||
@ -47,6 +53,7 @@ Here's how `revive` is different from `golint`:
|
||||
- [revive](#revive)
|
||||
- [Usage](#usage)
|
||||
- [Text Editors](#text-editors)
|
||||
- [Bazel](#bazel)
|
||||
- [Installation](#installation)
|
||||
- [Command Line Flags](#command-line-flags)
|
||||
- [Sample Invocations](#sample-invocations)
|
||||
@ -80,6 +87,10 @@ Here's how `revive` is different from `golint`:
|
||||
|
||||
Since the default behavior of `revive` is compatible with `golint`, without providing any additional flags, the only difference you'd notice is faster execution.
|
||||
|
||||
### Bazel
|
||||
|
||||
If you want to use revive with Bazel, take a look at the [rules](https://github.com/atlassian/bazel-tools/tree/master/gorevive) that Atlassian maintains.
|
||||
|
||||
### Text Editors
|
||||
|
||||
- Support for VSCode in [vscode-go](https://github.com/Microsoft/vscode-go/pull/1699).
|
||||
@ -129,7 +140,7 @@ revive -config revive.toml -exclude file1.go -exclude file2.go -formatter friend
|
||||
- The output will be formatted with the `friendly` formatter
|
||||
- The linter will analyze `github.com/mgechev/revive` and the files in `package`
|
||||
|
||||
### Comment Annotations
|
||||
### Comment Directives
|
||||
|
||||
Using comments, you can disable the linter for the entire file or only range of lines:
|
||||
|
||||
@ -156,6 +167,28 @@ func Public() private {
|
||||
|
||||
This way, `revive` will not warn you for that you're returning an object of an unexported type, from an exported function.
|
||||
|
||||
You can document why you disable the linter by adding a trailing text in the directive, for example
|
||||
|
||||
```go
|
||||
//revive:disable Until the code is stable
|
||||
```
|
||||
```go
|
||||
//revive:disable:cyclomatic High complexity score but easy to understand
|
||||
```
|
||||
|
||||
You can also configure `revive` to enforce documenting linter disabling directives by adding
|
||||
|
||||
```toml
|
||||
[directive.specify-disable-reason]
|
||||
```
|
||||
|
||||
in the configuration. You can set the severity (defaults to _warning_) of the violation of this directive
|
||||
|
||||
```toml
|
||||
[directive.specify-disable-reason]
|
||||
severity = "error"
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
`revive` can be configured with a TOML file. Here's a sample configuration with explanation for the individual properties:
|
||||
@ -296,10 +329,11 @@ List of all available rules. The rules ported from `golint` are left unchanged a
|
||||
| [`empty-lines`](./RULES_DESCRIPTIONS.md#empty-lines) | n/a | Warns when there are heading or trailing newlines in a block | no | no |
|
||||
| [`line-length-limit`](./RULES_DESCRIPTIONS.md#line-length-limit) | int | Specifies the maximum number of characters in a line | no | no |
|
||||
| [`call-to-gc`](./RULES_DESCRIPTIONS.md#call-to-gc) | n/a | Warns on explicit call to the garbage collector | no | no |
|
||||
| [`duplicated-imports`](./RULES_DESCRIPTIONS#duplicated-imports) | n/a | Looks for packages that are imported two or more times | no | no |
|
||||
| [`duplicated-imports`](./RULES_DESCRIPTIONS.md#duplicated-imports) | n/a | Looks for packages that are imported two or more times | no | no |
|
||||
| [`import-shadowing`](./RULES_DESCRIPTIONS.md#import-shadowing) | n/a | Spots identifiers that shadow an import | no | no |
|
||||
| [`bare-return`](./RULES_DESCRIPTIONS#bare-return) | n/a | Warns on bare returns | no | no |
|
||||
| [`bare-return`](./RULES_DESCRIPTIONS.md#bare-return) | n/a | Warns on bare returns | no | no |
|
||||
| [`unused-receiver`](./RULES_DESCRIPTIONS.md#unused-receiver) | n/a | Suggests to rename or remove unused method receivers | no | no |
|
||||
| [`unhandled-error`](./RULES_DESCRIPTIONS.md#unhandled-error) | []string | Warns on unhandled errors returned by funcion calls | no | yes |
|
||||
|
||||
## Configurable rules
|
||||
|
||||
@ -387,7 +421,7 @@ Each formatter needs to implement the following interface:
|
||||
|
||||
```go
|
||||
type Formatter interface {
|
||||
Format(<-chan Failure, RulesConfig) (string, error)
|
||||
Format(<-chan Failure, Config) (string, error)
|
||||
Name() string
|
||||
}
|
||||
```
|
||||
@ -440,13 +474,13 @@ Currently, type checking is enabled by default. If you want to run the linter wi
|
||||
:---: |:---: |:---: |:---: |:---: |:---: |
|
||||
[mgechev](https://github.com/mgechev) |[chavacava](https://github.com/chavacava) |[xuri](https://github.com/xuri) |[gsamokovarov](https://github.com/gsamokovarov) |[morphy2k](https://github.com/morphy2k) |[tamird](https://github.com/tamird) |
|
||||
|
||||
[<img alt="AragurDEV" src="https://avatars0.githubusercontent.com/u/11004008?v=4&s=117" width="117">](https://github.com/AragurDEV) |[<img alt="yangdiangzb" src="https://avatars3.githubusercontent.com/u/16643665?v=4&s=117" width="117">](https://github.com/yangdiangzb) |[<img alt="jamesmaidment" src="https://avatars3.githubusercontent.com/u/2050324?v=4&s=117" width="117">](https://github.com/jamesmaidment) |[<img alt="mapreal19" src="https://avatars2.githubusercontent.com/u/3055997?v=4&s=117" width="117">](https://github.com/mapreal19) |[<img alt="markelog" src="https://avatars0.githubusercontent.com/u/945528?v=4&s=117" width="117">](https://github.com/markelog) |[<img alt="paul-at-start" src="https://avatars2.githubusercontent.com/u/5486775?v=4&s=117" width="117">](https://github.com/paul-at-start) |
|
||||
[<img alt="AragurDEV" src="https://avatars0.githubusercontent.com/u/11004008?v=4&s=117" width="117">](https://github.com/AragurDEV) |[<img alt="yangdiangzb" src="https://avatars3.githubusercontent.com/u/16643665?v=4&s=117" width="117">](https://github.com/yangdiangzb) |[<img alt="jamesmaidment" src="https://avatars3.githubusercontent.com/u/2050324?v=4&s=117" width="117">](https://github.com/jamesmaidment) |[<img alt="mapreal19" src="https://avatars2.githubusercontent.com/u/3055997?v=4&s=117" width="117">](https://github.com/mapreal19) |[<img alt="markelog" src="https://avatars0.githubusercontent.com/u/945528?v=4&s=117" width="117">](https://github.com/markelog) |[<img alt="pa-m" src="https://avatars2.githubusercontent.com/u/5503106?v=4&s=117" width="117">](https://github.com/pa-m) |
|
||||
:---: |:---: |:---: |:---: |:---: |:---: |
|
||||
[AragurDEV](https://github.com/AragurDEV) |[yangdiangzb](https://github.com/yangdiangzb) |[jamesmaidment](https://github.com/jamesmaidment) |[mapreal19](https://github.com/mapreal19) |[markelog](https://github.com/markelog) |[paul-at-start](https://github.com/paul-at-start) |
|
||||
[AragurDEV](https://github.com/AragurDEV) |[yangdiangzb](https://github.com/yangdiangzb) |[jamesmaidment](https://github.com/jamesmaidment) |[mapreal19](https://github.com/mapreal19) |[markelog](https://github.com/markelog) |[pa-m](https://github.com/pa-m) |
|
||||
|
||||
[<img alt="psapezhko" src="https://avatars3.githubusercontent.com/u/10865586?v=4&s=117" width="117">](https://github.com/psapezhko) |[<img alt="ridvansumset" src="https://avatars2.githubusercontent.com/u/26631560?v=4&s=117" width="117">](https://github.com/ridvansumset) |[<img alt="Jarema" src="https://avatars0.githubusercontent.com/u/7369771?v=4&s=117" width="117">](https://github.com/Jarema) |[<img alt="vkrol" src="https://avatars3.githubusercontent.com/u/153412?v=4&s=117" width="117">](https://github.com/vkrol) |[<img alt="haya14busa" src="https://avatars0.githubusercontent.com/u/3797062?v=4&s=117" width="117">](https://github.com/haya14busa) |
|
||||
:---: |:---: |:---: |:---: |:---: |
|
||||
[psapezhko](https://github.com/psapezhko) |[ridvansumset](https://github.com/ridvansumset) |[Jarema](https://github.com/Jarema) |[vkrol](https://github.com/vkrol) |[haya14busa](https://github.com/haya14busa) |
|
||||
[<img alt="paul-at-start" src="https://avatars2.githubusercontent.com/u/5486775?v=4&s=117" width="117">](https://github.com/paul-at-start) |[<img alt="psapezhko" src="https://avatars3.githubusercontent.com/u/10865586?v=4&s=117" width="117">](https://github.com/psapezhko) |[<img alt="ridvansumset" src="https://avatars2.githubusercontent.com/u/26631560?v=4&s=117" width="117">](https://github.com/ridvansumset) |[<img alt="Jarema" src="https://avatars0.githubusercontent.com/u/7369771?v=4&s=117" width="117">](https://github.com/Jarema) |[<img alt="vkrol" src="https://avatars3.githubusercontent.com/u/153412?v=4&s=117" width="117">](https://github.com/vkrol) |[<img alt="haya14busa" src="https://avatars0.githubusercontent.com/u/3797062?v=4&s=117" width="117">](https://github.com/haya14busa) |
|
||||
:---: |:---: |:---: |:---: |:---: |:---: |
|
||||
[paul-at-start](https://github.com/paul-at-start) |[psapezhko](https://github.com/psapezhko) |[ridvansumset](https://github.com/ridvansumset) |[Jarema](https://github.com/Jarema) |[vkrol](https://github.com/vkrol) |[haya14busa](https://github.com/haya14busa) |
|
||||
|
||||
## License
|
||||
|
||||
|
@ -51,6 +51,7 @@ List of all available rules.
|
||||
- [var-naming](#var-naming)
|
||||
- [var-declaration](#var-declaration)
|
||||
- [unexported-return](#unexported-return)
|
||||
- [unhandled-error](#unhandled-error)
|
||||
- [unnecessary-stmt](#unnecessary-stmt)
|
||||
- [unreachable-code](#unreachable-code)
|
||||
- [unused-parameter](#unused-parameter)
|
||||
@ -434,6 +435,18 @@ _Description_: This rule warns when an exported function or method returns a val
|
||||
|
||||
_Configuration_: N/A
|
||||
|
||||
## unhandled-error
|
||||
|
||||
_Description_: This rule warns when errors returned by a function are not explicitly handled on the caller side.
|
||||
|
||||
_Configuration_: function names to ignore
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[unhandled-error]
|
||||
arguments =["fmt.Printf", "myFunction"]
|
||||
```
|
||||
## unnecessary-stmt
|
||||
|
||||
_Description_: This rule suggests to remove redundant statements like a `break` at the end of a case block, for improving the code's readability.
|
||||
|
@ -77,6 +77,7 @@ var allRules = append([]lint.Rule{
|
||||
&rule.ImportShadowingRule{},
|
||||
&rule.BareReturnRule{},
|
||||
&rule.UnusedReceiverRule{},
|
||||
&rule.UnhandledErrorRule{},
|
||||
}, defaultRules...)
|
||||
|
||||
var allFormatters = []lint.Formatter{
|
||||
@ -141,6 +142,12 @@ func normalizeConfig(config *lint.Config) {
|
||||
}
|
||||
config.Rules[k] = v
|
||||
}
|
||||
for k, v := range config.Directives {
|
||||
if v.Severity == "" {
|
||||
v.Severity = severity
|
||||
}
|
||||
config.Directives[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,13 @@ func foo1() {
|
||||
var invalid_name2 = 1 //revive:disable-line:var-naming
|
||||
}
|
||||
|
||||
func foo11() {
|
||||
//revive:disable-next-line:var-naming I'm an Eiffel programmer thus I like underscores
|
||||
var invalid_name = 0
|
||||
var invalid_name2 = 1 //revive:disable-line:var-naming I'm an Eiffel programmer thus I like underscores
|
||||
}
|
||||
|
||||
|
||||
func foo2() {
|
||||
// revive:disable-next-line:var-naming
|
||||
//revive:disable
|
||||
@ -22,3 +29,10 @@ func foo3() {
|
||||
/* revive:disable-next-line:var-naming */
|
||||
var invalid_name3 = 0 // MATCH /don't use underscores in Go names; var invalid_name3 should be invalidName3/
|
||||
}
|
||||
|
||||
func foo2p1() {
|
||||
//revive:disable Underscores are fine
|
||||
var invalid_name = 0
|
||||
//revive:enable No! Underscores are not nice!
|
||||
var invalid_name2 = 1 // MATCH /don't use underscores in Go names; var invalid_name2 should be invalidName2/
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package fixtures
|
||||
|
||||
import "time"
|
||||
|
||||
type decodeAndValidateRequest struct {
|
||||
// BEAWRE : the flag of URLParam should match the const string URLParam
|
||||
URLParam string `json:"-" path:"url_param" validate:"numeric"`
|
||||
@ -17,6 +19,8 @@ type decodeAndValidateRequest struct {
|
||||
MandatoryStruct4 mandatoryStruct `json:"mandatoryStruct" required:"false"`
|
||||
OptionalStruct *optionalStruct `json:"optionalStruct,omitempty"`
|
||||
OptionalQuery string `json:"-" querystring:"queryfoo"`
|
||||
optionalQuery string `json:"-" querystring:"queryfoo"` // MATCH /tag on not-exported field optionalQuery/
|
||||
|
||||
}
|
||||
|
||||
type RangeAllocation struct {
|
||||
|
19
fixtures/unhandled-error-w-ignorelist.go
Normal file
19
fixtures/unhandled-error-w-ignorelist.go
Normal file
@ -0,0 +1,19 @@
|
||||
package fixtures
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func unhandledError1(a int) (int, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func unhandledError2() error {
|
||||
_, err := unhandledError1(1)
|
||||
unhandledError1(1)
|
||||
fmt.Fprintf(nil, "") // MATCH /Unhandled error in call to function fmt.Fprintf/
|
||||
os.Chdir("..")
|
||||
_ = os.Chdir("..")
|
||||
return err
|
||||
}
|
19
fixtures/unhandled-error.go
Normal file
19
fixtures/unhandled-error.go
Normal file
@ -0,0 +1,19 @@
|
||||
package fixtures
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func unhandledError1(a int) (int, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func unhandledError2() error {
|
||||
_, err := unhandledError1(1)
|
||||
unhandledError1(1) // MATCH /Unhandled error in call to function unhandledError1/
|
||||
fmt.Fprintf(nil, "") // MATCH /Unhandled error in call to function fmt.Fprintf/
|
||||
os.Chdir("..") // MATCH /Unhandled error in call to function os.Chdir/
|
||||
_ = os.Chdir("..")
|
||||
return err
|
||||
}
|
@ -28,7 +28,7 @@ type issue struct {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Checkstyle) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Checkstyle) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var issues = map[string][]issue{}
|
||||
for failure := range failures {
|
||||
buf := new(bytes.Buffer)
|
||||
|
@ -18,7 +18,7 @@ func (f *Default) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Default) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure)
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ func (f *Friendly) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Friendly) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
errorMap := map[string]int{}
|
||||
warningMap := map[string]int{}
|
||||
totalErrors := 0
|
||||
|
@ -24,7 +24,7 @@ type jsonObject struct {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *JSON) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *JSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var slice []jsonObject
|
||||
for failure := range failures {
|
||||
obj := jsonObject{}
|
||||
|
@ -19,7 +19,7 @@ func (f *NDJSON) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *NDJSON) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
for failure := range failures {
|
||||
obj := jsonObject{}
|
||||
|
@ -18,7 +18,7 @@ func (f *Plain) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Plain) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName)
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package formatter
|
||||
|
||||
import "github.com/mgechev/revive/lint"
|
||||
|
||||
func severity(config lint.RulesConfig, failure lint.Failure) lint.Severity {
|
||||
if config, ok := config[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
func severity(config lint.Config, failure lint.Failure) lint.Severity {
|
||||
if config, ok := config.Rules[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
return lint.SeverityError
|
||||
}
|
||||
if config, ok := config.Directives[failure.RuleName]; ok && config.Severity == lint.SeverityError {
|
||||
return lint.SeverityError
|
||||
}
|
||||
return lint.SeverityWarning
|
||||
|
@ -32,7 +32,7 @@ func formatFailure(failure lint.Failure, severity lint.Severity) []string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Stylish) Format(failures <-chan lint.Failure, config lint.RulesConfig) (string, error) {
|
||||
func (f *Stylish) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var result [][]string
|
||||
var totalErrors = 0
|
||||
var total = 0
|
||||
@ -82,7 +82,8 @@ func (f *Stylish) Format(failures <-chan lint.Failure, config lint.RulesConfig)
|
||||
} else if total > 0 && totalErrors == 0 {
|
||||
suffix = color.YellowString("\n ✖" + suffix)
|
||||
} else {
|
||||
suffix = color.GreenString("\n" + suffix)
|
||||
suffix, output = "", ""
|
||||
}
|
||||
|
||||
return output + suffix, nil
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ func (f *Unix) Name() string {
|
||||
}
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Unix) Format(failures <-chan lint.Failure, _ lint.RulesConfig) (string, error) {
|
||||
func (f *Unix) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
||||
}
|
||||
|
17
go.mod
17
go.mod
@ -1,15 +1,14 @@
|
||||
module github.com/mgechev/revive
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.0
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/fatih/color v1.7.0
|
||||
github.com/fatih/structtag v1.0.0
|
||||
github.com/mattn/go-colorable v0.0.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.3 // indirect
|
||||
github.com/mgechev/dots v0.0.0-20180605013149-8e09d8ea2757
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc
|
||||
github.com/pkg/errors v0.8.0
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e // indirect
|
||||
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1
|
||||
github.com/mattn/go-colorable v0.1.2 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.4 // indirect
|
||||
github.com/mgechev/dots v0.0.0-20190603122614-18fa4c4b71cc
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
github.com/pkg/errors v0.8.1
|
||||
golang.org/x/sys v0.0.0-20190801053355-cbf593c0f2f3 // indirect
|
||||
golang.org/x/tools v0.0.0-20190802005412-e9bb7d36c060
|
||||
)
|
||||
|
130
go.sum
130
go.sum
@ -1,22 +1,152 @@
|
||||
github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
|
||||
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
|
||||
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mgechev/dots v0.0.0-20180605013149-8e09d8ea2757 h1:KTwJ7Lo3KDKMknRYN5JEFRGIM4IkG59QjFFM2mxsMEU=
|
||||
github.com/mgechev/dots v0.0.0-20180605013149-8e09d8ea2757/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/dots v0.0.0-20190603122614-18fa4c4b71cc h1:ErGdrZWM/CrAz0FVwcznlAScsmr2pdSMMPMwFL9TNmw=
|
||||
github.com/mgechev/dots v0.0.0-20190603122614-18fa4c4b71cc/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc h1:rQ1O4ZLYR2xXHXgBCCfIIGnuZ0lidMQw2S5n1oOv+Wg=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190603122648-4c4f7f33c9ed h1:Lf5SX+bXEwoj3Y6Nfu5qtffzOXhPQA9REb2R5W4nGP8=
|
||||
golang.org/x/sys v0.0.0-20190603122648-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606125941-79a91cf218c4 h1:HWs74PINelUuEfUbwnBO+1N52oVhkjuLqpbUmyrFA1s=
|
||||
golang.org/x/sys v0.0.0-20190606125941-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606203914-7fc4e5ec1444 h1:U3H/lfsEYy6ld4rCevWA3QgxHlZLiFUfuBFIEc/Ifyo=
|
||||
golang.org/x/sys v0.0.0-20190606203914-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190608055321-5b15430b70e3 h1:vzXBGBUiVyR0q7D4t89HsaY0TYhmviZBVOVUyvyMBjU=
|
||||
golang.org/x/sys v0.0.0-20190608055321-5b15430b70e3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190614215434-b47fdc937951 h1:pGVEuw9fHbMpaZlMbLwssX14J35+8blxkNxbyMcO/qE=
|
||||
golang.org/x/sys v0.0.0-20190614215434-b47fdc937951/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616132722-15dcb6c0061f h1:bWC5mWiwVGXbr7P6ugM+hu6QytMFEjbwt+SO2r1M2+o=
|
||||
golang.org/x/sys v0.0.0-20190616132722-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190619193031-17bc6164aac4 h1:wlKUlj5/boPTVWT2ysl97FAXePcoaV9FNqFOzsNuwpk=
|
||||
golang.org/x/sys v0.0.0-20190619193031-17bc6164aac4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190621215844-d432491b9138 h1:mOtS9UBYWoZzbGWv3/XJz0cxMw8MTBbNKy3pRvP2Mjk=
|
||||
golang.org/x/sys v0.0.0-20190621215844-d432491b9138/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624145334-c5567b49c5d0 h1:z1lZzI4A4GjnM80wHx8Yrjtcr56DRJ6VJnipc0bbG3o=
|
||||
golang.org/x/sys v0.0.0-20190624145334-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190629205408-04f50cda93cb h1:IeU57h/r0+/v829dDR8bskefuF1Cx4KAxce1Cn0LFvE=
|
||||
golang.org/x/sys v0.0.0-20190629205408-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710165549-6ec70d6a5542 h1:PNYRbnC8XRk0bAINQCfprVc169PXbLRG2HI1HXbUEg8=
|
||||
golang.org/x/sys v0.0.0-20190710165549-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712063909-fae7ac547cb7 h1:kj9oKJYzWQH5MvoBhxb9WQn/LqYor42sKTvsrUCCu7g=
|
||||
golang.org/x/sys v0.0.0-20190712063909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726004620-94b544f455ef h1:pKfIZaaN+ipVf4Vz//s+8MRZkgZvBsds3BOAAYmXV/Y=
|
||||
golang.org/x/sys v0.0.0-20190726004620-94b544f455ef/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726093751-fc99dfbffb4e h1:AhhcJML1FGXN10f3s4/WyoO5/8u7Rfb1ZyB47O8qp+s=
|
||||
golang.org/x/sys v0.0.0-20190726093751-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190731002446-1393eb018365 h1:skmasgLtLOsII4VBh/TRzNotE2cn3rMyPB9/A67wLDU=
|
||||
golang.org/x/sys v0.0.0-20190731002446-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190801053355-cbf593c0f2f3 h1:sL449jA3eITt86CGRvhic5g9AuQEnaqaasKTT+ieDng=
|
||||
golang.org/x/sys v0.0.0-20190801053355-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1 h1:dzEuQYa6+a3gROnSlgly5ERUm4SZKJt+dh+4iSbO+bI=
|
||||
golang.org/x/tools v0.0.0-20180911133044-677d2ff680c1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190604003109-8aaa1484dc10 h1:VULJh5DQg4Inr1Xiypf4pj72xB4lPJdiUgV2Ra5M8og=
|
||||
golang.org/x/tools v0.0.0-20190604003109-8aaa1484dc10/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606052759-4d9ae51c2468 h1:ousb8Vkhj77qYml3n9BuPuYaR6LnZyLh5LXB2qq8wzI=
|
||||
golang.org/x/tools v0.0.0-20190606052759-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614153353-1edc8e83c897 h1:Tcg4xXCU7bSKi8WaBg3N7lJTHwLv45zG+LXHG6WxorM=
|
||||
golang.org/x/tools v0.0.0-20190614153353-1edc8e83c897/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614185444-d1d6cdd8a67e h1:bZRjbS2OsoCz4Whw4o7b1Il1obBuaKSM0FXMfFrRut4=
|
||||
golang.org/x/tools v0.0.0-20190614185444-d1d6cdd8a67e/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614215004-5aca471b1d59 h1:rBGSbLsDaYExWFDBn79o7jc1xpwfxSofsWJNihAfWto=
|
||||
golang.org/x/tools v0.0.0-20190614215004-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617174016-6fea9ef05e7a h1:gdHr3XuwwcWiVcNwFFdaMAXkLC9SGW3+yn63Pol+wL0=
|
||||
golang.org/x/tools v0.0.0-20190617174016-6fea9ef05e7a/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617192825-da514acc4774 h1:hL3a3nRZsZm7FQGehfRB1w5y8mbrLyMSAgki4j0z3I0=
|
||||
golang.org/x/tools v0.0.0-20190617192825-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190618173119-fdf1049a943a h1:UYXkp2zG6yfcsx4WdMshPebv30sUsSXURy1nWiwy0oc=
|
||||
golang.org/x/tools v0.0.0-20190618173119-fdf1049a943a/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190623014052-6e04913cbbac h1:p4rf0zoHymrHSlKS+xG2PhJOtxOEWC9nu4NamWffoY0=
|
||||
golang.org/x/tools v0.0.0-20190623014052-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624225754-a101b041ded4 h1:3Yme/SSFE6mHkvIIhjjiTrlmOsy6m0aNnyTDZPYJIy4=
|
||||
golang.org/x/tools v0.0.0-20190624225754-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190625183255-252024b82959 h1:icB3Kx/bolKriEovlxmFvvEBA1b24q6lQkwGLiO7Kgo=
|
||||
golang.org/x/tools v0.0.0-20190625183255-252024b82959/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628234000-fb37f6ba8261 h1:D/pylchHpGxucPHRiQsXXfyk9R4ywTUoH6FaZx0R0Q0=
|
||||
golang.org/x/tools v0.0.0-20190628234000-fb37f6ba8261/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190701204812-38ae2c8f6412 h1:eiOZ97iLROr9dsoElIcgpaBTnypaUM6FklHaTH1WCUQ=
|
||||
golang.org/x/tools v0.0.0-20190701204812-38ae2c8f6412/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190702152726-7e72c71c505f h1:XEee/4s6OPmxKNwnku3HeR9yvHS+DfiMUBnZRvGYKsc=
|
||||
golang.org/x/tools v0.0.0-20190702152726-7e72c71c505f/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190702203059-44aeb8b7c377 h1:ImzDLDLqRaKKO5JeKsrJF73YTtC/UGJYXQfnhCykd1U=
|
||||
golang.org/x/tools v0.0.0-20190702203059-44aeb8b7c377/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190703183741-063514c48b26 h1:RSQtTb58BibjAu/zvYqkvmSd3hCVrxUHtxtJgdmlo8U=
|
||||
golang.org/x/tools v0.0.0-20190703183741-063514c48b26/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190708153032-60762fc531e6 h1:+09ssZLX5m87DAU4ovA5R2dT8+rxQJOSrnvJ08dNOvI=
|
||||
golang.org/x/tools v0.0.0-20190708153032-60762fc531e6/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190708212629-c8855242db9c h1:61VlnXlxJ8ZQ0ScNJLbv0lMvy2zxMOMtmqgczqTjoSw=
|
||||
golang.org/x/tools v0.0.0-20190708212629-c8855242db9c/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190709205837-f82b303b69d7 h1:POOqS8nIAlVjiggJspEagLTIm7FecjZ3zjL+UXqxaeo=
|
||||
golang.org/x/tools v0.0.0-20190709205837-f82b303b69d7/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190709213823-7b25e351ac0e h1:KLE9RrAWtdmIWsb/E0ljAvXMM5lZf4Gtr3HYCEE4FIE=
|
||||
golang.org/x/tools v0.0.0-20190709213823-7b25e351ac0e/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190710195507-286818132824 h1:2492BvYnJkin59SmTIFBH3mlkrNxeIKPJuM3GrK/gYo=
|
||||
golang.org/x/tools v0.0.0-20190710195507-286818132824/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190712214651-8b927904ee0d h1:KlQeLUDWg+cWfoNyo2obro62ANg9Ain//HXb8aKT2jg=
|
||||
golang.org/x/tools v0.0.0-20190712214651-8b927904ee0d/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190715053316-607ca053a137 h1:JD/hG/8Cy1uTpUou5cTdh88WzXO985+ykk+/kD2B6Og=
|
||||
golang.org/x/tools v0.0.0-20190715053316-607ca053a137/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190715174000-9e48ab1d90cd h1:WtY90UqL9QgrhtGZXkSuv3VgN9O2zchNuw7mCsy5EAo=
|
||||
golang.org/x/tools v0.0.0-20190715174000-9e48ab1d90cd/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190715222530-9b2cb0e5f602 h1:aPQ4URfBa5qpEffv7JQWB643/X9MIooR7AmSM4PhjbQ=
|
||||
golang.org/x/tools v0.0.0-20190715222530-9b2cb0e5f602/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190716023037-fefcef05abb1 h1:XTh+ATqTLtxlslz1/eewT2lc16Tn566b6TuHLllcamo=
|
||||
golang.org/x/tools v0.0.0-20190716023037-fefcef05abb1/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190716155013-919acb9f1ffd h1:awSISWvdxEIWPMBRrxodeas3r6hNs3R7fSCAKsN+2eg=
|
||||
golang.org/x/tools v0.0.0-20190716155013-919acb9f1ffd/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190716192744-0b5a7f81db50 h1:MjNb67gYViBJS2G0qlnUyUvz01HpvT0hWbRK6+CVeB4=
|
||||
golang.org/x/tools v0.0.0-20190716192744-0b5a7f81db50/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190719014327-e377ae9d6386 h1:XYHge1yfBqxAoAUYFNfgQBUM/pLA4cVgBCxSVP4SFM8=
|
||||
golang.org/x/tools v0.0.0-20190719014327-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190723023520-8bb11ff117ca h1:jxUITcp9Zck5mo94NnK9nxKhcrCHHu3Uxn3zT/Ab6GQ=
|
||||
golang.org/x/tools v0.0.0-20190723023520-8bb11ff117ca/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190724192812-8aa4eac1a7c1 h1:fUhbXHMxzVzne8fm8LoGbgb29YxdSR6sXVOA6M1+79c=
|
||||
golang.org/x/tools v0.0.0-20190724192812-8aa4eac1a7c1/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190725162026-2e34cfcb95cb h1:nCidYN5rpBq6eOoKgsg8OPyARFROWoopPyENOA1AADU=
|
||||
golang.org/x/tools v0.0.0-20190725162026-2e34cfcb95cb/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190727014933-1bd56024c620 h1:V0R1y3ny4Qty3oJpgixfc7BF1GqcBtG492UJDH4XNi4=
|
||||
golang.org/x/tools v0.0.0-20190727014933-1bd56024c620/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190727174842-db2fa46ec33c h1:jOixaG4f/xPYap0HSnwlewCOTxw7qq5hqxvCyw5Kfvg=
|
||||
golang.org/x/tools v0.0.0-20190727174842-db2fa46ec33c/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190728085240-fc6e2057e7f6 h1:HwL5zE73Lj6ca2l3QvmH+z+mMQJp6w6/7zcxRJcGNRw=
|
||||
golang.org/x/tools v0.0.0-20190728085240-fc6e2057e7f6/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190729094940-ff9f1409240a h1:s5iVUvQeQogtT5N2UsrWTExwB/vujdgo2oRIoKqGHP0=
|
||||
golang.org/x/tools v0.0.0-20190729094940-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190730215743-ed3277de2799 h1:QDcqPiPMwhOSeNS/65AYJQxAYb2HIYLFw6NRbGQUtSU=
|
||||
golang.org/x/tools v0.0.0-20190730215743-ed3277de2799/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190731224408-1e85ed8060aa h1:crSxYKs/4ckOznRS/ORDRJikUnqR5pcExM2Q9fNghbw=
|
||||
golang.org/x/tools v0.0.0-20190731224408-1e85ed8060aa/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
golang.org/x/tools v0.0.0-20190802005412-e9bb7d36c060 h1:ixdNsWPlaysKGPShUO+OF5eF/dSQOGTB86mRds+STrY=
|
||||
golang.org/x/tools v0.0.0-20190802005412-e9bb7d36c060/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
|
||||
|
@ -12,12 +12,21 @@ type RuleConfig struct {
|
||||
// RulesConfig defines the config for all rules.
|
||||
type RulesConfig = map[string]RuleConfig
|
||||
|
||||
// DirectiveConfig is type used for the linter directive configuration.
|
||||
type DirectiveConfig struct {
|
||||
Severity Severity
|
||||
}
|
||||
|
||||
// DirectivesConfig defines the config for all directives.
|
||||
type DirectivesConfig = map[string]DirectiveConfig
|
||||
|
||||
// Config defines the config of the linter.
|
||||
type Config struct {
|
||||
IgnoreGeneratedHeader bool `toml:"ignoreGeneratedHeader"`
|
||||
Confidence float64
|
||||
Severity Severity
|
||||
Rules RulesConfig `toml:"rule"`
|
||||
ErrorCode int `toml:"errorCode"`
|
||||
WarningCode int `toml:"warningCode"`
|
||||
Rules RulesConfig `toml:"rule"`
|
||||
ErrorCode int `toml:"errorCode"`
|
||||
WarningCode int `toml:"warningCode"`
|
||||
Directives DirectivesConfig `toml:"directive"`
|
||||
}
|
||||
|
41
lint/file.go
41
lint/file.go
@ -97,9 +97,12 @@ func (f *File) isMain() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const directiveSpecifyDisableReason = "specify-disable-reason"
|
||||
|
||||
func (f *File) lint(rules []Rule, config Config, failures chan Failure) {
|
||||
rulesConfig := config.Rules
|
||||
disabledIntervals := f.disabledIntervals(rules)
|
||||
_, mustSpecifyDisableReason := config.Directives[directiveSpecifyDisableReason]
|
||||
disabledIntervals := f.disabledIntervals(rules, mustSpecifyDisableReason, failures)
|
||||
for _, currentRule := range rules {
|
||||
ruleConfig := rulesConfig[currentRule.Name()]
|
||||
currentFailures := currentRule.Apply(f, ruleConfig.Arguments)
|
||||
@ -126,9 +129,15 @@ type enableDisableConfig struct {
|
||||
position int
|
||||
}
|
||||
|
||||
func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
re := regexp.MustCompile(`^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*$`)
|
||||
const directiveRE = `^//[\s]*revive:(enable|disable)(?:-(line|next-line))?(?::([^\s]+))?[\s]*(?: (.+))?$`
|
||||
const directivePos = 1
|
||||
const modifierPos = 2
|
||||
const rulesPos = 3
|
||||
const reasonPos = 4
|
||||
|
||||
var re = regexp.MustCompile(directiveRE)
|
||||
|
||||
func (f *File) disabledIntervals(rules []Rule, mustSpecifyDisableReason bool, failures chan Failure) disabledIntervalsMap {
|
||||
enabledDisabledRulesMap := make(map[string][]enableDisableConfig)
|
||||
|
||||
getEnabledDisabledIntervals := func() disabledIntervalsMap {
|
||||
@ -202,16 +211,26 @@ func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
}
|
||||
|
||||
ruleNames := []string{}
|
||||
if len(match) > 2 {
|
||||
tempNames := strings.Split(match[3], ",")
|
||||
for _, name := range tempNames {
|
||||
name = strings.Trim(name, "\n")
|
||||
if len(name) > 0 {
|
||||
ruleNames = append(ruleNames, name)
|
||||
}
|
||||
tempNames := strings.Split(match[rulesPos], ",")
|
||||
for _, name := range tempNames {
|
||||
name = strings.Trim(name, "\n")
|
||||
if len(name) > 0 {
|
||||
ruleNames = append(ruleNames, name)
|
||||
}
|
||||
}
|
||||
|
||||
mustCheckDisablingReason := mustSpecifyDisableReason && match[directivePos] == "disable"
|
||||
if mustCheckDisablingReason && strings.Trim(match[reasonPos], " ") == "" {
|
||||
failures <- Failure{
|
||||
Confidence: 1,
|
||||
RuleName: directiveSpecifyDisableReason,
|
||||
Failure: "reason of lint disabling not found",
|
||||
Position: ToFailurePosition(c.Pos(), c.End(), f),
|
||||
Node: c,
|
||||
}
|
||||
continue // skip this linter disabling directive
|
||||
}
|
||||
|
||||
// TODO: optimize
|
||||
if len(ruleNames) == 0 {
|
||||
for _, rule := range rules {
|
||||
@ -219,7 +238,7 @@ func (f *File) disabledIntervals(rules []Rule) disabledIntervalsMap {
|
||||
}
|
||||
}
|
||||
|
||||
handleRules(filename, match[2], match[1] == "enable", line, ruleNames)
|
||||
handleRules(filename, match[modifierPos], match[directivePos] == "enable", line, ruleNames)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,6 @@ type FormatterMetadata struct {
|
||||
|
||||
// Formatter defines an interface for failure formatters
|
||||
type Formatter interface {
|
||||
Format(<-chan Failure, RulesConfig) (string, error)
|
||||
Format(<-chan Failure, Config) (string, error)
|
||||
Name() string
|
||||
}
|
||||
|
6
main.go
6
main.go
@ -44,7 +44,7 @@ func main() {
|
||||
|
||||
var output string
|
||||
go (func() {
|
||||
output, err = formatter.Format(formatChan, config.Rules)
|
||||
output, err = formatter.Format(formatChan, *config)
|
||||
if err != nil {
|
||||
fail(err.Error())
|
||||
}
|
||||
@ -62,6 +62,10 @@ func main() {
|
||||
if c, ok := config.Rules[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||
exitCode = config.ErrorCode
|
||||
}
|
||||
if c, ok := config.Directives[f.RuleName]; ok && c.Severity == lint.SeverityError {
|
||||
exitCode = config.ErrorCode
|
||||
}
|
||||
|
||||
formatChan <- f
|
||||
}
|
||||
|
||||
|
5
renovate.json
Normal file
5
renovate.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
@ -2,7 +2,6 @@ package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
@ -13,6 +12,11 @@ type ImportsBlacklistRule struct{}
|
||||
// Apply applies the rule to given file.
|
||||
func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
|
||||
var failures []lint.Failure
|
||||
|
||||
if file.IsTest() {
|
||||
return failures // skip, test file
|
||||
}
|
||||
|
||||
blacklist := make(map[string]bool, len(arguments))
|
||||
|
||||
for _, arg := range arguments {
|
||||
@ -20,25 +24,25 @@ func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Invalid argument to the imports-blacklist rule. Expecting a string, got %T", arg))
|
||||
}
|
||||
// we add quotes if nt present, because when parsed, the value of the AST node, will be quoted
|
||||
// we add quotes if not present, because when parsed, the value of the AST node, will be quoted
|
||||
if len(argStr) > 2 && argStr[0] != '"' && argStr[len(argStr)-1] != '"' {
|
||||
argStr = fmt.Sprintf(`"%s"`, argStr)
|
||||
}
|
||||
blacklist[argStr] = true
|
||||
}
|
||||
|
||||
fileAst := file.AST
|
||||
walker := blacklistedImports{
|
||||
file: file,
|
||||
fileAst: fileAst,
|
||||
onFailure: func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
},
|
||||
blacklist: blacklist,
|
||||
for _, is := range file.AST.Imports {
|
||||
path := is.Path
|
||||
if path != nil && blacklist[path.Value] {
|
||||
failures = append(failures, lint.Failure{
|
||||
Confidence: 1,
|
||||
Failure: "should not use the following blacklisted import: " + path.Value,
|
||||
Node: is,
|
||||
Category: "imports",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ast.Walk(walker, fileAst)
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
@ -46,24 +50,3 @@ func (r *ImportsBlacklistRule) Apply(file *lint.File, arguments lint.Arguments)
|
||||
func (r *ImportsBlacklistRule) Name() string {
|
||||
return "imports-blacklist"
|
||||
}
|
||||
|
||||
type blacklistedImports struct {
|
||||
file *lint.File
|
||||
fileAst *ast.File
|
||||
onFailure func(lint.Failure)
|
||||
blacklist map[string]bool
|
||||
}
|
||||
|
||||
func (w blacklistedImports) Visit(_ ast.Node) ast.Visitor {
|
||||
for _, is := range w.fileAst.Imports {
|
||||
if is.Path != nil && !w.file.IsTest() && w.blacklist[is.Path.Value] {
|
||||
w.onFailure(lint.Failure{
|
||||
Confidence: 1,
|
||||
Failure: fmt.Sprintf("should not use the following blacklisted import: %s", is.Path.Value),
|
||||
Node: is,
|
||||
Category: "imports",
|
||||
})
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/structtag"
|
||||
"github.com/mgechev/revive/lint"
|
||||
"go/ast"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/structtag"
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
// StructTagRule lints struct tags.
|
||||
@ -58,6 +59,10 @@ func (w lintStructTagRule) Visit(node ast.Node) ast.Visitor {
|
||||
// checkTaggedField checks the tag of the given field.
|
||||
// precondition: the field has a tag
|
||||
func (w lintStructTagRule) checkTaggedField(f *ast.Field) {
|
||||
if len(f.Names) > 0 && !f.Names[0].IsExported() {
|
||||
w.addFailure(f, "tag on not-exported field "+f.Names[0].Name)
|
||||
}
|
||||
|
||||
tags, err := structtag.Parse(strings.Trim(f.Tag.Value, "`"))
|
||||
if err != nil || tags == nil {
|
||||
w.addFailure(f.Tag, "malformed tag")
|
||||
|
120
rule/unhandled-error.go
Normal file
120
rule/unhandled-error.go
Normal file
@ -0,0 +1,120 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
// UnhandledErrorRule lints given else constructs.
|
||||
type UnhandledErrorRule struct{}
|
||||
|
||||
type ignoreListType map[string]struct{}
|
||||
|
||||
// Apply applies the rule to given file.
|
||||
func (r *UnhandledErrorRule) Apply(file *lint.File, args lint.Arguments) []lint.Failure {
|
||||
var failures []lint.Failure
|
||||
|
||||
ignoreList := make(ignoreListType, len(args))
|
||||
|
||||
for _, arg := range args {
|
||||
argStr, ok := arg.(string)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Invalid argument to the unhandled-error rule. Expecting a string, got %T", arg))
|
||||
}
|
||||
|
||||
ignoreList[argStr] = struct{}{}
|
||||
}
|
||||
|
||||
walker := &lintUnhandledErrors{
|
||||
ignoreList: ignoreList,
|
||||
pkg: file.Pkg,
|
||||
onFailure: func(failure lint.Failure) {
|
||||
failures = append(failures, failure)
|
||||
},
|
||||
}
|
||||
|
||||
file.Pkg.TypeCheck()
|
||||
ast.Walk(walker, file.AST)
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
// Name returns the rule name.
|
||||
func (r *UnhandledErrorRule) Name() string {
|
||||
return "unhandled-error"
|
||||
}
|
||||
|
||||
type lintUnhandledErrors struct {
|
||||
ignoreList ignoreListType
|
||||
pkg *lint.Package
|
||||
onFailure func(lint.Failure)
|
||||
}
|
||||
|
||||
// Visit looks for statements that are function calls.
|
||||
// If the called function returns a value of type error a failure will be created.
|
||||
func (w *lintUnhandledErrors) Visit(node ast.Node) ast.Visitor {
|
||||
switch n := node.(type) {
|
||||
case *ast.ExprStmt:
|
||||
fCall, ok := n.X.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return nil // not a function call
|
||||
}
|
||||
|
||||
funcType := w.pkg.TypeOf(fCall)
|
||||
if funcType == nil {
|
||||
return nil // skip, type info not available
|
||||
}
|
||||
|
||||
switch t := funcType.(type) {
|
||||
case *types.Named:
|
||||
if !w.isTypeError(t) {
|
||||
return nil // func call does not return an error
|
||||
}
|
||||
|
||||
w.addFailure(fCall)
|
||||
default:
|
||||
retTypes, ok := funcType.Underlying().(*types.Tuple)
|
||||
if !ok {
|
||||
return nil // skip, unable to retrieve return type of the called function
|
||||
}
|
||||
|
||||
if w.returnsAnError(retTypes) {
|
||||
w.addFailure(fCall)
|
||||
}
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *lintUnhandledErrors) addFailure(n *ast.CallExpr) {
|
||||
funcName := gofmt(n.Fun)
|
||||
if _, mustIgnore := w.ignoreList[funcName]; mustIgnore {
|
||||
return
|
||||
}
|
||||
|
||||
w.onFailure(lint.Failure{
|
||||
Category: "bad practice",
|
||||
Confidence: 1,
|
||||
Node: n,
|
||||
Failure: fmt.Sprintf("Unhandled error in call to function %v", funcName),
|
||||
})
|
||||
}
|
||||
|
||||
func (*lintUnhandledErrors) isTypeError(t *types.Named) bool {
|
||||
const errorTypeName = "_.error"
|
||||
|
||||
return t.Obj().Id() == errorTypeName
|
||||
}
|
||||
|
||||
func (w *lintUnhandledErrors) returnsAnError(tt *types.Tuple) bool {
|
||||
for i := 0; i < tt.Len(); i++ {
|
||||
nt, ok := tt.At(i).Type().(*types.Named)
|
||||
if ok && w.isTypeError(nt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -3,7 +3,6 @@ package rule
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
@ -38,205 +37,66 @@ type lintUnusedParamRule struct {
|
||||
func (w lintUnusedParamRule) Visit(node ast.Node) ast.Visitor {
|
||||
switch n := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
fv := newFuncVisitor(retrieveNamedParams(n.Type.Params.List))
|
||||
if n.Body != nil {
|
||||
ast.Walk(fv, n.Body)
|
||||
checkUnusedParams(w, fv.params, n)
|
||||
params := retrieveNamedParams(n.Type.Params)
|
||||
if len(params) < 1 {
|
||||
return nil // skip, func without parameters
|
||||
}
|
||||
return nil
|
||||
|
||||
if n.Body == nil {
|
||||
return nil // skip, is a function prototype
|
||||
}
|
||||
|
||||
// inspect the func body looking for references to parameters
|
||||
fselect := func(n ast.Node) bool {
|
||||
ident, isAnID := n.(*ast.Ident)
|
||||
|
||||
if !isAnID {
|
||||
return false
|
||||
}
|
||||
|
||||
_, isAParam := params[ident.Obj]
|
||||
if isAParam {
|
||||
params[ident.Obj] = false // mark as used
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
_ = pick(n.Body, fselect, nil)
|
||||
|
||||
for _, p := range n.Type.Params.List {
|
||||
for _, n := range p.Names {
|
||||
if params[n.Obj] {
|
||||
w.onFailure(lint.Failure{
|
||||
Confidence: 1,
|
||||
Node: n,
|
||||
Category: "bad practice",
|
||||
Failure: fmt.Sprintf("parameter '%s' seems to be unused, consider removing or renaming it as _", n.Name),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil // full method body already inspected
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
type scope struct {
|
||||
vars map[string]bool
|
||||
}
|
||||
|
||||
func newScope() scope {
|
||||
return scope{make(map[string]bool, 0)}
|
||||
}
|
||||
|
||||
func (s *scope) addVars(exps []ast.Expr) {
|
||||
for _, e := range exps {
|
||||
if id, ok := e.(*ast.Ident); ok {
|
||||
s.vars[id.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type scopeStack struct {
|
||||
stk []scope
|
||||
}
|
||||
|
||||
func (s *scopeStack) openScope() {
|
||||
s.stk = append(s.stk, newScope())
|
||||
}
|
||||
|
||||
func (s *scopeStack) closeScope() {
|
||||
if len(s.stk) > 0 {
|
||||
s.stk = s.stk[:len(s.stk)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (s *scopeStack) currentScope() scope {
|
||||
if len(s.stk) > 0 {
|
||||
return s.stk[len(s.stk)-1]
|
||||
func retrieveNamedParams(params *ast.FieldList) map[*ast.Object]bool {
|
||||
result := map[*ast.Object]bool{}
|
||||
if params.List == nil {
|
||||
return result
|
||||
}
|
||||
|
||||
panic("no current scope")
|
||||
}
|
||||
|
||||
func newScopeStack() scopeStack {
|
||||
return scopeStack{make([]scope, 0)}
|
||||
}
|
||||
|
||||
type funcVisitor struct {
|
||||
sStk scopeStack
|
||||
params map[string]bool
|
||||
}
|
||||
|
||||
func newFuncVisitor(params map[string]bool) funcVisitor {
|
||||
return funcVisitor{sStk: newScopeStack(), params: params}
|
||||
}
|
||||
|
||||
func walkStmtList(v ast.Visitor, list []ast.Stmt) {
|
||||
for _, s := range list {
|
||||
ast.Walk(v, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (v funcVisitor) Visit(node ast.Node) ast.Visitor {
|
||||
varSelector := func(n ast.Node) bool {
|
||||
id, ok := n.(*ast.Ident)
|
||||
return ok && id.Obj != nil && id.Obj.Kind.String() == "var"
|
||||
}
|
||||
switch n := node.(type) {
|
||||
case *ast.BlockStmt:
|
||||
v.sStk.openScope()
|
||||
walkStmtList(v, n.List)
|
||||
v.sStk.closeScope()
|
||||
return nil
|
||||
case *ast.AssignStmt:
|
||||
var uses []ast.Node
|
||||
if isOpAssign(n.Tok) { // Case of id += expr
|
||||
uses = append(uses, pickFromExpList(n.Lhs, varSelector, nil)...)
|
||||
} else { // Case of id[expr] = expr
|
||||
indexSelector := func(n ast.Node) bool {
|
||||
_, ok := n.(*ast.IndexExpr)
|
||||
return ok
|
||||
}
|
||||
f := func(n ast.Node) []ast.Node {
|
||||
ie, ok := n.(*ast.IndexExpr)
|
||||
if !ok { // not possible
|
||||
return nil
|
||||
}
|
||||
|
||||
return pick(ie.Index, varSelector, nil)
|
||||
}
|
||||
|
||||
uses = append(uses, pickFromExpList(n.Lhs, indexSelector, f)...)
|
||||
}
|
||||
|
||||
uses = append(uses, pickFromExpList(n.Rhs, varSelector, nil)...)
|
||||
|
||||
markParamListAsUsed(uses, v)
|
||||
cs := v.sStk.currentScope()
|
||||
cs.addVars(n.Lhs)
|
||||
case *ast.Ident:
|
||||
if n.Obj != nil {
|
||||
if n.Obj.Kind.String() == "var" {
|
||||
markParamAsUsed(n, v)
|
||||
}
|
||||
}
|
||||
case *ast.ForStmt:
|
||||
v.sStk.openScope()
|
||||
if n.Init != nil {
|
||||
ast.Walk(v, n.Init)
|
||||
}
|
||||
uses := pickFromExpList([]ast.Expr{n.Cond}, varSelector, nil)
|
||||
markParamListAsUsed(uses, v)
|
||||
ast.Walk(v, n.Body)
|
||||
v.sStk.closeScope()
|
||||
return nil
|
||||
case *ast.SwitchStmt:
|
||||
v.sStk.openScope()
|
||||
if n.Init != nil {
|
||||
ast.Walk(v, n.Init)
|
||||
}
|
||||
uses := pickFromExpList([]ast.Expr{n.Tag}, varSelector, nil)
|
||||
markParamListAsUsed(uses, v)
|
||||
// Analyze cases (they are not BlockStmt but a list of Stmt)
|
||||
cases := n.Body.List
|
||||
for _, c := range cases {
|
||||
cc, ok := c.(*ast.CaseClause)
|
||||
if !ok {
|
||||
for _, p := range params.List {
|
||||
for _, n := range p.Names {
|
||||
if n.Name == "_" {
|
||||
continue
|
||||
}
|
||||
uses := pickFromExpList(cc.List, varSelector, nil)
|
||||
markParamListAsUsed(uses, v)
|
||||
v.sStk.openScope()
|
||||
for _, stmt := range cc.Body {
|
||||
ast.Walk(v, stmt)
|
||||
}
|
||||
v.sStk.closeScope()
|
||||
}
|
||||
|
||||
v.sStk.closeScope()
|
||||
return nil
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func retrieveNamedParams(pl []*ast.Field) map[string]bool {
|
||||
result := make(map[string]bool, len(pl))
|
||||
for _, p := range pl {
|
||||
for _, n := range p.Names {
|
||||
if n.Name != "_" {
|
||||
result[n.Name] = true
|
||||
}
|
||||
result[n.Obj] = true
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func checkUnusedParams(w lintUnusedParamRule, params map[string]bool, n *ast.FuncDecl) {
|
||||
for k, v := range params {
|
||||
if v {
|
||||
w.onFailure(lint.Failure{
|
||||
Confidence: 0.8, // confidence is not 1.0 because of shadow variables
|
||||
Node: n,
|
||||
Category: "bad practice",
|
||||
Failure: fmt.Sprintf("parameter '%s' seems to be unused, consider removing or renaming it as _", k),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func markParamListAsUsed(ids []ast.Node, v funcVisitor) {
|
||||
for _, id := range ids {
|
||||
markParamAsUsed(id.(*ast.Ident), v)
|
||||
}
|
||||
}
|
||||
|
||||
func markParamAsUsed(id *ast.Ident, v funcVisitor) { // TODO: constraint parameters to receive just a list of params and a scope stack
|
||||
for _, s := range v.sStk.stk {
|
||||
if s.vars[id.Name] {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if v.params[id.Name] {
|
||||
v.params[id.Name] = false
|
||||
}
|
||||
}
|
||||
|
||||
func isOpAssign(aTok token.Token) bool {
|
||||
return aTok == token.ADD_ASSIGN || aTok == token.AND_ASSIGN ||
|
||||
aTok == token.MUL_ASSIGN || aTok == token.OR_ASSIGN ||
|
||||
aTok == token.QUO_ASSIGN || aTok == token.REM_ASSIGN ||
|
||||
aTok == token.SHL_ASSIGN || aTok == token.SHR_ASSIGN ||
|
||||
aTok == token.SUB_ASSIGN || aTok == token.XOR_ASSIGN
|
||||
}
|
||||
|
@ -14,3 +14,13 @@ func TestImportsBlacklist(t *testing.T) {
|
||||
Arguments: args,
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkImportsBlacklist(b *testing.B) {
|
||||
args := []interface{}{"crypto/md5", "crypto/sha1"}
|
||||
var t *testing.T
|
||||
for i := 0; i <= b.N; i++ {
|
||||
testRule(t, "imports-blacklist", &rule.ImportsBlacklistRule{}, &lint.RuleConfig{
|
||||
Arguments: args,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
18
test/unhandled-error_test.go
Normal file
18
test/unhandled-error_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
"github.com/mgechev/revive/rule"
|
||||
)
|
||||
|
||||
func TestUnhandledError(t *testing.T) {
|
||||
testRule(t, "unhandled-error", &rule.UnhandledErrorRule{})
|
||||
}
|
||||
|
||||
func TestUnhandledErrorWithBlacklist(t *testing.T) {
|
||||
args := []interface{}{"os.Chdir", "unhandledError1"}
|
||||
|
||||
testRule(t, "unhandled-error-w-ignorelist", &rule.UnhandledErrorRule{}, &lint.RuleConfig{Arguments: args})
|
||||
}
|
@ -9,3 +9,10 @@ import (
|
||||
func TestUnusedParam(t *testing.T) {
|
||||
testRule(t, "unused-param", &rule.UnusedParamRule{})
|
||||
}
|
||||
|
||||
func BenchmarkUnusedParam(b *testing.B) {
|
||||
var t *testing.T
|
||||
for i := 0; i <= b.N; i++ {
|
||||
testRule(t, "unused-param", &rule.UnusedParamRule{})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user