Isaev Denis 95ec0cf21e
dramatically reduce memory usage ()
Run all linters per package. It allows unloading package data when it's
processed. It dramatically reduces memory (and CPU because of GC) usage.

Relates: 
2019-09-30 16:19:41 +03:00

122 lines
2.6 KiB
Go

package golinters
import (
"fmt"
"go/ast"
"go/token"
"sync"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)
type nakedretVisitor struct {
maxLength int
f *token.FileSet
issues []result.Issue
}
func (v *nakedretVisitor) processFuncDecl(funcDecl *ast.FuncDecl) {
file := v.f.File(funcDecl.Pos())
functionLineLength := file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line
// Scan the body for usage of the named returns
for _, stmt := range funcDecl.Body.List {
s, ok := stmt.(*ast.ReturnStmt)
if !ok {
continue
}
if len(s.Results) != 0 {
continue
}
file := v.f.File(s.Pos())
if file == nil || functionLineLength <= v.maxLength {
continue
}
if funcDecl.Name == nil {
continue
}
v.issues = append(v.issues, result.Issue{
FromLinter: nakedretName,
Text: fmt.Sprintf("naked return in func `%s` with %d lines of code",
funcDecl.Name.Name, functionLineLength),
Pos: v.f.Position(s.Pos()),
})
}
}
func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor {
funcDecl, ok := node.(*ast.FuncDecl)
if !ok {
return v
}
var namedReturns []*ast.Ident
// We've found a function
if funcDecl.Type != nil && funcDecl.Type.Results != nil {
for _, field := range funcDecl.Type.Results.List {
for _, ident := range field.Names {
if ident != nil {
namedReturns = append(namedReturns, ident)
}
}
}
}
if len(namedReturns) == 0 || funcDecl.Body == nil {
return v
}
v.processFuncDecl(funcDecl)
return v
}
const nakedretName = "nakedret"
func NewNakedret() *goanalysis.Linter {
var mu sync.Mutex
var resIssues []result.Issue
analyzer := &analysis.Analyzer{
Name: goanalysis.TheOnlyAnalyzerName,
Doc: goanalysis.TheOnlyanalyzerDoc,
}
return goanalysis.NewLinter(
nakedretName,
"Finds naked returns in functions greater than a specified function length",
[]*analysis.Analyzer{analyzer},
nil,
).WithContextSetter(func(lintCtx *linter.Context) {
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
var res []result.Issue
for _, file := range pass.Files {
v := nakedretVisitor{
maxLength: lintCtx.Settings().Nakedret.MaxFuncLines,
f: pass.Fset,
}
ast.Walk(&v, file)
res = append(res, v.issues...)
}
if len(res) == 0 {
return nil, nil
}
mu.Lock()
resIssues = append(resIssues, res...)
mu.Unlock()
return nil, nil
}
}).WithIssuesReporter(func(*linter.Context) []result.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeSyntax)
}