
The whitespace linter was added in #673. Enable it and fix found issues. Add auto-fixing to the whitespace linter.
168 lines
4.1 KiB
Go
168 lines
4.1 KiB
Go
package golinters
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/types"
|
|
"path/filepath"
|
|
"runtime"
|
|
"runtime/debug"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/config"
|
|
|
|
"github.com/go-lintpack/lintpack"
|
|
"golang.org/x/tools/go/loader"
|
|
|
|
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
|
"github.com/golangci/golangci-lint/pkg/result"
|
|
)
|
|
|
|
type Gocritic struct{}
|
|
|
|
func (Gocritic) Name() string {
|
|
return "gocritic"
|
|
}
|
|
|
|
func (Gocritic) Desc() string {
|
|
return "The most opinionated Go source code linter"
|
|
}
|
|
|
|
func (Gocritic) normalizeCheckerInfoParams(info *lintpack.CheckerInfo) lintpack.CheckerParams {
|
|
// lowercase info param keys here because golangci-lint's config parser lowercases all strings
|
|
ret := lintpack.CheckerParams{}
|
|
for k, v := range info.Params {
|
|
ret[strings.ToLower(k)] = v
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (lint Gocritic) configureCheckerInfo(info *lintpack.CheckerInfo, allParams map[string]config.GocriticCheckSettings) error {
|
|
params := allParams[strings.ToLower(info.Name)]
|
|
if params == nil { // no config for this checker
|
|
return nil
|
|
}
|
|
|
|
infoParams := lint.normalizeCheckerInfoParams(info)
|
|
for k, p := range params {
|
|
v, ok := infoParams[k]
|
|
if ok {
|
|
v.Value = 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 (lint Gocritic) buildEnabledCheckers(lintCtx *linter.Context, lintpackCtx *lintpack.Context) ([]*lintpack.Checker, error) {
|
|
s := lintCtx.Settings().Gocritic
|
|
allParams := s.GetLowercasedParams()
|
|
|
|
var enabledCheckers []*lintpack.Checker
|
|
for _, info := range lintpack.GetCheckersInfo() {
|
|
if !s.IsCheckEnabled(info.Name) {
|
|
continue
|
|
}
|
|
|
|
if err := lint.configureCheckerInfo(info, allParams); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := lintpack.NewChecker(lintpackCtx, info)
|
|
enabledCheckers = append(enabledCheckers, c)
|
|
}
|
|
|
|
return enabledCheckers, nil
|
|
}
|
|
|
|
func (lint Gocritic) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
|
|
sizes := types.SizesFor("gc", runtime.GOARCH)
|
|
lintpackCtx := lintpack.NewContext(lintCtx.Program.Fset, sizes)
|
|
|
|
enabledCheckers, err := lint.buildEnabledCheckers(lintCtx, lintpackCtx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
issuesCh := make(chan result.Issue, 1024)
|
|
var panicErr error
|
|
go func() {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
panicErr = fmt.Errorf("panic occurred: %s", err)
|
|
lintCtx.Log.Warnf("Panic: %s", debug.Stack())
|
|
}
|
|
}()
|
|
|
|
for _, pkgInfo := range lintCtx.Program.InitialPackages() {
|
|
lintpackCtx.SetPackageInfo(&pkgInfo.Info, pkgInfo.Pkg)
|
|
lint.runOnPackage(lintpackCtx, enabledCheckers, pkgInfo, issuesCh)
|
|
}
|
|
close(issuesCh)
|
|
}()
|
|
|
|
var res []result.Issue
|
|
for i := range issuesCh {
|
|
res = append(res, i)
|
|
}
|
|
if panicErr != nil {
|
|
return nil, panicErr
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (lint Gocritic) runOnPackage(lintpackCtx *lintpack.Context, checkers []*lintpack.Checker,
|
|
pkgInfo *loader.PackageInfo, ret chan<- result.Issue) {
|
|
for _, f := range pkgInfo.Files {
|
|
filename := filepath.Base(lintpackCtx.FileSet.Position(f.Pos()).Filename)
|
|
lintpackCtx.SetFileInfo(filename, f)
|
|
|
|
lint.runOnFile(lintpackCtx, f, checkers, ret)
|
|
}
|
|
}
|
|
|
|
func (lint Gocritic) runOnFile(ctx *lintpack.Context, f *ast.File, checkers []*lintpack.Checker,
|
|
ret chan<- result.Issue) {
|
|
var wg sync.WaitGroup
|
|
wg.Add(len(checkers))
|
|
for _, c := range checkers {
|
|
// All checkers are expected to use *lint.Context
|
|
// as read-only structure, so no copying is required.
|
|
go func(c *lintpack.Checker) {
|
|
defer wg.Done()
|
|
|
|
for _, warn := range c.Check(f) {
|
|
pos := ctx.FileSet.Position(warn.Node.Pos())
|
|
ret <- result.Issue{
|
|
Pos: pos,
|
|
Text: fmt.Sprintf("%s: %s", c.Info.Name, warn.Text),
|
|
FromLinter: lint.Name(),
|
|
}
|
|
}
|
|
}(c)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|