Skip to content

dev: simplify revive implementation #5552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 35 additions & 62 deletions pkg/golinters/revive/revive.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package revive
import (
"bytes"
"cmp"
"encoding/json"
"fmt"
"go/token"
"os"
Expand Down Expand Up @@ -34,12 +33,6 @@ var (
isDebug = logutils.HaveDebugTag(logutils.DebugKeyRevive)
)

// jsonObject defines a JSON object of a failure
type jsonObject struct {
Severity lint.Severity
lint.Failure `json:",inline"`
}

func New(settings *config.ReviveSettings) *goanalysis.Linter {
var mu sync.Mutex
var resIssues []goanalysis.Issue
Expand All @@ -63,7 +56,7 @@ func New(settings *config.ReviveSettings) *goanalysis.Linter {
}

analyzer.Run = func(pass *analysis.Pass) (any, error) {
issues, err := w.run(lintCtx, pass)
issues, err := w.run(pass)
if err != nil {
return nil, err
}
Expand All @@ -85,7 +78,6 @@ func New(settings *config.ReviveSettings) *goanalysis.Linter {

type wrapper struct {
revive lint.Linter
formatter lint.Formatter
lintingRules []lint.Rule
conf *lint.Config
}
Expand All @@ -103,101 +95,70 @@ func newWrapper(settings *config.ReviveSettings) (*wrapper, error) {
return nil, err
}

formatter, err := reviveConfig.GetFormatter("json")
if err != nil {
return nil, err
}

lintingRules, err := reviveConfig.GetLintingRules(conf, []lint.Rule{})
if err != nil {
return nil, err
}

return &wrapper{
revive: lint.New(os.ReadFile, settings.MaxOpenFiles),
formatter: formatter,
lintingRules: lintingRules,
conf: conf,
}, nil
}

func (w *wrapper) run(lintCtx *linter.Context, pass *analysis.Pass) ([]goanalysis.Issue, error) {
func (w *wrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) {
packages := [][]string{internal.GetGoFileNames(pass)}

failures, err := w.revive.Lint(packages, w.lintingRules, *w.conf)
if err != nil {
return nil, err
}

formatChan := make(chan lint.Failure)
exitChan := make(chan bool)

var output string
go func() {
output, err = w.formatter.Format(formatChan, *w.conf)
if err != nil {
lintCtx.Log.Errorf("Format error: %v", err)
}
exitChan <- true
}()

for f := range failures {
if f.Confidence < w.conf.Confidence {
var issues []goanalysis.Issue
for failure := range failures {
if failure.Confidence < w.conf.Confidence {
continue
}

formatChan <- f
}

close(formatChan)
<-exitChan

var results []jsonObject
err = json.Unmarshal([]byte(output), &results)
if err != nil {
return nil, err
}

var issues []goanalysis.Issue
for i := range results {
issues = append(issues, toIssue(pass, &results[i]))
issues = append(issues, w.toIssue(pass, &failure))
}

return issues, nil
}

func toIssue(pass *analysis.Pass, object *jsonObject) goanalysis.Issue {
lineRangeTo := object.Position.End.Line
if object.RuleName == (&rule.ExportedRule{}).Name() {
lineRangeTo = object.Position.Start.Line
func (w *wrapper) toIssue(pass *analysis.Pass, failure *lint.Failure) goanalysis.Issue {
lineRangeTo := failure.Position.End.Line
if failure.RuleName == (&rule.ExportedRule{}).Name() {
lineRangeTo = failure.Position.Start.Line
}

issue := &result.Issue{
Severity: string(object.Severity),
Text: fmt.Sprintf("%s: %s", object.RuleName, object.Failure.Failure),
Severity: string(severity(w.conf, failure)),
Text: fmt.Sprintf("%s: %s", failure.RuleName, failure.Failure),
Pos: token.Position{
Filename: object.Position.Start.Filename,
Line: object.Position.Start.Line,
Offset: object.Position.Start.Offset,
Column: object.Position.Start.Column,
Filename: failure.Position.Start.Filename,
Line: failure.Position.Start.Line,
Offset: failure.Position.Start.Offset,
Column: failure.Position.Start.Column,
},
LineRange: &result.Range{
From: object.Position.Start.Line,
From: failure.Position.Start.Line,
To: lineRangeTo,
},
FromLinter: linterName,
}

if object.ReplacementLine != "" {
f := pass.Fset.File(token.Pos(object.Position.Start.Offset))
if failure.ReplacementLine != "" {
f := pass.Fset.File(token.Pos(failure.Position.Start.Offset))

// Skip cgo files because the positions are wrong.
if object.GetFilename() == f.Name() {
if failure.GetFilename() == f.Name() {
issue.SuggestedFixes = []analysis.SuggestedFix{{
TextEdits: []analysis.TextEdit{{
Pos: f.LineStart(object.Position.Start.Line),
End: goanalysis.EndOfLinePos(f, object.Position.End.Line),
NewText: []byte(object.ReplacementLine),
Pos: f.LineStart(failure.Position.Start.Line),
End: goanalysis.EndOfLinePos(f, failure.Position.End.Line),
NewText: []byte(failure.ReplacementLine),
}},
}}
}
Expand Down Expand Up @@ -489,3 +450,15 @@ func extractRulesName(rules []lint.Rule) []string {

return names
}

// Extracted from https://github.com/mgechev/revive/blob/v1.7.0/formatter/severity.go
// Modified to use pointers (related to hugeParam rule).
func severity(config *lint.Config, failure *lint.Failure) lint.Severity {
if cfg, ok := config.Rules[failure.RuleName]; ok && cfg.Severity == lint.SeverityError {
return lint.SeverityError
}
if cfg, ok := config.Directives[failure.RuleName]; ok && cfg.Severity == lint.SeverityError {
return lint.SeverityError
}
return lint.SeverityWarning
}
Loading