Isaev Denis f1c1dbfab4
Feature/enable autofix on whitespace (#674)
The whitespace linter was added in #673. Enable it and fix found issues.
Add auto-fixing to the whitespace linter.
2019-09-10 16:56:44 +03:00

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