support gofmt and goimports
This commit is contained in:
parent
e65203921b
commit
ffd2723a3d
15
Gopkg.lock
generated
15
Gopkg.lock
generated
@ -69,6 +69,15 @@
|
||||
revision = "c34cdb4725f4c3844d095133c6e40e448b86589b"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golangci/gofmt"
|
||||
packages = [
|
||||
"gofmt",
|
||||
"goimports"
|
||||
]
|
||||
revision = "2076e05ced53480baa8ba14a05b80a415bec9377"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/golangci/golangci-shared"
|
||||
@ -202,7 +211,9 @@
|
||||
"go/gcexportdata",
|
||||
"go/internal/gcimporter",
|
||||
"go/loader",
|
||||
"go/types/typeutil"
|
||||
"go/types/typeutil",
|
||||
"imports",
|
||||
"internal/fastwalk"
|
||||
]
|
||||
revision = "87723262609ca8fd55d449c027454c29cadefd68"
|
||||
|
||||
@ -221,6 +232,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "29f17160680b1e8db1390003f80c17e24a82a83631d5c67f7a8fe7b29fd2a0f5"
|
||||
inputs-digest = "98e0ff9e850bf286e0e15b788f303a6baffcccb7c4d9a55155f28eee386cb43b"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/golangci/golangci-lint/internal/commands"
|
||||
"github.com/golangci/golangci-shared/pkg/analytics"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
func main() {
|
||||
log.SetFlags(0) // don't print time
|
||||
analytics.SetLogLevel(logrus.WarnLevel)
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
e := commands.NewExecutor()
|
||||
if err := e.Execute(); err != nil {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/golangci/golangci-lint/pkg"
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||
"github.com/golangci/golangci-lint/pkg/golinters"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-lint/pkg/result/processors"
|
||||
@ -41,6 +42,8 @@ func (e *Executor) initRun() {
|
||||
runCmd.Flags().BoolVar(&rc.Govet.CheckShadowing, "govet.check-shadowing", true, "Govet: check for shadowed variables")
|
||||
|
||||
runCmd.Flags().Float64Var(&rc.Golint.MinConfidence, "golint.min-confidence", 0.8, "Golint: minimum confidence of a problem to print it")
|
||||
|
||||
runCmd.Flags().BoolVar(&rc.Gofmt.Simplify, "gofmt.simplify", true, "Gofmt: simplify code")
|
||||
}
|
||||
|
||||
func (e Executor) executeRun(cmd *cobra.Command, args []string) {
|
||||
@ -60,17 +63,11 @@ func (e Executor) executeRun(cmd *cobra.Command, args []string) {
|
||||
|
||||
var exec executors.Executor
|
||||
|
||||
for _, path := range args {
|
||||
if strings.HasSuffix(path, ".go") && len(args) != 1 {
|
||||
return fmt.Errorf("Specific files for analysis are allowed only if one file is set"), 1
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
args = []string{"./..."}
|
||||
}
|
||||
|
||||
paths, err := golinters.GetPathsForAnalysis(args)
|
||||
paths, err := fsutils.GetPathsForAnalysis(args)
|
||||
if err != nil {
|
||||
return err, 1
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package config
|
||||
|
||||
import "github.com/golangci/golangci-lint/pkg/fsutils"
|
||||
|
||||
type OutFormat string
|
||||
|
||||
const (
|
||||
@ -16,7 +18,7 @@ type Common struct {
|
||||
}
|
||||
|
||||
type Run struct {
|
||||
Paths []string
|
||||
Paths *fsutils.ProjectPaths
|
||||
BuildTags []string
|
||||
|
||||
OutFormat string
|
||||
@ -33,6 +35,9 @@ type Run struct {
|
||||
Golint struct {
|
||||
MinConfidence float64
|
||||
}
|
||||
Gofmt struct {
|
||||
Simplify bool
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@ -43,7 +48,6 @@ type Config struct {
|
||||
func NewDefault() *Config {
|
||||
return &Config{
|
||||
Run: Run{
|
||||
Paths: []string{"./..."},
|
||||
OutFormat: OutFormatColoredLineNumber,
|
||||
},
|
||||
}
|
||||
|
@ -1,10 +1,103 @@
|
||||
package fsutils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golangci/golangci-shared/pkg/analytics"
|
||||
)
|
||||
|
||||
func GetProjectRoot() string {
|
||||
return path.Join(build.Default.GOPATH, "src", "github.com", "golangci", "golangci-worker")
|
||||
}
|
||||
|
||||
type ProjectPaths struct {
|
||||
Files []string
|
||||
Dirs []string
|
||||
IsDirsRun bool
|
||||
}
|
||||
|
||||
func (p ProjectPaths) MixedPaths() []string {
|
||||
if p.IsDirsRun {
|
||||
return p.Dirs
|
||||
}
|
||||
|
||||
return p.Files
|
||||
}
|
||||
|
||||
func processPaths(root string, paths []string, maxPaths int) ([]string, error) {
|
||||
if len(paths) > maxPaths {
|
||||
analytics.Log(context.TODO()).Warnf("Gofmt: got too much paths (%d), analyze first %d", len(paths), maxPaths)
|
||||
paths = paths[:maxPaths]
|
||||
}
|
||||
|
||||
ret := []string{}
|
||||
for i := range paths {
|
||||
if !filepath.IsAbs(paths[i]) {
|
||||
ret = append(ret, paths[i])
|
||||
continue
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(root, paths[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get relative path for path %s and root %s: %s",
|
||||
paths[i], root, err)
|
||||
}
|
||||
ret = append(ret, relPath)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func GetPathsForAnalysis(inputPaths []string) (*ProjectPaths, error) {
|
||||
for _, path := range inputPaths {
|
||||
if strings.HasSuffix(path, ".go") && len(inputPaths) != 1 {
|
||||
return nil, fmt.Errorf("Specific files for analysis are allowed only if one file is set")
|
||||
}
|
||||
}
|
||||
|
||||
excludeDirs := []string{"vendor", "testdata", "examples", "Godeps"}
|
||||
pr := NewPathResolver(excludeDirs, []string{".go"})
|
||||
paths, err := pr.Resolve(inputPaths...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't resolve paths: %s", err)
|
||||
}
|
||||
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get working dir: %s", err)
|
||||
}
|
||||
|
||||
files, err := processPaths(root, paths.Files(), 10000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't process resolved files: %s", err)
|
||||
}
|
||||
|
||||
dirs, err := processPaths(root, paths.Dirs(), 1000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't process resolved dirs: %s", err)
|
||||
}
|
||||
|
||||
for i := range dirs {
|
||||
dir := dirs[i]
|
||||
if dir != "." {
|
||||
dirs[i] = "./" + dir
|
||||
}
|
||||
}
|
||||
|
||||
return &ProjectPaths{
|
||||
Files: files,
|
||||
Dirs: dirs,
|
||||
IsDirsRun: len(dirs) != 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func IsDir(filename string) bool {
|
||||
fi, err := os.Stat(filename)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-shared/pkg/executors"
|
||||
|
||||
errcheckAPI "github.com/kisielk/errcheck/golangci"
|
||||
)
|
||||
|
||||
@ -20,8 +19,7 @@ func (errcheck) Name() string {
|
||||
|
||||
func (e errcheck) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
|
||||
errCfg := &cfg.Errcheck
|
||||
|
||||
issues, err := errcheckAPI.Run(cfg.Paths, cfg.BuildTags, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
|
||||
issues, err := errcheckAPI.Run(cfg.Paths.MixedPaths(), cfg.BuildTags, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
gofmtAPI "github.com/golangci/gofmt/gofmt"
|
||||
goimportsAPI "github.com/golangci/gofmt/goimports"
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-shared/pkg/analytics"
|
||||
@ -24,21 +26,25 @@ func (g gofmt) Name() string {
|
||||
return "gofmt"
|
||||
}
|
||||
|
||||
func getFirstDeletedLineNumberInHunk(h *diff.Hunk) (int, error) {
|
||||
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, nil
|
||||
return lineNumber, firstAddedLineNumber, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("didn't find deletion line in hunk %s", string(h.Body))
|
||||
return 0, firstAddedLineNumber, fmt.Errorf("didn't find deletion line in hunk %s", string(h.Body))
|
||||
}
|
||||
|
||||
func (g gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
||||
@ -59,10 +65,14 @@ func (g gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
||||
}
|
||||
|
||||
for _, hunk := range d.Hunks {
|
||||
lineNumber, err := getFirstDeletedLineNumberInHunk(hunk)
|
||||
deletedLine, addedLine, err := getFirstDeletedAndAddedLineNumberInHunk(hunk)
|
||||
if err != nil {
|
||||
analytics.Log(context.TODO()).Infof("Can't get first deleted line number for hunk: %s", err)
|
||||
lineNumber = int(hunk.OrigStartLine) // use first line if no deletions:
|
||||
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"
|
||||
@ -72,7 +82,7 @@ func (g gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
||||
i := result.Issue{
|
||||
FromLinter: g.Name(),
|
||||
File: d.NewName,
|
||||
LineNumber: lineNumber,
|
||||
LineNumber: deletedLine,
|
||||
Text: text,
|
||||
}
|
||||
issues = append(issues, i)
|
||||
@ -83,31 +93,29 @@ func (g gofmt) extractIssuesFromPatch(patch string) ([]result.Issue, error) {
|
||||
}
|
||||
|
||||
func (g gofmt) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
|
||||
// TODO: cfg support
|
||||
paths, err := getPathsForGoProject(exec.WorkDir())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get files to analyze: %s", err)
|
||||
}
|
||||
var issues []result.Issue
|
||||
|
||||
args := []string{"-d"}
|
||||
if !g.useGoimports {
|
||||
args = append(args, "-s")
|
||||
}
|
||||
args = append(args, paths.files...)
|
||||
out, err := exec.Run(ctx, g.Name(), args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't run gofmt: %s, %s", err, out)
|
||||
}
|
||||
for _, f := range cfg.Paths.Files {
|
||||
var diff []byte
|
||||
var err error
|
||||
if g.useGoimports {
|
||||
diff, err = goimportsAPI.Run(f)
|
||||
} else {
|
||||
diff, err = gofmtAPI.Run(f, cfg.Gofmt.Simplify)
|
||||
}
|
||||
if err != nil { // TODO: skip
|
||||
return nil, err
|
||||
}
|
||||
if diff == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(out) == 0 { // no diff => no issues
|
||||
return &result.Result{
|
||||
Issues: []result.Issue{},
|
||||
}, nil
|
||||
}
|
||||
is, err := g.extractIssuesFromPatch(string(diff))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't extract issues from gofmt diff output %q: %s", string(diff), err)
|
||||
}
|
||||
|
||||
issues, err := g.extractIssuesFromPatch(out)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't extract issues from gofmt diff output %q: %s", out, err)
|
||||
issues = append(issues, is...)
|
||||
}
|
||||
|
||||
return &result.Result{
|
||||
|
@ -1,50 +0,0 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
|
||||
func TestGofmtIssueFound(t *testing.T) {
|
||||
const source = `package p
|
||||
|
||||
func noFmt() error {
|
||||
return nil
|
||||
}
|
||||
`
|
||||
|
||||
ExpectIssues(t, gofmt{}, source, []result.Issue{NewIssue("gofmt", "File is not gofmt-ed with -s", 4)})
|
||||
}
|
||||
|
||||
func TestGofmtNoIssue(t *testing.T) {
|
||||
const source = `package p
|
||||
|
||||
func fmted() error {
|
||||
return nil
|
||||
}
|
||||
`
|
||||
|
||||
ExpectIssues(t, gofmt{}, source, []result.Issue{})
|
||||
}
|
||||
|
||||
func TestGoimportsIssueFound(t *testing.T) {
|
||||
const source = `package p
|
||||
func noFmt() error {return nil}
|
||||
`
|
||||
|
||||
lint := gofmt{useGoimports: true}
|
||||
ExpectIssues(t, lint, source, []result.Issue{NewIssue("goimports", "File is not goimports-ed", 2)})
|
||||
}
|
||||
|
||||
func TestGoimportsNoIssue(t *testing.T) {
|
||||
const source = `package p
|
||||
|
||||
func fmted() error {
|
||||
return nil
|
||||
}
|
||||
`
|
||||
|
||||
lint := gofmt{useGoimports: true}
|
||||
ExpectIssues(t, lint, source, []result.Issue{})
|
||||
}
|
@ -21,17 +21,20 @@ func (golint) Name() string {
|
||||
|
||||
func (g golint) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
|
||||
var issues []result.Issue
|
||||
for _, path := range cfg.Paths {
|
||||
var i []result.Issue
|
||||
var err error
|
||||
if isDir(path) {
|
||||
i, err = lintDir(path, cfg.Golint.MinConfidence)
|
||||
} else {
|
||||
i, err = lintFiles(cfg.Golint.MinConfidence, path)
|
||||
if cfg.Paths.IsDirsRun {
|
||||
for _, path := range cfg.Paths.Dirs {
|
||||
i, err := lintDir(path, cfg.Golint.MinConfidence)
|
||||
if err != nil {
|
||||
// TODO: skip and warn
|
||||
return nil, fmt.Errorf("can't lint dir %s: %s", path, err)
|
||||
}
|
||||
issues = append(issues, i...)
|
||||
}
|
||||
} else {
|
||||
i, err := lintFiles(cfg.Golint.MinConfidence, cfg.Paths.Files...)
|
||||
if err != nil {
|
||||
// TODO: skip and warn
|
||||
return nil, fmt.Errorf("can't lint dir %s: %s", path, err)
|
||||
return nil, fmt.Errorf("can't lint files %s: %s", cfg.Paths.Files, err)
|
||||
}
|
||||
issues = append(issues, i...)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ func (govet) Name() string {
|
||||
}
|
||||
|
||||
func (g govet) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
|
||||
issues, err := govetAPI.Run(cfg.Paths, cfg.BuildTags, cfg.Govet.CheckShadowing)
|
||||
issues, err := govetAPI.Run(cfg.Paths.MixedPaths(), cfg.BuildTags, cfg.Govet.CheckShadowing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,207 +0,0 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/template"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-shared/pkg/analytics"
|
||||
"github.com/golangci/golangci-shared/pkg/executors"
|
||||
)
|
||||
|
||||
type linterConfig struct {
|
||||
messageTemplate *template.Template
|
||||
pattern *regexp.Regexp
|
||||
excludeByMessagePattern *regexp.Regexp
|
||||
args []string
|
||||
issuesFoundExitCode int
|
||||
}
|
||||
|
||||
func newLinterConfig(messageTemplate, pattern, excludeByMessagePattern string, args ...string) *linterConfig {
|
||||
if messageTemplate == "" {
|
||||
messageTemplate = "{{.message}}"
|
||||
}
|
||||
|
||||
var excludeByMessagePatternRe *regexp.Regexp
|
||||
if excludeByMessagePattern != "" {
|
||||
excludeByMessagePatternRe = regexp.MustCompile(excludeByMessagePattern)
|
||||
}
|
||||
|
||||
return &linterConfig{
|
||||
messageTemplate: template.Must(template.New("message").Parse(messageTemplate)),
|
||||
pattern: regexp.MustCompile(pattern),
|
||||
excludeByMessagePattern: excludeByMessagePatternRe,
|
||||
args: args,
|
||||
issuesFoundExitCode: 1,
|
||||
}
|
||||
}
|
||||
|
||||
type linter struct {
|
||||
name string
|
||||
|
||||
linterConfig
|
||||
}
|
||||
|
||||
func newLinter(name string, cfg *linterConfig) *linter {
|
||||
return &linter{
|
||||
name: name,
|
||||
linterConfig: *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (lint linter) Name() string {
|
||||
return lint.name
|
||||
}
|
||||
|
||||
func (lint linter) doesExitCodeMeansIssuesWereFound(err error) bool {
|
||||
ee, ok := err.(*exec.ExitError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
status, ok := ee.Sys().(syscall.WaitStatus)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
exitCode := status.ExitStatus()
|
||||
return exitCode == lint.issuesFoundExitCode
|
||||
}
|
||||
|
||||
func getOutTail(out string, linesCount int) string {
|
||||
lines := strings.Split(out, "\n")
|
||||
if len(lines) <= linesCount {
|
||||
return out
|
||||
}
|
||||
|
||||
return strings.Join(lines[len(lines)-linesCount:], "\n")
|
||||
}
|
||||
|
||||
func (lint linter) Run(ctx context.Context, exec executors.Executor, cfg *config.Run) (*result.Result, error) {
|
||||
paths := &ProjectPaths{
|
||||
files: cfg.Paths,
|
||||
dirs: []string{}, // TODO
|
||||
}
|
||||
var err error
|
||||
if len(cfg.Paths) == 0 {
|
||||
paths, err = getPathsForGoProject(exec.WorkDir())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get files to analyze: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
retIssues := []result.Issue{}
|
||||
|
||||
const maxFilesPerRun = 100 // run one linter multiple times with groups of files: limit memory usage in the cost of higher CPU usage
|
||||
|
||||
for len(paths.files) != 0 {
|
||||
args := append([]string{}, lint.args...)
|
||||
|
||||
filesCount := len(paths.files)
|
||||
if filesCount > maxFilesPerRun {
|
||||
filesCount = maxFilesPerRun
|
||||
}
|
||||
files := paths.files[:filesCount]
|
||||
args = append(args, files...)
|
||||
|
||||
out, err := exec.Run(ctx, lint.name, args...)
|
||||
if err != nil && !lint.doesExitCodeMeansIssuesWereFound(err) {
|
||||
out = getOutTail(out, 10)
|
||||
return nil, fmt.Errorf("can't run linter %s with args %v: %s, %s", lint.name, lint.args, err, out)
|
||||
}
|
||||
|
||||
issues := lint.parseLinterOut(out)
|
||||
retIssues = append(retIssues, issues...)
|
||||
|
||||
paths.files = paths.files[filesCount:]
|
||||
}
|
||||
|
||||
return &result.Result{
|
||||
Issues: retIssues,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type regexpVars map[string]string
|
||||
|
||||
func buildMatchedRegexpVars(match []string, pattern *regexp.Regexp) regexpVars {
|
||||
result := regexpVars{}
|
||||
for i, name := range pattern.SubexpNames() {
|
||||
if i != 0 && name != "" {
|
||||
result[name] = match[i]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (lint linter) parseLinterOutLine(line string) (regexpVars, error) {
|
||||
match := lint.pattern.FindStringSubmatch(line)
|
||||
if match == nil {
|
||||
return nil, fmt.Errorf("can't match line %q against regexp", line)
|
||||
}
|
||||
|
||||
return buildMatchedRegexpVars(match, lint.pattern), nil
|
||||
}
|
||||
|
||||
func (lint linter) makeIssue(vars regexpVars) (*result.Issue, error) {
|
||||
var messageBuffer bytes.Buffer
|
||||
err := lint.messageTemplate.Execute(&messageBuffer, vars)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't execute message template: %s", err)
|
||||
}
|
||||
|
||||
if vars["path"] == "" {
|
||||
return nil, fmt.Errorf("no path in vars %+v", vars)
|
||||
}
|
||||
|
||||
var line int
|
||||
if vars["line"] != "" {
|
||||
line, err = strconv.Atoi(vars["line"])
|
||||
if err != nil {
|
||||
analytics.Log(context.TODO()).Warnf("Can't parse line %q: %s", vars["line"], err)
|
||||
}
|
||||
}
|
||||
|
||||
return &result.Issue{
|
||||
FromLinter: lint.name,
|
||||
File: vars["path"],
|
||||
LineNumber: line,
|
||||
Text: messageBuffer.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lint linter) parseLinterOut(out string) []result.Issue {
|
||||
issues := []result.Issue{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(out))
|
||||
for scanner.Scan() {
|
||||
vars, err := lint.parseLinterOutLine(scanner.Text())
|
||||
if err != nil {
|
||||
analytics.Log(context.TODO()).Warnf("Can't parse linter %s out line: %s", lint.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
message := vars["message"]
|
||||
ex := lint.excludeByMessagePattern
|
||||
if message != "" && ex != nil && ex.MatchString(message) {
|
||||
continue
|
||||
}
|
||||
|
||||
issue, err := lint.makeIssue(vars)
|
||||
if err != nil {
|
||||
analytics.Log(context.TODO()).Warnf("Can't make issue: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
issues = append(issues, *issue)
|
||||
}
|
||||
|
||||
return issues
|
||||
}
|
@ -6,5 +6,5 @@ const pathLineColMessage = `^(?P<path>.*?\.go):(?P<line>\d+):(?P<col>\d+):\s*(?P
|
||||
const pathLineMessage = `^(?P<path>.*?\.go):(?P<line>\d+):\s*(?P<message>.*)$`
|
||||
|
||||
func GetSupportedLinters() []pkg.Linter {
|
||||
return []pkg.Linter{govet{}, errcheck{}, golint{}}
|
||||
return []pkg.Linter{govet{}, errcheck{}, golint{}, gofmt{}, gofmt{useGoimports: true}}
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
"github.com/golangci/golangci-shared/pkg/executors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func NewIssue(linter, message string, line int) result.Issue {
|
||||
return result.Issue{
|
||||
FromLinter: linter,
|
||||
Text: message,
|
||||
File: "p/f.go",
|
||||
LineNumber: line,
|
||||
}
|
||||
}
|
||||
|
||||
func ExpectIssues(t *testing.T, linter pkg.Linter, source string, issues []result.Issue) {
|
||||
exec, err := executors.NewTempDirShell("test.expectissues")
|
||||
assert.NoError(t, err)
|
||||
defer exec.Clean()
|
||||
|
||||
subDir := path.Join(exec.WorkDir(), "p")
|
||||
assert.NoError(t, os.Mkdir(subDir, os.ModePerm))
|
||||
err = ioutil.WriteFile(path.Join(subDir, "f.go"), []byte(source), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
||||
res, err := linter.Run(context.Background(), exec, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, issues, res.Issues)
|
||||
}
|
8
pkg/golinters/testdata/gofmt.go
vendored
Normal file
8
pkg/golinters/testdata/gofmt.go
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package testdata
|
||||
|
||||
import "fmt"
|
||||
|
||||
func gofmtNotSimplified() {
|
||||
var x []string
|
||||
fmt.Print(x[1:len(x)]) // ERROR "File is not gofmt-ed with -s"
|
||||
}
|
11
pkg/golinters/testdata/goimports.go
vendored
Normal file
11
pkg/golinters/testdata/goimports.go
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"fmt" // ERROR "File is not goimports-ed"
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
)
|
||||
|
||||
func bar() {
|
||||
fmt.Print("x")
|
||||
_ = config.Config{}
|
||||
}
|
@ -1,111 +1,12 @@
|
||||
package golinters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/config"
|
||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||
"github.com/golangci/golangci-shared/pkg/analytics"
|
||||
)
|
||||
|
||||
type ProjectPaths struct {
|
||||
files []string
|
||||
dirs []string
|
||||
}
|
||||
|
||||
func processPaths(root string, paths []string, maxPaths int) ([]string, error) {
|
||||
if len(paths) > maxPaths {
|
||||
analytics.Log(context.TODO()).Warnf("Gofmt: got too much paths (%d), analyze first %d", len(paths), maxPaths)
|
||||
paths = paths[:maxPaths]
|
||||
}
|
||||
|
||||
ret := []string{}
|
||||
for i := range paths {
|
||||
if !filepath.IsAbs(paths[i]) {
|
||||
ret = append(ret, paths[i])
|
||||
continue
|
||||
}
|
||||
|
||||
relPath, err := filepath.Rel(root, paths[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get relative path for path %s and root %s: %s",
|
||||
paths[i], root, err)
|
||||
}
|
||||
ret = append(ret, relPath)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getPathsForGoProject(root string) (*ProjectPaths, error) {
|
||||
excludeDirs := []string{"vendor", "testdata", "examples", "Godeps"}
|
||||
pr := fsutils.NewPathResolver(excludeDirs, []string{".go"})
|
||||
paths, err := pr.Resolve(path.Join(root, "..."))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't resolve paths: %s", err)
|
||||
}
|
||||
|
||||
files, err := processPaths(root, paths.Files(), 10000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't process resolved files: %s", err)
|
||||
}
|
||||
|
||||
dirs, err := processPaths(root, paths.Dirs(), 1000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't process resolved dirs: %s", err)
|
||||
}
|
||||
|
||||
for i := range dirs {
|
||||
dir := dirs[i]
|
||||
if dir != "." {
|
||||
dirs[i] = "./" + dir
|
||||
}
|
||||
}
|
||||
|
||||
return &ProjectPaths{
|
||||
files: files,
|
||||
dirs: dirs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetPathsForAnalysis(inputPaths []string) ([]string, error) {
|
||||
excludeDirs := []string{"vendor", "testdata", "examples", "Godeps"}
|
||||
pr := fsutils.NewPathResolver(excludeDirs, []string{".go"})
|
||||
paths, err := pr.Resolve(inputPaths...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't resolve paths: %s", err)
|
||||
}
|
||||
|
||||
root, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get working dir: %s", err)
|
||||
}
|
||||
|
||||
if len(paths.Files()) == 1 {
|
||||
// special case: only one files was set for analysis
|
||||
return processPaths(root, paths.Files(), 1)
|
||||
}
|
||||
|
||||
dirs, err := processPaths(root, paths.Dirs(), 1000)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't process resolved dirs: %s", err)
|
||||
}
|
||||
|
||||
for i := range dirs {
|
||||
dir := dirs[i]
|
||||
if dir != "." {
|
||||
dirs[i] = "./" + dir
|
||||
}
|
||||
}
|
||||
|
||||
return dirs, nil
|
||||
}
|
||||
|
||||
func formatCode(code string, cfg *config.Run) string {
|
||||
if strings.Contains(code, "`") {
|
||||
return code // TODO: properly escape or remove
|
||||
@ -113,8 +14,3 @@ func formatCode(code string, cfg *config.Run) string {
|
||||
|
||||
return fmt.Sprintf("`%s`", code)
|
||||
}
|
||||
|
||||
func isDir(filename string) bool {
|
||||
fi, err := os.Stat(filename)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ func (r SimpleRunner) Run(ctx context.Context, linters []Linter, exec executors.
|
||||
res, err := linter.Run(ctx, exec, cfg)
|
||||
os.Stdout, os.Stderr = savedStdout, savedStderr
|
||||
if err != nil {
|
||||
analytics.Log(ctx).Warnf("Can't run linter %+v: %s", linter, err)
|
||||
analytics.Log(ctx).Warnf("Can't run linter %s: %s", linter.Name(), err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
27
vendor/github.com/golangci/gofmt/gofmt/LICENSE
generated
vendored
Normal file
27
vendor/github.com/golangci/gofmt/gofmt/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
104
vendor/github.com/golangci/gofmt/gofmt/doc.go
generated
vendored
Normal file
104
vendor/github.com/golangci/gofmt/gofmt/doc.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Gofmt formats Go programs.
|
||||
It uses tabs for indentation and blanks for alignment.
|
||||
Alignment assumes that an editor is using a fixed-width font.
|
||||
|
||||
Without an explicit path, it processes the standard input. Given a file,
|
||||
it operates on that file; given a directory, it operates on all .go files in
|
||||
that directory, recursively. (Files starting with a period are ignored.)
|
||||
By default, gofmt prints the reformatted sources to standard output.
|
||||
|
||||
Usage:
|
||||
gofmt [flags] [path ...]
|
||||
|
||||
The flags are:
|
||||
-d
|
||||
Do not print reformatted sources to standard output.
|
||||
If a file's formatting is different than gofmt's, print diffs
|
||||
to standard output.
|
||||
-e
|
||||
Print all (including spurious) errors.
|
||||
-l
|
||||
Do not print reformatted sources to standard output.
|
||||
If a file's formatting is different from gofmt's, print its name
|
||||
to standard output.
|
||||
-r rule
|
||||
Apply the rewrite rule to the source before reformatting.
|
||||
-s
|
||||
Try to simplify code (after applying the rewrite rule, if any).
|
||||
-w
|
||||
Do not print reformatted sources to standard output.
|
||||
If a file's formatting is different from gofmt's, overwrite it
|
||||
with gofmt's version. If an error occurred during overwriting,
|
||||
the original file is restored from an automatic backup.
|
||||
|
||||
Debugging support:
|
||||
-cpuprofile filename
|
||||
Write cpu profile to the specified file.
|
||||
|
||||
|
||||
The rewrite rule specified with the -r flag must be a string of the form:
|
||||
|
||||
pattern -> replacement
|
||||
|
||||
Both pattern and replacement must be valid Go expressions.
|
||||
In the pattern, single-character lowercase identifiers serve as
|
||||
wildcards matching arbitrary sub-expressions; those expressions
|
||||
will be substituted for the same identifiers in the replacement.
|
||||
|
||||
When gofmt reads from standard input, it accepts either a full Go program
|
||||
or a program fragment. A program fragment must be a syntactically
|
||||
valid declaration list, statement list, or expression. When formatting
|
||||
such a fragment, gofmt preserves leading indentation as well as leading
|
||||
and trailing spaces, so that individual sections of a Go program can be
|
||||
formatted by piping them through gofmt.
|
||||
|
||||
Examples
|
||||
|
||||
To check files for unnecessary parentheses:
|
||||
|
||||
gofmt -r '(a) -> a' -l *.go
|
||||
|
||||
To remove the parentheses:
|
||||
|
||||
gofmt -r '(a) -> a' -w *.go
|
||||
|
||||
To convert the package tree from explicit slice upper bounds to implicit ones:
|
||||
|
||||
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src
|
||||
|
||||
The simplify command
|
||||
|
||||
When invoked with -s gofmt will make the following source transformations where possible.
|
||||
|
||||
An array, slice, or map composite literal of the form:
|
||||
[]T{T{}, T{}}
|
||||
will be simplified to:
|
||||
[]T{{}, {}}
|
||||
|
||||
A slice expression of the form:
|
||||
s[a:len(s)]
|
||||
will be simplified to:
|
||||
s[a:]
|
||||
|
||||
A range of the form:
|
||||
for x, _ = range v {...}
|
||||
will be simplified to:
|
||||
for x = range v {...}
|
||||
|
||||
A range of the form:
|
||||
for _ = range v {...}
|
||||
will be simplified to:
|
||||
for range v {...}
|
||||
|
||||
This may result in changes that are incompatible with earlier versions of Go.
|
||||
*/
|
||||
package gofmt
|
||||
|
||||
// BUG(rsc): The implementation of -r is a bit slow.
|
||||
// BUG(gri): If -w fails, the restored original file may not have some of the
|
||||
// original file attributes.
|
323
vendor/github.com/golangci/gofmt/gofmt/gofmt.go
generated
vendored
Normal file
323
vendor/github.com/golangci/gofmt/gofmt/gofmt.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gofmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// main operation modes
|
||||
list = flag.Bool("gofmt.l", false, "list files whose formatting differs from gofmt's")
|
||||
write = flag.Bool("gofmt.w", false, "write result to (source) file instead of stdout")
|
||||
rewriteRule = flag.String("gofmt.r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
|
||||
simplifyAST = flag.Bool("gofmt.s", false, "simplify code")
|
||||
doDiff = flag.Bool("gofmt.d", false, "display diffs instead of rewriting files")
|
||||
allErrors = flag.Bool("gofmt.e", false, "report all errors (not just the first 10 on different lines)")
|
||||
|
||||
// debugging
|
||||
cpuprofile = flag.String("gofmt.cpuprofile", "", "write cpu profile to this file")
|
||||
)
|
||||
|
||||
const (
|
||||
tabWidth = 8
|
||||
printerMode = printer.UseSpaces | printer.TabIndent
|
||||
)
|
||||
|
||||
var (
|
||||
fileSet = token.NewFileSet() // per process FileSet
|
||||
exitCode = 0
|
||||
rewrite func(*ast.File) *ast.File
|
||||
parserMode parser.Mode
|
||||
)
|
||||
|
||||
func report(err error) {
|
||||
scanner.PrintError(os.Stderr, err)
|
||||
exitCode = 2
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
func initParserMode() {
|
||||
parserMode = parser.ParseComments
|
||||
if *allErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
}
|
||||
}
|
||||
|
||||
func isGoFile(f os.FileInfo) bool {
|
||||
// ignore non-Go files
|
||||
name := f.Name()
|
||||
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||
}
|
||||
|
||||
// If in == nil, the source is the contents of the file with the given filename.
|
||||
func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
|
||||
var perm os.FileMode = 0644
|
||||
if in == nil {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in = f
|
||||
perm = fi.Mode().Perm()
|
||||
}
|
||||
|
||||
src, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, stdin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rewrite != nil {
|
||||
if sourceAdj == nil {
|
||||
file = rewrite(file)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "warning: rewrite ignored for incomplete programs\n")
|
||||
}
|
||||
}
|
||||
|
||||
ast.SortImports(fileSet, file)
|
||||
|
||||
if *simplifyAST {
|
||||
simplify(file)
|
||||
}
|
||||
|
||||
res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(src, res) {
|
||||
// formatting has changed
|
||||
if *list {
|
||||
fmt.Fprintln(out, filename)
|
||||
}
|
||||
if *write {
|
||||
// make a temporary backup before overwriting original
|
||||
bakname, err := backupFile(filename+".", src, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filename, res, perm)
|
||||
if err != nil {
|
||||
os.Rename(bakname, filename)
|
||||
return err
|
||||
}
|
||||
err = os.Remove(bakname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if *doDiff {
|
||||
data, err := diff(src, res, filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("computing diff: %s", err)
|
||||
}
|
||||
fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
|
||||
out.Write(data)
|
||||
}
|
||||
}
|
||||
|
||||
if !*list && !*write && !*doDiff {
|
||||
_, err = out.Write(res)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func visitFile(path string, f os.FileInfo, err error) error {
|
||||
if err == nil && isGoFile(f) {
|
||||
err = processFile(path, nil, os.Stdout, false)
|
||||
}
|
||||
// Don't complain if a file was deleted in the meantime (i.e.
|
||||
// the directory changed concurrently while running gofmt).
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
report(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func walkDir(path string) {
|
||||
filepath.Walk(path, visitFile)
|
||||
}
|
||||
|
||||
func gofmtMain() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if *cpuprofile != "" {
|
||||
f, err := os.Create(*cpuprofile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err)
|
||||
exitCode = 2
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
initParserMode()
|
||||
initRewrite()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
if *write {
|
||||
fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input")
|
||||
exitCode = 2
|
||||
return
|
||||
}
|
||||
if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
|
||||
report(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < flag.NArg(); i++ {
|
||||
path := flag.Arg(i)
|
||||
switch dir, err := os.Stat(path); {
|
||||
case err != nil:
|
||||
report(err)
|
||||
case dir.IsDir():
|
||||
walkDir(path)
|
||||
default:
|
||||
if err := processFile(path, nil, os.Stdout, false); err != nil {
|
||||
report(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeTempFile(dir, prefix string, data []byte) (string, error) {
|
||||
file, err := ioutil.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = file.Write(data)
|
||||
if err1 := file.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(file.Name())
|
||||
return "", err
|
||||
}
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
func diff(b1, b2 []byte, filename string) (data []byte, err error) {
|
||||
f1, err := writeTempFile("", "gofmt", b1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(f1)
|
||||
|
||||
f2, err := writeTempFile("", "gofmt", b2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(f2)
|
||||
|
||||
cmd := "diff"
|
||||
if runtime.GOOS == "plan9" {
|
||||
cmd = "/bin/ape/diff"
|
||||
}
|
||||
|
||||
data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
|
||||
if len(data) > 0 {
|
||||
// diff exits with a non-zero status when the files don't match.
|
||||
// Ignore that failure as long as we get output.
|
||||
return replaceTempFilename(data, filename)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// replaceTempFilename replaces temporary filenames in diff with actual one.
|
||||
//
|
||||
// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
|
||||
// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
|
||||
// ...
|
||||
// ->
|
||||
// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
|
||||
// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
|
||||
// ...
|
||||
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
|
||||
bs := bytes.SplitN(diff, []byte{'\n'}, 3)
|
||||
if len(bs) < 3 {
|
||||
return nil, fmt.Errorf("got unexpected diff for %s", filename)
|
||||
}
|
||||
// Preserve timestamps.
|
||||
var t0, t1 []byte
|
||||
if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
|
||||
t0 = bs[0][i:]
|
||||
}
|
||||
if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
|
||||
t1 = bs[1][i:]
|
||||
}
|
||||
// Always print filepath with slash separator.
|
||||
f := filepath.ToSlash(filename)
|
||||
bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
|
||||
bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
|
||||
return bytes.Join(bs, []byte{'\n'}), nil
|
||||
}
|
||||
|
||||
const chmodSupported = runtime.GOOS != "windows"
|
||||
|
||||
// backupFile writes data to a new file named filename<number> with permissions perm,
|
||||
// with <number randomly chosen such that the file name is unique. backupFile returns
|
||||
// the chosen file name.
|
||||
func backupFile(filename string, data []byte, perm os.FileMode) (string, error) {
|
||||
// create backup file
|
||||
f, err := ioutil.TempFile(filepath.Dir(filename), filepath.Base(filename))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bakname := f.Name()
|
||||
if chmodSupported {
|
||||
err = f.Chmod(perm)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
os.Remove(bakname)
|
||||
return bakname, err
|
||||
}
|
||||
}
|
||||
|
||||
// write data to backup file
|
||||
n, err := f.Write(data)
|
||||
if err == nil && n < len(data) {
|
||||
err = io.ErrShortWrite
|
||||
}
|
||||
if err1 := f.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
|
||||
return bakname, err
|
||||
}
|
46
vendor/github.com/golangci/gofmt/gofmt/golangci.go
generated
vendored
Normal file
46
vendor/github.com/golangci/gofmt/gofmt/golangci.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
package gofmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/printer"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func Run(filename string, needSimplify bool) ([]byte, error) {
|
||||
src, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initParserMode()
|
||||
|
||||
file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ast.SortImports(fileSet, file)
|
||||
|
||||
if needSimplify {
|
||||
simplify(file)
|
||||
}
|
||||
|
||||
res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(src, res) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// formatting has changed
|
||||
data, err := diff(src, res, filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error computing diff: %s", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
176
vendor/github.com/golangci/gofmt/gofmt/internal.go
generated
vendored
Normal file
176
vendor/github.com/golangci/gofmt/gofmt/internal.go
generated
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// TODO(gri): This file and the file src/go/format/internal.go are
|
||||
// the same (but for this comment and the package name). Do not modify
|
||||
// one without the other. Determine if we can factor out functionality
|
||||
// in a public API. See also #11844 for context.
|
||||
|
||||
package gofmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parse parses src, which was read from the named file,
|
||||
// as a Go source file, declaration, or statement list.
|
||||
func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
|
||||
file *ast.File,
|
||||
sourceAdj func(src []byte, indent int) []byte,
|
||||
indentAdj int,
|
||||
err error,
|
||||
) {
|
||||
// Try as whole source file.
|
||||
file, err = parser.ParseFile(fset, filename, src, parserMode)
|
||||
// If there's no error, return. If the error is that the source file didn't begin with a
|
||||
// package line and source fragments are ok, fall through to
|
||||
// try as a source fragment. Stop and return on any other error.
|
||||
if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
|
||||
return
|
||||
}
|
||||
|
||||
// If this is a declaration list, make it a source file
|
||||
// by inserting a package clause.
|
||||
// Insert using a ;, not a newline, so that the line numbers
|
||||
// in psrc match the ones in src.
|
||||
psrc := append([]byte("package p;"), src...)
|
||||
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
|
||||
if err == nil {
|
||||
sourceAdj = func(src []byte, indent int) []byte {
|
||||
// Remove the package clause.
|
||||
// Gofmt has turned the ; into a \n.
|
||||
src = src[indent+len("package p\n"):]
|
||||
return bytes.TrimSpace(src)
|
||||
}
|
||||
return
|
||||
}
|
||||
// If the error is that the source file didn't begin with a
|
||||
// declaration, fall through to try as a statement list.
|
||||
// Stop and return on any other error.
|
||||
if !strings.Contains(err.Error(), "expected declaration") {
|
||||
return
|
||||
}
|
||||
|
||||
// If this is a statement list, make it a source file
|
||||
// by inserting a package clause and turning the list
|
||||
// into a function body. This handles expressions too.
|
||||
// Insert using a ;, not a newline, so that the line numbers
|
||||
// in fsrc match the ones in src. Add an extra '\n' before the '}'
|
||||
// to make sure comments are flushed before the '}'.
|
||||
fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '\n', '}')
|
||||
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
|
||||
if err == nil {
|
||||
sourceAdj = func(src []byte, indent int) []byte {
|
||||
// Cap adjusted indent to zero.
|
||||
if indent < 0 {
|
||||
indent = 0
|
||||
}
|
||||
// Remove the wrapping.
|
||||
// Gofmt has turned the ; into a \n\n.
|
||||
// There will be two non-blank lines with indent, hence 2*indent.
|
||||
src = src[2*indent+len("package p\n\nfunc _() {"):]
|
||||
// Remove only the "}\n" suffix: remaining whitespaces will be trimmed anyway
|
||||
src = src[:len(src)-len("}\n")]
|
||||
return bytes.TrimSpace(src)
|
||||
}
|
||||
// Gofmt has also indented the function body one level.
|
||||
// Adjust that with indentAdj.
|
||||
indentAdj = -1
|
||||
}
|
||||
|
||||
// Succeeded, or out of options.
|
||||
return
|
||||
}
|
||||
|
||||
// format formats the given package file originally obtained from src
|
||||
// and adjusts the result based on the original source via sourceAdj
|
||||
// and indentAdj.
|
||||
func format(
|
||||
fset *token.FileSet,
|
||||
file *ast.File,
|
||||
sourceAdj func(src []byte, indent int) []byte,
|
||||
indentAdj int,
|
||||
src []byte,
|
||||
cfg printer.Config,
|
||||
) ([]byte, error) {
|
||||
if sourceAdj == nil {
|
||||
// Complete source file.
|
||||
var buf bytes.Buffer
|
||||
err := cfg.Fprint(&buf, fset, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// Partial source file.
|
||||
// Determine and prepend leading space.
|
||||
i, j := 0, 0
|
||||
for j < len(src) && isSpace(src[j]) {
|
||||
if src[j] == '\n' {
|
||||
i = j + 1 // byte offset of last line in leading space
|
||||
}
|
||||
j++
|
||||
}
|
||||
var res []byte
|
||||
res = append(res, src[:i]...)
|
||||
|
||||
// Determine and prepend indentation of first code line.
|
||||
// Spaces are ignored unless there are no tabs,
|
||||
// in which case spaces count as one tab.
|
||||
indent := 0
|
||||
hasSpace := false
|
||||
for _, b := range src[i:j] {
|
||||
switch b {
|
||||
case ' ':
|
||||
hasSpace = true
|
||||
case '\t':
|
||||
indent++
|
||||
}
|
||||
}
|
||||
if indent == 0 && hasSpace {
|
||||
indent = 1
|
||||
}
|
||||
for i := 0; i < indent; i++ {
|
||||
res = append(res, '\t')
|
||||
}
|
||||
|
||||
// Format the source.
|
||||
// Write it without any leading and trailing space.
|
||||
cfg.Indent = indent + indentAdj
|
||||
var buf bytes.Buffer
|
||||
err := cfg.Fprint(&buf, fset, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := sourceAdj(buf.Bytes(), cfg.Indent)
|
||||
|
||||
// If the adjusted output is empty, the source
|
||||
// was empty but (possibly) for white space.
|
||||
// The result is the incoming source.
|
||||
if len(out) == 0 {
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// Otherwise, append output to leading space.
|
||||
res = append(res, out...)
|
||||
|
||||
// Determine and append trailing space.
|
||||
i = len(src)
|
||||
for i > 0 && isSpace(src[i-1]) {
|
||||
i--
|
||||
}
|
||||
return append(res, src[i:]...), nil
|
||||
}
|
||||
|
||||
// isSpace reports whether the byte is a space character.
|
||||
// isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'.
|
||||
func isSpace(b byte) bool {
|
||||
return b == ' ' || b == '\t' || b == '\n' || b == '\r'
|
||||
}
|
303
vendor/github.com/golangci/gofmt/gofmt/rewrite.go
generated
vendored
Normal file
303
vendor/github.com/golangci/gofmt/gofmt/rewrite.go
generated
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gofmt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func initRewrite() {
|
||||
if *rewriteRule == "" {
|
||||
rewrite = nil // disable any previous rewrite
|
||||
return
|
||||
}
|
||||
f := strings.Split(*rewriteRule, "->")
|
||||
if len(f) != 2 {
|
||||
fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
pattern := parseExpr(f[0], "pattern")
|
||||
replace := parseExpr(f[1], "replacement")
|
||||
rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) }
|
||||
}
|
||||
|
||||
// parseExpr parses s as an expression.
|
||||
// It might make sense to expand this to allow statement patterns,
|
||||
// but there are problems with preserving formatting and also
|
||||
// with what a wildcard for a statement looks like.
|
||||
func parseExpr(s, what string) ast.Expr {
|
||||
x, err := parser.ParseExpr(s)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Keep this function for debugging.
|
||||
/*
|
||||
func dump(msg string, val reflect.Value) {
|
||||
fmt.Printf("%s:\n", msg)
|
||||
ast.Print(fileSet, val.Interface())
|
||||
fmt.Println()
|
||||
}
|
||||
*/
|
||||
|
||||
// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
|
||||
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
|
||||
cmap := ast.NewCommentMap(fileSet, p, p.Comments)
|
||||
m := make(map[string]reflect.Value)
|
||||
pat := reflect.ValueOf(pattern)
|
||||
repl := reflect.ValueOf(replace)
|
||||
|
||||
var rewriteVal func(val reflect.Value) reflect.Value
|
||||
rewriteVal = func(val reflect.Value) reflect.Value {
|
||||
// don't bother if val is invalid to start with
|
||||
if !val.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
val = apply(rewriteVal, val)
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
if match(m, pat, val) {
|
||||
val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos()))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File)
|
||||
r.Comments = cmap.Filter(r).Comments() // recreate comments list
|
||||
return r
|
||||
}
|
||||
|
||||
// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y.
|
||||
func set(x, y reflect.Value) {
|
||||
// don't bother if x cannot be set or y is invalid
|
||||
if !x.CanSet() || !y.IsValid() {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if x := recover(); x != nil {
|
||||
if s, ok := x.(string); ok &&
|
||||
(strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) {
|
||||
// x cannot be set to y - ignore this rewrite
|
||||
return
|
||||
}
|
||||
panic(x)
|
||||
}
|
||||
}()
|
||||
x.Set(y)
|
||||
}
|
||||
|
||||
// Values/types for special cases.
|
||||
var (
|
||||
objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
|
||||
scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
|
||||
|
||||
identType = reflect.TypeOf((*ast.Ident)(nil))
|
||||
objectPtrType = reflect.TypeOf((*ast.Object)(nil))
|
||||
positionType = reflect.TypeOf(token.NoPos)
|
||||
callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
|
||||
scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
|
||||
)
|
||||
|
||||
// apply replaces each AST field x in val with f(x), returning val.
|
||||
// To avoid extra conversions, f operates on the reflect.Value form.
|
||||
func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value {
|
||||
if !val.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// *ast.Objects introduce cycles and are likely incorrect after
|
||||
// rewrite; don't follow them but replace with nil instead
|
||||
if val.Type() == objectPtrType {
|
||||
return objectPtrNil
|
||||
}
|
||||
|
||||
// similarly for scopes: they are likely incorrect after a rewrite;
|
||||
// replace them with nil
|
||||
if val.Type() == scopePtrType {
|
||||
return scopePtrNil
|
||||
}
|
||||
|
||||
switch v := reflect.Indirect(val); v.Kind() {
|
||||
case reflect.Slice:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
e := v.Index(i)
|
||||
set(e, f(e))
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
e := v.Field(i)
|
||||
set(e, f(e))
|
||||
}
|
||||
case reflect.Interface:
|
||||
e := v.Elem()
|
||||
set(v, f(e))
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func isWildcard(s string) bool {
|
||||
rune, size := utf8.DecodeRuneInString(s)
|
||||
return size == len(s) && unicode.IsLower(rune)
|
||||
}
|
||||
|
||||
// match reports whether pattern matches val,
|
||||
// recording wildcard submatches in m.
|
||||
// If m == nil, match checks whether pattern == val.
|
||||
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
|
||||
// Wildcard matches any expression. If it appears multiple
|
||||
// times in the pattern, it must match the same expression
|
||||
// each time.
|
||||
if m != nil && pattern.IsValid() && pattern.Type() == identType {
|
||||
name := pattern.Interface().(*ast.Ident).Name
|
||||
if isWildcard(name) && val.IsValid() {
|
||||
// wildcards only match valid (non-nil) expressions.
|
||||
if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
|
||||
if old, ok := m[name]; ok {
|
||||
return match(nil, old, val)
|
||||
}
|
||||
m[name] = val
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, pattern and val must match recursively.
|
||||
if !pattern.IsValid() || !val.IsValid() {
|
||||
return !pattern.IsValid() && !val.IsValid()
|
||||
}
|
||||
if pattern.Type() != val.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Special cases.
|
||||
switch pattern.Type() {
|
||||
case identType:
|
||||
// For identifiers, only the names need to match
|
||||
// (and none of the other *ast.Object information).
|
||||
// This is a common case, handle it all here instead
|
||||
// of recursing down any further via reflection.
|
||||
p := pattern.Interface().(*ast.Ident)
|
||||
v := val.Interface().(*ast.Ident)
|
||||
return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
|
||||
case objectPtrType, positionType:
|
||||
// object pointers and token positions always match
|
||||
return true
|
||||
case callExprType:
|
||||
// For calls, the Ellipsis fields (token.Position) must
|
||||
// match since that is how f(x) and f(x...) are different.
|
||||
// Check them here but fall through for the remaining fields.
|
||||
p := pattern.Interface().(*ast.CallExpr)
|
||||
v := val.Interface().(*ast.CallExpr)
|
||||
if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
p := reflect.Indirect(pattern)
|
||||
v := reflect.Indirect(val)
|
||||
if !p.IsValid() || !v.IsValid() {
|
||||
return !p.IsValid() && !v.IsValid()
|
||||
}
|
||||
|
||||
switch p.Kind() {
|
||||
case reflect.Slice:
|
||||
if p.Len() != v.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < p.Len(); i++ {
|
||||
if !match(m, p.Index(i), v.Index(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Struct:
|
||||
for i := 0; i < p.NumField(); i++ {
|
||||
if !match(m, p.Field(i), v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Interface:
|
||||
return match(m, p.Elem(), v.Elem())
|
||||
}
|
||||
|
||||
// Handle token integers, etc.
|
||||
return p.Interface() == v.Interface()
|
||||
}
|
||||
|
||||
// subst returns a copy of pattern with values from m substituted in place
|
||||
// of wildcards and pos used as the position of tokens from the pattern.
|
||||
// if m == nil, subst returns a copy of pattern and doesn't change the line
|
||||
// number information.
|
||||
func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value {
|
||||
if !pattern.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
// Wildcard gets replaced with map value.
|
||||
if m != nil && pattern.Type() == identType {
|
||||
name := pattern.Interface().(*ast.Ident).Name
|
||||
if isWildcard(name) {
|
||||
if old, ok := m[name]; ok {
|
||||
return subst(nil, old, reflect.Value{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pos.IsValid() && pattern.Type() == positionType {
|
||||
// use new position only if old position was valid in the first place
|
||||
if old := pattern.Interface().(token.Pos); !old.IsValid() {
|
||||
return pattern
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
// Otherwise copy.
|
||||
switch p := pattern; p.Kind() {
|
||||
case reflect.Slice:
|
||||
v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
|
||||
for i := 0; i < p.Len(); i++ {
|
||||
v.Index(i).Set(subst(m, p.Index(i), pos))
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Struct:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
for i := 0; i < p.NumField(); i++ {
|
||||
v.Field(i).Set(subst(m, p.Field(i), pos))
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Ptr:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
if elem := p.Elem(); elem.IsValid() {
|
||||
v.Set(subst(m, elem, pos).Addr())
|
||||
}
|
||||
return v
|
||||
|
||||
case reflect.Interface:
|
||||
v := reflect.New(p.Type()).Elem()
|
||||
if elem := p.Elem(); elem.IsValid() {
|
||||
v.Set(subst(m, elem, pos))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
return pattern
|
||||
}
|
165
vendor/github.com/golangci/gofmt/gofmt/simplify.go
generated
vendored
Normal file
165
vendor/github.com/golangci/gofmt/gofmt/simplify.go
generated
vendored
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gofmt
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type simplifier struct{}
|
||||
|
||||
func (s simplifier) Visit(node ast.Node) ast.Visitor {
|
||||
switch n := node.(type) {
|
||||
case *ast.CompositeLit:
|
||||
// array, slice, and map composite literals may be simplified
|
||||
outer := n
|
||||
var keyType, eltType ast.Expr
|
||||
switch typ := outer.Type.(type) {
|
||||
case *ast.ArrayType:
|
||||
eltType = typ.Elt
|
||||
case *ast.MapType:
|
||||
keyType = typ.Key
|
||||
eltType = typ.Value
|
||||
}
|
||||
|
||||
if eltType != nil {
|
||||
var ktyp reflect.Value
|
||||
if keyType != nil {
|
||||
ktyp = reflect.ValueOf(keyType)
|
||||
}
|
||||
typ := reflect.ValueOf(eltType)
|
||||
for i, x := range outer.Elts {
|
||||
px := &outer.Elts[i]
|
||||
// look at value of indexed/named elements
|
||||
if t, ok := x.(*ast.KeyValueExpr); ok {
|
||||
if keyType != nil {
|
||||
s.simplifyLiteral(ktyp, keyType, t.Key, &t.Key)
|
||||
}
|
||||
x = t.Value
|
||||
px = &t.Value
|
||||
}
|
||||
s.simplifyLiteral(typ, eltType, x, px)
|
||||
}
|
||||
// node was simplified - stop walk (there are no subnodes to simplify)
|
||||
return nil
|
||||
}
|
||||
|
||||
case *ast.SliceExpr:
|
||||
// a slice expression of the form: s[a:len(s)]
|
||||
// can be simplified to: s[a:]
|
||||
// if s is "simple enough" (for now we only accept identifiers)
|
||||
//
|
||||
// Note: This may not be correct because len may have been redeclared in another
|
||||
// file belonging to the same package. However, this is extremely unlikely
|
||||
// and so far (April 2016, after years of supporting this rewrite feature)
|
||||
// has never come up, so let's keep it working as is (see also #15153).
|
||||
if n.Max != nil {
|
||||
// - 3-index slices always require the 2nd and 3rd index
|
||||
break
|
||||
}
|
||||
if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
|
||||
// the array/slice object is a single, resolved identifier
|
||||
if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() {
|
||||
// the high expression is a function call with a single argument
|
||||
if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil {
|
||||
// the function called is "len" and it is not locally defined; and
|
||||
// because we don't have dot imports, it must be the predefined len()
|
||||
if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj {
|
||||
// the len argument is the array/slice object
|
||||
n.High = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
|
||||
// but we leave them as is since sometimes we want to be very explicit
|
||||
// about the lower bound.
|
||||
// An example where the 0 helps:
|
||||
// x, y, z := b[0:2], b[2:4], b[4:6]
|
||||
// An example where it does not:
|
||||
// x, y := b[:n], b[n:]
|
||||
|
||||
case *ast.RangeStmt:
|
||||
// - a range of the form: for x, _ = range v {...}
|
||||
// can be simplified to: for x = range v {...}
|
||||
// - a range of the form: for _ = range v {...}
|
||||
// can be simplified to: for range v {...}
|
||||
if isBlank(n.Value) {
|
||||
n.Value = nil
|
||||
}
|
||||
if isBlank(n.Key) && n.Value == nil {
|
||||
n.Key = nil
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) {
|
||||
ast.Walk(s, x) // simplify x
|
||||
|
||||
// if the element is a composite literal and its literal type
|
||||
// matches the outer literal's element type exactly, the inner
|
||||
// literal type may be omitted
|
||||
if inner, ok := x.(*ast.CompositeLit); ok {
|
||||
if match(nil, typ, reflect.ValueOf(inner.Type)) {
|
||||
inner.Type = nil
|
||||
}
|
||||
}
|
||||
// if the outer literal's element type is a pointer type *T
|
||||
// and the element is & of a composite literal of type T,
|
||||
// the inner &T may be omitted.
|
||||
if ptr, ok := astType.(*ast.StarExpr); ok {
|
||||
if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
|
||||
if inner, ok := addr.X.(*ast.CompositeLit); ok {
|
||||
if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
|
||||
inner.Type = nil // drop T
|
||||
*px = inner // drop &
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isBlank(x ast.Expr) bool {
|
||||
ident, ok := x.(*ast.Ident)
|
||||
return ok && ident.Name == "_"
|
||||
}
|
||||
|
||||
func simplify(f *ast.File) {
|
||||
// remove empty declarations such as "const ()", etc
|
||||
removeEmptyDeclGroups(f)
|
||||
|
||||
var s simplifier
|
||||
ast.Walk(s, f)
|
||||
}
|
||||
|
||||
func removeEmptyDeclGroups(f *ast.File) {
|
||||
i := 0
|
||||
for _, d := range f.Decls {
|
||||
if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) {
|
||||
f.Decls[i] = d
|
||||
i++
|
||||
}
|
||||
}
|
||||
f.Decls = f.Decls[:i]
|
||||
}
|
||||
|
||||
func isEmpty(f *ast.File, g *ast.GenDecl) bool {
|
||||
if g.Doc != nil || g.Specs != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, c := range f.Comments {
|
||||
// if there is a comment in the declaration, it is not considered empty
|
||||
if g.Pos() <= c.Pos() && c.End() <= g.End() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
27
vendor/github.com/golangci/gofmt/goimports/LICENSE
generated
vendored
Normal file
27
vendor/github.com/golangci/gofmt/goimports/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
358
vendor/github.com/golangci/gofmt/goimports/goimports.go
generated
vendored
Normal file
358
vendor/github.com/golangci/gofmt/goimports/goimports.go
generated
vendored
Normal file
@ -0,0 +1,358 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package goimports
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
var (
|
||||
// main operation modes
|
||||
list = flag.Bool("goimports.l", false, "list files whose formatting differs from goimport's")
|
||||
write = flag.Bool("goimports.w", false, "write result to (source) file instead of stdout")
|
||||
doDiff = flag.Bool("goimports.d", false, "display diffs instead of rewriting files")
|
||||
srcdir = flag.String("goimports.srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.")
|
||||
verbose bool // verbose logging
|
||||
|
||||
cpuProfile = flag.String("goimports.cpuprofile", "", "CPU profile output")
|
||||
memProfile = flag.String("goimports.memprofile", "", "memory profile output")
|
||||
memProfileRate = flag.Int("goimports.memrate", 0, "if > 0, sets runtime.MemProfileRate")
|
||||
|
||||
options = &imports.Options{
|
||||
TabWidth: 8,
|
||||
TabIndent: true,
|
||||
Comments: true,
|
||||
Fragment: true,
|
||||
}
|
||||
exitCode = 0
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&options.AllErrors, "goimports.e", false, "report all errors (not just the first 10 on different lines)")
|
||||
flag.StringVar(&imports.LocalPrefix, "goimports.local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
|
||||
}
|
||||
|
||||
func report(err error) {
|
||||
scanner.PrintError(os.Stderr, err)
|
||||
exitCode = 2
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func isGoFile(f os.FileInfo) bool {
|
||||
// ignore non-Go files
|
||||
name := f.Name()
|
||||
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
|
||||
}
|
||||
|
||||
// argumentType is which mode goimports was invoked as.
|
||||
type argumentType int
|
||||
|
||||
const (
|
||||
// fromStdin means the user is piping their source into goimports.
|
||||
fromStdin argumentType = iota
|
||||
|
||||
// singleArg is the common case from editors, when goimports is run on
|
||||
// a single file.
|
||||
singleArg
|
||||
|
||||
// multipleArg is when the user ran "goimports file1.go file2.go"
|
||||
// or ran goimports on a directory tree.
|
||||
multipleArg
|
||||
)
|
||||
|
||||
func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error {
|
||||
opt := options
|
||||
if argType == fromStdin {
|
||||
nopt := *options
|
||||
nopt.Fragment = true
|
||||
opt = &nopt
|
||||
}
|
||||
|
||||
if in == nil {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
in = f
|
||||
}
|
||||
|
||||
src, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target := filename
|
||||
if *srcdir != "" {
|
||||
// Determine whether the provided -srcdirc is a directory or file
|
||||
// and then use it to override the target.
|
||||
//
|
||||
// See https://github.com/dominikh/go-mode.el/issues/146
|
||||
if isFile(*srcdir) {
|
||||
if argType == multipleArg {
|
||||
return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories")
|
||||
}
|
||||
target = *srcdir
|
||||
} else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) {
|
||||
// For a file which doesn't exist on disk yet, but might shortly.
|
||||
// e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk.
|
||||
// The goimports on-save hook writes the buffer to a temp file
|
||||
// first and runs goimports before the actual save to newfile.go.
|
||||
// The editor's buffer is named "newfile.go" so that is passed to goimports as:
|
||||
// goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go
|
||||
// and then the editor reloads the result from the tmp file and writes
|
||||
// it to newfile.go.
|
||||
target = *srcdir
|
||||
} else {
|
||||
// Pretend that file is from *srcdir in order to decide
|
||||
// visible imports correctly.
|
||||
target = filepath.Join(*srcdir, filepath.Base(filename))
|
||||
}
|
||||
}
|
||||
|
||||
res, err := imports.Process(target, src, opt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(src, res) {
|
||||
// formatting has changed
|
||||
if *list {
|
||||
fmt.Fprintln(out, filename)
|
||||
}
|
||||
if *write {
|
||||
if argType == fromStdin {
|
||||
// filename is "<standard input>"
|
||||
return errors.New("can't use -w on stdin")
|
||||
}
|
||||
err = ioutil.WriteFile(filename, res, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if *doDiff {
|
||||
if argType == fromStdin {
|
||||
filename = "stdin.go" // because <standard input>.orig looks silly
|
||||
}
|
||||
data, err := diff(src, res, filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("computing diff: %s", err)
|
||||
}
|
||||
fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
|
||||
out.Write(data)
|
||||
}
|
||||
}
|
||||
|
||||
if !*list && !*write && !*doDiff {
|
||||
_, err = out.Write(res)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func visitFile(path string, f os.FileInfo, err error) error {
|
||||
if err == nil && isGoFile(f) {
|
||||
err = processFile(path, nil, os.Stdout, multipleArg)
|
||||
}
|
||||
if err != nil {
|
||||
report(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func walkDir(path string) {
|
||||
filepath.Walk(path, visitFile)
|
||||
}
|
||||
|
||||
// parseFlags parses command line flags and returns the paths to process.
|
||||
// It's a var so that custom implementations can replace it in other files.
|
||||
var parseFlags = func() []string {
|
||||
flag.BoolVar(&verbose, "v", false, "verbose logging")
|
||||
|
||||
flag.Parse()
|
||||
return flag.Args()
|
||||
}
|
||||
|
||||
func bufferedFileWriter(dest string) (w io.Writer, close func()) {
|
||||
f, err := os.Create(dest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bw := bufio.NewWriter(f)
|
||||
return bw, func() {
|
||||
if err := bw.Flush(); err != nil {
|
||||
log.Fatalf("error flushing %v: %v", dest, err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func gofmtMain() {
|
||||
flag.Usage = usage
|
||||
paths := parseFlags()
|
||||
|
||||
if *cpuProfile != "" {
|
||||
bw, flush := bufferedFileWriter(*cpuProfile)
|
||||
pprof.StartCPUProfile(bw)
|
||||
defer flush()
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
// doTrace is a conditionally compiled wrapper around runtime/trace. It is
|
||||
// used to allow goimports to compile under gccgo, which does not support
|
||||
// runtime/trace. See https://golang.org/issue/15544.
|
||||
if *memProfileRate > 0 {
|
||||
runtime.MemProfileRate = *memProfileRate
|
||||
bw, flush := bufferedFileWriter(*memProfile)
|
||||
defer func() {
|
||||
runtime.GC() // materialize all statistics
|
||||
if err := pprof.WriteHeapProfile(bw); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
flush()
|
||||
}()
|
||||
}
|
||||
|
||||
if verbose {
|
||||
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
|
||||
imports.Debug = true
|
||||
}
|
||||
if options.TabWidth < 0 {
|
||||
fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth)
|
||||
exitCode = 2
|
||||
return
|
||||
}
|
||||
|
||||
if len(paths) == 0 {
|
||||
if err := processFile("<standard input>", os.Stdin, os.Stdout, fromStdin); err != nil {
|
||||
report(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
argType := singleArg
|
||||
if len(paths) > 1 {
|
||||
argType = multipleArg
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
switch dir, err := os.Stat(path); {
|
||||
case err != nil:
|
||||
report(err)
|
||||
case dir.IsDir():
|
||||
walkDir(path)
|
||||
default:
|
||||
if err := processFile(path, nil, os.Stdout, argType); err != nil {
|
||||
report(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func writeTempFile(dir, prefix string, data []byte) (string, error) {
|
||||
file, err := ioutil.TempFile(dir, prefix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = file.Write(data)
|
||||
if err1 := file.Close(); err == nil {
|
||||
err = err1
|
||||
}
|
||||
if err != nil {
|
||||
os.Remove(file.Name())
|
||||
return "", err
|
||||
}
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
func diff(b1, b2 []byte, filename string) (data []byte, err error) {
|
||||
f1, err := writeTempFile("", "gofmt", b1)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(f1)
|
||||
|
||||
f2, err := writeTempFile("", "gofmt", b2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer os.Remove(f2)
|
||||
|
||||
cmd := "diff"
|
||||
if runtime.GOOS == "plan9" {
|
||||
cmd = "/bin/ape/diff"
|
||||
}
|
||||
|
||||
data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput()
|
||||
if len(data) > 0 {
|
||||
// diff exits with a non-zero status when the files don't match.
|
||||
// Ignore that failure as long as we get output.
|
||||
return replaceTempFilename(data, filename)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// replaceTempFilename replaces temporary filenames in diff with actual one.
|
||||
//
|
||||
// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
|
||||
// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
|
||||
// ...
|
||||
// ->
|
||||
// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
|
||||
// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
|
||||
// ...
|
||||
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
|
||||
bs := bytes.SplitN(diff, []byte{'\n'}, 3)
|
||||
if len(bs) < 3 {
|
||||
return nil, fmt.Errorf("got unexpected diff for %s", filename)
|
||||
}
|
||||
// Preserve timestamps.
|
||||
var t0, t1 []byte
|
||||
if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
|
||||
t0 = bs[0][i:]
|
||||
}
|
||||
if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
|
||||
t1 = bs[1][i:]
|
||||
}
|
||||
// Always print filepath with slash separator.
|
||||
f := filepath.ToSlash(filename)
|
||||
bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
|
||||
bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
|
||||
return bytes.Join(bs, []byte{'\n'}), nil
|
||||
}
|
||||
|
||||
// isFile reports whether name is a file.
|
||||
func isFile(name string) bool {
|
||||
fi, err := os.Stat(name)
|
||||
return err == nil && fi.Mode().IsRegular()
|
||||
}
|
||||
|
||||
// isDir reports whether name is a directory.
|
||||
func isDir(name string) bool {
|
||||
fi, err := os.Stat(name)
|
||||
return err == nil && fi.IsDir()
|
||||
}
|
33
vendor/github.com/golangci/gofmt/goimports/golangci.go
generated
vendored
Normal file
33
vendor/github.com/golangci/gofmt/goimports/golangci.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package goimports
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
func Run(filename string) ([]byte, error) {
|
||||
src, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := imports.Process(filename, src, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(src, res) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// formatting has changed
|
||||
data, err := diff(src, res, filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error computing diff: %s", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
1125
vendor/golang.org/x/tools/imports/fix.go
generated
vendored
Normal file
1125
vendor/golang.org/x/tools/imports/fix.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
310
vendor/golang.org/x/tools/imports/imports.go
generated
vendored
Normal file
310
vendor/golang.org/x/tools/imports/imports.go
generated
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run mkstdlib.go
|
||||
|
||||
// Package imports implements a Go pretty-printer (like package "go/format")
|
||||
// that also adds or removes import statements as necessary.
|
||||
package imports // import "golang.org/x/tools/imports"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Options specifies options for processing files.
|
||||
type Options struct {
|
||||
Fragment bool // Accept fragment of a source file (no package statement)
|
||||
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
||||
|
||||
Comments bool // Print comments (true if nil *Options provided)
|
||||
TabIndent bool // Use tabs for indent (true if nil *Options provided)
|
||||
TabWidth int // Tab width (8 if nil *Options provided)
|
||||
|
||||
FormatOnly bool // Disable the insertion and deletion of imports
|
||||
}
|
||||
|
||||
// Process formats and adjusts imports for the provided file.
|
||||
// If opt is nil the defaults are used.
|
||||
//
|
||||
// Note that filename's directory influences which imports can be chosen,
|
||||
// so it is important that filename be accurate.
|
||||
// To process data ``as if'' it were in filename, pass the data as a non-nil src.
|
||||
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
||||
if opt == nil {
|
||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||
}
|
||||
if src == nil {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
src = b
|
||||
}
|
||||
|
||||
fileSet := token.NewFileSet()
|
||||
file, adjust, err := parse(fileSet, filename, src, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !opt.FormatOnly {
|
||||
_, err = fixImports(fileSet, file, filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sortImports(fileSet, file)
|
||||
imps := astutil.Imports(fileSet, file)
|
||||
var spacesBefore []string // import paths we need spaces before
|
||||
for _, impSection := range imps {
|
||||
// Within each block of contiguous imports, see if any
|
||||
// import lines are in different group numbers. If so,
|
||||
// we'll need to put a space between them so it's
|
||||
// compatible with gofmt.
|
||||
lastGroup := -1
|
||||
for _, importSpec := range impSection {
|
||||
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
||||
groupNum := importGroup(importPath)
|
||||
if groupNum != lastGroup && lastGroup != -1 {
|
||||
spacesBefore = append(spacesBefore, importPath)
|
||||
}
|
||||
lastGroup = groupNum
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
printerMode := printer.UseSpaces
|
||||
if opt.TabIndent {
|
||||
printerMode |= printer.TabIndent
|
||||
}
|
||||
printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err = printConfig.Fprint(&buf, fileSet, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := buf.Bytes()
|
||||
if adjust != nil {
|
||||
out = adjust(src, out)
|
||||
}
|
||||
if len(spacesBefore) > 0 {
|
||||
out, err = addImportSpaces(bytes.NewReader(out), spacesBefore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err = format.Source(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// parse parses src, which was read from filename,
|
||||
// as a Go source file or statement list.
|
||||
func parse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
|
||||
parserMode := parser.Mode(0)
|
||||
if opt.Comments {
|
||||
parserMode |= parser.ParseComments
|
||||
}
|
||||
if opt.AllErrors {
|
||||
parserMode |= parser.AllErrors
|
||||
}
|
||||
|
||||
// Try as whole source file.
|
||||
file, err := parser.ParseFile(fset, filename, src, parserMode)
|
||||
if err == nil {
|
||||
return file, nil, nil
|
||||
}
|
||||
// If the error is that the source file didn't begin with a
|
||||
// package line and we accept fragmented input, fall through to
|
||||
// try as a source fragment. Stop and return on any other error.
|
||||
if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// If this is a declaration list, make it a source file
|
||||
// by inserting a package clause.
|
||||
// Insert using a ;, not a newline, so that parse errors are on
|
||||
// the correct line.
|
||||
const prefix = "package main;"
|
||||
psrc := append([]byte(prefix), src...)
|
||||
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
|
||||
if err == nil {
|
||||
// Gofmt will turn the ; into a \n.
|
||||
// Do that ourselves now and update the file contents,
|
||||
// so that positions and line numbers are correct going forward.
|
||||
psrc[len(prefix)-1] = '\n'
|
||||
fset.File(file.Package).SetLinesForContent(psrc)
|
||||
|
||||
// If a main function exists, we will assume this is a main
|
||||
// package and leave the file.
|
||||
if containsMainFunc(file) {
|
||||
return file, nil, nil
|
||||
}
|
||||
|
||||
adjust := func(orig, src []byte) []byte {
|
||||
// Remove the package clause.
|
||||
src = src[len(prefix):]
|
||||
return matchSpace(orig, src)
|
||||
}
|
||||
return file, adjust, nil
|
||||
}
|
||||
// If the error is that the source file didn't begin with a
|
||||
// declaration, fall through to try as a statement list.
|
||||
// Stop and return on any other error.
|
||||
if !strings.Contains(err.Error(), "expected declaration") {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// If this is a statement list, make it a source file
|
||||
// by inserting a package clause and turning the list
|
||||
// into a function body. This handles expressions too.
|
||||
// Insert using a ;, not a newline, so that the line numbers
|
||||
// in fsrc match the ones in src.
|
||||
fsrc := append(append([]byte("package p; func _() {"), src...), '}')
|
||||
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
|
||||
if err == nil {
|
||||
adjust := func(orig, src []byte) []byte {
|
||||
// Remove the wrapping.
|
||||
// Gofmt has turned the ; into a \n\n.
|
||||
src = src[len("package p\n\nfunc _() {"):]
|
||||
src = src[:len(src)-len("}\n")]
|
||||
// Gofmt has also indented the function body one level.
|
||||
// Remove that indent.
|
||||
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
|
||||
return matchSpace(orig, src)
|
||||
}
|
||||
return file, adjust, nil
|
||||
}
|
||||
|
||||
// Failed, and out of options.
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// containsMainFunc checks if a file contains a function declaration with the
|
||||
// function signature 'func main()'
|
||||
func containsMainFunc(file *ast.File) bool {
|
||||
for _, decl := range file.Decls {
|
||||
if f, ok := decl.(*ast.FuncDecl); ok {
|
||||
if f.Name.Name != "main" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(f.Type.Params.List) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.Type.Results != nil && len(f.Type.Results.List) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func cutSpace(b []byte) (before, middle, after []byte) {
|
||||
i := 0
|
||||
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
|
||||
i++
|
||||
}
|
||||
j := len(b)
|
||||
for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
|
||||
j--
|
||||
}
|
||||
if i <= j {
|
||||
return b[:i], b[i:j], b[j:]
|
||||
}
|
||||
return nil, nil, b[j:]
|
||||
}
|
||||
|
||||
// matchSpace reformats src to use the same space context as orig.
|
||||
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
|
||||
// 2) matchSpace copies the indentation of the first non-blank line in orig
|
||||
// to every non-blank line in src.
|
||||
// 3) matchSpace copies the trailing space from orig and uses it in place
|
||||
// of src's trailing space.
|
||||
func matchSpace(orig []byte, src []byte) []byte {
|
||||
before, _, after := cutSpace(orig)
|
||||
i := bytes.LastIndex(before, []byte{'\n'})
|
||||
before, indent := before[:i+1], before[i+1:]
|
||||
|
||||
_, src, _ = cutSpace(src)
|
||||
|
||||
var b bytes.Buffer
|
||||
b.Write(before)
|
||||
for len(src) > 0 {
|
||||
line := src
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, src = line[:i+1], line[i+1:]
|
||||
} else {
|
||||
src = nil
|
||||
}
|
||||
if len(line) > 0 && line[0] != '\n' { // not blank
|
||||
b.Write(indent)
|
||||
}
|
||||
b.Write(line)
|
||||
}
|
||||
b.Write(after)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)
|
||||
|
||||
func addImportSpaces(r io.Reader, breaks []string) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
in := bufio.NewReader(r)
|
||||
inImports := false
|
||||
done := false
|
||||
for {
|
||||
s, err := in.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !inImports && !done && strings.HasPrefix(s, "import") {
|
||||
inImports = true
|
||||
}
|
||||
if inImports && (strings.HasPrefix(s, "var") ||
|
||||
strings.HasPrefix(s, "func") ||
|
||||
strings.HasPrefix(s, "const") ||
|
||||
strings.HasPrefix(s, "type")) {
|
||||
done = true
|
||||
inImports = false
|
||||
}
|
||||
if inImports && len(breaks) > 0 {
|
||||
if m := impLine.FindStringSubmatch(s); m != nil {
|
||||
if m[1] == breaks[0] {
|
||||
out.WriteByte('\n')
|
||||
breaks = breaks[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Fprint(&out, s)
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
173
vendor/golang.org/x/tools/imports/mkindex.go
generated
vendored
Normal file
173
vendor/golang.org/x/tools/imports/mkindex.go
generated
vendored
Normal file
@ -0,0 +1,173 @@
|
||||
// +build ignore
|
||||
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Command mkindex creates the file "pkgindex.go" containing an index of the Go
|
||||
// standard library. The file is intended to be built as part of the imports
|
||||
// package, so that the package may be used in environments where a GOROOT is
|
||||
// not available (such as App Engine).
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
pkgIndex = make(map[string][]pkg)
|
||||
exports = make(map[string]map[string]bool)
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Don't use GOPATH.
|
||||
ctx := build.Default
|
||||
ctx.GOPATH = ""
|
||||
|
||||
// Populate pkgIndex global from GOROOT.
|
||||
for _, path := range ctx.SrcDirs() {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
children, err := f.Readdir(-1)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
for _, child := range children {
|
||||
if child.IsDir() {
|
||||
loadPkg(path, child.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Populate exports global.
|
||||
for _, ps := range pkgIndex {
|
||||
for _, p := range ps {
|
||||
e := loadExports(p.dir)
|
||||
if e != nil {
|
||||
exports[p.dir] = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct source file.
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, pkgIndexHead)
|
||||
fmt.Fprintf(&buf, "var pkgIndexMaster = %#v\n", pkgIndex)
|
||||
fmt.Fprintf(&buf, "var exportsMaster = %#v\n", exports)
|
||||
src := buf.Bytes()
|
||||
|
||||
// Replace main.pkg type name with pkg.
|
||||
src = bytes.Replace(src, []byte("main.pkg"), []byte("pkg"), -1)
|
||||
// Replace actual GOROOT with "/go".
|
||||
src = bytes.Replace(src, []byte(ctx.GOROOT), []byte("/go"), -1)
|
||||
// Add some line wrapping.
|
||||
src = bytes.Replace(src, []byte("}, "), []byte("},\n"), -1)
|
||||
src = bytes.Replace(src, []byte("true, "), []byte("true,\n"), -1)
|
||||
|
||||
var err error
|
||||
src, err = format.Source(src)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Write out source file.
|
||||
err = ioutil.WriteFile("pkgindex.go", src, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
const pkgIndexHead = `package imports
|
||||
|
||||
func init() {
|
||||
pkgIndexOnce.Do(func() {
|
||||
pkgIndex.m = pkgIndexMaster
|
||||
})
|
||||
loadExports = func(dir string) map[string]bool {
|
||||
return exportsMaster[dir]
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
type pkg struct {
|
||||
importpath string // full pkg import path, e.g. "net/http"
|
||||
dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
|
||||
}
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
||||
func loadPkg(root, importpath string) {
|
||||
shortName := path.Base(importpath)
|
||||
if shortName == "testdata" {
|
||||
return
|
||||
}
|
||||
|
||||
dir := filepath.Join(root, importpath)
|
||||
pkgIndex[shortName] = append(pkgIndex[shortName], pkg{
|
||||
importpath: importpath,
|
||||
dir: dir,
|
||||
})
|
||||
|
||||
pkgDir, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
children, err := pkgDir.Readdir(-1)
|
||||
pkgDir.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, child := range children {
|
||||
name := child.Name()
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
|
||||
continue
|
||||
}
|
||||
if child.IsDir() {
|
||||
loadPkg(root, filepath.Join(importpath, name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadExports(dir string) map[string]bool {
|
||||
exports := make(map[string]bool)
|
||||
buildPkg, err := build.ImportDir(dir, 0)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no buildable Go source files in") {
|
||||
return nil
|
||||
}
|
||||
log.Printf("could not import %q: %v", dir, err)
|
||||
return nil
|
||||
}
|
||||
for _, file := range buildPkg.GoFiles {
|
||||
f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
|
||||
if err != nil {
|
||||
log.Printf("could not parse %q: %v", file, err)
|
||||
continue
|
||||
}
|
||||
for name := range f.Scope.Objects {
|
||||
if ast.IsExported(name) {
|
||||
exports[name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return exports
|
||||
}
|
107
vendor/golang.org/x/tools/imports/mkstdlib.go
generated
vendored
Normal file
107
vendor/golang.org/x/tools/imports/mkstdlib.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// +build ignore
|
||||
|
||||
// mkstdlib generates the zstdlib.go file, containing the Go standard
|
||||
// library API symbols. It's baked into the binary to avoid scanning
|
||||
// GOPATH in the common case.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func mustOpen(name string) io.Reader {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func api(base string) string {
|
||||
return filepath.Join(runtime.GOROOT(), "api", base)
|
||||
}
|
||||
|
||||
var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
|
||||
|
||||
func main() {
|
||||
var buf bytes.Buffer
|
||||
outf := func(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&buf, format, args...)
|
||||
}
|
||||
outf("// Code generated by mkstdlib.go. DO NOT EDIT.\n\n")
|
||||
outf("package imports\n")
|
||||
outf("var stdlib = map[string]string{\n")
|
||||
f := io.MultiReader(
|
||||
mustOpen(api("go1.txt")),
|
||||
mustOpen(api("go1.1.txt")),
|
||||
mustOpen(api("go1.2.txt")),
|
||||
mustOpen(api("go1.3.txt")),
|
||||
mustOpen(api("go1.4.txt")),
|
||||
mustOpen(api("go1.5.txt")),
|
||||
mustOpen(api("go1.6.txt")),
|
||||
mustOpen(api("go1.7.txt")),
|
||||
mustOpen(api("go1.8.txt")),
|
||||
mustOpen(api("go1.9.txt")),
|
||||
mustOpen(api("go1.10.txt")),
|
||||
)
|
||||
sc := bufio.NewScanner(f)
|
||||
fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
|
||||
ambiguous := map[string]bool{}
|
||||
var keys []string
|
||||
for sc.Scan() {
|
||||
l := sc.Text()
|
||||
has := func(v string) bool { return strings.Contains(l, v) }
|
||||
if has("struct, ") || has("interface, ") || has(", method (") {
|
||||
continue
|
||||
}
|
||||
if m := sym.FindStringSubmatch(l); m != nil {
|
||||
full := m[1]
|
||||
key := path.Base(full) + "." + m[2]
|
||||
if exist, ok := fullImport[key]; ok {
|
||||
if exist != full {
|
||||
ambiguous[key] = true
|
||||
}
|
||||
} else {
|
||||
fullImport[key] = full
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := sc.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
if ambiguous[key] {
|
||||
outf("\t// %q is ambiguous\n", key)
|
||||
} else {
|
||||
outf("\t%q: %q,\n", key, fullImport[key])
|
||||
}
|
||||
}
|
||||
outf("\n")
|
||||
for _, sym := range [...]string{"Alignof", "ArbitraryType", "Offsetof", "Pointer", "Sizeof"} {
|
||||
outf("\t%q: %q,\n", "unsafe."+sym, "unsafe")
|
||||
}
|
||||
outf("}\n")
|
||||
fmtbuf, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile("zstdlib.go", fmtbuf, 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
212
vendor/golang.org/x/tools/imports/sortimports.go
generated
vendored
Normal file
212
vendor/golang.org/x/tools/imports/sortimports.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Hacked up copy of go/ast/import.go
|
||||
|
||||
package imports
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// sortImports sorts runs of consecutive import lines in import blocks in f.
|
||||
// It also removes duplicate imports when it is possible to do so without data loss.
|
||||
func sortImports(fset *token.FileSet, f *ast.File) {
|
||||
for i, d := range f.Decls {
|
||||
d, ok := d.(*ast.GenDecl)
|
||||
if !ok || d.Tok != token.IMPORT {
|
||||
// Not an import declaration, so we're done.
|
||||
// Imports are always first.
|
||||
break
|
||||
}
|
||||
|
||||
if len(d.Specs) == 0 {
|
||||
// Empty import block, remove it.
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
}
|
||||
|
||||
if !d.Lparen.IsValid() {
|
||||
// Not a block: sorted by default.
|
||||
continue
|
||||
}
|
||||
|
||||
// Identify and sort runs of specs on successive lines.
|
||||
i := 0
|
||||
specs := d.Specs[:0]
|
||||
for j, s := range d.Specs {
|
||||
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
|
||||
// j begins a new run. End this one.
|
||||
specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
|
||||
i = j
|
||||
}
|
||||
}
|
||||
specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
|
||||
d.Specs = specs
|
||||
|
||||
// Deduping can leave a blank line before the rparen; clean that up.
|
||||
if len(d.Specs) > 0 {
|
||||
lastSpec := d.Specs[len(d.Specs)-1]
|
||||
lastLine := fset.Position(lastSpec.Pos()).Line
|
||||
if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
|
||||
fset.File(d.Rparen).MergeLine(rParenLine - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importPath(s ast.Spec) string {
|
||||
t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value)
|
||||
if err == nil {
|
||||
return t
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func importName(s ast.Spec) string {
|
||||
n := s.(*ast.ImportSpec).Name
|
||||
if n == nil {
|
||||
return ""
|
||||
}
|
||||
return n.Name
|
||||
}
|
||||
|
||||
func importComment(s ast.Spec) string {
|
||||
c := s.(*ast.ImportSpec).Comment
|
||||
if c == nil {
|
||||
return ""
|
||||
}
|
||||
return c.Text()
|
||||
}
|
||||
|
||||
// collapse indicates whether prev may be removed, leaving only next.
|
||||
func collapse(prev, next ast.Spec) bool {
|
||||
if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
|
||||
return false
|
||||
}
|
||||
return prev.(*ast.ImportSpec).Comment == nil
|
||||
}
|
||||
|
||||
type posSpan struct {
|
||||
Start token.Pos
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
// Can't short-circuit here even if specs are already sorted,
|
||||
// since they might yet need deduplication.
|
||||
// A lone import, however, may be safely ignored.
|
||||
if len(specs) <= 1 {
|
||||
return specs
|
||||
}
|
||||
|
||||
// Record positions for specs.
|
||||
pos := make([]posSpan, len(specs))
|
||||
for i, s := range specs {
|
||||
pos[i] = posSpan{s.Pos(), s.End()}
|
||||
}
|
||||
|
||||
// Identify comments in this range.
|
||||
// Any comment from pos[0].Start to the final line counts.
|
||||
lastLine := fset.Position(pos[len(pos)-1].End).Line
|
||||
cstart := len(f.Comments)
|
||||
cend := len(f.Comments)
|
||||
for i, g := range f.Comments {
|
||||
if g.Pos() < pos[0].Start {
|
||||
continue
|
||||
}
|
||||
if i < cstart {
|
||||
cstart = i
|
||||
}
|
||||
if fset.Position(g.End()).Line > lastLine {
|
||||
cend = i
|
||||
break
|
||||
}
|
||||
}
|
||||
comments := f.Comments[cstart:cend]
|
||||
|
||||
// Assign each comment to the import spec preceding it.
|
||||
importComment := map[*ast.ImportSpec][]*ast.CommentGroup{}
|
||||
specIndex := 0
|
||||
for _, g := range comments {
|
||||
for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
|
||||
specIndex++
|
||||
}
|
||||
s := specs[specIndex].(*ast.ImportSpec)
|
||||
importComment[s] = append(importComment[s], g)
|
||||
}
|
||||
|
||||
// Sort the import specs by import path.
|
||||
// Remove duplicates, when possible without data loss.
|
||||
// Reassign the import paths to have the same position sequence.
|
||||
// Reassign each comment to abut the end of its spec.
|
||||
// Sort the comments by new position.
|
||||
sort.Sort(byImportSpec(specs))
|
||||
|
||||
// Dedup. Thanks to our sorting, we can just consider
|
||||
// adjacent pairs of imports.
|
||||
deduped := specs[:0]
|
||||
for i, s := range specs {
|
||||
if i == len(specs)-1 || !collapse(s, specs[i+1]) {
|
||||
deduped = append(deduped, s)
|
||||
} else {
|
||||
p := s.Pos()
|
||||
fset.File(p).MergeLine(fset.Position(p).Line)
|
||||
}
|
||||
}
|
||||
specs = deduped
|
||||
|
||||
// Fix up comment positions
|
||||
for i, s := range specs {
|
||||
s := s.(*ast.ImportSpec)
|
||||
if s.Name != nil {
|
||||
s.Name.NamePos = pos[i].Start
|
||||
}
|
||||
s.Path.ValuePos = pos[i].Start
|
||||
s.EndPos = pos[i].End
|
||||
for _, g := range importComment[s] {
|
||||
for _, c := range g.List {
|
||||
c.Slash = pos[i].End
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(byCommentPos(comments))
|
||||
|
||||
return specs
|
||||
}
|
||||
|
||||
type byImportSpec []ast.Spec // slice of *ast.ImportSpec
|
||||
|
||||
func (x byImportSpec) Len() int { return len(x) }
|
||||
func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byImportSpec) Less(i, j int) bool {
|
||||
ipath := importPath(x[i])
|
||||
jpath := importPath(x[j])
|
||||
|
||||
igroup := importGroup(ipath)
|
||||
jgroup := importGroup(jpath)
|
||||
if igroup != jgroup {
|
||||
return igroup < jgroup
|
||||
}
|
||||
|
||||
if ipath != jpath {
|
||||
return ipath < jpath
|
||||
}
|
||||
iname := importName(x[i])
|
||||
jname := importName(x[j])
|
||||
|
||||
if iname != jname {
|
||||
return iname < jname
|
||||
}
|
||||
return importComment(x[i]) < importComment(x[j])
|
||||
}
|
||||
|
||||
type byCommentPos []*ast.CommentGroup
|
||||
|
||||
func (x byCommentPos) Len() int { return len(x) }
|
||||
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }
|
9734
vendor/golang.org/x/tools/imports/zstdlib.go
generated
vendored
Normal file
9734
vendor/golang.org/x/tools/imports/zstdlib.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
191
vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go
generated
vendored
Normal file
191
vendor/golang.org/x/tools/internal/fastwalk/fastwalk.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package fastwalk provides a faster version of filepath.Walk for file system
|
||||
// scanning tools.
|
||||
package fastwalk
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TraverseLink is used as a return value from WalkFuncs to indicate that the
|
||||
// symlink named in the call may be traversed.
|
||||
var TraverseLink = errors.New("fastwalk: traverse symlink, assuming target is a directory")
|
||||
|
||||
// Walk is a faster implementation of filepath.Walk.
|
||||
//
|
||||
// filepath.Walk's design necessarily calls os.Lstat on each file,
|
||||
// even if the caller needs less info.
|
||||
// Many tools need only the type of each file.
|
||||
// On some platforms, this information is provided directly by the readdir
|
||||
// system call, avoiding the need to stat each file individually.
|
||||
// fastwalk_unix.go contains a fork of the syscall routines.
|
||||
//
|
||||
// See golang.org/issue/16399
|
||||
//
|
||||
// Walk walks the file tree rooted at root, calling walkFn for
|
||||
// each file or directory in the tree, including root.
|
||||
//
|
||||
// If fastWalk returns filepath.SkipDir, the directory is skipped.
|
||||
//
|
||||
// Unlike filepath.Walk:
|
||||
// * file stat calls must be done by the user.
|
||||
// The only provided metadata is the file type, which does not include
|
||||
// any permission bits.
|
||||
// * multiple goroutines stat the filesystem concurrently. The provided
|
||||
// walkFn must be safe for concurrent use.
|
||||
// * fastWalk can follow symlinks if walkFn returns the TraverseLink
|
||||
// sentinel error. It is the walkFn's responsibility to prevent
|
||||
// fastWalk from going into symlink cycles.
|
||||
func Walk(root string, walkFn func(path string, typ os.FileMode) error) error {
|
||||
// TODO(bradfitz): make numWorkers configurable? We used a
|
||||
// minimum of 4 to give the kernel more info about multiple
|
||||
// things we want, in hopes its I/O scheduling can take
|
||||
// advantage of that. Hopefully most are in cache. Maybe 4 is
|
||||
// even too low of a minimum. Profile more.
|
||||
numWorkers := 4
|
||||
if n := runtime.NumCPU(); n > numWorkers {
|
||||
numWorkers = n
|
||||
}
|
||||
|
||||
// Make sure to wait for all workers to finish, otherwise
|
||||
// walkFn could still be called after returning. This Wait call
|
||||
// runs after close(e.donec) below.
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
w := &walker{
|
||||
fn: walkFn,
|
||||
enqueuec: make(chan walkItem, numWorkers), // buffered for performance
|
||||
workc: make(chan walkItem, numWorkers), // buffered for performance
|
||||
donec: make(chan struct{}),
|
||||
|
||||
// buffered for correctness & not leaking goroutines:
|
||||
resc: make(chan error, numWorkers),
|
||||
}
|
||||
defer close(w.donec)
|
||||
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
wg.Add(1)
|
||||
go w.doWork(&wg)
|
||||
}
|
||||
todo := []walkItem{{dir: root}}
|
||||
out := 0
|
||||
for {
|
||||
workc := w.workc
|
||||
var workItem walkItem
|
||||
if len(todo) == 0 {
|
||||
workc = nil
|
||||
} else {
|
||||
workItem = todo[len(todo)-1]
|
||||
}
|
||||
select {
|
||||
case workc <- workItem:
|
||||
todo = todo[:len(todo)-1]
|
||||
out++
|
||||
case it := <-w.enqueuec:
|
||||
todo = append(todo, it)
|
||||
case err := <-w.resc:
|
||||
out--
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if out == 0 && len(todo) == 0 {
|
||||
// It's safe to quit here, as long as the buffered
|
||||
// enqueue channel isn't also readable, which might
|
||||
// happen if the worker sends both another unit of
|
||||
// work and its result before the other select was
|
||||
// scheduled and both w.resc and w.enqueuec were
|
||||
// readable.
|
||||
select {
|
||||
case it := <-w.enqueuec:
|
||||
todo = append(todo, it)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// doWork reads directories as instructed (via workc) and runs the
|
||||
// user's callback function.
|
||||
func (w *walker) doWork(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-w.donec:
|
||||
return
|
||||
case it := <-w.workc:
|
||||
select {
|
||||
case <-w.donec:
|
||||
return
|
||||
case w.resc <- w.walk(it.dir, !it.callbackDone):
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type walker struct {
|
||||
fn func(path string, typ os.FileMode) error
|
||||
|
||||
donec chan struct{} // closed on fastWalk's return
|
||||
workc chan walkItem // to workers
|
||||
enqueuec chan walkItem // from workers
|
||||
resc chan error // from workers
|
||||
}
|
||||
|
||||
type walkItem struct {
|
||||
dir string
|
||||
callbackDone bool // callback already called; don't do it again
|
||||
}
|
||||
|
||||
func (w *walker) enqueue(it walkItem) {
|
||||
select {
|
||||
case w.enqueuec <- it:
|
||||
case <-w.donec:
|
||||
}
|
||||
}
|
||||
|
||||
func (w *walker) onDirEnt(dirName, baseName string, typ os.FileMode) error {
|
||||
joined := dirName + string(os.PathSeparator) + baseName
|
||||
if typ == os.ModeDir {
|
||||
w.enqueue(walkItem{dir: joined})
|
||||
return nil
|
||||
}
|
||||
|
||||
err := w.fn(joined, typ)
|
||||
if typ == os.ModeSymlink {
|
||||
if err == TraverseLink {
|
||||
// Set callbackDone so we don't call it twice for both the
|
||||
// symlink-as-symlink and the symlink-as-directory later:
|
||||
w.enqueue(walkItem{dir: joined, callbackDone: true})
|
||||
return nil
|
||||
}
|
||||
if err == filepath.SkipDir {
|
||||
// Permit SkipDir on symlinks too.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *walker) walk(root string, runUserCallback bool) error {
|
||||
if runUserCallback {
|
||||
err := w.fn(root, os.ModeDir)
|
||||
if err == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return readDir(root, w.onDirEnt)
|
||||
}
|
13
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_fileno.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build freebsd openbsd netbsd
|
||||
|
||||
package fastwalk
|
||||
|
||||
import "syscall"
|
||||
|
||||
func direntInode(dirent *syscall.Dirent) uint64 {
|
||||
return uint64(dirent.Fileno)
|
||||
}
|
14
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_dirent_ino.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin
|
||||
// +build !appengine
|
||||
|
||||
package fastwalk
|
||||
|
||||
import "syscall"
|
||||
|
||||
func direntInode(dirent *syscall.Dirent) uint64 {
|
||||
return uint64(dirent.Ino)
|
||||
}
|
29
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go
generated
vendored
Normal file
29
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_portable.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build appengine !linux,!darwin,!freebsd,!openbsd,!netbsd
|
||||
|
||||
package fastwalk
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// readDir calls fn for each directory entry in dirName.
|
||||
// It does not descend into directories or follow symlinks.
|
||||
// If fn returns a non-nil error, readDir returns with that error
|
||||
// immediately.
|
||||
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
|
||||
fis, err := ioutil.ReadDir(dirName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, fi := range fis {
|
||||
if err := fn(dirName, fi.Name(), fi.Mode()&os.ModeType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
123
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
Normal file
123
vendor/golang.org/x/tools/internal/fastwalk/fastwalk_unix.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd
|
||||
// +build !appengine
|
||||
|
||||
package fastwalk
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const blockSize = 8 << 10
|
||||
|
||||
// unknownFileMode is a sentinel (and bogus) os.FileMode
|
||||
// value used to represent a syscall.DT_UNKNOWN Dirent.Type.
|
||||
const unknownFileMode os.FileMode = os.ModeNamedPipe | os.ModeSocket | os.ModeDevice
|
||||
|
||||
func readDir(dirName string, fn func(dirName, entName string, typ os.FileMode) error) error {
|
||||
fd, err := syscall.Open(dirName, 0, 0)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "open", Path: dirName, Err: err}
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
// The buffer must be at least a block long.
|
||||
buf := make([]byte, blockSize) // stack-allocated; doesn't escape
|
||||
bufp := 0 // starting read position in buf
|
||||
nbuf := 0 // end valid data in buf
|
||||
for {
|
||||
if bufp >= nbuf {
|
||||
bufp = 0
|
||||
nbuf, err = syscall.ReadDirent(fd, buf)
|
||||
if err != nil {
|
||||
return os.NewSyscallError("readdirent", err)
|
||||
}
|
||||
if nbuf <= 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
consumed, name, typ := parseDirEnt(buf[bufp:nbuf])
|
||||
bufp += consumed
|
||||
if name == "" || name == "." || name == ".." {
|
||||
continue
|
||||
}
|
||||
// Fallback for filesystems (like old XFS) that don't
|
||||
// support Dirent.Type and have DT_UNKNOWN (0) there
|
||||
// instead.
|
||||
if typ == unknownFileMode {
|
||||
fi, err := os.Lstat(dirName + "/" + name)
|
||||
if err != nil {
|
||||
// It got deleted in the meantime.
|
||||
if os.IsNotExist(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
typ = fi.Mode() & os.ModeType
|
||||
}
|
||||
if err := fn(dirName, name, typ); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseDirEnt(buf []byte) (consumed int, name string, typ os.FileMode) {
|
||||
// golang.org/issue/15653
|
||||
dirent := (*syscall.Dirent)(unsafe.Pointer(&buf[0]))
|
||||
if v := unsafe.Offsetof(dirent.Reclen) + unsafe.Sizeof(dirent.Reclen); uintptr(len(buf)) < v {
|
||||
panic(fmt.Sprintf("buf size of %d smaller than dirent header size %d", len(buf), v))
|
||||
}
|
||||
if len(buf) < int(dirent.Reclen) {
|
||||
panic(fmt.Sprintf("buf size %d < record length %d", len(buf), dirent.Reclen))
|
||||
}
|
||||
consumed = int(dirent.Reclen)
|
||||
if direntInode(dirent) == 0 { // File absent in directory.
|
||||
return
|
||||
}
|
||||
switch dirent.Type {
|
||||
case syscall.DT_REG:
|
||||
typ = 0
|
||||
case syscall.DT_DIR:
|
||||
typ = os.ModeDir
|
||||
case syscall.DT_LNK:
|
||||
typ = os.ModeSymlink
|
||||
case syscall.DT_BLK:
|
||||
typ = os.ModeDevice
|
||||
case syscall.DT_FIFO:
|
||||
typ = os.ModeNamedPipe
|
||||
case syscall.DT_SOCK:
|
||||
typ = os.ModeSocket
|
||||
case syscall.DT_UNKNOWN:
|
||||
typ = unknownFileMode
|
||||
default:
|
||||
// Skip weird things.
|
||||
// It's probably a DT_WHT (http://lwn.net/Articles/325369/)
|
||||
// or something. Revisit if/when this package is moved outside
|
||||
// of goimports. goimports only cares about regular files,
|
||||
// symlinks, and directories.
|
||||
return
|
||||
}
|
||||
|
||||
nameBuf := (*[unsafe.Sizeof(dirent.Name)]byte)(unsafe.Pointer(&dirent.Name[0]))
|
||||
nameLen := bytes.IndexByte(nameBuf[:], 0)
|
||||
if nameLen < 0 {
|
||||
panic("failed to find terminating 0 byte in dirent")
|
||||
}
|
||||
|
||||
// Special cases for common things:
|
||||
if nameLen == 1 && nameBuf[0] == '.' {
|
||||
name = "."
|
||||
} else if nameLen == 2 && nameBuf[0] == '.' && nameBuf[1] == '.' {
|
||||
name = ".."
|
||||
} else {
|
||||
name = string(nameBuf[:nameLen])
|
||||
}
|
||||
return
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user