Speedup program loading on 20%.
Don't typecheck func bodies for non-local packages. Works only if megacheck and interfacer are disabled: they require all func bodies to build SSA repr. Export GL_DEBUG=load to get logs for this feature.
This commit is contained in:
parent
6480aa8b19
commit
a1a9215fcc
@ -14,6 +14,7 @@ import (
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/lint"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
|
||||
"github.com/golangci/golangci-lint/pkg/logutils"
|
||||
"github.com/golangci/golangci-lint/pkg/printers"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
||||
@ -231,12 +232,14 @@ func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
|
||||
}
|
||||
|
||||
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
|
||||
if !logutils.HaveDebugTag("linters_output") {
|
||||
// Don't allow linters and loader to print anything
|
||||
log.SetOutput(ioutil.Discard)
|
||||
savedStdout, savedStderr := setOutputToDevNull()
|
||||
defer func() {
|
||||
os.Stdout, os.Stderr = savedStdout, savedStderr
|
||||
}()
|
||||
}
|
||||
|
||||
issues, err := e.runAnalysis(ctx, args)
|
||||
if err != nil {
|
||||
|
@ -75,7 +75,6 @@ func (g Gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
||||
for _, hunk := range d.Hunks {
|
||||
deletedLine, addedLine, err := getFirstDeletedAndAddedLineNumberInHunk(hunk)
|
||||
if err != nil {
|
||||
logrus.Infof("Can't get first deleted line number for hunk: %s", err)
|
||||
if addedLine > 1 {
|
||||
deletedLine = addedLine - 1 // use previous line, TODO: use both prev and next lines
|
||||
} else {
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/logutils"
|
||||
|
||||
"github.com/golangci/go-tools/ssa"
|
||||
"github.com/golangci/go-tools/ssa/ssautil"
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
@ -21,6 +23,8 @@ import (
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
var loadDebugf = logutils.Debug("load")
|
||||
|
||||
func isFullImportNeeded(linters []linter.Config) bool {
|
||||
for _, linter := range linters {
|
||||
if linter.NeedsProgramLoading() {
|
||||
@ -64,6 +68,89 @@ func normalizePaths(paths []string) ([]string, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getCurrentProjectImportPath() (string, error) {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath == "" {
|
||||
return "", fmt.Errorf("no GOPATH env variable")
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("can't get workind directory: %s", err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(wd, gopath) {
|
||||
return "", fmt.Errorf("currently no in gopath: %q isn't a prefix of %q", gopath, wd)
|
||||
}
|
||||
|
||||
path := strings.TrimPrefix(wd, gopath)
|
||||
path = strings.TrimPrefix(path, string(os.PathSeparator)) // if GOPATH contains separator at the end
|
||||
src := "src" + string(os.PathSeparator)
|
||||
if !strings.HasPrefix(path, src) {
|
||||
return "", fmt.Errorf("currently no in gopath/src: %q isn't a prefix of %q", src, path)
|
||||
}
|
||||
|
||||
path = strings.TrimPrefix(path, src)
|
||||
path = strings.Replace(path, string(os.PathSeparator), "/", -1)
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func isLocalProjectAnalysis(args []string) bool {
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "..") || filepath.IsAbs(arg) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func getTypeCheckFuncBodies(cfg *config.Run, linters []linter.Config, pkgProg *packages.Program) func(string) bool {
|
||||
if !isLocalProjectAnalysis(cfg.Args) {
|
||||
loadDebugf("analysis in nonlocal, don't optimize loading by not typechecking func bodies")
|
||||
return nil
|
||||
}
|
||||
|
||||
if isSSAReprNeeded(linters) {
|
||||
loadDebugf("ssa repr is needed, don't optimize loading by not typechecking func bodies")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(pkgProg.Dirs()) == 0 {
|
||||
// files run, in this mode packages are fake: can't check their path properly
|
||||
return nil
|
||||
}
|
||||
|
||||
projPath, err := getCurrentProjectImportPath()
|
||||
if err != nil {
|
||||
logrus.Infof("can't get cur project path: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return func(path string) bool {
|
||||
if strings.HasPrefix(path, ".") {
|
||||
loadDebugf("%s: dot import: typecheck func bodies", path)
|
||||
return true
|
||||
}
|
||||
|
||||
isLocalPath := strings.HasPrefix(path, projPath)
|
||||
if isLocalPath {
|
||||
localPath := strings.TrimPrefix(path, projPath)
|
||||
localPath = strings.TrimPrefix(localPath, "/")
|
||||
if strings.HasPrefix(localPath, "vendor/") {
|
||||
loadDebugf("%s: local vendor import: DO NOT typecheck func bodies", path)
|
||||
return false
|
||||
}
|
||||
|
||||
loadDebugf("%s: local import: typecheck func bodies", path)
|
||||
return true
|
||||
}
|
||||
|
||||
loadDebugf("%s: not local import: DO NOT typecheck func bodies", path)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *config.Run, pkgProg *packages.Program) (*loader.Program, *loader.Config, error) {
|
||||
if !isFullImportNeeded(linters) {
|
||||
return nil, nil, nil
|
||||
@ -79,6 +166,7 @@ func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *con
|
||||
Build: bctx,
|
||||
AllowErrors: true, // Try to analyze partially
|
||||
ParserMode: parser.ParseComments, // AST will be reused by linters
|
||||
TypeCheckFuncBodies: getTypeCheckFuncBodies(cfg, linters, pkgProg),
|
||||
}
|
||||
|
||||
var loaderArgs []string
|
||||
|
@ -51,3 +51,7 @@ func Debug(tag string) DebugFunc {
|
||||
func IsDebugEnabled() bool {
|
||||
return len(enabledDebugs) != 0
|
||||
}
|
||||
|
||||
func HaveDebugTag(tag string) bool {
|
||||
return enabledDebugs[tag]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user