Add "revive" linter (#1729)
This commit is contained in:
parent
ce2e778220
commit
35b6f35476
@ -350,6 +350,13 @@ linters-settings:
|
||||
rowserrcheck:
|
||||
packages:
|
||||
- github.com/jmoiron/sqlx
|
||||
revive:
|
||||
# see https://github.com/mgechev/revive#available-rules for details.
|
||||
ignore-generated-header: true
|
||||
severity: warning
|
||||
rules:
|
||||
- name: indent-error-flow
|
||||
severity: warning
|
||||
testpackage:
|
||||
# regexp pattern to skip files
|
||||
skip-regexp: (export|internal)_test\.go
|
||||
|
@ -117,6 +117,7 @@ linters:
|
||||
# - nestif
|
||||
# - prealloc
|
||||
# - testpackage
|
||||
# - revive
|
||||
# - wsl
|
||||
|
||||
issues:
|
||||
|
5
go.mod
5
go.mod
@ -39,6 +39,8 @@ require (
|
||||
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb // v1.0
|
||||
github.com/mattn/go-colorable v0.1.8
|
||||
github.com/mbilski/exhaustivestruct v1.2.0
|
||||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81
|
||||
github.com/mgechev/revive v1.0.3
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-ps v1.0.0
|
||||
github.com/moricho/tparallel v0.2.1
|
||||
@ -69,9 +71,8 @@ require (
|
||||
github.com/ultraware/whitespace v0.0.4
|
||||
github.com/uudashr/gocognit v1.0.1
|
||||
github.com/valyala/quicktemplate v1.6.3
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect
|
||||
golang.org/x/text v0.3.4 // indirect
|
||||
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130
|
||||
golang.org/x/tools v0.1.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
honnef.co/go/tools v0.0.1-2020.1.6
|
||||
mvdan.cc/gofumpt v0.1.0
|
||||
|
18
go.sum
generated
18
go.sum
generated
@ -67,6 +67,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
|
||||
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@ -255,12 +257,18 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo=
|
||||
github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc=
|
||||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 h1:QASJXOGm2RZ5Ardbc86qNFvby9AqkLDibfChMtAg5QM=
|
||||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||
github.com/mgechev/revive v1.0.3 h1:z3FL6IFFN3JKzHYHD8O1ExH9g/4lAGJ5x1+9rPZgsFg=
|
||||
github.com/mgechev/revive v1.0.3/go.mod h1:POGGZagSo/0frdr7VeAifzS5Uka0d0GPiM35MsTO8nE=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@ -293,6 +301,8 @@ github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
@ -522,8 +532,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 h1:bNEHhJCnrwMKNMmOx3yAynp5vs5/gRy+XWFtZFu7NBM=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@ -582,8 +592,8 @@ golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210102185154-773b96fafca2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130 h1:8qSBr5nyKsEgkP918Pu5FFDZpTtLIjXSo6mrtdVOFfk=
|
||||
golang.org/x/tools v0.0.0-20210105210202-9ed45478a130/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -268,6 +268,7 @@ type LintersSettings struct {
|
||||
Gofumpt GofumptSettings
|
||||
ErrorLint ErrorLintSettings
|
||||
Makezero MakezeroSettings
|
||||
Revive ReviveSettings
|
||||
Thelper ThelperSettings
|
||||
Forbidigo ForbidigoSettings
|
||||
Ifshort IfshortSettings
|
||||
@ -397,6 +398,23 @@ type MakezeroSettings struct {
|
||||
Always bool
|
||||
}
|
||||
|
||||
type ReviveSettings struct {
|
||||
IgnoreGeneratedHeader bool `mapstructure:"ignore-generated-header"`
|
||||
Confidence float64
|
||||
Severity string
|
||||
Rules []struct {
|
||||
Name string
|
||||
Arguments []interface{}
|
||||
Severity string
|
||||
}
|
||||
ErrorCode int `mapstructure:"error-code"`
|
||||
WarningCode int `mapstructure:"warning-code"`
|
||||
Directives []struct {
|
||||
Name string
|
||||
Severity string
|
||||
}
|
||||
}
|
||||
|
||||
type ThelperSettings struct {
|
||||
Test struct {
|
||||
First bool `mapstructure:"first"`
|
||||
|
173
pkg/golinters/revive.go
Normal file
173
pkg/golinters/revive.go
Normal file
@ -0,0 +1,173 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mgechev/dots"
|
||||
reviveConfig "github.com/mgechev/revive/config"
|
||||
"github.com/mgechev/revive/lint"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const reviveName = "revive"
|
||||
|
||||
// jsonObject defines a JSON object of an failure
|
||||
type jsonObject struct {
|
||||
Severity lint.Severity
|
||||
lint.Failure `json:",inline"`
|
||||
}
|
||||
|
||||
// NewNewRevive returns a new Revive linter.
|
||||
func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
reviveName,
|
||||
"Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var files []string
|
||||
|
||||
for _, file := range pass.Files {
|
||||
files = append(files, pass.Fset.PositionFor(file.Pos(), false).Filename)
|
||||
}
|
||||
|
||||
conf, err := setReviveConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
formatter, err := reviveConfig.GetFormatter("json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
revive := lint.New(ioutil.ReadFile)
|
||||
|
||||
lintingRules, err := reviveConfig.GetLintingRules(conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packages, err := dots.ResolvePackages(files, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
failures, err := revive.Lint(packages, lintingRules, *conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
formatChan := make(chan lint.Failure)
|
||||
exitChan := make(chan bool)
|
||||
|
||||
var output string
|
||||
go func() {
|
||||
output, err = formatter.Format(formatChan, *conf)
|
||||
if err != nil {
|
||||
lintCtx.Log.Errorf("Format error: %v", err)
|
||||
}
|
||||
exitChan <- true
|
||||
}()
|
||||
|
||||
for f := range failures {
|
||||
if f.Confidence < conf.Confidence {
|
||||
continue
|
||||
}
|
||||
|
||||
formatChan <- f
|
||||
}
|
||||
|
||||
close(formatChan)
|
||||
<-exitChan
|
||||
|
||||
var results []jsonObject
|
||||
err = json.Unmarshal([]byte(output), &results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range results {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Severity: string(results[i].Severity),
|
||||
Text: fmt.Sprintf("%q", results[i].Failure.Failure),
|
||||
Pos: token.Position{
|
||||
Filename: results[i].Position.Start.Filename,
|
||||
Line: results[i].Position.Start.Line,
|
||||
Offset: results[i].Position.Start.Offset,
|
||||
Column: results[i].Position.Start.Column,
|
||||
},
|
||||
LineRange: &result.Range{
|
||||
From: results[i].Position.Start.Line,
|
||||
To: results[i].Position.End.Line,
|
||||
},
|
||||
FromLinter: reviveName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return issues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func setReviveConfig(cfg *config.ReviveSettings) (*lint.Config, error) {
|
||||
// Get revive default configuration
|
||||
conf, err := reviveConfig.GetConfig("")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Default is false
|
||||
conf.IgnoreGeneratedHeader = cfg.IgnoreGeneratedHeader
|
||||
|
||||
if cfg.Severity != "" {
|
||||
conf.Severity = lint.Severity(cfg.Severity)
|
||||
}
|
||||
|
||||
if cfg.Confidence != 0 {
|
||||
conf.Confidence = cfg.Confidence
|
||||
}
|
||||
|
||||
// By default golangci-lint ignores missing doc comments, follow same convention by removing this default rule
|
||||
// Relevant issue: https://github.com/golangci/golangci-lint/issues/456
|
||||
delete(conf.Rules, "exported")
|
||||
|
||||
if len(cfg.Rules) != 0 {
|
||||
// Clear default rules, only use rules defined in config
|
||||
conf.Rules = make(map[string]lint.RuleConfig, len(cfg.Rules))
|
||||
}
|
||||
for _, r := range cfg.Rules {
|
||||
conf.Rules[r.Name] = lint.RuleConfig{Arguments: r.Arguments, Severity: lint.Severity(r.Severity)}
|
||||
}
|
||||
|
||||
conf.ErrorCode = cfg.ErrorCode
|
||||
conf.WarningCode = cfg.WarningCode
|
||||
|
||||
if len(cfg.Directives) != 0 {
|
||||
// Clear default Directives, only use Directives defined in config
|
||||
conf.Directives = make(map[string]lint.DirectiveConfig, len(cfg.Directives))
|
||||
}
|
||||
for _, d := range cfg.Directives {
|
||||
conf.Directives[d.Name] = lint.DirectiveConfig{Severity: lint.Severity(d.Severity)}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
@ -96,6 +96,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
||||
var thelperCfg *config.ThelperSettings
|
||||
var predeclaredCfg *config.PredeclaredSettings
|
||||
var ifshortCfg *config.IfshortSettings
|
||||
var reviveCfg *config.ReviveSettings
|
||||
if m.cfg != nil {
|
||||
govetCfg = &m.cfg.LintersSettings.Govet
|
||||
testpackageCfg = &m.cfg.LintersSettings.Testpackage
|
||||
@ -104,6 +105,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
||||
thelperCfg = &m.cfg.LintersSettings.Thelper
|
||||
predeclaredCfg = &m.cfg.LintersSettings.Predeclared
|
||||
ifshortCfg = &m.cfg.LintersSettings.Ifshort
|
||||
reviveCfg = &m.cfg.LintersSettings.Revive
|
||||
}
|
||||
const megacheckName = "megacheck"
|
||||
lcs := []*linter.Config{
|
||||
@ -352,6 +354,9 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
||||
linter.NewConfig(golinters.NewPredeclared(predeclaredCfg)).
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/nishanths/predeclared"),
|
||||
linter.NewConfig(golinters.NewRevive(reviveCfg)).
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/mgechev/revive"),
|
||||
|
||||
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
|
||||
linter.NewConfig(golinters.NewNoLintLint()).
|
||||
|
7
test/testdata/configs/revive.yml
vendored
Normal file
7
test/testdata/configs/revive.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
linters-settings:
|
||||
revive:
|
||||
ignore-generated-header: true
|
||||
severity: warning
|
||||
rules:
|
||||
- name: indent-error-flow
|
||||
severity: warning
|
13
test/testdata/revive.go
vendored
Normal file
13
test/testdata/revive.go
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//args: -Erevive
|
||||
//config_path: testdata/configs/revive.yml
|
||||
package testdata
|
||||
|
||||
import "time"
|
||||
|
||||
func testRevive(t *time.Duration) error {
|
||||
if t == nil {
|
||||
return nil
|
||||
} else { // ERROR "if block ends with a return statement, so drop this else and outdent its block"
|
||||
return nil
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user