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:
Ludovic Fernandez 2022-05-23 12:39:57 +02:00 committed by GitHub
parent 75be924e98
commit f9d815115c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
74 changed files with 2260 additions and 1687 deletions

5
go.mod
View file

@ -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
View file

@ -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=

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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,

View file

@ -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)
}

View file

@ -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),
)
}

View file

@ -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()),
})

View file

@ -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
}

View file

@ -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)
}

View file

@ -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.

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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,

View file

@ -211,3 +211,7 @@ func valueToString(v interface{}) string {
return fmt.Sprint(v)
}
func DummyRun(_ *analysis.Pass) (interface{}, error) {
return nil, nil
}

View file

@ -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",

View file

@ -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
}

View file

@ -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))
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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{

View file

@ -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"
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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(

View file

@ -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)
}

View file

@ -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

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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.",

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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,

View file

@ -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",

View file

@ -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) {

View file

@ -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",
}

View file

@ -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)
}