Skip to content
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

dev: clean context loader #4480

Merged
merged 7 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
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
7 changes: 3 additions & 4 deletions pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,19 +363,18 @@ func (c *runCommand) runAnalysis(ctx context.Context, args []string) ([]result.I
c.reportData.AddLinter(lc.Name(), isEnabled, lc.EnabledByDefault)
}

lintCtx, err := c.contextLoader.Load(ctx, lintersToRun)
lintCtx, err := c.contextLoader.Load(ctx, c.log.Child(logutils.DebugKeyLintersContext), lintersToRun)
if err != nil {
return nil, fmt.Errorf("context loading failed: %w", err)
}
lintCtx.Log = c.log.Child(logutils.DebugKeyLintersContext)

runner, err := lint.NewRunner(c.log.Child(logutils.DebugKeyRunner),
c.cfg, c.goenv, c.lineCache, c.fileCache, c.dbManager, lintCtx.Packages)
c.cfg, c.goenv, c.lineCache, c.fileCache, c.dbManager, lintCtx)
if err != nil {
return nil, err
}

return runner.Run(ctx, lintersToRun, lintCtx)
return runner.Run(ctx, lintersToRun)
}

func (c *runCommand) setOutputToDevNull() (savedStdout, savedStderr *os.File) {
Expand Down
69 changes: 31 additions & 38 deletions pkg/lint/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,37 @@ func NewContextLoader(cfg *config.Config, log logutils.Log, goenv *goutil.Env,
}
}

func (cl *ContextLoader) Load(ctx context.Context, log logutils.Log, linters []*linter.Config) (*linter.Context, error) {
loadMode := cl.findLoadMode(linters)
pkgs, err := cl.loadPackages(ctx, loadMode)
if err != nil {
return nil, fmt.Errorf("failed to load packages: %w", err)
}

deduplicatedPkgs := cl.filterDuplicatePackages(pkgs)

if len(deduplicatedPkgs) == 0 {
return nil, exitcodes.ErrNoGoFiles
}

ret := &linter.Context{
Packages: deduplicatedPkgs,

// At least `unused` linters works properly only on original (not deduplicated) packages,
// see https://github.com/golangci/golangci-lint/pull/585.
OriginalPackages: pkgs,

Cfg: cl.cfg,
Log: log,
FileCache: cl.fileCache,
LineCache: cl.lineCache,
PkgCache: cl.pkgCache,
LoadGuard: cl.loadGuard,
}

return ret, nil
}

