Skip to content

Speedup program loading on 20%. #91

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

Merged
merged 1 commit into from
Jun 13, 2018
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
15 changes: 9 additions & 6 deletions pkg/commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -231,12 +232,14 @@ func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
}

func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
// Don't allow linters and loader to print anything
log.SetOutput(ioutil.Discard)
savedStdout, savedStderr := setOutputToDevNull()
defer func() {
os.Stdout, os.Stderr = savedStdout, savedStderr
}()
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 {
Expand Down
1 change: 0 additions & 1 deletion pkg/golinters/gofmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
94 changes: 91 additions & 3 deletions pkg/lint/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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() {
Expand Down Expand Up @@ -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
Expand All @@ -76,9 +163,10 @@ func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *con

bctx := pkgProg.BuildContext()
loadcfg := &loader.Config{
Build: bctx,
AllowErrors: true, // Try to analyze partially
ParserMode: parser.ParseComments, // AST will be reused by linters
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
Expand Down
4 changes: 4 additions & 0 deletions pkg/logutils/logutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@ func Debug(tag string) DebugFunc {
func IsDebugEnabled() bool {
return len(enabledDebugs) != 0
}

func HaveDebugTag(tag string) bool {
return enabledDebugs[tag]
}