Skip to content

Commit

Permalink
dev: new processor order (#5322)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored Jan 15, 2025
1 parent 6d29861 commit 362aea5
Show file tree
Hide file tree
Showing 25 changed files with 130 additions and 44 deletions.
8 changes: 4 additions & 4 deletions pkg/fsutils/fsutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ func Getwd() (string, error) {
return
}

evaledWd, err := EvalSymlinks(cachedWd)
evaluatedWd, err := EvalSymlinks(cachedWd)
if err != nil {
cachedWd, cachedWdError = "", fmt.Errorf("can't eval symlinks on wd %s: %w", cachedWd, err)
return
}

cachedWd = evaledWd
cachedWd = evaluatedWd
})

return cachedWd, cachedWdError
Expand Down Expand Up @@ -83,8 +83,8 @@ func ShortestRelPath(path, wd string) (string, error) {
path = evaledPath

// make path absolute and then relative to be able to fix this case:
// we are in /test dir, we want to normalize ../test, and have file file.go in this dir;
// it must have normalized path file.go, not ../test/file.go,
// we are in `/test` dir, we want to normalize `../test`, and have file `file.go` in this dir;
// it must have normalized path `file.go`, not `../test/file.go`,
var absPath string
if filepath.IsAbs(path) {
absPath = path
Expand Down
42 changes: 42 additions & 0 deletions pkg/fsutils/fsutils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package fsutils

import (
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestShortestRelPath(t *testing.T) {
testCases := []struct {
desc string
path string
wd string
expected string
}{
{
desc: "based on parent path",
path: "fsutils_test.go",
wd: filepath.Join("..", "fsutils"),
expected: "fsutils_test.go",
},
{
desc: "based on current working directory",
path: "fsutils_test.go",
wd: "",
expected: "fsutils_test.go",
},
}

for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()

rel, err := ShortestRelPath("fsutils_test.go", filepath.Join("..", "fsutils"))
require.NoError(t, err)

assert.Equal(t, test.expected, rel)
})
}
}
19 changes: 11 additions & 8 deletions pkg/lint/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti
// Must be after FilenameUnadjuster.
processors.NewInvalidIssue(log.Child(logutils.DebugKeyInvalidIssue)),

// Must be before diff, nolint and exclude autogenerated processor at least.
// Must be before Diff, SkipFiles, SkipDirs, ExcludeRules processors at least.
processors.NewPathPrettifier(log),

// must be after PathPrettifier.
skipFilesProcessor,
skipDirsProcessor, // must be after path prettifier
skipDirsProcessor,

processors.NewAutogeneratedExclude(cfg.Issues.ExcludeGenerated),

Expand All @@ -94,20 +96,21 @@ func NewRunner(log logutils.Log, cfg *config.Config, args []string, goenv *gouti

processors.NewNolint(log.Child(logutils.DebugKeyNolint), dbManager, enabledLinters),

processors.NewUniqByLine(cfg),
processors.NewDiff(&cfg.Issues),

// The fixer still needs to see paths for the issues that are relative to the current directory.
processors.NewFixer(cfg, log, fileCache, metaFormatter),

// Must be after the Fixer.
processors.NewUniqByLine(cfg),
processors.NewMaxPerFileFromLinter(cfg),
processors.NewMaxSameIssues(cfg.Issues.MaxSameIssues, log.Child(logutils.DebugKeyMaxSameIssues), cfg),
processors.NewMaxFromLinter(cfg.Issues.MaxIssuesPerLinter, log.Child(logutils.DebugKeyMaxFromLinter), cfg),

// Now we can modify the issues for output.
processors.NewSourceCode(lineCache, log.Child(logutils.DebugKeySourceCode)),
processors.NewPathShortener(),
processors.NewSeverity(log.Child(logutils.DebugKeySeverityRules), files, &cfg.Severity),

// The fixer still needs to see paths for the issues that are relative to the current directory.
processors.NewFixer(cfg, log, fileCache, metaFormatter),

// Now we can modify the issues for output.
processors.NewPathPrefixer(cfg.Output.PathPrefix),
processors.NewSortResults(cfg),
},
Expand Down
4 changes: 4 additions & 0 deletions pkg/result/processors/autogenerated_exclude.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ type fileSummary struct {
generated bool
}

