mirror of
https://github.com/scratchfoundation/golangci-lint.git
synced 2025-08-28 22:28:43 -04:00
bump golang.org/x/tools to HEAD (#2875)
* bump golang.org/x/tools to HEAD * fix: adapt linters to the new validation system.
This commit is contained in:
parent
75be924e98
commit
f9d815115c
74 changed files with 2260 additions and 1687 deletions
5
go.mod
5
go.mod
|
@ -99,7 +99,7 @@ require (
|
|||
github.com/yagipy/maintidx v1.0.0
|
||||
github.com/yeya24/promlinter v0.2.0
|
||||
gitlab.com/bosi/decorder v0.2.1
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a
|
||||
golang.org/x/tools v0.1.11-0.20220518213611-904e24e9fcf9
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
honnef.co/go/tools v0.3.1
|
||||
mvdan.cc/gofumpt v0.3.1
|
||||
|
@ -165,11 +165,10 @@ require (
|
|||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/ini.v1 v1.66.4 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
|
9
go.sum
generated
9
go.sum
generated
|
@ -917,8 +917,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -1207,10 +1208,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
|||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
|
@ -1220,8 +1219,8 @@ golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
|||
golang.org/x/tools v0.1.9-0.20211228192929-ee1ca4ffc4da/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a h1:ofrrl6c6NG5/IOSx/R1cyiQxxjqlur0h/TvbUhkH0II=
|
||||
golang.org/x/tools v0.1.11-0.20220316014157-77aa08bb151a/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11-0.20220518213611-904e24e9fcf9 h1:/IsvdCr9GrirHTBBfgW/iJ4uDhMPf+OnOLrp4og7MzU=
|
||||
golang.org/x/tools v0.1.11-0.20220518213611-904e24e9fcf9/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
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=
|
||||
|
|
|
@ -11,9 +11,7 @@ func NewAsciicheck() *goanalysis.Linter {
|
|||
return goanalysis.NewLinter(
|
||||
"asciicheck",
|
||||
"Simple linter to check that your code does not contain non-ASCII identifiers",
|
||||
[]*analysis.Analyzer{
|
||||
asciicheck.NewAnalyzer(),
|
||||
},
|
||||
[]*analysis.Analyzer{asciicheck.NewAnalyzer()},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewBodyclose() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
bodyclose.Analyzer,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"bodyclose",
|
||||
"checks whether HTTP response body is closed successfully",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{bodyclose.Analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -8,11 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewContextCheck() *goanalysis.Linter {
|
||||
analyzer := contextcheck.NewAnalyzer()
|
||||
return goanalysis.NewLinter(
|
||||
"contextcheck",
|
||||
"check the function whether use a non-inherited context",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
[]*analysis.Analyzer{contextcheck.NewAnalyzer()},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -12,28 +12,36 @@ import (
|
|||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const deadcodeName = "deadcode"
|
||||
|
||||
func NewDeadcode() *goanalysis.Linter {
|
||||
const linterName = "deadcode"
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: deadcodeName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
|
||||
issues, err := deadcodeAPI.Run(prog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(issues))
|
||||
for _, i := range issues {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("%s is unused", formatCode(i.UnusedIdentName, nil)),
|
||||
FromLinter: linterName,
|
||||
FromLinter: deadcodeName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
@ -41,8 +49,9 @@ func NewDeadcode() *goanalysis.Linter {
|
|||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
deadcodeName,
|
||||
"Finds unused code",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
|
|
|
@ -13,8 +13,6 @@ import (
|
|||
func NewDecorder(settings *config.DecorderSettings) *goanalysis.Linter {
|
||||
a := decorder.Analyzer
|
||||
|
||||
analyzers := []*analysis.Analyzer{a}
|
||||
|
||||
// disable all rules/checks by default
|
||||
cfg := map[string]interface{}{
|
||||
"disable-dec-num-check": true,
|
||||
|
@ -32,7 +30,7 @@ func NewDecorder(settings *config.DecorderSettings) *goanalysis.Linter {
|
|||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{a},
|
||||
map[string]map[string]interface{}{a.Name: cfg},
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -15,23 +15,25 @@ import (
|
|||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const depguardLinterName = "depguard"
|
||||
const depguardName = "depguard"
|
||||
|
||||
func NewDepguard() *goanalysis.Linter {
|
||||
func NewDepguard(settings *config.DepGuardSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: depguardLinterName,
|
||||
Name: depguardName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
depguardLinterName,
|
||||
depguardName,
|
||||
"Go linter that checks if package imports are in a list of acceptable packages",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
dg, err := newDepGuard(&lintCtx.Settings().Depguard)
|
||||
dg, err := newDepGuard(settings)
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
if err != nil {
|
||||
|
@ -153,7 +155,7 @@ func (g guardian) run(loadConfig *loader.Config, prog *loader.Program, pass *ana
|
|||
goanalysis.NewIssue(&result.Issue{
|
||||
Pos: issue.Position,
|
||||
Text: g.createMsg(issue.PackageName),
|
||||
FromLinter: depguardLinterName,
|
||||
FromLinter: depguardName,
|
||||
}, pass),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,51 +8,65 @@ import (
|
|||
|
||||
"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 dogsledLinterName = "dogsled"
|
||||
const dogsledName = "dogsled"
|
||||
|
||||
func NewDogsled() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewDogsled(settings *config.DogsledSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: dogsledLinterName,
|
||||
Name: dogsledName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
dogsledLinterName,
|
||||
"Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var pkgIssues []goanalysis.Issue
|
||||
for _, f := range pass.Files {
|
||||
v := returnsVisitor{
|
||||
maxBlanks: lintCtx.Settings().Dogsled.MaxBlankIdentifiers,
|
||||
f: pass.Fset,
|
||||
}
|
||||
ast.Walk(&v, f)
|
||||
for i := range v.issues {
|
||||
pkgIssues = append(pkgIssues, goanalysis.NewIssue(&v.issues[i], pass))
|
||||
}
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runDogsled(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, pkgIssues...)
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
dogsledName,
|
||||
"Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runDogsled(pass *analysis.Pass, settings *config.DogsledSettings) []goanalysis.Issue {
|
||||
var reports []goanalysis.Issue
|
||||
for _, f := range pass.Files {
|
||||
v := &returnsVisitor{
|
||||
maxBlanks: settings.MaxBlankIdentifiers,
|
||||
f: pass.Fset,
|
||||
}
|
||||
|
||||
ast.Walk(v, f)
|
||||
|
||||
for i := range v.issues {
|
||||
reports = append(reports, goanalysis.NewIssue(&v.issues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
return reports
|
||||
}
|
||||
|
||||
type returnsVisitor struct {
|
||||
f *token.FileSet
|
||||
maxBlanks int
|
||||
|
@ -87,7 +101,7 @@ func (v *returnsVisitor) Visit(node ast.Node) ast.Visitor {
|
|||
|
||||
if numBlank > v.maxBlanks {
|
||||
v.issues = append(v.issues, result.Issue{
|
||||
FromLinter: dogsledLinterName,
|
||||
FromLinter: dogsledName,
|
||||
Text: fmt.Sprintf("declaration has %v blank identifiers", numBlank),
|
||||
Pos: v.f.Position(assgnStmt.Pos()),
|
||||
})
|
||||
|
|
|
@ -9,36 +9,59 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const duplLinterName = "dupl"
|
||||
const duplName = "dupl"
|
||||
|
||||
func NewDupl() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewDupl(settings *config.DuplSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: duplLinterName,
|
||||
Name: duplName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runDupl(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
duplLinterName,
|
||||
duplName,
|
||||
"Tool for code clone detection",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runDupl(pass *analysis.Pass, settings *config.DuplSettings) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
issues, err := duplAPI.Run(fileNames, lintCtx.Settings().Dupl.Threshold)
|
||||
issues, err := duplAPI.Run(fileNames, settings.Threshold)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -48,15 +71,18 @@ func NewDupl() *goanalysis.Linter {
|
|||
}
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(issues))
|
||||
|
||||
for _, i := range issues {
|
||||
toFilename, err := fsutils.ShortestRelPath(i.To.Filename(), "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get shortest rel path for %q", i.To.Filename())
|
||||
}
|
||||
|
||||
dupl := fmt.Sprintf("%s:%d-%d", toFilename, i.To.LineStart(), i.To.LineEnd())
|
||||
text := fmt.Sprintf("%d-%d lines are duplicate of %s",
|
||||
i.From.LineStart(), i.From.LineEnd(),
|
||||
formatCode(dupl, lintCtx.Cfg))
|
||||
formatCode(dupl, nil))
|
||||
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: i.From.Filename(),
|
||||
|
@ -67,17 +93,9 @@ func NewDupl() *goanalysis.Linter {
|
|||
To: i.From.LineEnd(),
|
||||
},
|
||||
Text: text,
|
||||
FromLinter: duplLinterName,
|
||||
FromLinter: duplName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ import (
|
|||
func NewDurationCheck() *goanalysis.Linter {
|
||||
a := durationcheck.Analyzer
|
||||
|
||||
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil).
|
||||
WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
[]*analysis.Analyzer{a},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -22,26 +22,27 @@ import (
|
|||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
func NewErrcheck() *goanalysis.Linter {
|
||||
const linterName = "errcheck"
|
||||
const errcheckName = "errcheck"
|
||||
|
||||
func NewErrcheck(settings *config.ErrcheckSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var res []goanalysis.Issue
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: errcheckName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
errcheckName,
|
||||
"Errcheck is a program for checking for unchecked errors "+
|
||||
"in go programs. These unchecked errors can be critical bugs in some cases",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
// copied from errcheck
|
||||
checker, err := getChecker(&lintCtx.Settings().Errcheck)
|
||||
checker, err := getChecker(settings)
|
||||
if err != nil {
|
||||
lintCtx.Log.Errorf("failed to get checker: %v", err)
|
||||
return
|
||||
|
@ -50,6 +51,27 @@ func NewErrcheck() *goanalysis.Linter {
|
|||
checker.Tags = lintCtx.Cfg.Run.BuildTags
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runErrCheck(lintCtx, pass, checker)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runErrCheck(lintCtx *linter.Context, pass *analysis.Pass, checker *errcheck.Checker) []goanalysis.Issue {
|
||||
pkg := &packages.Package{
|
||||
Fset: pass.Fset,
|
||||
Syntax: pass.Files,
|
||||
|
@ -57,31 +79,28 @@ func NewErrcheck() *goanalysis.Linter {
|
|||
TypesInfo: pass.TypesInfo,
|
||||
}
|
||||
|
||||
errcheckIssues := checker.CheckPackage(pkg).Unique()
|
||||
if len(errcheckIssues.UncheckedErrors) == 0 {
|
||||
return nil, nil
|
||||
lintIssues := checker.CheckPackage(pkg).Unique()
|
||||
if len(lintIssues.UncheckedErrors) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(errcheckIssues.UncheckedErrors))
|
||||
for i, err := range errcheckIssues.UncheckedErrors {
|
||||
var text string
|
||||
issues := make([]goanalysis.Issue, len(lintIssues.UncheckedErrors))
|
||||
|
||||
for i, err := range lintIssues.UncheckedErrors {
|
||||
text := "Error return value is not checked"
|
||||
|
||||
if err.FuncName != "" {
|
||||
code := err.SelectorName
|
||||
if err.SelectorName == "" {
|
||||
code = err.FuncName
|
||||
}
|
||||
|
||||
text = fmt.Sprintf(
|
||||
"Error return value of %s is not checked",
|
||||
formatCode(code, lintCtx.Cfg),
|
||||
)
|
||||
} else {
|
||||
text = "Error return value is not checked"
|
||||
text = fmt.Sprintf("Error return value of %s is not checked", formatCode(code, lintCtx.Cfg))
|
||||
}
|
||||
|
||||
issues[i] = goanalysis.NewIssue(
|
||||
&result.Issue{
|
||||
FromLinter: linterName,
|
||||
FromLinter: errcheckName,
|
||||
Text: text,
|
||||
Pos: err.Pos,
|
||||
},
|
||||
|
@ -89,15 +108,7 @@ func NewErrcheck() *goanalysis.Linter {
|
|||
)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
res = append(res, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return res
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues
|
||||
}
|
||||
|
||||
// parseIgnoreConfig was taken from errcheck in order to keep the API identical.
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewErrName() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
analyzer.New(),
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"errname",
|
||||
"Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`.",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{analyzer.New()},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ func NewExhaustive(settings *config.ExhaustiveSettings) *goanalysis.Linter {
|
|||
}
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, cfg).
|
||||
WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
[]*analysis.Analyzer{a},
|
||||
cfg,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
|
||||
func NewExhaustruct(settings *config.ExhaustructSettings) *goanalysis.Linter {
|
||||
var include, exclude []string
|
||||
|
||||
if settings != nil {
|
||||
include = settings.Include
|
||||
exclude = settings.Exclude
|
||||
|
@ -21,6 +20,10 @@ func NewExhaustruct(settings *config.ExhaustructSettings) *goanalysis.Linter {
|
|||
linterLogger.Fatalf("exhaustruct configuration: %v", err)
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil).
|
||||
WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
[]*analysis.Analyzer{a},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -7,64 +7,76 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewForbidigo() *goanalysis.Linter {
|
||||
const linterName = "forbidigo"
|
||||
const forbidigoName = "forbidigo"
|
||||
|
||||
//nolint:dupl
|
||||
func NewForbidigo(settings *config.ForbidigoSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: forbidigoName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runForbidigo(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
forbidigoName,
|
||||
"Forbids identifiers",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
s := &lintCtx.Settings().Forbidigo
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var res []goanalysis.Issue
|
||||
func runForbidigo(pass *analysis.Pass, settings *config.ForbidigoSettings) ([]goanalysis.Issue, error) {
|
||||
options := []forbidigo.Option{
|
||||
forbidigo.OptionExcludeGodocExamples(s.ExcludeGodocExamples),
|
||||
// disable "//permit" directives so only "//nolint" directives matters within golangci lint
|
||||
forbidigo.OptionExcludeGodocExamples(settings.ExcludeGodocExamples),
|
||||
// disable "//permit" directives so only "//nolint" directives matters within golangci-lint
|
||||
forbidigo.OptionIgnorePermitDirectives(true),
|
||||
}
|
||||
forbid, err := forbidigo.NewLinter(s.Forbid, options...)
|
||||
|
||||
forbid, err := forbidigo.NewLinter(settings.Forbid, options...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to create linter %q", linterName)
|
||||
return nil, errors.Wrapf(err, "failed to create linter %q", forbidigoName)
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for _, file := range pass.Files {
|
||||
hints, err := forbid.Run(pass.Fset, file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "forbidigo linter failed on file %q", file.Name.String())
|
||||
}
|
||||
|
||||
for _, hint := range hints {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: hint.Position(),
|
||||
Text: hint.Details(),
|
||||
FromLinter: linterName,
|
||||
FromLinter: forbidigoName,
|
||||
}, pass))
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -8,57 +8,69 @@ import (
|
|||
"github.com/ultraware/funlen"
|
||||
"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 funlenLinterName = "funlen"
|
||||
const funlenName = "funlen"
|
||||
|
||||
func NewFunlen() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewFunlen(settings *config.FunlenSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: funlenLinterName,
|
||||
Name: funlenName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
funlenLinterName,
|
||||
"Tool for detection of long functions",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var issues []funlen.Message
|
||||
for _, file := range pass.Files {
|
||||
fileIssues := funlen.Run(file, pass.Fset, lintCtx.Settings().Funlen.Lines, lintCtx.Settings().Funlen.Statements)
|
||||
issues = append(issues, fileIssues...)
|
||||
}
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runFunlen(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, len(issues))
|
||||
for k, i := range issues {
|
||||
res[k] = goanalysis.NewIssue(&result.Issue{
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
funlenName,
|
||||
"Tool for detection of long functions",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runFunlen(pass *analysis.Pass, settings *config.FunlenSettings) []goanalysis.Issue {
|
||||
var lintIssues []funlen.Message
|
||||
for _, file := range pass.Files {
|
||||
fileIssues := funlen.Run(file, pass.Fset, settings.Lines, settings.Statements)
|
||||
lintIssues = append(lintIssues, fileIssues...)
|
||||
}
|
||||
|
||||
if len(lintIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(lintIssues))
|
||||
for k, i := range lintIssues {
|
||||
issues[k] = goanalysis.NewIssue(&result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: i.Pos.Filename,
|
||||
Line: i.Pos.Line,
|
||||
},
|
||||
Text: strings.TrimRight(i.Message, "\n"),
|
||||
FromLinter: funlenLinterName,
|
||||
FromLinter: funlenName,
|
||||
}, pass)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ const gciName = "gci"
|
|||
|
||||
func NewGci(settings *config.GciSettings) *goanalysis.Linter {
|
||||
var linterCfg map[string]map[string]interface{}
|
||||
|
||||
if settings != nil {
|
||||
cfg := map[string]interface{}{
|
||||
gci.NoInlineCommentsFlag: settings.NoInlineComments,
|
||||
|
|
|
@ -211,3 +211,7 @@ func valueToString(v interface{}) string {
|
|||
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
func DummyRun(_ *analysis.Pass) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ func NewGochecknoinits() *goanalysis.Linter {
|
|||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gochecknoinitsName,
|
||||
"Checks that no init functions are present in Go code",
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/uudashr/gocognit"
|
||||
"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"
|
||||
|
@ -15,54 +16,65 @@ import (
|
|||
|
||||
const gocognitName = "gocognit"
|
||||
|
||||
func NewGocognit() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewGocognit(settings *config.GocognitSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runGocognit(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gocognitName,
|
||||
"Computes and checks the cognitive complexity of functions",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGocognit(pass *analysis.Pass, settings *config.GocognitSettings) []goanalysis.Issue {
|
||||
var stats []gocognit.Stat
|
||||
for _, f := range pass.Files {
|
||||
stats = gocognit.ComplexityStats(f, pass.Fset, stats)
|
||||
}
|
||||
if len(stats) == 0 {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.SliceStable(stats, func(i, j int) bool {
|
||||
return stats[i].Complexity > stats[j].Complexity
|
||||
})
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(stats))
|
||||
issues := make([]goanalysis.Issue, 0, len(stats))
|
||||
for _, s := range stats {
|
||||
if s.Complexity <= lintCtx.Settings().Gocognit.MinComplexity {
|
||||
if s.Complexity <= settings.MinComplexity {
|
||||
break // Break as the stats is already sorted from greatest to least
|
||||
}
|
||||
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: s.Pos,
|
||||
Text: fmt.Sprintf("cognitive complexity %d of func %s is high (> %d)",
|
||||
s.Complexity, formatCode(s.FuncName, lintCtx.Cfg), lintCtx.Settings().Gocognit.MinComplexity),
|
||||
s.Complexity, formatCode(s.FuncName, nil), settings.MinComplexity),
|
||||
FromLinter: gocognitName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
goconstAPI "github.com/jgautheron/goconst"
|
||||
"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"
|
||||
|
@ -14,40 +15,43 @@ import (
|
|||
|
||||
const goconstName = "goconst"
|
||||
|
||||
func NewGoconst() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewGoconst(settings *config.GoConstSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goconstName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
goconstName,
|
||||
"Finds repeated strings that could be replaced by a constant",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := checkConstants(pass, lintCtx)
|
||||
if err != nil || len(issues) == 0 {
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGoconst(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
goconstName,
|
||||
"Finds repeated strings that could be replaced by a constant",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func checkConstants(pass *analysis.Pass, lintCtx *linter.Context) ([]goanalysis.Issue, error) {
|
||||
settings := lintCtx.Settings().Goconst
|
||||
|
||||
func runGoconst(pass *analysis.Pass, settings *config.GoConstSettings) ([]goanalysis.Issue, error) {
|
||||
cfg := goconstAPI.Config{
|
||||
IgnoreTests: settings.IgnoreTests,
|
||||
MatchWithConstants: settings.MatchWithConstants,
|
||||
|
@ -63,27 +67,28 @@ func checkConstants(pass *analysis.Pass, lintCtx *linter.Context) ([]goanalysis.
|
|||
cfg.ExcludeTypes[goconstAPI.Call] = true
|
||||
}
|
||||
|
||||
goconstIssues, err := goconstAPI.Run(pass.Files, pass.Fset, &cfg)
|
||||
lintIssues, err := goconstAPI.Run(pass.Files, pass.Fset, &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(goconstIssues) == 0 {
|
||||
if len(lintIssues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(goconstIssues))
|
||||
for _, i := range goconstIssues {
|
||||
textBegin := fmt.Sprintf("string %s has %d occurrences", formatCode(i.Str, lintCtx.Cfg), i.OccurrencesCount)
|
||||
var textEnd string
|
||||
res := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
for _, i := range lintIssues {
|
||||
text := fmt.Sprintf("string %s has %d occurrences", formatCode(i.Str, nil), i.OccurrencesCount)
|
||||
|
||||
if i.MatchingConst == "" {
|
||||
textEnd = ", make it a constant"
|
||||
text += ", make it a constant"
|
||||
} else {
|
||||
textEnd = fmt.Sprintf(", but such constant %s already exists", formatCode(i.MatchingConst, lintCtx.Cfg))
|
||||
text += fmt.Sprintf(", but such constant %s already exists", formatCode(i.MatchingConst, nil))
|
||||
}
|
||||
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: textBegin + textEnd,
|
||||
Text: text,
|
||||
FromLinter: goconstName,
|
||||
}, pass))
|
||||
}
|
||||
|
|
|
@ -22,16 +22,39 @@ import (
|
|||
|
||||
const gocriticName = "gocritic"
|
||||
|
||||
func NewGocritic() *goanalysis.Linter {
|
||||
func NewGocritic(settings *config.GocriticSettings, cfg *config.Config) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
sizes := types.SizesFor("gc", runtime.GOARCH)
|
||||
|
||||
wrapper := goCriticWrapper{
|
||||
settings: settings,
|
||||
cfg: cfg,
|
||||
sizes: sizes,
|
||||
}
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: gocriticName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := wrapper.run(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gocriticName,
|
||||
`Provides diagnostics that check for bugs, performance and style issues.
|
||||
|
@ -39,113 +62,47 @@ Extensible without recompilation through dynamic rules.
|
|||
Dynamic rules are written declaratively with AST patterns, filters, report message and optional suggestion.`,
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
linterCtx := gocriticlinter.NewContext(pass.Fset, sizes)
|
||||
enabledCheckers, err := buildEnabledCheckers(lintCtx, linterCtx)
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
type goCriticWrapper struct {
|
||||
settings *config.GocriticSettings
|
||||
cfg *config.Config
|
||||
sizes types.Sizes
|
||||
}
|
||||
|
||||
func (w goCriticWrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) {
|
||||
linterCtx := gocriticlinter.NewContext(pass.Fset, w.sizes)
|
||||
|
||||
enabledCheckers, err := w.buildEnabledCheckers(linterCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linterCtx.SetPackageInfo(pass.TypesInfo, pass.Pkg)
|
||||
|
||||
pkgIssues := runGocriticOnPackage(linterCtx, enabledCheckers, pass.Files)
|
||||
res := make([]goanalysis.Issue, 0, len(pkgIssues))
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(pkgIssues))
|
||||
for i := range pkgIssues {
|
||||
res = append(res, goanalysis.NewIssue(&pkgIssues[i], pass))
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
issues = append(issues, goanalysis.NewIssue(&pkgIssues[i], pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func normalizeCheckerInfoParams(info *gocriticlinter.CheckerInfo) gocriticlinter.CheckerParams {
|
||||
// lowercase info param keys here because golangci-lint's config parser lowercases all strings
|
||||
ret := gocriticlinter.CheckerParams{}
|
||||
for k, v := range info.Params {
|
||||
ret[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func configureCheckerInfo(
|
||||
lintCtx *linter.Context,
|
||||
info *gocriticlinter.CheckerInfo,
|
||||
allParams map[string]config.GocriticCheckSettings) error {
|
||||
params := allParams[strings.ToLower(info.Name)]
|
||||
if params == nil { // no config for this checker
|
||||
return nil
|
||||
}
|
||||
|
||||
infoParams := normalizeCheckerInfoParams(info)
|
||||
for k, p := range params {
|
||||
v, ok := infoParams[k]
|
||||
if ok {
|
||||
v.Value = normalizeCheckerParamsValue(lintCtx, p)
|
||||
continue
|
||||
}
|
||||
|
||||
// param `k` isn't supported
|
||||
if len(info.Params) == 0 {
|
||||
return fmt.Errorf("checker %s config param %s doesn't exist: checker doesn't have params",
|
||||
info.Name, k)
|
||||
}
|
||||
|
||||
var supportedKeys []string
|
||||
for sk := range info.Params {
|
||||
supportedKeys = append(supportedKeys, sk)
|
||||
}
|
||||
sort.Strings(supportedKeys)
|
||||
|
||||
return fmt.Errorf("checker %s config param %s doesn't exist, all existing: %s",
|
||||
info.Name, k, supportedKeys)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// normalizeCheckerParamsValue normalizes value types.
|
||||
// go-critic asserts that CheckerParam.Value has some specific types,
|
||||
// but the file parsers (TOML, YAML, JSON) don't create the same representation for raw type.
|
||||
// then we have to convert value types into the expected value types.
|
||||
// Maybe in the future, this kind of conversion will be done in go-critic itself.
|
||||
func normalizeCheckerParamsValue(lintCtx *linter.Context, p interface{}) interface{} {
|
||||
rv := reflect.ValueOf(p)
|
||||
switch rv.Type().Kind() {
|
||||
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
|
||||
return int(rv.Int())
|
||||
case reflect.Bool:
|
||||
return rv.Bool()
|
||||
case reflect.String:
|
||||
// Perform variable substitution.
|
||||
return strings.ReplaceAll(rv.String(), "${configDir}", lintCtx.Cfg.GetConfigDir())
|
||||
default:
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
func buildEnabledCheckers(lintCtx *linter.Context, linterCtx *gocriticlinter.Context) ([]*gocriticlinter.Checker, error) {
|
||||
s := lintCtx.Settings().Gocritic
|
||||
allParams := s.GetLowercasedParams()
|
||||
func (w goCriticWrapper) buildEnabledCheckers(linterCtx *gocriticlinter.Context) ([]*gocriticlinter.Checker, error) {
|
||||
allParams := w.settings.GetLowercasedParams()
|
||||
|
||||
var enabledCheckers []*gocriticlinter.Checker
|
||||
for _, info := range gocriticlinter.GetCheckersInfo() {
|
||||
if !s.IsCheckEnabled(info.Name) {
|
||||
if !w.settings.IsCheckEnabled(info.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := configureCheckerInfo(lintCtx, info, allParams); err != nil {
|
||||
if err := w.configureCheckerInfo(info, allParams); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -172,14 +129,14 @@ func runGocriticOnPackage(linterCtx *gocriticlinter.Context, checkers []*gocriti
|
|||
return res
|
||||
}
|
||||
|
||||
func runGocriticOnFile(ctx *gocriticlinter.Context, f *ast.File, checkers []*gocriticlinter.Checker) []result.Issue {
|
||||
func runGocriticOnFile(linterCtx *gocriticlinter.Context, f *ast.File, checkers []*gocriticlinter.Checker) []result.Issue {
|
||||
var res []result.Issue
|
||||
|
||||
for _, c := range checkers {
|
||||
// All checkers are expected to use *lint.Context
|
||||
// as read-only structure, so no copying is required.
|
||||
for _, warn := range c.Check(f) {
|
||||
pos := ctx.FileSet.Position(warn.Node.Pos())
|
||||
pos := linterCtx.FileSet.Position(warn.Node.Pos())
|
||||
issue := result.Issue{
|
||||
Pos: pos,
|
||||
Text: fmt.Sprintf("%s: %s", c.Info.Name, warn.Text),
|
||||
|
@ -202,3 +159,66 @@ func runGocriticOnFile(ctx *gocriticlinter.Context, f *ast.File, checkers []*goc
|
|||
|
||||
return res
|
||||
}
|
||||
|
||||
func (w goCriticWrapper) configureCheckerInfo(info *gocriticlinter.CheckerInfo, allParams map[string]config.GocriticCheckSettings) error {
|
||||
params := allParams[strings.ToLower(info.Name)]
|
||||
if params == nil { // no config for this checker
|
||||
return nil
|
||||
}
|
||||
|
||||
infoParams := normalizeCheckerInfoParams(info)
|
||||
for k, p := range params {
|
||||
v, ok := infoParams[k]
|
||||
if ok {
|
||||
v.Value = w.normalizeCheckerParamsValue(p)
|
||||
continue
|
||||
}
|
||||
|
||||
// param `k` isn't supported
|
||||
if len(info.Params) == 0 {
|
||||
return fmt.Errorf("checker %s config param %s doesn't exist: checker doesn't have params",
|
||||
info.Name, k)
|
||||
}
|
||||
|
||||
var supportedKeys []string
|
||||
for sk := range info.Params {
|
||||
supportedKeys = append(supportedKeys, sk)
|
||||
}
|
||||
sort.Strings(supportedKeys)
|
||||
|
||||
return fmt.Errorf("checker %s config param %s doesn't exist, all existing: %s",
|
||||
info.Name, k, supportedKeys)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeCheckerInfoParams(info *gocriticlinter.CheckerInfo) gocriticlinter.CheckerParams {
|
||||
// lowercase info param keys here because golangci-lint's config parser lowercases all strings
|
||||
ret := gocriticlinter.CheckerParams{}
|
||||
for k, v := range info.Params {
|
||||
ret[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// normalizeCheckerParamsValue normalizes value types.
|
||||
// go-critic asserts that CheckerParam.Value has some specific types,
|
||||
// but the file parsers (TOML, YAML, JSON) don't create the same representation for raw type.
|
||||
// then we have to convert value types into the expected value types.
|
||||
// Maybe in the future, this kind of conversion will be done in go-critic itself.
|
||||
func (w goCriticWrapper) normalizeCheckerParamsValue(p interface{}) interface{} {
|
||||
rv := reflect.ValueOf(p)
|
||||
switch rv.Type().Kind() {
|
||||
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
|
||||
return int(rv.Int())
|
||||
case reflect.Bool:
|
||||
return rv.Bool()
|
||||
case reflect.String:
|
||||
// Perform variable substitution.
|
||||
return strings.ReplaceAll(rv.String(), "${configDir}", w.cfg.GetConfigDir())
|
||||
default:
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/fzipp/gocyclo"
|
||||
"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"
|
||||
|
@ -14,48 +15,62 @@ import (
|
|||
|
||||
const gocycloName = "gocyclo"
|
||||
|
||||
func NewGocyclo() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewGocyclo(settings *config.GoCycloSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: gocycloName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runGoCyclo(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gocycloName,
|
||||
"Computes and checks the cyclomatic complexity of functions",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGoCyclo(pass *analysis.Pass, settings *config.GoCycloSettings) []goanalysis.Issue {
|
||||
var stats gocyclo.Stats
|
||||
for _, f := range pass.Files {
|
||||
stats = gocyclo.AnalyzeASTFile(f, pass.Fset, stats)
|
||||
}
|
||||
if len(stats) == 0 {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
stats = stats.SortAndFilter(-1, lintCtx.Settings().Gocyclo.MinComplexity)
|
||||
stats = stats.SortAndFilter(-1, settings.MinComplexity)
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(stats))
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(stats))
|
||||
for _, s := range stats {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
text := fmt.Sprintf("cyclomatic complexity %d of func %s is high (> %d)",
|
||||
s.Complexity, formatCode(s.FuncName, nil), settings.MinComplexity)
|
||||
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: s.Pos,
|
||||
Text: fmt.Sprintf("cyclomatic complexity %d of func %s is high (> %d)",
|
||||
s.Complexity, formatCode(s.FuncName, lintCtx.Cfg), lintCtx.Settings().Gocyclo.MinComplexity),
|
||||
Text: text,
|
||||
FromLinter: gocycloName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/tetafro/godot"
|
||||
"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"
|
||||
|
@ -13,54 +14,78 @@ import (
|
|||
|
||||
const godotName = "godot"
|
||||
|
||||
func NewGodot() *goanalysis.Linter {
|
||||
func NewGodot(settings *config.GodotSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: godotName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
godotName,
|
||||
"Check if comments end in a period",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
cfg := lintCtx.Cfg.LintersSettings.Godot
|
||||
settings := godot.Settings{
|
||||
Scope: godot.Scope(cfg.Scope),
|
||||
Exclude: cfg.Exclude,
|
||||
Period: cfg.Period,
|
||||
Capital: cfg.Capital,
|
||||
var dotSettings godot.Settings
|
||||
|
||||
if settings != nil {
|
||||
dotSettings = godot.Settings{
|
||||
Scope: godot.Scope(settings.Scope),
|
||||
Exclude: settings.Exclude,
|
||||
Period: settings.Period,
|
||||
Capital: settings.Capital,
|
||||
}
|
||||
|
||||
// Convert deprecated setting
|
||||
// todo(butuzov): remove on v2 release
|
||||
if cfg.CheckAll { // nolint:staticcheck // Keep for retro-compatibility.
|
||||
settings.Scope = godot.AllScope
|
||||
if settings.CheckAll { // nolint:staticcheck // Keep for retro-compatibility.
|
||||
dotSettings.Scope = godot.AllScope
|
||||
}
|
||||
}
|
||||
|
||||
if settings.Scope == "" {
|
||||
settings.Scope = godot.DeclScope
|
||||
if dotSettings.Scope == "" {
|
||||
dotSettings.Scope = godot.DeclScope
|
||||
}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var issues []godot.Issue
|
||||
for _, file := range pass.Files {
|
||||
iss, err := godot.Run(file, pass.Fset, settings)
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: godotName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGodot(pass, dotSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
issues = append(issues, iss...)
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, len(issues))
|
||||
for k, i := range issues {
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
godotName,
|
||||
"Check if comments end in a period",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGodot(pass *analysis.Pass, settings godot.Settings) ([]goanalysis.Issue, error) {
|
||||
var lintIssues []godot.Issue
|
||||
for _, file := range pass.Files {
|
||||
iss, err := godot.Run(file, pass.Fset, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lintIssues = append(lintIssues, iss...)
|
||||
}
|
||||
|
||||
if len(lintIssues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(lintIssues))
|
||||
for k, i := range lintIssues {
|
||||
issue := result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: i.Message,
|
||||
|
@ -70,16 +95,8 @@ func NewGodot() *goanalysis.Linter {
|
|||
},
|
||||
}
|
||||
|
||||
res[k] = goanalysis.NewIssue(&issue, pass)
|
||||
issues[k] = goanalysis.NewIssue(&issue, pass)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/matoous/godox"
|
||||
"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"
|
||||
|
@ -15,33 +16,53 @@ import (
|
|||
|
||||
const godoxName = "godox"
|
||||
|
||||
func NewGodox() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewGodox(settings *config.GodoxSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: godoxName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
godoxName,
|
||||
"Tool for detection of FIXME, TODO and other comment keywords",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var issues []godox.Message
|
||||
for _, file := range pass.Files {
|
||||
issues = append(issues, godox.Run(file, pass.Fset, lintCtx.Settings().Godox.Keywords...)...)
|
||||
}
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runGodox(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, len(issues))
|
||||
for k, i := range issues {
|
||||
res[k] = goanalysis.NewIssue(&result.Issue{
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
godoxName,
|
||||
"Tool for detection of FIXME, TODO and other comment keywords",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGodox(pass *analysis.Pass, settings *config.GodoxSettings) []goanalysis.Issue {
|
||||
var messages []godox.Message
|
||||
for _, file := range pass.Files {
|
||||
messages = append(messages, godox.Run(file, pass.Fset, settings.Keywords...)...)
|
||||
}
|
||||
|
||||
if len(messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(messages))
|
||||
|
||||
for k, i := range messages {
|
||||
issues[k] = goanalysis.NewIssue(&result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: i.Pos.Filename,
|
||||
Line: i.Pos.Line,
|
||||
|
@ -51,13 +72,5 @@ func NewGodox() *goanalysis.Linter {
|
|||
}, pass)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ func NewGoerr113() *goanalysis.Linter {
|
|||
return goanalysis.NewLinter(
|
||||
"goerr113",
|
||||
"Golang linter to check the errors handling expressions",
|
||||
[]*analysis.Analyzer{
|
||||
err113.NewAnalyzer(),
|
||||
},
|
||||
[]*analysis.Analyzer{err113.NewAnalyzer()},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -7,20 +7,23 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
const gofmtName = "gofmt"
|
||||
|
||||
func NewGofmt() *goanalysis.Linter {
|
||||
func NewGofmt(settings *config.GoFmtSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: gofmtName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gofmtName,
|
||||
"Gofmt checks whether code was gofmt-ed. By default "+
|
||||
|
@ -29,31 +32,9 @@ func NewGofmt() *goanalysis.Linter {
|
|||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
for _, f := range fileNames {
|
||||
diff, err := gofmtAPI.Run(f, lintCtx.Settings().Gofmt.Simplify)
|
||||
if err != nil { // TODO: skip
|
||||
return nil, err
|
||||
}
|
||||
if diff == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
is, err := extractIssuesFromPatch(string(diff), lintCtx.Log, lintCtx, gofmtName)
|
||||
issues, err := runGofmt(lintCtx, pass, settings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't extract issues from gofmt diff output %q", string(diff))
|
||||
}
|
||||
|
||||
for i := range is {
|
||||
issues = append(issues, goanalysis.NewIssue(&is[i], pass))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
|
@ -70,3 +51,34 @@ func NewGofmt() *goanalysis.Linter {
|
|||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGofmt(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoFmtSettings) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
for _, f := range fileNames {
|
||||
diff, err := gofmtAPI.Run(f, settings.Simplify)
|
||||
if err != nil { // TODO: skip
|
||||
return nil, err
|
||||
}
|
||||
if diff == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
is, err := extractIssuesFromPatch(string(diff), lintCtx, gofmtName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't extract issues from gofmt diff output %q", string(diff))
|
||||
}
|
||||
|
||||
for i := range is {
|
||||
issues = append(issues, goanalysis.NewIssue(&is[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -229,7 +229,7 @@ func getErrorTextForLinter(lintCtx *linter.Context, linterName string) string {
|
|||
return text
|
||||
}
|
||||
|
||||
func extractIssuesFromPatch(patch string, log logutils.Log, lintCtx *linter.Context, linterName string) ([]result.Issue, error) {
|
||||
func extractIssuesFromPatch(patch string, lintCtx *linter.Context, linterName string) ([]result.Issue, error) {
|
||||
diffs, err := diffpkg.ParseMultiFileDiff([]byte(patch))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't parse patch")
|
||||
|
@ -239,18 +239,20 @@ func extractIssuesFromPatch(patch string, log logutils.Log, lintCtx *linter.Cont
|
|||
return nil, fmt.Errorf("got no diffs from patch parser: %v", diffs)
|
||||
}
|
||||
|
||||
issues := []result.Issue{}
|
||||
var issues []result.Issue
|
||||
for _, d := range diffs {
|
||||
if len(d.Hunks) == 0 {
|
||||
log.Warnf("Got no hunks in diff %+v", d)
|
||||
lintCtx.Log.Warnf("Got no hunks in diff %+v", d)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, hunk := range d.Hunks {
|
||||
p := hunkChangesParser{
|
||||
log: log,
|
||||
log: lintCtx.Log,
|
||||
}
|
||||
|
||||
changes := p.parse(hunk)
|
||||
|
||||
for _, change := range changes {
|
||||
change := change // fix scope
|
||||
i := result.Issue{
|
||||
|
|
|
@ -3,6 +3,7 @@ package golinters
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
|
@ -18,30 +19,60 @@ import (
|
|||
|
||||
const gofumptName = "gofumpt"
|
||||
|
||||
func NewGofumpt() *goanalysis.Linter {
|
||||
type differ interface {
|
||||
Diff(out io.Writer, a io.ReadSeeker, b io.ReadSeeker) error
|
||||
}
|
||||
|
||||
func NewGofumpt(settings *config.GofumptSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
differ := difflib.New()
|
||||
|
||||
diff := difflib.New()
|
||||
|
||||
var options format.Options
|
||||
|
||||
if settings != nil {
|
||||
options = format.Options{
|
||||
LangVersion: getLangVersion(settings),
|
||||
ModulePath: settings.ModulePath,
|
||||
ExtraRules: settings.ExtraRules,
|
||||
}
|
||||
}
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: gofumptName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gofumptName,
|
||||
"Gofumpt checks whether code was gofumpt-ed.",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
settings := lintCtx.Settings().Gofumpt
|
||||
|
||||
options := format.Options{
|
||||
LangVersion: getLangVersion(settings),
|
||||
ModulePath: settings.ModulePath,
|
||||
ExtraRules: settings.ExtraRules,
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGofumpt(lintCtx, pass, diff, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGofumpt(lintCtx *linter.Context, pass *analysis.Pass, diff differ, options format.Options) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
|
@ -68,13 +99,13 @@ func NewGofumpt() *goanalysis.Linter {
|
|||
return nil, fmt.Errorf("error while running gofumpt: %w", err)
|
||||
}
|
||||
|
||||
err = differ.Diff(&out, bytes.NewReader(input), bytes.NewReader(output))
|
||||
err = diff.Diff(&out, bytes.NewReader(input), bytes.NewReader(output))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while running gofumpt: %w", err)
|
||||
}
|
||||
|
||||
diff := out.String()
|
||||
is, err := extractIssuesFromPatch(diff, lintCtx.Log, lintCtx, gofumptName)
|
||||
is, err := extractIssuesFromPatch(diff, lintCtx, gofumptName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't extract issues from gofumpt diff output %q", diff)
|
||||
}
|
||||
|
@ -85,23 +116,11 @@ func NewGofumpt() *goanalysis.Linter {
|
|||
}
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func getLangVersion(settings config.GofumptSettings) string {
|
||||
if settings.LangVersion == "" {
|
||||
func getLangVersion(settings *config.GofumptSettings) string {
|
||||
if settings == nil || settings.LangVersion == "" {
|
||||
// TODO: defaults to "1.15", in the future (v2) must be set by using build.Default.ReleaseTags like staticcheck.
|
||||
return "1.15"
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
goheader "github.com/denis-tingaikin/go-header"
|
||||
"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"
|
||||
|
@ -14,50 +15,78 @@ import (
|
|||
|
||||
const goHeaderName = "goheader"
|
||||
|
||||
func NewGoHeader() *goanalysis.Linter {
|
||||
func NewGoHeader(settings *config.GoHeaderSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var issues []goanalysis.Issue
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
conf := &goheader.Configuration{}
|
||||
if settings != nil {
|
||||
conf = &goheader.Configuration{
|
||||
Values: settings.Values,
|
||||
Template: settings.Template,
|
||||
TemplatePath: settings.TemplatePath,
|
||||
}
|
||||
}
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goHeaderName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGoHeader(pass, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
goHeaderName,
|
||||
"Checks is file header matches to pattern",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
cfg := lintCtx.Cfg.LintersSettings.Goheader
|
||||
c := &goheader.Configuration{
|
||||
Values: cfg.Values,
|
||||
Template: cfg.Template,
|
||||
TemplatePath: cfg.TemplatePath,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
if c.TemplatePath == "" && c.Template == "" {
|
||||
|
||||
func runGoHeader(pass *analysis.Pass, conf *goheader.Configuration) ([]goanalysis.Issue, error) {
|
||||
if conf.TemplatePath == "" && conf.Template == "" {
|
||||
// User did not pass template, so then do not run go-header linter
|
||||
return nil, nil
|
||||
}
|
||||
template, err := c.GetTemplate()
|
||||
|
||||
template, err := conf.GetTemplate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values, err := c.GetValues()
|
||||
|
||||
values, err := conf.GetValues()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a := goheader.New(goheader.WithTemplate(template), goheader.WithValues(values))
|
||||
var res []goanalysis.Issue
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for _, file := range pass.Files {
|
||||
path := pass.Fset.Position(file.Pos()).Filename
|
||||
i := a.Analyze(&goheader.Target{
|
||||
File: file,
|
||||
Path: path,
|
||||
})
|
||||
|
||||
i := a.Analyze(&goheader.Target{File: file, Path: path})
|
||||
|
||||
if i == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
issue := result.Issue{
|
||||
Pos: token.Position{
|
||||
Line: i.Location().Line + 1,
|
||||
|
@ -67,19 +96,9 @@ func NewGoHeader() *goanalysis.Linter {
|
|||
Text: i.Message(),
|
||||
FromLinter: goHeaderName,
|
||||
}
|
||||
res = append(res, goanalysis.NewIssue(&issue, pass))
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
|
||||
issues = append(issues, goanalysis.NewIssue(&issue, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
issues = append(issues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return issues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -8,28 +8,53 @@ import (
|
|||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/imports"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
)
|
||||
|
||||
const goimportsName = "goimports"
|
||||
|
||||
func NewGoimports() *goanalysis.Linter {
|
||||
func NewGoimports(settings *config.GoImportsSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goimportsName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
goimportsName,
|
||||
"In addition to fixing imports, goimports also formats your code in the same style as gofmt.",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
imports.LocalPrefix = lintCtx.Settings().Goimports.LocalPrefixes
|
||||
imports.LocalPrefix = settings.LocalPrefixes
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGoiImports(lintCtx, pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runGoiImports(lintCtx *linter.Context, pass *analysis.Pass) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
|
@ -47,7 +72,7 @@ func NewGoimports() *goanalysis.Linter {
|
|||
continue
|
||||
}
|
||||
|
||||
is, err := extractIssuesFromPatch(string(diff), lintCtx.Log, lintCtx, goimportsName)
|
||||
is, err := extractIssuesFromPatch(string(diff), lintCtx, goimportsName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't extract issues from gofmt diff output %q", string(diff))
|
||||
}
|
||||
|
@ -57,17 +82,5 @@ func NewGoimports() *goanalysis.Linter {
|
|||
}
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -2,35 +2,71 @@ package golinters
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sync"
|
||||
|
||||
lintAPI "github.com/golangci/lint-1"
|
||||
"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"
|
||||
)
|
||||
|
||||
func golintProcessPkg(minConfidence float64, files []*ast.File, fset *token.FileSet,
|
||||
typesPkg *types.Package, typesInfo *types.Info) ([]result.Issue, error) {
|
||||
l := new(lintAPI.Linter)
|
||||
ps, err := l.LintPkg(files, fset, typesPkg, typesInfo)
|
||||
const golintName = "golint"
|
||||
|
||||
//nolint:dupl
|
||||
func NewGolint(settings *config.GoLintSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: golintName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runGoLint(pass, settings)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't lint %d files: %s", len(files), err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
golintName,
|
||||
"Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runGoLint(pass *analysis.Pass, settings *config.GoLintSettings) ([]goanalysis.Issue, error) {
|
||||
l := new(lintAPI.Linter)
|
||||
|
||||
ps, err := l.LintPkg(pass.Files, pass.Fset, pass.Pkg, pass.TypesInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't lint %d files: %s", len(pass.Files), err)
|
||||
}
|
||||
|
||||
if len(ps) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]result.Issue, 0, len(ps)) // This is worst case
|
||||
lintIssues := make([]*result.Issue, 0, len(ps)) // This is worst case
|
||||
for idx := range ps {
|
||||
if ps[idx].Confidence >= minConfidence {
|
||||
issues = append(issues, result.Issue{
|
||||
if ps[idx].Confidence >= settings.MinConfidence {
|
||||
lintIssues = append(lintIssues, &result.Issue{
|
||||
Pos: ps[idx].Position,
|
||||
Text: ps[idx].Text,
|
||||
FromLinter: golintName,
|
||||
|
@ -39,40 +75,10 @@ func golintProcessPkg(minConfidence float64, files []*ast.File, fset *token.File
|
|||
}
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
for _, issue := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(issue, pass))
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
const golintName = "golint"
|
||||
|
||||
func NewGolint() *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: golintName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
golintName,
|
||||
"Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
res, err := golintProcessPkg(lintCtx.Settings().Golint.MinConfidence, pass.Files, pass.Fset, pass.Pkg, pass.TypesInfo)
|
||||
if err != nil || len(res) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
for i := range res {
|
||||
resIssues = append(resIssues, goanalysis.NewIssue(&res[i], pass))
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ func NewGoModDirectives(settings *config.GoModDirectivesSettings) *goanalysis.Li
|
|||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/ryancurrah/gomodguard"
|
||||
"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"
|
||||
|
@ -19,31 +20,18 @@ const (
|
|||
)
|
||||
|
||||
// NewGomodguard returns a new Gomodguard linter.
|
||||
func NewGomodguard() *goanalysis.Linter {
|
||||
var (
|
||||
issues []goanalysis.Issue
|
||||
mu = sync.Mutex{}
|
||||
analyzer = &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
)
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gomodguardName,
|
||||
gomodguardDesc,
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
linterCfg := lintCtx.Cfg.LintersSettings.Gomodguard
|
||||
func NewGomodguard(settings *config.GoModGuardSettings) *goanalysis.Linter {
|
||||
var issues []goanalysis.Issue
|
||||
var mu sync.Mutex
|
||||
|
||||
processorCfg := &gomodguard.Configuration{}
|
||||
processorCfg.Allowed.Modules = linterCfg.Allowed.Modules
|
||||
processorCfg.Allowed.Domains = linterCfg.Allowed.Domains
|
||||
processorCfg.Blocked.LocalReplaceDirectives = linterCfg.Blocked.LocalReplaceDirectives
|
||||
if settings != nil {
|
||||
processorCfg.Allowed.Modules = settings.Allowed.Modules
|
||||
processorCfg.Allowed.Domains = settings.Allowed.Domains
|
||||
processorCfg.Blocked.LocalReplaceDirectives = settings.Blocked.LocalReplaceDirectives
|
||||
|
||||
for n := range linterCfg.Blocked.Modules {
|
||||
for k, v := range linterCfg.Blocked.Modules[n] {
|
||||
for n := range settings.Blocked.Modules {
|
||||
for k, v := range settings.Blocked.Modules[n] {
|
||||
m := map[string]gomodguard.BlockedModule{k: {
|
||||
Recommendations: v.Recommendations,
|
||||
Reason: v.Reason,
|
||||
|
@ -53,8 +41,8 @@ func NewGomodguard() *goanalysis.Linter {
|
|||
}
|
||||
}
|
||||
|
||||
for n := range linterCfg.Blocked.Versions {
|
||||
for k, v := range linterCfg.Blocked.Versions[n] {
|
||||
for n := range settings.Blocked.Versions {
|
||||
for k, v := range settings.Blocked.Versions[n] {
|
||||
m := map[string]gomodguard.BlockedVersion{k: {
|
||||
Version: v.Version,
|
||||
Reason: v.Reason,
|
||||
|
@ -63,7 +51,20 @@ func NewGomodguard() *goanalysis.Linter {
|
|||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gomodguardName,
|
||||
gomodguardDesc,
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
processor, err := gomodguard.NewProcessor(processorCfg)
|
||||
if err != nil {
|
||||
lintCtx.Log.Warnf("running gomodguard failed: %s: if you are not using go modules "+
|
||||
|
@ -73,7 +74,6 @@ func NewGomodguard() *goanalysis.Linter {
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
gasConfig := gosec.NewConfig()
|
||||
conf := gosec.NewConfig()
|
||||
|
||||
var filters []rules.RuleFilter
|
||||
if settings != nil {
|
||||
|
@ -36,18 +36,20 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
for k, v := range settings.Config {
|
||||
// Uses ToUpper because the parsing of the map's key change the key to lowercase.
|
||||
// The value is not impacted by that: the case is respected.
|
||||
gasConfig.Set(strings.ToUpper(k), v)
|
||||
conf.Set(strings.ToUpper(k), v)
|
||||
}
|
||||
}
|
||||
|
||||
ruleDefinitions := rules.Generate(false, filters...)
|
||||
|
||||
logger := log.New(io.Discard, "", 0)
|
||||
|
||||
ruleDefinitions := rules.Generate(false, filters...)
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: gosecName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
gosecName,
|
||||
"Inspects source code for security problems",
|
||||
|
@ -55,20 +57,38 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
gosecAnalyzer := gosec.NewAnalyzer(gasConfig, true, settings.ExcludeGenerated, false, settings.Concurrency, logger)
|
||||
// The `gosecAnalyzer` is here because of concurrency issue.
|
||||
gosecAnalyzer := gosec.NewAnalyzer(conf, true, settings.ExcludeGenerated, false, settings.Concurrency, logger)
|
||||
gosecAnalyzer.LoadRules(ruleDefinitions.RulesInfo())
|
||||
|
||||
issues := runGoSec(lintCtx, pass, settings, gosecAnalyzer)
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runGoSec(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoSecSettings, analyzer *gosec.Analyzer) []goanalysis.Issue {
|
||||
pkg := &packages.Package{
|
||||
Fset: pass.Fset,
|
||||
Syntax: pass.Files,
|
||||
Types: pass.Pkg,
|
||||
TypesInfo: pass.TypesInfo,
|
||||
}
|
||||
gosecAnalyzer.Check(pkg)
|
||||
issues, _, _ := gosecAnalyzer.Report()
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
|
||||
analyzer.Check(pkg)
|
||||
|
||||
secIssues, _, _ := analyzer.Report()
|
||||
if len(secIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
severity, err := convertToScore(settings.Severity)
|
||||
if err != nil {
|
||||
lintCtx.Log.Warnf("The provided severity %v", err)
|
||||
|
@ -78,11 +98,15 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
if err != nil {
|
||||
lintCtx.Log.Warnf("The provided confidence %v", err)
|
||||
}
|
||||
issues = filterIssues(issues, severity, confidence)
|
||||
res := make([]goanalysis.Issue, 0, len(issues))
|
||||
for _, i := range issues {
|
||||
|
||||
secIssues = filterIssues(secIssues, severity, confidence)
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(secIssues))
|
||||
for _, i := range secIssues {
|
||||
text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
|
||||
|
||||
var r *result.Range
|
||||
|
||||
line, err := strconv.Atoi(i.Line)
|
||||
if err != nil {
|
||||
r = &result.Range{}
|
||||
|
@ -99,7 +123,7 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
continue
|
||||
}
|
||||
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: i.File,
|
||||
Line: line,
|
||||
|
@ -111,15 +135,7 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
|
|||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues
|
||||
}
|
||||
|
||||
// based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188
|
||||
|
|
|
@ -119,33 +119,34 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
|
||||
var settings map[string]map[string]interface{}
|
||||
if cfg != nil {
|
||||
settings = cfg.Settings
|
||||
func NewGovet(settings *config.GovetSettings) *goanalysis.Linter {
|
||||
var conf map[string]map[string]interface{}
|
||||
if settings != nil {
|
||||
conf = settings.Settings
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"govet",
|
||||
"Vet examines Go source code and reports suspicious constructs, "+
|
||||
"such as Printf calls whose arguments do not align with the format string",
|
||||
analyzersFromConfig(cfg),
|
||||
settings,
|
||||
analyzersFromConfig(settings),
|
||||
conf,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer {
|
||||
if cfg == nil {
|
||||
func analyzersFromConfig(settings *config.GovetSettings) []*analysis.Analyzer {
|
||||
if settings == nil {
|
||||
return defaultAnalyzers
|
||||
}
|
||||
|
||||
if cfg.CheckShadowing {
|
||||
if settings.CheckShadowing {
|
||||
// Keeping for backward compatibility.
|
||||
cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name)
|
||||
settings.Enable = append(settings.Enable, shadow.Analyzer.Name)
|
||||
}
|
||||
|
||||
var enabledAnalyzers []*analysis.Analyzer
|
||||
for _, a := range allAnalyzers {
|
||||
if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) {
|
||||
if isAnalyzerEnabled(a.Name, settings, defaultAnalyzers) {
|
||||
enabledAnalyzers = append(enabledAnalyzers, a)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,46 +22,61 @@ func NewInterfacer() *goanalysis.Linter {
|
|||
Name: interfacerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runInterfacer(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
interfacerName,
|
||||
"Linter that suggests narrower interface types",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
ssa := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
||||
ssaPkg := ssa.Pkg
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runInterfacer(pass *analysis.Pass) ([]goanalysis.Issue, error) {
|
||||
c := &check.Checker{}
|
||||
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
c.Program(prog)
|
||||
|
||||
ssa := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
||||
ssaPkg := ssa.Pkg
|
||||
c.ProgramSSA(ssaPkg.Prog)
|
||||
|
||||
issues, err := c.Check()
|
||||
lintIssues, err := c.Check()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(issues) == 0 {
|
||||
if len(lintIssues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(issues))
|
||||
for _, i := range issues {
|
||||
issues := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
for _, i := range lintIssues {
|
||||
pos := pass.Fset.Position(i.Pos())
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: pos,
|
||||
Text: i.Message(),
|
||||
FromLinter: interfacerName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -11,11 +11,74 @@ import (
|
|||
|
||||
"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 lllName = "lll"
|
||||
|
||||
//nolint:dupl
|
||||
func NewLLL(settings *config.LllSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: lllName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runLll(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
lllName,
|
||||
"Reports long lines",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runLll(pass *analysis.Pass, settings *config.LllSettings) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
spaces := strings.Repeat(" ", settings.TabWidth)
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for _, f := range fileNames {
|
||||
lintIssues, err := getLLLIssuesForFile(f, settings.LineLength, spaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&lintIssues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func getLLLIssuesForFile(filename string, maxLineLen int, tabSpaces string) ([]result.Issue, error) {
|
||||
var res []result.Issue
|
||||
|
||||
|
@ -72,53 +135,3 @@ func getLLLIssuesForFile(filename string, maxLineLen int, tabSpaces string) ([]r
|
|||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
const lllName = "lll"
|
||||
|
||||
func NewLLL() *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: lllName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
lllName,
|
||||
"Reports long lines",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
var res []goanalysis.Issue
|
||||
spaces := strings.Repeat(" ", lintCtx.Settings().Lll.TabWidth)
|
||||
for _, f := range fileNames {
|
||||
issues, err := getLLLIssuesForFile(f, lintCtx.Settings().Lll.LineLength, spaces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range issues {
|
||||
res = append(res, goanalysis.NewIssue(&issues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -24,9 +24,7 @@ func NewMaintIdx(cfg *config.MaintIdxSettings) *goanalysis.Linter {
|
|||
return goanalysis.NewLinter(
|
||||
analyzer.Name,
|
||||
analyzer.Doc,
|
||||
[]*analysis.Analyzer{
|
||||
analyzer,
|
||||
},
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
cfgMap,
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"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"
|
||||
|
@ -14,47 +15,61 @@ import (
|
|||
|
||||
const makezeroName = "makezero"
|
||||
|
||||
func NewMakezero() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewMakezero(settings *config.MakezeroSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: makezeroName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runMakeZero(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
makezeroName,
|
||||
"Finds slice declarations with non-zero initial length",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
s := &lintCtx.Settings().Makezero
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runMakeZero(pass *analysis.Pass, settings *config.MakezeroSettings) ([]goanalysis.Issue, error) {
|
||||
zero := makezero.NewLinter(settings.Always)
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var res []goanalysis.Issue
|
||||
linter := makezero.NewLinter(s.Always)
|
||||
for _, file := range pass.Files {
|
||||
hints, err := linter.Run(pass.Fset, pass.TypesInfo, file)
|
||||
hints, err := zero.Run(pass.Fset, pass.TypesInfo, file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "makezero linter failed on file %q", file.Name.String())
|
||||
}
|
||||
|
||||
for _, hint := range hints {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: hint.Position(),
|
||||
Text: hint.Details(),
|
||||
FromLinter: makezeroName,
|
||||
}, pass))
|
||||
}
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -7,52 +7,68 @@ import (
|
|||
malignedAPI "github.com/golangci/maligned"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewMaligned() *goanalysis.Linter {
|
||||
const linterName = "maligned"
|
||||
const malignedName = "maligned"
|
||||
|
||||
//nolint:dupl
|
||||
func NewMaligned(settings *config.MalignedSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var res []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: malignedName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
"Tool to detect Go structs that would take less memory if their fields were sorted",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runMaligned(pass, settings)
|
||||
|
||||
malignedIssues := malignedAPI.Run(prog)
|
||||
if len(malignedIssues) == 0 {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(malignedIssues))
|
||||
for _, i := range malignedIssues {
|
||||
text := fmt.Sprintf("struct of size %d bytes could be of size %d bytes", i.OldSize, i.NewSize)
|
||||
if lintCtx.Settings().Maligned.SuggestNewOrder {
|
||||
text += fmt.Sprintf(":\n%s", formatCodeBlock(i.NewStructDef, lintCtx.Cfg))
|
||||
}
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: text,
|
||||
FromLinter: linterName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
res = append(res, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
malignedName,
|
||||
"Tool to detect Go structs that would take less memory if their fields were sorted",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return res
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runMaligned(pass *analysis.Pass, settings *config.MalignedSettings) []goanalysis.Issue {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
|
||||
malignedIssues := malignedAPI.Run(prog)
|
||||
if len(malignedIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(malignedIssues))
|
||||
for _, i := range malignedIssues {
|
||||
text := fmt.Sprintf("struct of size %d bytes could be of size %d bytes", i.OldSize, i.NewSize)
|
||||
if settings.SuggestNewOrder {
|
||||
text += fmt.Sprintf(":\n%s", formatCodeBlock(i.NewStructDef, nil))
|
||||
}
|
||||
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: text,
|
||||
FromLinter: malignedName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -9,27 +9,122 @@ import (
|
|||
"github.com/golangci/misspell"
|
||||
"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"
|
||||
)
|
||||
|
||||
func runMisspellOnFile(fileName string, r *misspell.Replacer, lintCtx *linter.Context) ([]result.Issue, error) {
|
||||
var res []result.Issue
|
||||
fileContent, err := lintCtx.FileCache.GetFileBytes(fileName)
|
||||
const misspellName = "misspell"
|
||||
|
||||
func NewMisspell(settings *config.MisspellSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: misspellName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
misspellName,
|
||||
"Finds commonly misspelled English words in comments",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
replacer, ruleErr := createMisspellReplacer(settings)
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
if ruleErr != nil {
|
||||
return nil, ruleErr
|
||||
}
|
||||
|
||||
issues, err := runMisspell(lintCtx, pass, replacer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get file %s contents: %s", fileName, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runMisspell(lintCtx *linter.Context, pass *analysis.Pass, replacer *misspell.Replacer) ([]goanalysis.Issue, error) {
|
||||
var fileNames []string
|
||||
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for _, filename := range fileNames {
|
||||
lintIssues, err := runMisspellOnFile(lintCtx, filename, replacer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&lintIssues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func createMisspellReplacer(settings *config.MisspellSettings) (*misspell.Replacer, error) {
|
||||
replacer := &misspell.Replacer{
|
||||
Replacements: misspell.DictMain,
|
||||
}
|
||||
|
||||
// Figure out regional variations
|
||||
switch strings.ToUpper(settings.Locale) {
|
||||
case "":
|
||||
// nothing
|
||||
case "US":
|
||||
replacer.AddRuleList(misspell.DictAmerican)
|
||||
case "UK", "GB":
|
||||
replacer.AddRuleList(misspell.DictBritish)
|
||||
case "NZ", "AU", "CA":
|
||||
return nil, fmt.Errorf("unknown locale: %q", settings.Locale)
|
||||
}
|
||||
|
||||
if len(settings.IgnoreWords) != 0 {
|
||||
replacer.RemoveRule(settings.IgnoreWords)
|
||||
}
|
||||
|
||||
// It can panic.
|
||||
replacer.Compile()
|
||||
|
||||
return replacer, nil
|
||||
}
|
||||
|
||||
func runMisspellOnFile(lintCtx *linter.Context, filename string, replacer *misspell.Replacer) ([]result.Issue, error) {
|
||||
var res []result.Issue
|
||||
fileContent, err := lintCtx.FileCache.GetFileBytes(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get file %s contents: %s", filename, err)
|
||||
}
|
||||
|
||||
// use r.Replace, not r.ReplaceGo because r.ReplaceGo doesn't find
|
||||
// issues inside strings: it searches only inside comments. r.Replace
|
||||
// searches all words: it treats input as a plain text. A standalone misspell
|
||||
// tool uses r.Replace by default.
|
||||
_, diffs := r.Replace(string(fileContent))
|
||||
_, diffs := replacer.Replace(string(fileContent))
|
||||
for _, diff := range diffs {
|
||||
text := fmt.Sprintf("`%s` is a misspelling of `%s`", diff.Original, diff.Corrected)
|
||||
pos := token.Position{
|
||||
Filename: fileName,
|
||||
Filename: filename,
|
||||
Line: diff.Line,
|
||||
Column: diff.Column + 1,
|
||||
}
|
||||
|
@ -51,82 +146,3 @@ func runMisspellOnFile(fileName string, r *misspell.Replacer, lintCtx *linter.Co
|
|||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
const misspellName = "misspell"
|
||||
|
||||
func NewMisspell() *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
var ruleErr error
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: misspellName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
misspellName,
|
||||
"Finds commonly misspelled English words in comments",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
r := misspell.Replacer{
|
||||
Replacements: misspell.DictMain,
|
||||
}
|
||||
|
||||
// Figure out regional variations
|
||||
settings := lintCtx.Settings().Misspell
|
||||
locale := settings.Locale
|
||||
switch strings.ToUpper(locale) {
|
||||
case "":
|
||||
// nothing
|
||||
case "US":
|
||||
r.AddRuleList(misspell.DictAmerican)
|
||||
case "UK", "GB":
|
||||
r.AddRuleList(misspell.DictBritish)
|
||||
case "NZ", "AU", "CA":
|
||||
ruleErr = fmt.Errorf("unknown locale: %q", locale)
|
||||
}
|
||||
|
||||
if ruleErr == nil {
|
||||
if len(settings.IgnoreWords) != 0 {
|
||||
r.RemoveRule(settings.IgnoreWords)
|
||||
}
|
||||
|
||||
r.Compile()
|
||||
}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
if ruleErr != nil {
|
||||
return nil, ruleErr
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
for _, f := range pass.Files {
|
||||
pos := pass.Fset.PositionFor(f.Pos(), false)
|
||||
fileNames = append(fileNames, pos.Filename)
|
||||
}
|
||||
|
||||
var res []goanalysis.Issue
|
||||
for _, f := range fileNames {
|
||||
issues, err := runMisspellOnFile(f, &r, lintCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range issues {
|
||||
res = append(res, goanalysis.NewIssue(&issues[i], pass))
|
||||
}
|
||||
}
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -8,11 +8,66 @@ import (
|
|||
|
||||
"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 nakedretName = "nakedret"
|
||||
|
||||
//nolint:dupl
|
||||
func NewNakedret(settings *config.NakedretSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: nakedretName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runNakedRet(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
nakedretName,
|
||||
"Finds naked returns in functions greater than a specified function length",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runNakedRet(pass *analysis.Pass, settings *config.NakedretSettings) []goanalysis.Issue {
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
for _, file := range pass.Files {
|
||||
v := nakedretVisitor{
|
||||
maxLength: settings.MaxFuncLines,
|
||||
f: pass.Fset,
|
||||
}
|
||||
|
||||
ast.Walk(&v, file)
|
||||
|
||||
for i := range v.issues {
|
||||
issues = append(issues, goanalysis.NewIssue(&v.issues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
||||
type nakedretVisitor struct {
|
||||
maxLength int
|
||||
f *token.FileSet
|
||||
|
@ -77,47 +132,3 @@ func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor {
|
|||
v.processFuncDecl(funcDecl)
|
||||
return v
|
||||
}
|
||||
|
||||
const nakedretName = "nakedret"
|
||||
|
||||
func NewNakedret() *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: nakedretName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
nakedretName,
|
||||
"Finds naked returns in functions greater than a specified function length",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var res []goanalysis.Issue
|
||||
for _, file := range pass.Files {
|
||||
v := nakedretVisitor{
|
||||
maxLength: lintCtx.Settings().Nakedret.MaxFuncLines,
|
||||
f: pass.Fset,
|
||||
}
|
||||
ast.Walk(&v, file)
|
||||
for i := range v.issues {
|
||||
res = append(res, goanalysis.NewIssue(&v.issues[i], pass))
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/nakabonne/nestif"
|
||||
"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"
|
||||
|
@ -14,52 +15,65 @@ import (
|
|||
|
||||
const nestifName = "nestif"
|
||||
|
||||
func NewNestif() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewNestif(settings *config.NestifSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runNestIf(pass, settings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
nestifName,
|
||||
"Reports deeply nested if statements",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
checker := &nestif.Checker{
|
||||
MinComplexity: lintCtx.Settings().Nestif.MinComplexity,
|
||||
}
|
||||
var issues []nestif.Issue
|
||||
for _, f := range pass.Files {
|
||||
issues = append(issues, checker.Check(f, pass.Fset)...)
|
||||
}
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
sort.SliceStable(issues, func(i, j int) bool {
|
||||
return issues[i].Complexity > issues[j].Complexity
|
||||
func runNestIf(pass *analysis.Pass, settings *config.NestifSettings) []goanalysis.Issue {
|
||||
checker := &nestif.Checker{
|
||||
MinComplexity: settings.MinComplexity,
|
||||
}
|
||||
|
||||
var lintIssues []nestif.Issue
|
||||
for _, f := range pass.Files {
|
||||
lintIssues = append(lintIssues, checker.Check(f, pass.Fset)...)
|
||||
}
|
||||
|
||||
if len(lintIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
sort.SliceStable(lintIssues, func(i, j int) bool {
|
||||
return lintIssues[i].Complexity > lintIssues[j].Complexity
|
||||
})
|
||||
|
||||
res := make([]goanalysis.Issue, 0, len(issues))
|
||||
for _, i := range issues {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
for _, i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: i.Message,
|
||||
FromLinter: nestifName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
func NewNilErr() *goanalysis.Linter {
|
||||
a := nilerr.Analyzer
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
"Finds the code that returns nil even if it checks that the error is not nil.",
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewNoctx() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
noctx.Analyzer,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"noctx",
|
||||
"noctx finds sending http request without context.Context",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{noctx.Analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -7,31 +7,53 @@ import (
|
|||
|
||||
"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/golinters/nolintlint"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const NolintlintName = "nolintlint"
|
||||
const NoLintLintName = "nolintlint"
|
||||
|
||||
func NewNoLintLint() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewNoLintLint(settings *config.NoLintLintSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: NolintlintName,
|
||||
Name: NoLintLintName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runNoLintLint(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
NolintlintName,
|
||||
NoLintLintName,
|
||||
"Reports ill-formed or insufficient nolint directives",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runNoLintLint(pass *analysis.Pass, settings *config.NoLintLintSettings) ([]goanalysis.Issue, error) {
|
||||
var needs nolintlint.Needs
|
||||
settings := lintCtx.Settings().NoLintLint
|
||||
if settings.RequireExplanation {
|
||||
needs |= nolintlint.NeedsExplanation
|
||||
}
|
||||
|
@ -54,40 +76,33 @@ func NewNoLintLint() *goanalysis.Linter {
|
|||
for _, n := range pass.Files {
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
issues, err := lnt.Run(pass.Fset, nodes...)
|
||||
|
||||
lintIssues, err := lnt.Run(pass.Fset, nodes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("linter failed to run: %s", err)
|
||||
}
|
||||
var res []goanalysis.Issue
|
||||
for _, i := range issues {
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
for _, i := range lintIssues {
|
||||
expectNoLint := false
|
||||
var expectedNolintLinter string
|
||||
if ii, ok := i.(nolintlint.UnusedCandidate); ok {
|
||||
expectedNolintLinter = ii.ExpectedLinter
|
||||
expectNoLint = true
|
||||
}
|
||||
|
||||
issue := &result.Issue{
|
||||
FromLinter: NolintlintName,
|
||||
FromLinter: NoLintLintName,
|
||||
Text: i.Details(),
|
||||
Pos: i.Position(),
|
||||
ExpectNoLint: expectNoLint,
|
||||
ExpectedNoLintLinter: expectedNolintLinter,
|
||||
Replacement: i.Replacement(),
|
||||
}
|
||||
res = append(res, goanalysis.NewIssue(issue, pass))
|
||||
|
||||
issues = append(issues, goanalysis.NewIssue(issue, pass))
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewParallelTest() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
paralleltest.NewAnalyzer(),
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"paralleltest",
|
||||
"paralleltest detects missing usage of t.Parallel() method in your Go test",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{paralleltest.NewAnalyzer()},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/alexkohler/prealloc/pkg"
|
||||
"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"
|
||||
|
@ -14,44 +15,51 @@ import (
|
|||
|
||||
const preallocName = "prealloc"
|
||||
|
||||
func NewPrealloc() *goanalysis.Linter {
|
||||
//nolint:dupl
|
||||
func NewPreAlloc(settings *config.PreallocSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: preallocName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
preallocName,
|
||||
"Finds slice declarations that could potentially be preallocated",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
s := &lintCtx.Settings().Prealloc
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runPreAlloc(pass, settings)
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var res []goanalysis.Issue
|
||||
hints := pkg.Check(pass.Files, s.Simple, s.RangeLoops, s.ForLoops)
|
||||
for _, hint := range hints {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: pass.Fset.Position(hint.Pos),
|
||||
Text: fmt.Sprintf("Consider preallocating %s", formatCode(hint.DeclaredSliceName, lintCtx.Cfg)),
|
||||
FromLinter: preallocName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
preallocName,
|
||||
"Finds slice declarations that could potentially be pre-allocated",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runPreAlloc(pass *analysis.Pass, settings *config.PreallocSettings) []goanalysis.Issue {
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
hints := pkg.Check(pass.Files, settings.Simple, settings.RangeLoops, settings.ForLoops)
|
||||
|
||||
for _, hint := range hints {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: pass.Fset.Position(hint.Pos),
|
||||
Text: fmt.Sprintf("Consider pre-allocating %s", formatCode(hint.DeclaredSliceName, nil)),
|
||||
FromLinter: preallocName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -7,57 +7,71 @@ import (
|
|||
"github.com/yeya24/promlinter"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewPromlinter() *goanalysis.Linter {
|
||||
const promlinterName = "promlinter"
|
||||
|
||||
func NewPromlinter(settings *config.PromlinterSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
const linterName = "promlinter"
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
var promSettings promlinter.Setting
|
||||
if settings != nil {
|
||||
promSettings = promlinter.Setting{
|
||||
Strict: settings.Strict,
|
||||
DisabledLintFuncs: settings.DisabledLinters,
|
||||
}
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
"Check Prometheus metrics naming via promlint",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
strict := lintCtx.Cfg.LintersSettings.Promlinter.Strict
|
||||
disabledLinters := lintCtx.Cfg.LintersSettings.Promlinter.DisabledLinters
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := promlinter.RunLint(pass.Fset, pass.Files, promlinter.Setting{
|
||||
Strict: strict,
|
||||
DisabledLintFuncs: disabledLinters,
|
||||
})
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: promlinterName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runPromLinter(pass, promSettings)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, len(issues))
|
||||
for k, i := range issues {
|
||||
issue := result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("Metric: %s Error: %s", i.Metric, i.Text),
|
||||
FromLinter: linterName,
|
||||
}
|
||||
|
||||
res[k] = goanalysis.NewIssue(&issue, pass)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
promlinterName,
|
||||
"Check Prometheus metrics naming via promlint",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runPromLinter(pass *analysis.Pass, promSettings promlinter.Setting) []goanalysis.Issue {
|
||||
lintIssues := promlinter.RunLint(pass.Fset, pass.Files, promSettings)
|
||||
|
||||
if len(lintIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(lintIssues))
|
||||
for k, i := range lintIssues {
|
||||
issue := result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("Metric: %s Error: %s", i.Metric, i.Text),
|
||||
FromLinter: promlinterName,
|
||||
}
|
||||
|
||||
issues[k] = goanalysis.NewIssue(&issue, pass)
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
reviveConfig "github.com/mgechev/revive/config"
|
||||
|
@ -33,12 +34,15 @@ type jsonObject struct {
|
|||
}
|
||||
|
||||
// NewRevive returns a new Revive linter.
|
||||
func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
|
||||
var issues []goanalysis.Issue
|
||||
//nolint:dupl
|
||||
func NewRevive(settings *config.ReviveSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
|
@ -48,13 +52,34 @@ func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
|
|||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runRevive(lintCtx, pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runRevive(lintCtx *linter.Context, pass *analysis.Pass, settings *config.ReviveSettings) ([]goanalysis.Issue, error) {
|
||||
var files []string
|
||||
for _, file := range pass.Files {
|
||||
files = append(files, pass.Fset.PositionFor(file.Pos(), false).Filename)
|
||||
}
|
||||
packages := [][]string{files}
|
||||
|
||||
conf, err := getReviveConfig(cfg)
|
||||
conf, err := getReviveConfig(settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -64,7 +89,7 @@ func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
revive := lint.New(os.ReadFile, cfg.MaxOpenFiles)
|
||||
revive := lint.New(os.ReadFile, settings.MaxOpenFiles)
|
||||
|
||||
lintingRules, err := reviveConfig.GetLintingRules(conf, []lint.Rule{})
|
||||
if err != nil {
|
||||
|
@ -105,15 +130,12 @@ func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for i := range results {
|
||||
issues = append(issues, reviveToIssue(pass, &results[i]))
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return issues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
||||
func reviveToIssue(pass *analysis.Pass, object *jsonObject) goanalysis.Issue {
|
||||
|
|
|
@ -4,20 +4,22 @@ import (
|
|||
"github.com/jingyugao/rowserrcheck/passes/rowserr"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewRowsErrCheck() *goanalysis.Linter {
|
||||
analyzer := rowserr.NewAnalyzer()
|
||||
func NewRowsErrCheck(settings *config.RowsErrCheckSettings) *goanalysis.Linter {
|
||||
var pkgs []string
|
||||
if settings != nil {
|
||||
pkgs = settings.Packages
|
||||
}
|
||||
|
||||
analyzer := rowserr.NewAnalyzer(pkgs...)
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"rowserrcheck",
|
||||
"checks whether Err of rows is checked successfully",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo).
|
||||
WithContextSetter(func(lintCtx *linter.Context) {
|
||||
pkgs := lintCtx.Settings().RowsErrCheck.Packages
|
||||
analyzer.Run = rowserr.NewRun(pkgs...)
|
||||
})
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
const scopelintName = "scopelint"
|
||||
|
||||
//nolint:dupl
|
||||
func NewScopelint() *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
@ -22,41 +23,51 @@ func NewScopelint() *goanalysis.Linter {
|
|||
analyzer := &analysis.Analyzer{
|
||||
Name: scopelintName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runScopeLint(pass)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
scopelintName,
|
||||
"Scopelint checks for unpinned variables in go programs",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var res []result.Issue
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runScopeLint(pass *analysis.Pass) []goanalysis.Issue {
|
||||
var lintIssues []result.Issue
|
||||
|
||||
for _, file := range pass.Files {
|
||||
n := Node{
|
||||
fset: pass.Fset,
|
||||
DangerObjects: map[*ast.Object]int{},
|
||||
UnsafeObjects: map[*ast.Object]int{},
|
||||
SkipFuncs: map[*ast.FuncLit]int{},
|
||||
issues: &res,
|
||||
issues: &lintIssues,
|
||||
}
|
||||
ast.Walk(&n, file)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
return nil, nil
|
||||
var issues []goanalysis.Issue
|
||||
for i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&lintIssues[i], pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
for i := range res {
|
||||
resIssues = append(resIssues, goanalysis.NewIssue(&res[i], pass))
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues
|
||||
}
|
||||
|
||||
// The code below is copy-pasted from https://github.com/kyoh86/scopelint 92cbe2cc9276abda0e309f52cc9e309d407f174e
|
||||
|
|
|
@ -8,14 +8,12 @@ import (
|
|||
)
|
||||
|
||||
func NewSQLCloseCheck() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
analyzer.NewAnalyzer(),
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"sqlclosecheck",
|
||||
"Checks that sql.Rows and sql.Stmt are closed.",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{
|
||||
analyzer.NewAnalyzer(),
|
||||
},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package golinters // nolint:dupl
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -7,49 +7,65 @@ import (
|
|||
structcheckAPI "github.com/golangci/check/cmd/structcheck"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewStructcheck() *goanalysis.Linter {
|
||||
const linterName = "structcheck"
|
||||
const structcheckName = "structcheck"
|
||||
|
||||
//nolint:dupl
|
||||
func NewStructcheck(settings *config.StructCheckSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var res []goanalysis.Issue
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: structcheckName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
"Finds unused struct fields",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
checkExported := lintCtx.Settings().Structcheck.CheckExportedFields
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runStructCheck(pass, settings)
|
||||
|
||||
structcheckIssues := structcheckAPI.Run(prog, checkExported)
|
||||
if len(structcheckIssues) == 0 {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(structcheckIssues))
|
||||
for _, i := range structcheckIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("%s is unused", formatCode(i.FieldName, lintCtx.Cfg)),
|
||||
FromLinter: linterName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
res = append(res, issues...)
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return res
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
structcheckName,
|
||||
"Finds unused struct fields",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func runStructCheck(pass *analysis.Pass, settings *config.StructCheckSettings) []goanalysis.Issue {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
|
||||
lintIssues := structcheckAPI.Run(prog, settings.CheckExportedFields)
|
||||
if len(lintIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
|
||||
for _, i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("%s is unused", formatCode(i.FieldName, nil)),
|
||||
FromLinter: structcheckName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ func NewTagliatelle(settings *config.TagliatelleSettings) *goanalysis.Linter {
|
|||
|
||||
a := tagliatelle.New(cfg)
|
||||
|
||||
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, nil).
|
||||
WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
[]*analysis.Analyzer{a},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -11,10 +11,6 @@ import (
|
|||
func NewTenv(settings *config.TenvSettings) *goanalysis.Linter {
|
||||
a := tenv.Analyzer
|
||||
|
||||
analyzers := []*analysis.Analyzer{
|
||||
a,
|
||||
}
|
||||
|
||||
var cfg map[string]map[string]interface{}
|
||||
if settings != nil {
|
||||
cfg = map[string]map[string]interface{}{
|
||||
|
@ -27,7 +23,7 @@ func NewTenv(settings *config.TenvSettings) *goanalysis.Linter {
|
|||
return goanalysis.NewLinter(
|
||||
a.Name,
|
||||
a.Doc,
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{a},
|
||||
cfg,
|
||||
).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
func NewTestpackage(cfg *config.TestpackageSettings) *goanalysis.Linter {
|
||||
var a = testpackage.NewAnalyzer()
|
||||
|
||||
var settings map[string]map[string]interface{}
|
||||
if cfg != nil {
|
||||
settings = map[string]map[string]interface{}{
|
||||
|
@ -18,6 +19,7 @@ func NewTestpackage(cfg *config.TestpackageSettings) *goanalysis.Linter {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(a.Name, a.Doc, []*analysis.Analyzer{a}, settings).
|
||||
WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewTparallel() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
tparallel.Analyzer,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"tparallel",
|
||||
"tparallel detects inappropriate usage of t.Parallel() method in your Go test codes",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{tparallel.Analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -12,17 +12,13 @@ func NewTypecheck() *goanalysis.Linter {
|
|||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
return nil, nil
|
||||
},
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
linter := goanalysis.NewLinter(
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
"Like the front-end of a Go compiler, parses and type-checks Go code",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
|
||||
return linter
|
||||
}
|
||||
|
|
|
@ -11,26 +11,47 @@ import (
|
|||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const unconvertName = "unconvert"
|
||||
|
||||
//nolint:dupl
|
||||
func NewUnconvert() *goanalysis.Linter {
|
||||
const linterName = "unconvert"
|
||||
var mu sync.Mutex
|
||||
var res []goanalysis.Issue
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: unconvertName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runUnconvert(pass)
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
unconvertName,
|
||||
"Remove unnecessary type conversions",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runUnconvert(pass *analysis.Pass) []goanalysis.Issue {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
|
||||
positions := unconvertAPI.Run(prog)
|
||||
if len(positions) == 0 {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(positions))
|
||||
|
@ -38,16 +59,9 @@ func NewUnconvert() *goanalysis.Linter {
|
|||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: pos,
|
||||
Text: "unnecessary conversion",
|
||||
FromLinter: linterName,
|
||||
FromLinter: unconvertName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
res = append(res, issues...)
|
||||
mu.Unlock()
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return res
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -8,33 +8,55 @@ import (
|
|||
"golang.org/x/tools/go/packages"
|
||||
"mvdan.cc/unparam/check"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewUnparam() *goanalysis.Linter {
|
||||
const linterName = "unparam"
|
||||
const unparamName = "unparam"
|
||||
|
||||
func NewUnparam(settings *config.UnparamSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: unparamName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Requires: []*analysis.Analyzer{buildssa.Analyzer},
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runUnparam(pass, settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
unparamName,
|
||||
"Reports unused function parameters",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
us := &lintCtx.Settings().Unparam
|
||||
if us.Algo != "cha" {
|
||||
if settings.Algo != "cha" {
|
||||
lintCtx.Log.Warnf("`linters-settings.unparam.algo` isn't supported by the newest `unparam`")
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
func runUnparam(pass *analysis.Pass, settings *config.UnparamSettings) ([]goanalysis.Issue, error) {
|
||||
ssa := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
|
||||
ssaPkg := ssa.Pkg
|
||||
|
||||
|
@ -46,7 +68,7 @@ func NewUnparam() *goanalysis.Linter {
|
|||
}
|
||||
|
||||
c := &check.Checker{}
|
||||
c.CheckExportedFuncs(us.CheckExported)
|
||||
c.CheckExportedFuncs(settings.CheckExported)
|
||||
c.Packages([]*packages.Package{pkg})
|
||||
c.ProgramSSA(ssaPkg.Prog)
|
||||
|
||||
|
@ -55,22 +77,14 @@ func NewUnparam() *goanalysis.Linter {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var res []goanalysis.Issue
|
||||
var issues []goanalysis.Issue
|
||||
for _, i := range unparamIssues {
|
||||
res = append(res, goanalysis.NewIssue(&result.Issue{
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: pass.Fset.Position(i.Pos()),
|
||||
Text: i.Message(),
|
||||
FromLinter: linterName,
|
||||
FromLinter: unparamName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -13,21 +13,51 @@ import (
|
|||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
const unusedName = "unused"
|
||||
|
||||
type UnusedSettings struct {
|
||||
GoVersion string
|
||||
}
|
||||
|
||||
func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter {
|
||||
const name = "unused"
|
||||
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: name,
|
||||
Name: unusedName,
|
||||
Doc: unused.Analyzer.Analyzer.Doc,
|
||||
Requires: unused.Analyzer.Analyzer.Requires,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues, err := runUnused(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
setAnalyzerGoVersion(analyzer, getGoVersion(settings))
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
unusedName,
|
||||
"Checks Go code for unused constants, variables, functions and types",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(lintCtx *linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
func runUnused(pass *analysis.Pass) ([]goanalysis.Issue, error) {
|
||||
res, err := unused.Analyzer.Analyzer.Run(pass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -41,6 +71,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter {
|
|||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
|
||||
// Inspired by https://github.com/dominikh/go-tools/blob/d694aadcb1f50c2d8ac0a1dd06217ebb9f654764/lintcmd/lint.go#L177-L197
|
||||
for _, object := range sr.Unused {
|
||||
if object.Kind == "type param" {
|
||||
|
@ -57,7 +88,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter {
|
|||
}
|
||||
|
||||
issue := goanalysis.NewIssue(&result.Issue{
|
||||
FromLinter: name,
|
||||
FromLinter: unusedName,
|
||||
Text: fmt.Sprintf("%s %s is unused", object.Kind, object.Name),
|
||||
Pos: object.Position,
|
||||
}, pass)
|
||||
|
@ -65,24 +96,5 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter {
|
|||
issues = append(issues, issue)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
setAnalyzerGoVersion(analyzer, getGoVersion(settings))
|
||||
|
||||
lnt := goanalysis.NewLinter(
|
||||
name,
|
||||
"Checks Go code for unused constants, variables, functions and types",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(lintCtx *linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
|
||||
return lnt
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package golinters // nolint:dupl
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -7,49 +7,66 @@ import (
|
|||
varcheckAPI "github.com/golangci/check/cmd/varcheck"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewVarcheck() *goanalysis.Linter {
|
||||
const linterName = "varcheck"
|
||||
const varcheckName = "varcheck"
|
||||
|
||||
func NewVarcheck(settings *config.VarCheckSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var res []goanalysis.Issue
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Name: varcheckName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
varcheckName,
|
||||
"Finds unused global variables and constants",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
checkExported := lintCtx.Settings().Varcheck.CheckExportedFields
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
issues := runVarCheck(pass, settings)
|
||||
|
||||
varcheckIssues := varcheckAPI.Run(prog, checkExported)
|
||||
if len(varcheckIssues) == 0 {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(varcheckIssues))
|
||||
for _, i := range varcheckIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("%s is unused", formatCode(i.VarName, lintCtx.Cfg)),
|
||||
FromLinter: linterName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
res = append(res, issues...)
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return res
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
func runVarCheck(pass *analysis.Pass, settings *config.VarCheckSettings) []goanalysis.Issue {
|
||||
prog := goanalysis.MakeFakeLoaderProgram(pass)
|
||||
|
||||
lintIssues := varcheckAPI.Run(prog, settings.CheckExportedFields)
|
||||
if len(lintIssues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, 0, len(lintIssues))
|
||||
|
||||
for _, i := range lintIssues {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
Pos: i.Pos,
|
||||
Text: fmt.Sprintf("%s is unused", formatCode(i.VarName, nil)),
|
||||
FromLinter: varcheckName,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
||||
|
|
|
@ -8,14 +8,10 @@ import (
|
|||
)
|
||||
|
||||
func NewWastedAssign() *goanalysis.Linter {
|
||||
analyzers := []*analysis.Analyzer{
|
||||
wastedassign.Analyzer,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
"wastedassign",
|
||||
"wastedassign finds wasted assignment statements.",
|
||||
analyzers,
|
||||
[]*analysis.Analyzer{wastedassign.Analyzer},
|
||||
nil,
|
||||
).WithLoadMode(goanalysis.LoadModeTypesInfo)
|
||||
}
|
||||
|
|
|
@ -8,41 +8,72 @@ import (
|
|||
"github.com/ultraware/whitespace"
|
||||
"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"
|
||||
)
|
||||
|
||||
func NewWhitespace() *goanalysis.Linter {
|
||||
const linterName = "whitespace"
|
||||
const whitespaceName = "whitespace"
|
||||
|
||||
//nolint:dupl
|
||||
func NewWhitespace(settings *config.WhitespaceSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: linterName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
var wsSettings whitespace.Settings
|
||||
if settings != nil {
|
||||
wsSettings = whitespace.Settings{
|
||||
MultiIf: settings.MultiIf,
|
||||
MultiFunc: settings.MultiFunc,
|
||||
}
|
||||
}
|
||||
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: whitespaceName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: goanalysis.DummyRun,
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
linterName,
|
||||
whitespaceName,
|
||||
"Tool for detection of leading and trailing whitespace",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
cfg := lintCtx.Cfg.LintersSettings.Whitespace
|
||||
settings := whitespace.Settings{MultiIf: cfg.MultiIf, MultiFunc: cfg.MultiFunc}
|
||||
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var issues []whitespace.Message
|
||||
for _, file := range pass.Files {
|
||||
issues = append(issues, whitespace.Run(file, pass.Fset, settings)...)
|
||||
issues, err := runWhitespace(lintCtx, pass, wsSettings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
res := make([]goanalysis.Issue, len(issues))
|
||||
for k, i := range issues {
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runWhitespace(lintCtx *linter.Context, pass *analysis.Pass, wsSettings whitespace.Settings) ([]goanalysis.Issue, error) {
|
||||
var messages []whitespace.Message
|
||||
for _, file := range pass.Files {
|
||||
messages = append(messages, whitespace.Run(file, pass.Fset, wsSettings)...)
|
||||
}
|
||||
|
||||
if len(messages) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues := make([]goanalysis.Issue, len(messages))
|
||||
for k, i := range messages {
|
||||
issue := result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: i.Pos.Filename,
|
||||
|
@ -50,7 +81,7 @@ func NewWhitespace() *goanalysis.Linter {
|
|||
},
|
||||
LineRange: &result.Range{From: i.Pos.Line, To: i.Pos.Line},
|
||||
Text: i.Message,
|
||||
FromLinter: linterName,
|
||||
FromLinter: whitespaceName,
|
||||
Replacement: &result.Replacement{},
|
||||
}
|
||||
|
||||
|
@ -70,16 +101,8 @@ func NewWhitespace() *goanalysis.Linter {
|
|||
}
|
||||
issue.Replacement.NewLines = []string{bracketLine}
|
||||
|
||||
res[k] = goanalysis.NewIssue(&issue, pass)
|
||||
issues[k] = goanalysis.NewIssue(&issue, pass)
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
resIssues = append(resIssues, res...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
return issues, nil
|
||||
}
|
||||
|
|
|
@ -6,78 +6,89 @@ import (
|
|||
"github.com/bombsimon/wsl/v3"
|
||||
"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 (
|
||||
name = "wsl"
|
||||
)
|
||||
const wslName = "wsl"
|
||||
|
||||
// NewWSL returns a new WSL linter.
|
||||
func NewWSL() *goanalysis.Linter {
|
||||
var (
|
||||
issues []goanalysis.Issue
|
||||
mu = sync.Mutex{}
|
||||
analyzer = &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
}
|
||||
)
|
||||
func NewWSL(settings *config.WSLSettings) *goanalysis.Linter {
|
||||
var mu sync.Mutex
|
||||
var resIssues []goanalysis.Issue
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
name,
|
||||
"Whitespace Linter - Forces you to use empty lines!",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithContextSetter(func(lintCtx *linter.Context) {
|
||||
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
|
||||
var (
|
||||
files = make([]string, 0, len(pass.Files))
|
||||
linterCfg = lintCtx.Cfg.LintersSettings.WSL
|
||||
processorCfg = wsl.Configuration{
|
||||
StrictAppend: linterCfg.StrictAppend,
|
||||
AllowAssignAndCallCuddle: linterCfg.AllowAssignAndCallCuddle,
|
||||
AllowAssignAndAnythingCuddle: linterCfg.AllowAssignAndAnythingCuddle,
|
||||
AllowMultiLineAssignCuddle: linterCfg.AllowMultiLineAssignCuddle,
|
||||
AllowCuddleDeclaration: linterCfg.AllowCuddleDeclaration,
|
||||
AllowTrailingComment: linterCfg.AllowTrailingComment,
|
||||
AllowSeparatedLeadingComment: linterCfg.AllowSeparatedLeadingComment,
|
||||
ForceCuddleErrCheckAndAssign: linterCfg.ForceCuddleErrCheckAndAssign,
|
||||
ForceCaseTrailingWhitespaceLimit: linterCfg.ForceCaseTrailingWhitespaceLimit,
|
||||
ForceExclusiveShortDeclarations: linterCfg.ForceExclusiveShortDeclarations,
|
||||
conf := &wsl.Configuration{
|
||||
AllowCuddleWithCalls: []string{"Lock", "RLock"},
|
||||
AllowCuddleWithRHS: []string{"Unlock", "RUnlock"},
|
||||
ErrorVariableNames: []string{"err"},
|
||||
}
|
||||
)
|
||||
|
||||
for _, file := range pass.Files {
|
||||
files = append(files, pass.Fset.PositionFor(file.Pos(), false).Filename)
|
||||
if settings != nil {
|
||||
conf.StrictAppend = settings.StrictAppend
|
||||
conf.AllowAssignAndCallCuddle = settings.AllowAssignAndCallCuddle
|
||||
conf.AllowAssignAndAnythingCuddle = settings.AllowAssignAndAnythingCuddle
|
||||
conf.AllowMultiLineAssignCuddle = settings.AllowMultiLineAssignCuddle
|
||||
conf.AllowCuddleDeclaration = settings.AllowCuddleDeclaration
|
||||
conf.AllowTrailingComment = settings.AllowTrailingComment
|
||||
conf.AllowSeparatedLeadingComment = settings.AllowSeparatedLeadingComment
|
||||
conf.ForceCuddleErrCheckAndAssign = settings.ForceCuddleErrCheckAndAssign
|
||||
conf.ForceCaseTrailingWhitespaceLimit = settings.ForceCaseTrailingWhitespaceLimit
|
||||
conf.ForceExclusiveShortDeclarations = settings.ForceExclusiveShortDeclarations
|
||||
}
|
||||
|
||||
wslErrors, _ := wsl.NewProcessorWithConfig(processorCfg).
|
||||
ProcessFiles(files)
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: goanalysis.TheOnlyAnalyzerName,
|
||||
Doc: goanalysis.TheOnlyanalyzerDoc,
|
||||
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||||
issues := runWSL(pass, conf)
|
||||
|
||||
if len(wslErrors) == 0 {
|
||||
if len(issues) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
resIssues = append(resIssues, issues...)
|
||||
mu.Unlock()
|
||||
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return goanalysis.NewLinter(
|
||||
wslName,
|
||||
"Whitespace Linter - Forces you to use empty lines!",
|
||||
[]*analysis.Analyzer{analyzer},
|
||||
nil,
|
||||
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return resIssues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
||||
func runWSL(pass *analysis.Pass, conf *wsl.Configuration) []goanalysis.Issue {
|
||||
var files = make([]string, 0, len(pass.Files))
|
||||
for _, file := range pass.Files {
|
||||
files = append(files, pass.Fset.PositionFor(file.Pos(), false).Filename)
|
||||
}
|
||||
|
||||
if conf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
wslErrors, _ := wsl.NewProcessorWithConfig(*conf).ProcessFiles(files)
|
||||
if len(wslErrors) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var issues []goanalysis.Issue
|
||||
for _, err := range wslErrors {
|
||||
issues = append(issues, goanalysis.NewIssue(&result.Issue{
|
||||
FromLinter: name,
|
||||
FromLinter: wslName,
|
||||
Pos: err.Position,
|
||||
Text: err.Reason,
|
||||
}, pass))
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
|
||||
return issues
|
||||
}).WithLoadMode(goanalysis.LoadModeSyntax)
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ func (lc *Config) WithLoadFiles() *Config {
|
|||
|
||||
func (lc *Config) WithLoadForGoAnalysis() *Config {
|
||||
lc = lc.WithLoadFiles()
|
||||
lc.LoadMode |= packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedTypesSizes
|
||||
lc.LoadMode |= packages.NeedImports | packages.NeedDeps | packages.NeedExportFile | packages.NeedTypesSizes
|
||||
lc.IsSlow = true
|
||||
return lc
|
||||
}
|
||||
|
|
|
@ -100,51 +100,104 @@ func enableLinterConfigs(lcs []*linter.Config, isEnabled func(lc *linter.Config)
|
|||
|
||||
//nolint:funlen
|
||||
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
||||
var bidichkCfg *config.BiDiChkSettings
|
||||
var cyclopCfg *config.Cyclop
|
||||
var decorderCfg *config.DecorderSettings
|
||||
var errchkjsonCfg *config.ErrChkJSONSettings
|
||||
var errorlintCfg *config.ErrorLintSettings
|
||||
var exhaustiveCfg *config.ExhaustiveSettings
|
||||
var exhaustiveStructCfg *config.ExhaustiveStructSettings
|
||||
var exhaustructCfg *config.ExhaustructSettings
|
||||
var gciCfg *config.GciSettings
|
||||
var goModDirectivesCfg *config.GoModDirectivesSettings
|
||||
var goMndCfg *config.GoMndSettings
|
||||
var gosecCfg *config.GoSecSettings
|
||||
var gosimpleCfg *config.StaticCheckSettings
|
||||
var govetCfg *config.GovetSettings
|
||||
var grouperCfg *config.GrouperSettings
|
||||
var ifshortCfg *config.IfshortSettings
|
||||
var importAsCfg *config.ImportAsSettings
|
||||
var ireturnCfg *config.IreturnSettings
|
||||
var maintIdxCfg *config.MaintIdxSettings
|
||||
var nilNilCfg *config.NilNilSettings
|
||||
var nlreturnCfg *config.NlreturnSettings
|
||||
var predeclaredCfg *config.PredeclaredSettings
|
||||
var reviveCfg *config.ReviveSettings
|
||||
var staticcheckCfg *config.StaticCheckSettings
|
||||
var stylecheckCfg *config.StaticCheckSettings
|
||||
var tagliatelleCfg *config.TagliatelleSettings
|
||||
var tenvCfg *config.TenvSettings
|
||||
var testpackageCfg *config.TestpackageSettings
|
||||
var thelperCfg *config.ThelperSettings
|
||||
var unusedCfg *config.StaticCheckSettings
|
||||
var varnamelenCfg *config.VarnamelenSettings
|
||||
var wrapcheckCfg *config.WrapcheckSettings
|
||||
var (
|
||||
bidichkCfg *config.BiDiChkSettings
|
||||
cyclopCfg *config.Cyclop
|
||||
decorderCfg *config.DecorderSettings
|
||||
depGuardCfg *config.DepGuardSettings
|
||||
dogsledCfg *config.DogsledSettings
|
||||
duplCfg *config.DuplSettings
|
||||
errcheckCfg *config.ErrcheckSettings
|
||||
errchkjsonCfg *config.ErrChkJSONSettings
|
||||
errorlintCfg *config.ErrorLintSettings
|
||||
exhaustiveCfg *config.ExhaustiveSettings
|
||||
exhaustiveStructCfg *config.ExhaustiveStructSettings
|
||||
exhaustructCfg *config.ExhaustructSettings
|
||||
forbidigoCfg *config.ForbidigoSettings
|
||||
funlenCfg *config.FunlenSettings
|
||||
gciCfg *config.GciSettings
|
||||
gocognitCfg *config.GocognitSettings
|
||||
goconstCfg *config.GoConstSettings
|
||||
gocriticCfg *config.GocriticSettings
|
||||
gocycloCfg *config.GoCycloSettings
|
||||
godotCfg *config.GodotSettings
|
||||
godoxCfg *config.GodoxSettings
|
||||
gofmtCfg *config.GoFmtSettings
|
||||
gofumptCfg *config.GofumptSettings
|
||||
goheaderCfg *config.GoHeaderSettings
|
||||
goimportsCfg *config.GoImportsSettings
|
||||
golintCfg *config.GoLintSettings
|
||||
goMndCfg *config.GoMndSettings
|
||||
goModDirectivesCfg *config.GoModDirectivesSettings
|
||||
gomodguardCfg *config.GoModGuardSettings
|
||||
gosecCfg *config.GoSecSettings
|
||||
gosimpleCfg *config.StaticCheckSettings
|
||||
govetCfg *config.GovetSettings
|
||||
grouperCfg *config.GrouperSettings
|
||||
ifshortCfg *config.IfshortSettings
|
||||
importAsCfg *config.ImportAsSettings
|
||||
ireturnCfg *config.IreturnSettings
|
||||
lllCfg *config.LllSettings
|
||||
maintIdxCfg *config.MaintIdxSettings
|
||||
makezeroCfg *config.MakezeroSettings
|
||||
malignedCfg *config.MalignedSettings
|
||||
misspellCfg *config.MisspellSettings
|
||||
nakedretCfg *config.NakedretSettings
|
||||
nestifCfg *config.NestifSettings
|
||||
nilNilCfg *config.NilNilSettings
|
||||
nlreturnCfg *config.NlreturnSettings
|
||||
noLintLintCfg *config.NoLintLintSettings
|
||||
preallocCfg *config.PreallocSettings
|
||||
predeclaredCfg *config.PredeclaredSettings
|
||||
promlinterCfg *config.PromlinterSettings
|
||||
reviveCfg *config.ReviveSettings
|
||||
rowserrcheckCfg *config.RowsErrCheckSettings
|
||||
staticcheckCfg *config.StaticCheckSettings
|
||||
structcheckCfg *config.StructCheckSettings
|
||||
stylecheckCfg *config.StaticCheckSettings
|
||||
tagliatelleCfg *config.TagliatelleSettings
|
||||
tenvCfg *config.TenvSettings
|
||||
testpackageCfg *config.TestpackageSettings
|
||||
thelperCfg *config.ThelperSettings
|
||||
unparamCfg *config.UnparamSettings
|
||||
unusedCfg *config.StaticCheckSettings
|
||||
varcheckCfg *config.VarCheckSettings
|
||||
varnamelenCfg *config.VarnamelenSettings
|
||||
whitespaceCfg *config.WhitespaceSettings
|
||||
wrapcheckCfg *config.WrapcheckSettings
|
||||
wslCfg *config.WSLSettings
|
||||
)
|
||||
|
||||
if m.cfg != nil {
|
||||
bidichkCfg = &m.cfg.LintersSettings.BiDiChk
|
||||
cyclopCfg = &m.cfg.LintersSettings.Cyclop
|
||||
errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON
|
||||
decorderCfg = &m.cfg.LintersSettings.Decorder
|
||||
depGuardCfg = &m.cfg.LintersSettings.Depguard
|
||||
dogsledCfg = &m.cfg.LintersSettings.Dogsled
|
||||
duplCfg = &m.cfg.LintersSettings.Dupl
|
||||
errcheckCfg = &m.cfg.LintersSettings.Errcheck
|
||||
errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON
|
||||
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
|
||||
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
|
||||
exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct
|
||||
exhaustructCfg = &m.cfg.LintersSettings.Exhaustruct
|
||||
forbidigoCfg = &m.cfg.LintersSettings.Forbidigo
|
||||
funlenCfg = &m.cfg.LintersSettings.Funlen
|
||||
gciCfg = &m.cfg.LintersSettings.Gci
|
||||
goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
|
||||
gocognitCfg = &m.cfg.LintersSettings.Gocognit
|
||||
goconstCfg = &m.cfg.LintersSettings.Goconst
|
||||
gocriticCfg = &m.cfg.LintersSettings.Gocritic
|
||||
gocycloCfg = &m.cfg.LintersSettings.Gocyclo
|
||||
godotCfg = &m.cfg.LintersSettings.Godot
|
||||
godoxCfg = &m.cfg.LintersSettings.Godox
|
||||
gofmtCfg = &m.cfg.LintersSettings.Gofmt
|
||||
gofumptCfg = &m.cfg.LintersSettings.Gofumpt
|
||||
goheaderCfg = &m.cfg.LintersSettings.Goheader
|
||||
goimportsCfg = &m.cfg.LintersSettings.Goimports
|
||||
golintCfg = &m.cfg.LintersSettings.Golint
|
||||
goMndCfg = &m.cfg.LintersSettings.Gomnd
|
||||
goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives
|
||||
gomodguardCfg = &m.cfg.LintersSettings.Gomodguard
|
||||
gosecCfg = &m.cfg.LintersSettings.Gosec
|
||||
gosimpleCfg = &m.cfg.LintersSettings.Gosimple
|
||||
govetCfg = &m.cfg.LintersSettings.Govet
|
||||
|
@ -152,20 +205,35 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
ifshortCfg = &m.cfg.LintersSettings.Ifshort
|
||||
importAsCfg = &m.cfg.LintersSettings.ImportAs
|
||||
ireturnCfg = &m.cfg.LintersSettings.Ireturn
|
||||
lllCfg = &m.cfg.LintersSettings.Lll
|
||||
maintIdxCfg = &m.cfg.LintersSettings.MaintIdx
|
||||
makezeroCfg = &m.cfg.LintersSettings.Makezero
|
||||
malignedCfg = &m.cfg.LintersSettings.Maligned
|
||||
misspellCfg = &m.cfg.LintersSettings.Misspell
|
||||
nakedretCfg = &m.cfg.LintersSettings.Nakedret
|
||||
nestifCfg = &m.cfg.LintersSettings.Nestif
|
||||
nilNilCfg = &m.cfg.LintersSettings.NilNil
|
||||
nlreturnCfg = &m.cfg.LintersSettings.Nlreturn
|
||||
noLintLintCfg = &m.cfg.LintersSettings.NoLintLint
|
||||
preallocCfg = &m.cfg.LintersSettings.Prealloc
|
||||
predeclaredCfg = &m.cfg.LintersSettings.Predeclared
|
||||
promlinterCfg = &m.cfg.LintersSettings.Promlinter
|
||||
reviveCfg = &m.cfg.LintersSettings.Revive
|
||||
rowserrcheckCfg = &m.cfg.LintersSettings.RowsErrCheck
|
||||
staticcheckCfg = &m.cfg.LintersSettings.Staticcheck
|
||||
structcheckCfg = &m.cfg.LintersSettings.Structcheck
|
||||
stylecheckCfg = &m.cfg.LintersSettings.Stylecheck
|
||||
tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle
|
||||
tenvCfg = &m.cfg.LintersSettings.Tenv
|
||||
testpackageCfg = &m.cfg.LintersSettings.Testpackage
|
||||
thelperCfg = &m.cfg.LintersSettings.Thelper
|
||||
unparamCfg = &m.cfg.LintersSettings.Unparam
|
||||
unusedCfg = &m.cfg.LintersSettings.Unused
|
||||
varcheckCfg = &m.cfg.LintersSettings.Varcheck
|
||||
varnamelenCfg = &m.cfg.LintersSettings.Varnamelen
|
||||
whitespaceCfg = &m.cfg.LintersSettings.Whitespace
|
||||
wrapcheckCfg = &m.cfg.LintersSettings.Wrapcheck
|
||||
wslCfg = &m.cfg.LintersSettings.WSL
|
||||
|
||||
if govetCfg != nil {
|
||||
govetCfg.Go = m.cfg.Run.Go
|
||||
|
@ -223,17 +291,17 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetUnused).
|
||||
WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"),
|
||||
|
||||
linter.NewConfig(golinters.NewDepguard()).
|
||||
linter.NewConfig(golinters.NewDepguard(depGuardCfg)).
|
||||
WithSince("v1.4.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule).
|
||||
WithURL("https://github.com/OpenPeeDeeP/depguard"),
|
||||
|
||||
linter.NewConfig(golinters.NewDogsled()).
|
||||
linter.NewConfig(golinters.NewDogsled(dogsledCfg)).
|
||||
WithSince("v1.19.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/alexkohler/dogsled"),
|
||||
|
||||
linter.NewConfig(golinters.NewDupl()).
|
||||
linter.NewConfig(golinters.NewDupl(duplCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/mibk/dupl"),
|
||||
|
@ -244,7 +312,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/charithe/durationcheck"),
|
||||
|
||||
linter.NewConfig(golinters.NewErrcheck()).
|
||||
linter.NewConfig(golinters.NewErrcheck(errcheckCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetBugs, linter.PresetError).
|
||||
|
@ -299,7 +367,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/kyoh86/exportloopref"),
|
||||
|
||||
linter.NewConfig(golinters.NewForbidigo()).
|
||||
linter.NewConfig(golinters.NewForbidigo(forbidigoCfg)).
|
||||
WithSince("v1.34.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/ashanbrown/forbidigo"),
|
||||
|
@ -309,7 +377,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/gostaticanalysis/forcetypeassert"),
|
||||
|
||||
linter.NewConfig(golinters.NewFunlen()).
|
||||
linter.NewConfig(golinters.NewFunlen(funlenCfg)).
|
||||
WithSince("v1.18.0").
|
||||
WithPresets(linter.PresetComplexity).
|
||||
WithURL("https://github.com/ultraware/funlen"),
|
||||
|
@ -329,34 +397,34 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/leighmcculloch/gochecknoinits"),
|
||||
|
||||
linter.NewConfig(golinters.NewGocognit()).
|
||||
linter.NewConfig(golinters.NewGocognit(gocognitCfg)).
|
||||
WithSince("v1.20.0").
|
||||
WithPresets(linter.PresetComplexity).
|
||||
WithURL("https://github.com/uudashr/gocognit"),
|
||||
|
||||
linter.NewConfig(golinters.NewGoconst()).
|
||||
linter.NewConfig(golinters.NewGoconst(goconstCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/jgautheron/goconst"),
|
||||
|
||||
linter.NewConfig(golinters.NewGocritic()).
|
||||
linter.NewConfig(golinters.NewGocritic(gocriticCfg, m.cfg)).
|
||||
WithSince("v1.12.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetMetaLinter).
|
||||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/go-critic/go-critic"),
|
||||
|
||||
linter.NewConfig(golinters.NewGocyclo()).
|
||||
linter.NewConfig(golinters.NewGocyclo(gocycloCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithPresets(linter.PresetComplexity).
|
||||
WithURL("https://github.com/fzipp/gocyclo"),
|
||||
|
||||
linter.NewConfig(golinters.NewGodot()).
|
||||
linter.NewConfig(golinters.NewGodot(godotCfg)).
|
||||
WithSince("v1.25.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetComment).
|
||||
WithAutoFix().
|
||||
WithURL("https://github.com/tetafro/godot"),
|
||||
|
||||
linter.NewConfig(golinters.NewGodox()).
|
||||
linter.NewConfig(golinters.NewGodox(godoxCfg)).
|
||||
WithSince("v1.19.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetComment).
|
||||
WithURL("https://github.com/matoous/godox"),
|
||||
|
@ -367,30 +435,30 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/Djarvur/go-err113"),
|
||||
|
||||
linter.NewConfig(golinters.NewGofmt()).
|
||||
linter.NewConfig(golinters.NewGofmt(gofmtCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithPresets(linter.PresetFormatting).
|
||||
WithAutoFix().
|
||||
WithURL("https://golang.org/cmd/gofmt/"),
|
||||
|
||||
linter.NewConfig(golinters.NewGofumpt()).
|
||||
linter.NewConfig(golinters.NewGofumpt(gofumptCfg)).
|
||||
WithSince("v1.28.0").
|
||||
WithPresets(linter.PresetFormatting).
|
||||
WithAutoFix().
|
||||
WithURL("https://github.com/mvdan/gofumpt"),
|
||||
|
||||
linter.NewConfig(golinters.NewGoHeader()).
|
||||
linter.NewConfig(golinters.NewGoHeader(goheaderCfg)).
|
||||
WithSince("v1.28.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/denis-tingaikin/go-header"),
|
||||
|
||||
linter.NewConfig(golinters.NewGoimports()).
|
||||
linter.NewConfig(golinters.NewGoimports(goimportsCfg)).
|
||||
WithSince("v1.20.0").
|
||||
WithPresets(linter.PresetFormatting, linter.PresetImport).
|
||||
WithAutoFix().
|
||||
WithURL("https://godoc.org/golang.org/x/tools/cmd/goimports"),
|
||||
|
||||
linter.NewConfig(golinters.NewGolint()).
|
||||
linter.NewConfig(golinters.NewGolint(golintCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetStyle).
|
||||
|
@ -407,7 +475,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle, linter.PresetModule).
|
||||
WithURL("https://github.com/ldez/gomoddirectives"),
|
||||
|
||||
linter.NewConfig(golinters.NewGomodguard()).
|
||||
linter.NewConfig(golinters.NewGomodguard(gomodguardCfg)).
|
||||
WithSince("v1.25.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule).
|
||||
WithURL("https://github.com/ryancurrah/gomodguard"),
|
||||
|
@ -473,7 +541,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/butuzov/ireturn"),
|
||||
|
||||
linter.NewConfig(golinters.NewLLL()).
|
||||
linter.NewConfig(golinters.NewLLL(lllCfg)).
|
||||
WithSince("v1.8.0").
|
||||
WithPresets(linter.PresetStyle),
|
||||
|
||||
|
@ -482,31 +550,31 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetComplexity).
|
||||
WithURL("https://github.com/yagipy/maintidx"),
|
||||
|
||||
linter.NewConfig(golinters.NewMakezero()).
|
||||
linter.NewConfig(golinters.NewMakezero(makezeroCfg)).
|
||||
WithSince("v1.34.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetBugs).
|
||||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/ashanbrown/makezero"),
|
||||
|
||||
linter.NewConfig(golinters.NewMaligned()).
|
||||
linter.NewConfig(golinters.NewMaligned(malignedCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetPerformance).
|
||||
WithURL("https://github.com/mdempsky/maligned").
|
||||
Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"),
|
||||
|
||||
linter.NewConfig(golinters.NewMisspell()).
|
||||
linter.NewConfig(golinters.NewMisspell(misspellCfg)).
|
||||
WithSince("v1.8.0").
|
||||
WithPresets(linter.PresetStyle, linter.PresetComment).
|
||||
WithAutoFix().
|
||||
WithURL("https://github.com/client9/misspell"),
|
||||
|
||||
linter.NewConfig(golinters.NewNakedret()).
|
||||
linter.NewConfig(golinters.NewNakedret(nakedretCfg)).
|
||||
WithSince("v1.19.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/alexkohler/nakedret"),
|
||||
|
||||
linter.NewConfig(golinters.NewNestif()).
|
||||
linter.NewConfig(golinters.NewNestif(nestifCfg)).
|
||||
WithSince("v1.25.0").
|
||||
WithPresets(linter.PresetComplexity).
|
||||
WithURL("https://github.com/nakabonne/nestif"),
|
||||
|
@ -551,7 +619,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle, linter.PresetTest).
|
||||
WithURL("https://github.com/kunwardeep/paralleltest"),
|
||||
|
||||
linter.NewConfig(golinters.NewPrealloc()).
|
||||
linter.NewConfig(golinters.NewPreAlloc(preallocCfg)).
|
||||
WithSince("v1.19.0").
|
||||
WithPresets(linter.PresetPerformance).
|
||||
WithURL("https://github.com/alexkohler/prealloc"),
|
||||
|
@ -561,7 +629,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/nishanths/predeclared"),
|
||||
|
||||
linter.NewConfig(golinters.NewPromlinter()).
|
||||
linter.NewConfig(golinters.NewPromlinter(promlinterCfg)).
|
||||
WithSince("v1.40.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/yeya24/promlinter"),
|
||||
|
@ -572,7 +640,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
ConsiderSlow().
|
||||
WithURL("https://github.com/mgechev/revive"),
|
||||
|
||||
linter.NewConfig(golinters.NewRowsErrCheck()).
|
||||
linter.NewConfig(golinters.NewRowsErrCheck(rowserrcheckCfg)).
|
||||
WithSince("v1.23.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetBugs, linter.PresetSQL).
|
||||
|
@ -599,7 +667,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithAlternativeNames(megacheckName).
|
||||
WithURL("https://staticcheck.io/"),
|
||||
|
||||
linter.NewConfig(golinters.NewStructcheck()).
|
||||
linter.NewConfig(golinters.NewStructcheck(structcheckCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetUnused).
|
||||
|
@ -653,7 +721,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/mdempsky/unconvert"),
|
||||
|
||||
linter.NewConfig(golinters.NewUnparam()).
|
||||
linter.NewConfig(golinters.NewUnparam(unparamCfg)).
|
||||
WithSince("v1.9.0").
|
||||
WithPresets(linter.PresetUnused).
|
||||
WithLoadForGoAnalysis().
|
||||
|
@ -669,7 +737,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithChangeTypes().
|
||||
WithURL("https://github.com/dominikh/go-tools/tree/master/unused"),
|
||||
|
||||
linter.NewConfig(golinters.NewVarcheck()).
|
||||
linter.NewConfig(golinters.NewVarcheck(varcheckCfg)).
|
||||
WithSince("v1.0.0").
|
||||
WithLoadForGoAnalysis().
|
||||
WithPresets(linter.PresetUnused).
|
||||
|
@ -688,7 +756,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithURL("https://github.com/sanposhiho/wastedassign").
|
||||
WithNoopFallback(m.cfg),
|
||||
|
||||
linter.NewConfig(golinters.NewWhitespace()).
|
||||
linter.NewConfig(golinters.NewWhitespace(whitespaceCfg)).
|
||||
WithSince("v1.19.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithAutoFix().
|
||||
|
@ -700,13 +768,13 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
WithLoadForGoAnalysis().
|
||||
WithURL("https://github.com/tomarrell/wrapcheck"),
|
||||
|
||||
linter.NewConfig(golinters.NewWSL()).
|
||||
linter.NewConfig(golinters.NewWSL(wslCfg)).
|
||||
WithSince("v1.20.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/bombsimon/wsl"),
|
||||
|
||||
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
|
||||
linter.NewConfig(golinters.NewNoLintLint()).
|
||||
linter.NewConfig(golinters.NewNoLintLint(noLintLintCfg)).
|
||||
WithSince("v1.26.0").
|
||||
WithPresets(linter.PresetStyle).
|
||||
WithURL("https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md"),
|
||||
|
@ -714,12 +782,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
|
|||
|
||||
enabledByDefault := map[string]bool{
|
||||
golinters.NewGovet(nil).Name(): true,
|
||||
golinters.NewErrcheck().Name(): true,
|
||||
golinters.NewErrcheck(errcheckCfg).Name(): true,
|
||||
golinters.NewStaticcheck(staticcheckCfg).Name(): true,
|
||||
golinters.NewUnused(unusedCfg).Name(): true,
|
||||
golinters.NewGosimple(gosimpleCfg).Name(): true,
|
||||
golinters.NewStructcheck().Name(): true,
|
||||
golinters.NewVarcheck().Name(): true,
|
||||
golinters.NewStructcheck(structcheckCfg).Name(): true,
|
||||
golinters.NewVarcheck(varcheckCfg).Name(): true,
|
||||
golinters.NewIneffassign().Name(): true,
|
||||
golinters.NewDeadcode().Name(): true,
|
||||
golinters.NewTypecheck().Name(): true,
|
||||
|
|
|
@ -126,7 +126,7 @@ func stringifyLoadMode(mode packages.LoadMode) string {
|
|||
m := map[packages.LoadMode]string{
|
||||
packages.NeedCompiledGoFiles: "compiled_files",
|
||||
packages.NeedDeps: "deps",
|
||||
packages.NeedExportsFile: "exports_file",
|
||||
packages.NeedExportFile: "exports_file",
|
||||
packages.NeedFiles: "files",
|
||||
packages.NeedImports: "imports",
|
||||
packages.NeedName: "name",
|
||||
|
|
|
@ -33,7 +33,7 @@ func (i *ignoredRange) doesMatch(issue *result.Issue) bool {
|
|||
}
|
||||
|
||||
// only allow selective nolinting of nolintlint
|
||||
nolintFoundForLinter := len(i.linters) == 0 && issue.FromLinter != golinters.NolintlintName
|
||||
nolintFoundForLinter := len(i.linters) == 0 && issue.FromLinter != golinters.NoLintLintName
|
||||
|
||||
for _, linterName := range i.linters {
|
||||
if linterName == issue.FromLinter {
|
||||
|
@ -48,7 +48,7 @@ func (i *ignoredRange) doesMatch(issue *result.Issue) bool {
|
|||
|
||||
// handle possible unused nolint directives
|
||||
// nolintlint generates potential issues for every nolint directive, and they are filtered out here
|
||||
if issue.FromLinter == golinters.NolintlintName && issue.ExpectNoLint {
|
||||
if issue.FromLinter == golinters.NoLintLintName && issue.ExpectNoLint {
|
||||
if issue.ExpectedNoLintLinter != "" {
|
||||
return i.matchedIssueFromLinter[issue.ExpectedNoLintLinter]
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func (p *Nolint) buildIgnoredRangesForFile(f *ast.File, fset *token.FileSet, fil
|
|||
|
||||
func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) {
|
||||
nolintDebugf("got issue: %v", *i)
|
||||
if i.FromLinter == golinters.NolintlintName && i.ExpectNoLint && i.ExpectedNoLintLinter != "" {
|
||||
if i.FromLinter == golinters.NoLintLintName && i.ExpectNoLint && i.ExpectedNoLintLinter != "" {
|
||||
// don't expect disabled linters to cover their nolint statements
|
||||
nolintDebugf("enabled linters: %v", p.enabledLinters)
|
||||
if p.enabledLinters[i.ExpectedNoLintLinter] == nil {
|
||||
|
@ -302,7 +302,7 @@ func (issues sortWithNolintlintLast) Len() int {
|
|||
}
|
||||
|
||||
func (issues sortWithNolintlintLast) Less(i, j int) bool {
|
||||
return issues[i].FromLinter != golinters.NolintlintName && issues[j].FromLinter == golinters.NolintlintName
|
||||
return issues[i].FromLinter != golinters.NoLintLintName && issues[j].FromLinter == golinters.NoLintLintName
|
||||
}
|
||||
|
||||
func (issues sortWithNolintlintLast) Swap(i, j int) {
|
||||
|
|
|
@ -286,7 +286,7 @@ func TestNolintUnused(t *testing.T) {
|
|||
Filename: fileName,
|
||||
Line: 3,
|
||||
},
|
||||
FromLinter: golinters.NolintlintName,
|
||||
FromLinter: golinters.NoLintLintName,
|
||||
ExpectNoLint: true,
|
||||
ExpectedNoLintLinter: "varcheck",
|
||||
}
|
||||
|
@ -297,7 +297,7 @@ func TestNolintUnused(t *testing.T) {
|
|||
Filename: fileName,
|
||||
Line: 5,
|
||||
},
|
||||
FromLinter: golinters.NolintlintName,
|
||||
FromLinter: golinters.NoLintLintName,
|
||||
ExpectNoLint: true,
|
||||
ExpectedNoLintLinter: "varcheck",
|
||||
}
|
||||
|
|
2
test/testdata/prealloc.go
vendored
2
test/testdata/prealloc.go
vendored
|
@ -2,7 +2,7 @@
|
|||
package testdata
|
||||
|
||||
func Prealloc(source []int) []int {
|
||||
var dest []int // ERROR "Consider preallocating `dest`"
|
||||
var dest []int // ERROR "Consider pre-allocating `dest`"
|
||||
for _, v := range source {
|
||||
dest = append(dest, v)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue