Denis Isaev a4a7100011 Fix #263: update goimports
The new version of goimports works 100x faster
with go modules. Also it has some new features:

$ git cherry -v 6c7e314b6563 92cdcd90bf52 | fgrep imports
+ 5bbcdc15656ef390fab5dd6e8daf95354f7171e3 imports: redesign fixImports
+ ee45598d2ff288037f53f9e13ae0b1a6e2165ad5 imports: create named imports for name/path mismatches (again)
+ 4c53570e0460bc32468f75bf9dd71c018d03bfa9 imports: ignore globals in different packages
+ 1d424dbce8dd500e9e449fd3ff9d0668c09e2ae1 imports: clean up customization seam
+ 6a3e9aa2ab7749d72d1006ee484271b4a11f96c2 imports: fix renamed sibling imports
+ 5f4a60f04f23ac48e0665f257413ae3eacf339be imports: fix renamed sibling imports more
+ bbccd8cae4a9a47e0f978e03ff4b5df88a9fde1e imports: use go/packages, support modules
+ d4971274fe382404aee0e8c163af974f2bf738e6 imports: don't remove imports that conflict with globals
2018-12-22 15:50:16 +03:00

136 lines
3.1 KiB
Go

package golinters
import (
"bytes"
"context"
"fmt"
"go/token"
gofmtAPI "github.com/golangci/gofmt/gofmt"
goimportsAPI "github.com/golangci/gofmt/goimports"
"golang.org/x/tools/imports"
"sourcegraph.com/sourcegraph/go-diff/diff"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/logutils"
"github.com/golangci/golangci-lint/pkg/result"
)
type Gofmt struct {
UseGoimports bool
}
func (g Gofmt) Name() string {
if g.UseGoimports {
return "goimports"
}
return "gofmt"
}
func (g Gofmt) Desc() string {
if g.UseGoimports {
return "Goimports does everything that gofmt does. Additionally it checks unused imports"
}
return "Gofmt checks whether code was gofmt-ed. By default " +
"this tool runs with -s option to check for code simplification"
}
func getFirstDeletedAndAddedLineNumberInHunk(h *diff.Hunk) (int, int, error) {
lines := bytes.Split(h.Body, []byte{'\n'})
lineNumber := int(h.OrigStartLine - 1)
firstAddedLineNumber := -1
for _, line := range lines {
lineNumber++
if len(line) == 0 {
continue
}
if line[0] == '+' && firstAddedLineNumber == -1 {
firstAddedLineNumber = lineNumber
}
if line[0] == '-' {
return lineNumber, firstAddedLineNumber, nil
}
}
return 0, firstAddedLineNumber, fmt.Errorf("didn't find deletion line in hunk %s", string(h.Body))
}
func (g Gofmt) extractIssuesFromPatch(patch string, log logutils.Log) ([]result.Issue, error) {
diffs, err := diff.ParseMultiFileDiff([]byte(patch))
if err != nil {
return nil, fmt.Errorf("can't parse patch: %s", err)
}
if len(diffs) == 0 {
return nil, fmt.Errorf("got no diffs from patch parser: %v", diffs)
}
issues := []result.Issue{}
for _, d := range diffs {
if len(d.Hunks) == 0 {
log.Warnf("Got no hunks in diff %+v", d)
continue
}
for _, hunk := range d.Hunks {
deletedLine, addedLine, err := getFirstDeletedAndAddedLineNumberInHunk(hunk)
if err != nil {
if addedLine > 1 {
deletedLine = addedLine - 1 // use previous line, TODO: use both prev and next lines
} else {
deletedLine = 1
}
}
text := "File is not `gofmt`-ed with `-s`"
if g.UseGoimports {
text = "File is not `goimports`-ed"
}
i := result.Issue{
FromLinter: g.Name(),
Pos: token.Position{
Filename: d.NewName,
Line: deletedLine,
},
Text: text,
}
issues = append(issues, i)
}
}
return issues, nil
}
func (g Gofmt) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
var issues []result.Issue
for _, f := range getAllFileNames(lintCtx) {
var diff []byte
var err error
if g.UseGoimports {
imports.LocalPrefix = lintCtx.Settings().Goimports.LocalPrefixes
diff, err = goimportsAPI.Run(f)
} else {
diff, err = gofmtAPI.Run(f, lintCtx.Settings().Gofmt.Simplify)
}
if err != nil { // TODO: skip
return nil, err
}
if diff == nil {
continue
}
is, err := g.extractIssuesFromPatch(string(diff), lintCtx.Log)
if err != nil {
return nil, fmt.Errorf("can't extract issues from gofmt diff output %q: %s", string(diff), err)
}
issues = append(issues, is...)
}
return issues, nil
}