// AutogeneratedExclude filters generated files.
// - mode "lax": see `isGeneratedFileLax` documentation.
// - mode "strict": see `isGeneratedFileStrict` documentation.
// - mode "disable": skips this processor.
type AutogeneratedExclude struct {
debugf logutils.DebugFunc

Expand Down
7 changes: 4 additions & 3 deletions pkg/result/processors/cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (

var _ Processor = (*Cgo)(nil)

// Cgo some linters (e.g. gosec, deadcode) return incorrect filepaths for cgo issues,
// also cgo files have strange issues looking like false positives.
// Cgo filters cgo artifacts.
//
// Require absolute filepath.
// Some linters (e.g. gosec, etc.) return incorrect file paths for cgo files.
//
// Require absolute file path.
type Cgo struct {
goCacheDir string
}
Expand Down
6 changes: 6 additions & 0 deletions pkg/result/processors/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const envGolangciDiffProcessorPatch = "GOLANGCI_DIFF_PROCESSOR_PATCH"

var _ Processor = (*Diff)(nil)

// Diff filters issues based on options `new`, `new-from-rev`, etc.
//
// Uses `git`.
// The paths inside the patch are relative to the path where git is run (the same location where golangci-lint is run).
//
// Warning: it doesn't use `path-prefix` option.
type Diff struct {
onlyNew bool
fromRev string
Expand Down
1 change: 1 addition & 0 deletions pkg/result/processors/exclude.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

var _ Processor = (*Exclude)(nil)

// Exclude filters reports only based on regular expressions applied to the report text.
type Exclude struct {
name string

Expand Down
7 changes: 7 additions & 0 deletions pkg/result/processors/exclude_rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ type excludeRule struct {
baseRule
}

// ExcludeRules filters reports based on multiple criteria:
// - linter names (string)
// - file path (regular expressions)
// - text (regular expressions)
// - code source (regular expressions)
//
// It uses the shortest relative paths and `path-prefix` option.
type ExcludeRules struct {
name string

Expand Down
8 changes: 5 additions & 3 deletions pkg/result/processors/filename_unadjuster.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ type adjustMap struct {
m map[string]posMapper
}

// FilenameUnadjuster is needed because a lot of linters use `fset.Position(f.Pos())` to get filename.
// And they return adjusted filename (e.g.` *.qtpl`) for an issue.
// FilenameUnadjuster fixes filename based on adjusted and unadjusted position (related to line directives and cgo).
//
// A lot of linters use `fset.Position(f.Pos())` to get filename,
// and they return adjusted filename (e.g.` *.qtpl`) for an issue.
// We need restore real `.go` filename to properly output it, parse it, etc.
//
// Require absolute filepath.
// Require absolute file path.
type FilenameUnadjuster struct {
m map[string]posMapper // map from adjusted filename to position mapper: adjusted -> unadjusted position
log logutils.Log
Expand Down
2 changes: 2 additions & 0 deletions pkg/result/processors/fixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ var _ Processor = (*Fixer)(nil)

const filePerm = 0644

// Fixer fixes reports if possible.
// The reports that are not fixed are passed to the next processor.
type Fixer struct {
cfg *config.Config
log logutils.Log
Expand Down
3 changes: 3 additions & 0 deletions pkg/result/processors/identifier_marker.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type replacePattern struct {
repl string
}

// IdentifierMarker modifies report text.
// It must be before [Exclude] and [ExcludeRules]:
// users configure exclusions based on the modified text.
type IdentifierMarker struct {
patterns map[string][]replacePattern
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/result/processors/invalid_issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (

var _ Processor = (*InvalidIssue)(nil)

// InvalidIssue filters invalid reports.
// - non-go files (except `go.mod`)
// - reports without file path
type InvalidIssue struct {
log logutils.Log
}
Expand Down
6 changes: 1 addition & 5 deletions pkg/result/processors/max_from_linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

var _ Processor = (*MaxFromLinter)(nil)

// MaxFromLinter limits the number of reports from the same linter.
type MaxFromLinter struct {
linterCounter map[string]int
limit int
Expand All @@ -34,11 +35,6 @@ func (p *MaxFromLinter) Process(issues []result.Issue) ([]result.Issue, error) {
}

return filterIssuesUnsafe(issues, func(issue *result.Issue) bool {
if issue.SuggestedFixes != nil && p.cfg.Issues.NeedFix {
// we need to fix all issues at once => we need to return all of them
return true
}

p.linterCounter[issue.FromLinter]++ // always inc for stat

return p.linterCounter[issue.FromLinter] <= p.limit
Expand Down
1 change: 1 addition & 0 deletions pkg/result/processors/max_per_file_from_linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

var _ Processor = (*MaxPerFileFromLinter)(nil)

// MaxPerFileFromLinter limits the number of reports by file and by linter.
type MaxPerFileFromLinter struct {
fileLinterCounter fileLinterCounter
maxPerFileFromLinterConfig map[string]int
Expand Down
7 changes: 2 additions & 5 deletions pkg/result/processors/max_same_issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

var _ Processor = (*MaxSameIssues)(nil)

// MaxSameIssues limits the number of reports with the same text.
type MaxSameIssues struct {
textCounter map[string]int
limit int
Expand All @@ -36,12 +37,8 @@ func (p *MaxSameIssues) Process(issues []result.Issue) ([]result.Issue, error) {
}

return filterIssuesUnsafe(issues, func(issue *result.Issue) bool {
if issue.SuggestedFixes != nil && p.cfg.Issues.NeedFix {
// we need to fix all issues at once => we need to return all of them
return true
}

p.textCounter[issue.Text]++ // always inc for stat

return p.textCounter[issue.Text] <= p.limit
}), nil
}
Expand Down
1 change: 1 addition & 0 deletions pkg/result/processors/nolint.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type fileData struct {
ignoredRanges []ignoredRange
}

// Nolint filters and sorts reports related to `nolint` directives.
type Nolint struct {
fileCache map[string]*fileData
dbManager *lintersdb.Manager
Expand Down
14 changes: 9 additions & 5 deletions pkg/result/processors/path_prefixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (

var _ Processor = (*PathPrefixer)(nil)

// PathPrefixer adds a customizable prefix to every output path
// PathPrefixer adds a customizable path prefix to report file paths for user facing.
// It uses the shortest relative paths and `path-prefix` option.
type PathPrefixer struct {
prefix string
}
Expand All @@ -24,11 +25,14 @@ func (*PathPrefixer) Name() string {

// Process adds the prefix to each path
func (p *PathPrefixer) Process(issues []result.Issue) ([]result.Issue, error) {
if p.prefix != "" {
for i := range issues {
issues[i].Pos.Filename = fsutils.WithPathPrefix(p.prefix, issues[i].Pos.Filename)
}
if p.prefix == "" {
return issues, nil
}

for i := range issues {
issues[i].Pos.Filename = fsutils.WithPathPrefix(p.prefix, issues[i].Pos.Filename)
}

return issues, nil
}

Expand Down
1 change: 1 addition & 0 deletions pkg/result/processors/path_prettifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

var _ Processor = (*PathPrettifier)(nil)

// PathPrettifier modifies report file path with the shortest relative path.
type PathPrettifier struct {
log logutils.Log
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/result/processors/path_shortener.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (

var _ Processor = (*PathShortener)(nil)

// PathShortener modifies text of the reports to reduce file path inside the text.
// It uses the rooted path name corresponding to the current directory (`wd`).
type PathShortener struct {
wd string
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/result/processors/severity.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type severityRule struct {
severity string
}

// Severity modifies report severity.
// It uses the same `baseRule` structure as [ExcludeRules] processor.
//
// Warning: it doesn't use `path-prefix` option.
type Severity struct {
name string

Expand Down
2 changes: 2 additions & 0 deletions pkg/result/processors/skip_dirs.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ var StdExcludeDirRegexps = []string{
normalizePathRegex("builtin"),
}

// SkipDirs filters reports based on directory names.
// It uses the shortest relative paths and `path-prefix` option.
type skipStat struct {
pattern string
count int
Expand Down
3 changes: 3 additions & 0 deletions pkg/result/processors/skip_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (

var _ Processor = (*SkipFiles)(nil)

// SkipFiles filters reports based on filename.
//
// It uses the shortest relative paths and `path-prefix` option.
type SkipFiles struct {
patterns []*regexp.Regexp
pathPrefix string
Expand Down
9 changes: 4 additions & 5 deletions pkg/result/processors/sort_results.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import (
"github.com/golangci/golangci-lint/pkg/result"
)

// Base propose of this functionality to sort results (issues)
// produced by various linters by analyzing code. We're achieving this
// by sorting results.Issues using processor step, and chain based
// rules that can compare different properties of the Issues struct.

const (
orderNameFile = "file"
orderNameLinter = "linter"
Expand All @@ -31,6 +26,10 @@ var _ Processor = (*SortResults)(nil)

type issueComparator func(a, b *result.Issue) int

// SortResults sorts reports based on criteria:
// - file names, line numbers, positions
// - linter names
// - severity names
type SortResults struct {
cmps map[string][]issueComparator

Expand Down
7 changes: 7 additions & 0 deletions pkg/result/processors/source_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import (

var _ Processor = (*SourceCode)(nil)

// SourceCode modifies displayed information based on [result.Issue.GetLineRange()].
//
// This is used:
// - to display the "UnderLinePointer".
// - in some rare cases to display multiple lines instead of one (ex: `dupl`)
//
// It requires to use [fsutils.LineCache] ([fsutils.FileCache]) to get the file information before the fixes.
type SourceCode struct {
lineCache *fsutils.LineCache
log logutils.Log
Expand Down
Loading

0 comments on commit 362aea5

Please sign in to comment.