2021-02-14 14:36:37 +01:00

174 lines
4.4 KiB
Go

package golinters
import (
"encoding/json"
"fmt"
"go/token"
"io/ioutil"
"github.com/mgechev/dots"
reviveConfig "github.com/mgechev/revive/config"
"github.com/mgechev/revive/lint"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
const reviveName = "revive"
// jsonObject defines a JSON object of an failure
type jsonObject struct {
Severity lint.Severity
lint.Failure `json:",inline"`
}
// NewNewRevive returns a new Revive linter.
func NewRevive(cfg *config.ReviveSettings) *goanalysis.Linter {
var issues []goanalysis.Issue
analyzer := &analysis.Analyzer{
Name: goanalysis.TheOnlyAnalyzerName,
Doc: goanalysis.TheOnlyanalyzerDoc,
}
return goanalysis.NewLinter(
reviveName,
"Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint.",
[]*analysis.Analyzer{analyzer},
nil,
).WithContextSetter(func(lintCtx *linter.Context) {
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
var files []string
for _, file := range pass.Files {
files = append(files, pass.Fset.PositionFor(file.Pos(), false).Filename)
}
conf, err := setReviveConfig(cfg)
if err != nil {
return nil, err
}
formatter, err := reviveConfig.GetFormatter("json")
if err != nil {
return nil, err
}
revive := lint.New(ioutil.ReadFile)
lintingRules, err := reviveConfig.GetLintingRules(conf)
if err != nil {
return nil, err
}
packages, err := dots.ResolvePackages(files, []string{})
if err != nil {
return nil, err
}
failures, err := revive.Lint(packages, lintingRules, *conf)
if err != nil {
return nil, err
}
formatChan := make(chan lint.Failure)
exitChan := make(chan bool)
var output string
go func() {
output, err = formatter.Format(formatChan, *conf)
if err != nil {
lintCtx.Log.Errorf("Format error: %v", err)
}
exitChan <- true
}()
for f := range failures {
if f.Confidence < conf.Confidence {
continue
}
formatChan <- f
}
close(formatChan)
<-exitChan
var results []jsonObject
err = json.Unmarshal([]byte(output), &results)
if err != nil {
return nil, err
}
for i := range results {
issues = append(issues, goanalysis.NewIssue(&result.Issue{
Severity: string(results[i].Severity),
Text: fmt.Sprintf("%q", results[i].Failure.Failure),
Pos: token.Position{
Filename: results[i].Position.Start.Filename,
Line: results[i].Position.Start.Line,
Offset: results[i].Position.Start.Offset,
Column: results[i].Position.Start.Column,
},
LineRange: &result.Range{
From: results[i].Position.Start.Line,
To: results[i].Position.End.Line,
},
FromLinter: reviveName,
}, pass))
}
return nil, nil
}
}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
return issues
}).WithLoadMode(goanalysis.LoadModeSyntax)
}
func setReviveConfig(cfg *config.ReviveSettings) (*lint.Config, error) {
// Get revive default configuration
conf, err := reviveConfig.GetConfig("")
if err != nil {
return nil, err
}
// Default is false
conf.IgnoreGeneratedHeader = cfg.IgnoreGeneratedHeader
if cfg.Severity != "" {
conf.Severity = lint.Severity(cfg.Severity)
}
if cfg.Confidence != 0 {
conf.Confidence = cfg.Confidence
}
// By default golangci-lint ignores missing doc comments, follow same convention by removing this default rule
// Relevant issue: https://github.com/golangci/golangci-lint/issues/456
delete(conf.Rules, "exported")
if len(cfg.Rules) != 0 {
// Clear default rules, only use rules defined in config
conf.Rules = make(map[string]lint.RuleConfig, len(cfg.Rules))
}
for _, r := range cfg.Rules {
conf.Rules[r.Name] = lint.RuleConfig{Arguments: r.Arguments, Severity: lint.Severity(r.Severity)}
}
conf.ErrorCode = cfg.ErrorCode
conf.WarningCode = cfg.WarningCode
if len(cfg.Directives) != 0 {
// Clear default Directives, only use Directives defined in config
conf.Directives = make(map[string]lint.DirectiveConfig, len(cfg.Directives))
}
for _, d := range cfg.Directives {
conf.Directives[d.Name] = lint.DirectiveConfig{Severity: lint.Severity(d.Severity)}
}
return conf, nil
}