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/config"
|
||||||
"github.com/golangci/golangci-lint/pkg/lint"
|
"github.com/golangci/golangci-lint/pkg/lint"
|
||||||
"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
|
"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/printers"
|
||||||
"github.com/golangci/golangci-lint/pkg/result"
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
"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 {
|
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
|
||||||
|
if !logutils.HaveDebugTag("linters_output") {
|
||||||
// Don't allow linters and loader to print anything
|
// Don't allow linters and loader to print anything
|
||||||
log.SetOutput(ioutil.Discard)
|
log.SetOutput(ioutil.Discard)
|
||||||
savedStdout, savedStderr := setOutputToDevNull()
|
savedStdout, savedStderr := setOutputToDevNull()
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Stdout, os.Stderr = savedStdout, savedStderr
|
os.Stdout, os.Stderr = savedStdout, savedStderr
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
issues, err := e.runAnalysis(ctx, args)
|
issues, err := e.runAnalysis(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,7 +75,6 @@ func (g Gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
|||||||
for _, hunk := range d.Hunks {
|
for _, hunk := range d.Hunks {
|
||||||
deletedLine, addedLine, err := getFirstDeletedAndAddedLineNumberInHunk(hunk)
|
deletedLine, addedLine, err := getFirstDeletedAndAddedLineNumberInHunk(hunk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Infof("Can't get first deleted line number for hunk: %s", err)
|
|
||||||
if addedLine > 1 {
|
if addedLine > 1 {
|
||||||
deletedLine = addedLine - 1 // use previous line, TODO: use both prev and next lines
|
deletedLine = addedLine - 1 // use previous line, TODO: use both prev and next lines
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golangci/golangci-lint/pkg/logutils"
|
||||||
|
|
||||||
"github.com/golangci/go-tools/ssa"
|
"github.com/golangci/go-tools/ssa"
|
||||||
"github.com/golangci/go-tools/ssa/ssautil"
|
"github.com/golangci/go-tools/ssa/ssautil"
|
||||||
"github.com/golangci/golangci-lint/pkg/config"
|
"github.com/golangci/golangci-lint/pkg/config"
|
||||||
@ -21,6 +23,8 @@ import (
|
|||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var loadDebugf = logutils.Debug("load")
|
||||||
|
|
||||||
func isFullImportNeeded(linters []linter.Config) bool {
|
func isFullImportNeeded(linters []linter.Config) bool {
|
||||||
for _, linter := range linters {
|
for _, linter := range linters {
|
||||||
if linter.NeedsProgramLoading() {
|
if linter.NeedsProgramLoading() {
|
||||||
@ -64,6 +68,89 @@ func normalizePaths(paths []string) ([]string, error) {
|
|||||||
return ret, nil
|
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) {
|
func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *config.Run, pkgProg *packages.Program) (*loader.Program, *loader.Config, error) {
|
||||||
if !isFullImportNeeded(linters) {
|
if !isFullImportNeeded(linters) {
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
@ -79,6 +166,7 @@ func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *con
|
|||||||
Build: bctx,
|
Build: bctx,
|
||||||
AllowErrors: true, // Try to analyze partially
|
AllowErrors: true, // Try to analyze partially
|
||||||
ParserMode: parser.ParseComments, // AST will be reused by linters
|
ParserMode: parser.ParseComments, // AST will be reused by linters
|
||||||
|
TypeCheckFuncBodies: getTypeCheckFuncBodies(cfg, linters, pkgProg),
|
||||||
}
|
}
|
||||||
|
|
||||||
var loaderArgs []string
|
var loaderArgs []string
|
||||||
|
@ -51,3 +51,7 @@ func Debug(tag string) DebugFunc {
|
|||||||
func IsDebugEnabled() bool {
|
func IsDebugEnabled() bool {
|
||||||
return len(enabledDebugs) != 0
|
return len(enabledDebugs) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HaveDebugTag(tag string) bool {
|
||||||
|
return enabledDebugs[tag]
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user