func (cl *ContextLoader) prepareBuildContext() {
// Set GOROOT to have working cross-compilation: cross-compiled binaries
// have invalid GOROOT. XXX: can't use runtime.GOROOT().
Expand Down Expand Up @@ -213,13 +244,6 @@ func (cl *ContextLoader) loadPackages(ctx context.Context, loadMode packages.Loa
return nil, fmt.Errorf("failed to load with go/packages: %w", err)
}

// Currently, go/packages doesn't guarantee that error will be returned
// if context was canceled. See
// https://github.com/golang/tools/commit/c5cec6710e927457c3c29d6c156415e8539a5111#r39261855
if ctx.Err() != nil {
return nil, fmt.Errorf("timed out to load packages: %w", ctx.Err())
}

if loadMode&packages.NeedSyntax == 0 {
// Needed e.g. for go/analysis loading.
fset := token.NewFileSet()
Expand Down Expand Up @@ -293,34 +317,3 @@ func (cl *ContextLoader) filterDuplicatePackages(pkgs []*packages.Package) []*pa

return retPkgs
}

func (cl *ContextLoader) Load(ctx context.Context, linters []*linter.Config) (*linter.Context, error) {
loadMode := cl.findLoadMode(linters)
pkgs, err := cl.loadPackages(ctx, loadMode)
if err != nil {
return nil, fmt.Errorf("failed to load packages: %w", err)
}

deduplicatedPkgs := cl.filterDuplicatePackages(pkgs)

if len(deduplicatedPkgs) == 0 {
return nil, exitcodes.ErrNoGoFiles
}

ret := &linter.Context{
Packages: deduplicatedPkgs,

// At least `unused` linters works properly only on original (not deduplicated) packages,
// see https://github.com/golangci/golangci-lint/pull/585.
OriginalPackages: pkgs,

Cfg: cl.cfg,
Log: cl.log,
FileCache: cl.fileCache,
LineCache: cl.lineCache,
PkgCache: cl.pkgCache,
LoadGuard: cl.loadGuard,
}

return ret, nil
}
81 changes: 41 additions & 40 deletions pkg/lint/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"runtime/debug"
"strings"

gopackages "golang.org/x/tools/go/packages"

"github.com/golangci/golangci-lint/internal/errorutil"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/fsutils"
Expand All @@ -22,14 +20,21 @@ import (
"github.com/golangci/golangci-lint/pkg/timeutils"
)

type processorStat struct {
inCount int
outCount int
}

type Runner struct {
Log logutils.Log

lintCtx *linter.Context
Processors []processors.Processor
Log logutils.Log
}

func NewRunner(log logutils.Log, cfg *config.Config, goenv *goutil.Env,
lineCache *fsutils.LineCache, fileCache *fsutils.FileCache,
dbManager *lintersdb.Manager, pkgs []*gopackages.Package) (*Runner, error) {
dbManager *lintersdb.Manager, lintCtx *linter.Context) (*Runner, error) {
// Beware that some processors need to add the path prefix when working with paths
// because they get invoked before the path prefixer (exclude and severity rules)
// or process other paths (skip files).
Expand Down Expand Up @@ -75,7 +80,7 @@ func NewRunner(log logutils.Log, cfg *config.Config, goenv *goutil.Env,
processors.NewCgo(goenv),

// Must go after Cgo.
processors.NewFilenameUnadjuster(pkgs, log.Child(logutils.DebugKeyFilenameUnadjuster)),
processors.NewFilenameUnadjuster(lintCtx.Packages, log.Child(logutils.DebugKeyFilenameUnadjuster)),

// Must be before diff, nolint and exclude autogenerated processor at least.
processors.NewPathPrettifier(),
Expand Down Expand Up @@ -107,10 +112,38 @@ func NewRunner(log logutils.Log, cfg *config.Config, goenv *goutil.Env,
processors.NewPathPrefixer(cfg.Output.PathPrefix),
processors.NewSortResults(cfg),
},
Log: log,
lintCtx: lintCtx,
Log: log,
}, nil
}

func (r *Runner) Run(ctx context.Context, linters []*linter.Config) ([]result.Issue, error) {
sw := timeutils.NewStopwatch("linters", r.Log)
defer sw.Print()

var (
lintErrors error
issues []result.Issue
)

for _, lc := range linters {
lc := lc
sw.TrackStage(lc.Name(), func() {
linterIssues, err := r.runLinterSafe(ctx, r.lintCtx, lc)
if err != nil {
lintErrors = errors.Join(lintErrors, fmt.Errorf("can't run linter %s", lc.Linter.Name()), err)
r.Log.Warnf("Can't run linter %s: %v", lc.Linter.Name(), err)

return
}

issues = append(issues, linterIssues...)
})
}

return r.processLintResults(issues), lintErrors
}

func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
lc *linter.Config) (ret []result.Issue, err error) {
defer func() {
Expand Down Expand Up @@ -151,12 +184,7 @@ func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
return issues, nil
}

type processorStat struct {
inCount int
outCount int
}

func (r Runner) processLintResults(inIssues []result.Issue) []result.Issue {
func (r *Runner) processLintResults(inIssues []result.Issue) []result.Issue {
sw := timeutils.NewStopwatch("processing", r.Log)

var issuesBefore, issuesAfter int
Expand Down Expand Up @@ -187,7 +215,7 @@ func (r Runner) processLintResults(inIssues []result.Issue) []result.Issue {
return outIssues
}

func (r Runner) printPerProcessorStat(stat map[string]processorStat) {
func (r *Runner) printPerProcessorStat(stat map[string]processorStat) {
parts := make([]string, 0, len(stat))
for name, ps := range stat {
if ps.inCount != 0 {
Expand All @@ -199,33 +227,6 @@ func (r Runner) printPerProcessorStat(stat map[string]processorStat) {
}
}

func (r Runner) Run(ctx context.Context, linters []*linter.Config, lintCtx *linter.Context) ([]result.Issue, error) {
sw := timeutils.NewStopwatch("linters", r.Log)
defer sw.Print()

var (
lintErrors error
issues []result.Issue
)

for _, lc := range linters {
lc := lc
sw.TrackStage(lc.Name(), func() {
linterIssues, err := r.runLinterSafe(ctx, lintCtx, lc)
if err != nil {
lintErrors = errors.Join(lintErrors, fmt.Errorf("can't run linter %s", lc.Linter.Name()), err)
r.Log.Warnf("Can't run linter %s: %v", lc.Linter.Name(), err)

return
}

issues = append(issues, linterIssues...)
})
}

return r.processLintResults(issues), lintErrors
}

func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch, statPerProcessor map[string]processorStat) []result.Issue {
for _, p := range r.Processors {
var newIssues []result.Issue
Expand Down
Loading