mirror of
https://github.com/golang/go
synced 2024-11-18 10:54:40 -07:00
imports: rename to internal/imports
For various reasons we need an internal-facing imports API. Move imports to internal/imports, leaving behind a small wrapper package. The wrapper package captures the globals at time of call into the options struct. Also converts the last goimports tests to use the test helpers, and fixes go/packages in module mode to work with empty modules, which was necessary to get those last tests converted. Change-Id: Ib1212c67908741a1800b992ef1935d563c6ade32 Reviewed-on: https://go-review.googlesource.com/c/tools/+/175437 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
parent
7e7c6e5214
commit
757ca719ca
@ -56,7 +56,13 @@ func (modules) Finalize(exported *Exported) error {
|
||||
// other weird stuff, and will be the working dir for the go command.
|
||||
// It depends on all the other modules.
|
||||
primaryDir := primaryDir(exported)
|
||||
if err := os.MkdirAll(primaryDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
exported.Config.Dir = primaryDir
|
||||
if exported.written[exported.primary] == nil {
|
||||
exported.written[exported.primary] = make(map[string]string)
|
||||
}
|
||||
exported.written[exported.primary]["go.mod"] = filepath.Join(primaryDir, "go.mod")
|
||||
primaryGomod := "module " + exported.primary + "\nrequire (\n"
|
||||
for other := range exported.written {
|
||||
|
59
imports/forward.go
Normal file
59
imports/forward.go
Normal file
@ -0,0 +1,59 @@
|
||||
// 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 (
|
||||
"go/build"
|
||||
|
||||
intimp "golang.org/x/tools/internal/imports"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Debug controls verbose logging.
|
||||
var Debug = false
|
||||
|
||||
// LocalPrefix is a comma-separated string of import path prefixes, which, if
|
||||
// set, instructs Process to sort the import paths with the given prefixes
|
||||
// into another group after 3rd-party packages.
|
||||
var LocalPrefix string
|
||||
|
||||
// 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) {
|
||||
intopt := &intimp.Options{
|
||||
Env: &intimp.ProcessEnv{
|
||||
GOPATH: build.Default.GOPATH,
|
||||
GOROOT: build.Default.GOROOT,
|
||||
Debug: Debug,
|
||||
LocalPrefix: LocalPrefix,
|
||||
},
|
||||
AllErrors: opt.AllErrors,
|
||||
Comments: opt.Comments,
|
||||
FormatOnly: opt.FormatOnly,
|
||||
Fragment: opt.Fragment,
|
||||
TabIndent: opt.TabIndent,
|
||||
TabWidth: opt.TabWidth,
|
||||
}
|
||||
return intimp.Process(filename, src, intopt)
|
||||
}
|
||||
|
||||
// VendorlessPath returns the devendorized version of the import path ipath.
|
||||
// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b".
|
||||
func VendorlessPath(ipath string) string {
|
||||
return intimp.VendorlessPath(ipath)
|
||||
}
|
@ -31,39 +31,27 @@ import (
|
||||
"golang.org/x/tools/internal/gopathwalk"
|
||||
)
|
||||
|
||||
// Debug controls verbose logging.
|
||||
var Debug = false
|
||||
|
||||
// LocalPrefix is a comma-separated string of import path prefixes, which, if
|
||||
// set, instructs Process to sort the import paths with the given prefixes
|
||||
// into another group after 3rd-party packages.
|
||||
var LocalPrefix string
|
||||
|
||||
func localPrefixes() []string {
|
||||
if LocalPrefix != "" {
|
||||
return strings.Split(LocalPrefix, ",")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// importToGroup is a list of functions which map from an import path to
|
||||
// a group number.
|
||||
var importToGroup = []func(importPath string) (num int, ok bool){
|
||||
func(importPath string) (num int, ok bool) {
|
||||
for _, p := range localPrefixes() {
|
||||
var importToGroup = []func(env *ProcessEnv, importPath string) (num int, ok bool){
|
||||
func(env *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
if env.LocalPrefix == "" {
|
||||
return
|
||||
}
|
||||
for _, p := range strings.Split(env.LocalPrefix, ",") {
|
||||
if strings.HasPrefix(importPath, p) || strings.TrimSuffix(p, "/") == importPath {
|
||||
return 3, true
|
||||
}
|
||||
}
|
||||
return
|
||||
},
|
||||
func(importPath string) (num int, ok bool) {
|
||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
if strings.HasPrefix(importPath, "appengine") {
|
||||
return 2, true
|
||||
}
|
||||
return
|
||||
},
|
||||
func(importPath string) (num int, ok bool) {
|
||||
func(_ *ProcessEnv, importPath string) (num int, ok bool) {
|
||||
if strings.Contains(importPath, ".") {
|
||||
return 1, true
|
||||
}
|
||||
@ -71,9 +59,9 @@ var importToGroup = []func(importPath string) (num int, ok bool){
|
||||
},
|
||||
}
|
||||
|
||||
func importGroup(importPath string) int {
|
||||
func importGroup(env *ProcessEnv, importPath string) int {
|
||||
for _, fn := range importToGroup {
|
||||
if n, ok := fn(importPath); ok {
|
||||
if n, ok := fn(env, importPath); ok {
|
||||
return n
|
||||
}
|
||||
}
|
||||
@ -241,7 +229,7 @@ type pass struct {
|
||||
fset *token.FileSet // fset used to parse f and its siblings.
|
||||
f *ast.File // the file being fixed.
|
||||
srcDir string // the directory containing f.
|
||||
fixEnv *fixEnv // the environment to use for go commands, etc.
|
||||
env *ProcessEnv // the environment to use for go commands, etc.
|
||||
loadRealPackageNames bool // if true, load package names from disk rather than guessing them.
|
||||
otherFiles []*ast.File // sibling files.
|
||||
|
||||
@ -266,7 +254,7 @@ func (p *pass) loadPackageNames(imports []*importInfo) error {
|
||||
unknown = append(unknown, imp.importPath)
|
||||
}
|
||||
|
||||
names, err := p.fixEnv.getResolver().loadPackageNames(unknown, p.srcDir)
|
||||
names, err := p.env.getResolver().loadPackageNames(unknown, p.srcDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -324,7 +312,7 @@ func (p *pass) load() bool {
|
||||
if p.loadRealPackageNames {
|
||||
err := p.loadPackageNames(append(imports, p.candidates...))
|
||||
if err != nil {
|
||||
if Debug {
|
||||
if p.env.Debug {
|
||||
log.Printf("loading package names: %v", err)
|
||||
}
|
||||
return false
|
||||
@ -448,13 +436,13 @@ func (p *pass) addCandidate(imp *importInfo, pkg *packageInfo) {
|
||||
// easily be extended by adding a file with an init function.
|
||||
var fixImports = fixImportsDefault
|
||||
|
||||
func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *fixEnv) error {
|
||||
func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) error {
|
||||
abs, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srcDir := filepath.Dir(abs)
|
||||
if Debug {
|
||||
if env.Debug {
|
||||
log.Printf("fixImports(filename=%q), abs=%q, srcDir=%q ...", filename, abs, srcDir)
|
||||
}
|
||||
|
||||
@ -486,7 +474,7 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *f
|
||||
// Third pass: get real package names where we had previously used
|
||||
// the naive algorithm. This is the first step that will use the
|
||||
// environment, so we provide it here for the first time.
|
||||
p = &pass{fset: fset, f: f, srcDir: srcDir, fixEnv: env}
|
||||
p = &pass{fset: fset, f: f, srcDir: srcDir, env: env}
|
||||
p.loadRealPackageNames = true
|
||||
p.otherFiles = otherFiles
|
||||
if p.load() {
|
||||
@ -510,9 +498,12 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *f
|
||||
return nil
|
||||
}
|
||||
|
||||
// fixEnv contains environment variables and settings that affect the use of
|
||||
// ProcessEnv contains environment variables and settings that affect the use of
|
||||
// the go command, the go/build package, etc.
|
||||
type fixEnv struct {
|
||||
type ProcessEnv struct {
|
||||
LocalPrefix string
|
||||
Debug bool
|
||||
|
||||
// If non-empty, these will be used instead of the
|
||||
// process-wide values.
|
||||
GOPATH, GOROOT, GO111MODULE, GOPROXY, GOFLAGS string
|
||||
@ -524,7 +515,7 @@ type fixEnv struct {
|
||||
resolver resolver
|
||||
}
|
||||
|
||||
func (e *fixEnv) env() []string {
|
||||
func (e *ProcessEnv) env() []string {
|
||||
env := os.Environ()
|
||||
add := func(k, v string) {
|
||||
if v != "" {
|
||||
@ -542,7 +533,7 @@ func (e *fixEnv) env() []string {
|
||||
return env
|
||||
}
|
||||
|
||||
func (e *fixEnv) getResolver() resolver {
|
||||
func (e *ProcessEnv) getResolver() resolver {
|
||||
if e.resolver != nil {
|
||||
return e.resolver
|
||||
}
|
||||
@ -557,7 +548,7 @@ func (e *fixEnv) getResolver() resolver {
|
||||
return &moduleResolver{env: e}
|
||||
}
|
||||
|
||||
func (e *fixEnv) newPackagesConfig(mode packages.LoadMode) *packages.Config {
|
||||
func (e *ProcessEnv) newPackagesConfig(mode packages.LoadMode) *packages.Config {
|
||||
return &packages.Config{
|
||||
Mode: mode,
|
||||
Dir: e.WorkingDir,
|
||||
@ -565,14 +556,14 @@ func (e *fixEnv) newPackagesConfig(mode packages.LoadMode) *packages.Config {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *fixEnv) buildContext() *build.Context {
|
||||
func (e *ProcessEnv) buildContext() *build.Context {
|
||||
ctx := build.Default
|
||||
ctx.GOROOT = e.GOROOT
|
||||
ctx.GOPATH = e.GOPATH
|
||||
return &ctx
|
||||
}
|
||||
|
||||
func (e *fixEnv) invokeGo(args ...string) (*bytes.Buffer, error) {
|
||||
func (e *ProcessEnv) invokeGo(args ...string) (*bytes.Buffer, error) {
|
||||
cmd := exec.Command("go", args...)
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &bytes.Buffer{}
|
||||
@ -581,7 +572,7 @@ func (e *fixEnv) invokeGo(args ...string) (*bytes.Buffer, error) {
|
||||
cmd.Env = e.env()
|
||||
cmd.Dir = e.WorkingDir
|
||||
|
||||
if Debug {
|
||||
if e.Debug {
|
||||
defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(cmd)) }(time.Now())
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
@ -632,7 +623,7 @@ type resolver interface {
|
||||
|
||||
// gopathResolver implements resolver for GOPATH and module workspaces using go/packages.
|
||||
type goPackagesResolver struct {
|
||||
env *fixEnv
|
||||
env *ProcessEnv
|
||||
}
|
||||
|
||||
func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||
@ -680,7 +671,7 @@ func (r *goPackagesResolver) scan(refs references) ([]*pkg, error) {
|
||||
}
|
||||
|
||||
func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||
dirScan, err := pass.fixEnv.getResolver().scan(refs)
|
||||
dirScan, err := pass.env.getResolver().scan(refs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -707,7 +698,7 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
|
||||
go func(pkgName string, symbols map[string]bool) {
|
||||
defer wg.Done()
|
||||
|
||||
found, err := findImport(ctx, pass.fixEnv, dirScan, pkgName, symbols, filename)
|
||||
found, err := findImport(ctx, pass.env, dirScan, pkgName, symbols, filename)
|
||||
|
||||
if err != nil {
|
||||
firstErrOnce.Do(func() {
|
||||
@ -778,7 +769,7 @@ func importPathToAssumedName(importPath string) string {
|
||||
|
||||
// gopathResolver implements resolver for GOPATH workspaces.
|
||||
type gopathResolver struct {
|
||||
env *fixEnv
|
||||
env *ProcessEnv
|
||||
}
|
||||
|
||||
func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (map[string]string, error) {
|
||||
@ -791,7 +782,7 @@ func (r *gopathResolver) loadPackageNames(importPaths []string, srcDir string) (
|
||||
|
||||
// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
|
||||
// If there's a problem, it returns "".
|
||||
func importPathToName(env *fixEnv, importPath, srcDir string) (packageName string) {
|
||||
func importPathToName(env *ProcessEnv, importPath, srcDir string) (packageName string) {
|
||||
// Fast path for standard library without going to disk.
|
||||
if _, ok := stdlib[importPath]; ok {
|
||||
return path.Base(importPath) // stdlib packages always match their paths.
|
||||
@ -927,7 +918,7 @@ func (r *gopathResolver) scan(_ references) ([]*pkg, error) {
|
||||
dir: dir,
|
||||
})
|
||||
}
|
||||
gopathwalk.Walk(gopathwalk.SrcDirsRoots(r.env.buildContext()), add, gopathwalk.Options{Debug: Debug, ModulesEnabled: false})
|
||||
gopathwalk.Walk(gopathwalk.SrcDirsRoots(r.env.buildContext()), add, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: false})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@ -946,8 +937,8 @@ func VendorlessPath(ipath string) string {
|
||||
|
||||
// loadExports returns the set of exported symbols in the package at dir.
|
||||
// It returns nil on error or if the package name in dir does not match expectPackage.
|
||||
func loadExports(ctx context.Context, env *fixEnv, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
||||
if Debug {
|
||||
func loadExports(ctx context.Context, env *ProcessEnv, expectPackage string, pkg *pkg) (map[string]bool, error) {
|
||||
if env.Debug {
|
||||
log.Printf("loading exports in dir %s (seeking package %s)", pkg.dir, expectPackage)
|
||||
}
|
||||
if pkg.goPackage != nil {
|
||||
@ -1020,7 +1011,7 @@ func loadExports(ctx context.Context, env *fixEnv, expectPackage string, pkg *pk
|
||||
}
|
||||
}
|
||||
|
||||
if Debug {
|
||||
if env.Debug {
|
||||
exportList := make([]string, 0, len(exports))
|
||||
for k := range exports {
|
||||
exportList = append(exportList, k)
|
||||
@ -1033,7 +1024,7 @@ func loadExports(ctx context.Context, env *fixEnv, expectPackage string, pkg *pk
|
||||
|
||||
// findImport searches for a package with the given symbols.
|
||||
// If no package is found, findImport returns ("", false, nil)
|
||||
func findImport(ctx context.Context, env *fixEnv, dirScan []*pkg, pkgName string, symbols map[string]bool, filename string) (*pkg, error) {
|
||||
func findImport(ctx context.Context, env *ProcessEnv, dirScan []*pkg, pkgName string, symbols map[string]bool, filename string) (*pkg, error) {
|
||||
pkgDir, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -1056,7 +1047,7 @@ func findImport(ctx context.Context, env *fixEnv, dirScan []*pkg, pkgName string
|
||||
// ones. Note that this sorts by the de-vendored name, so
|
||||
// there's no "penalty" for vendoring.
|
||||
sort.Sort(byDistanceOrImportPathShortLength(candidates))
|
||||
if Debug {
|
||||
if env.Debug {
|
||||
for i, c := range candidates {
|
||||
log.Printf("%s candidate %d/%d: %v in %v", pkgName, i+1, len(candidates), c.pkg.importPathShort, c.pkg.dir)
|
||||
}
|
||||
@ -1097,7 +1088,7 @@ func findImport(ctx context.Context, env *fixEnv, dirScan []*pkg, pkgName string
|
||||
|
||||
exports, err := loadExports(ctx, env, pkgName, c.pkg)
|
||||
if err != nil {
|
||||
if Debug {
|
||||
if env.Debug {
|
||||
log.Printf("loading exports in dir %s (seeking package %s): %v", c.pkg.dir, pkgName, err)
|
||||
}
|
||||
resc <- nil
|
@ -5,6 +5,7 @@
|
||||
package imports
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@ -14,6 +15,8 @@ import (
|
||||
"golang.org/x/tools/go/packages/packagestest"
|
||||
)
|
||||
|
||||
var testDebug = flag.Bool("debug", false, "enable debug output")
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
formatOnly bool
|
||||
@ -1116,8 +1119,7 @@ var _, _ = rand.Read, rand.NewZipf
|
||||
}
|
||||
|
||||
func TestSimpleCases(t *testing.T) {
|
||||
defer func(lp string) { LocalPrefix = lp }(LocalPrefix)
|
||||
LocalPrefix = "local.com,github.com/local"
|
||||
const localPrefix = "local.com,github.com/local"
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
options := &Options{
|
||||
@ -1163,7 +1165,11 @@ func TestSimpleCases(t *testing.T) {
|
||||
Files: fm{"bar/x.go": "package bar\nfunc Bar(){}\n"},
|
||||
},
|
||||
},
|
||||
}.processTest(t, "golang.org/fake", "x.go", nil, options, tt.out)
|
||||
}.test(t, func(t *goimportTest) {
|
||||
t.env.LocalPrefix = localPrefix
|
||||
t.assertProcessEquals("golang.org/fake", "x.go", nil, options, tt.out)
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1485,20 +1491,28 @@ func TestFindStdlib(t *testing.T) {
|
||||
for _, sym := range tt.symbols {
|
||||
input += fmt.Sprintf("var _ = %s.%s\n", tt.pkg, sym)
|
||||
}
|
||||
buf, err := Process("x.go", []byte(input), &Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(buf); !strings.Contains(got, tt.want) {
|
||||
t.Errorf("Process(%q) = %q, wanted it to contain %q", input, buf, tt.want)
|
||||
}
|
||||
testConfig{
|
||||
module: packagestest.Module{
|
||||
Name: "foo.com",
|
||||
Files: fm{"x.go": input},
|
||||
},
|
||||
}.test(t, func(t *goimportTest) {
|
||||
buf, err := t.process("foo.com", "x.go", nil, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := string(buf); !strings.Contains(got, tt.want) {
|
||||
t.Errorf("Process(%q) = %q, wanted it to contain %q", input, buf, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testConfig struct {
|
||||
gopathOnly bool
|
||||
module packagestest.Module
|
||||
modules []packagestest.Module
|
||||
gopathOnly bool
|
||||
goPackagesIncompatible bool
|
||||
module packagestest.Module
|
||||
modules []packagestest.Module
|
||||
}
|
||||
|
||||
// fm is the type for a packagestest.Module's Files, abbreviated for shorter lines.
|
||||
@ -1522,6 +1536,12 @@ func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
|
||||
|
||||
forceGoPackages := false
|
||||
var exporter packagestest.Exporter
|
||||
if c.gopathOnly && strings.HasPrefix(kind, "Modules") {
|
||||
t.Skip("test marked GOPATH-only")
|
||||
}
|
||||
if c.goPackagesIncompatible && strings.HasSuffix(kind, "_GoPackages") {
|
||||
t.Skip("test marked go/packages-incompatible")
|
||||
}
|
||||
switch kind {
|
||||
case "GOPATH":
|
||||
exporter = packagestest.GOPATH
|
||||
@ -1529,14 +1549,8 @@ func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
|
||||
exporter = packagestest.GOPATH
|
||||
forceGoPackages = true
|
||||
case "Modules":
|
||||
if c.gopathOnly {
|
||||
t.Skip("test marked GOPATH-only")
|
||||
}
|
||||
exporter = packagestest.Modules
|
||||
case "Modules_GoPackages":
|
||||
if c.gopathOnly {
|
||||
t.Skip("test marked GOPATH-only")
|
||||
}
|
||||
exporter = packagestest.Modules
|
||||
forceGoPackages = true
|
||||
default:
|
||||
@ -1554,12 +1568,13 @@ func (c testConfig) test(t *testing.T, fn func(*goimportTest)) {
|
||||
|
||||
it := &goimportTest{
|
||||
T: t,
|
||||
fixEnv: &fixEnv{
|
||||
env: &ProcessEnv{
|
||||
GOROOT: env["GOROOT"],
|
||||
GOPATH: env["GOPATH"],
|
||||
GO111MODULE: env["GO111MODULE"],
|
||||
WorkingDir: exported.Config.Dir,
|
||||
ForceGoPackages: forceGoPackages,
|
||||
Debug: *testDebug,
|
||||
},
|
||||
exported: exported,
|
||||
}
|
||||
@ -1572,23 +1587,36 @@ func (c testConfig) processTest(t *testing.T, module, file string, contents []by
|
||||
t.Helper()
|
||||
c.test(t, func(t *goimportTest) {
|
||||
t.Helper()
|
||||
t.process(module, file, contents, opts, want)
|
||||
t.assertProcessEquals(module, file, contents, opts, want)
|
||||
})
|
||||
}
|
||||
|
||||
type goimportTest struct {
|
||||
*testing.T
|
||||
fixEnv *fixEnv
|
||||
env *ProcessEnv
|
||||
exported *packagestest.Exported
|
||||
}
|
||||
|
||||
func (t *goimportTest) process(module, file string, contents []byte, opts *Options, want string) {
|
||||
func (t *goimportTest) process(module, file string, contents []byte, opts *Options) ([]byte, error) {
|
||||
t.Helper()
|
||||
f := t.exported.File(module, file)
|
||||
if f == "" {
|
||||
t.Fatalf("%v not found in exported files (typo in filename?)", file)
|
||||
}
|
||||
buf, err := process(f, contents, opts, t.fixEnv)
|
||||
return t.processNonModule(f, contents, opts)
|
||||
}
|
||||
|
||||
func (t *goimportTest) processNonModule(file string, contents []byte, opts *Options) ([]byte, error) {
|
||||
if opts == nil {
|
||||
opts = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||
}
|
||||
opts.Env = t.env
|
||||
opts.Env.Debug = *testDebug
|
||||
return Process(file, contents, opts)
|
||||
}
|
||||
|
||||
func (t *goimportTest) assertProcessEquals(module, file string, contents []byte, opts *Options, want string) {
|
||||
buf, err := t.process(module, file, contents, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() = %v", err)
|
||||
}
|
||||
@ -1775,9 +1803,8 @@ const _ = runtime.GOOS
|
||||
Files: fm{"t.go": tt.src},
|
||||
}}, tt.modules...),
|
||||
}.test(t, func(t *goimportTest) {
|
||||
defer func(s string) { LocalPrefix = s }(LocalPrefix)
|
||||
LocalPrefix = tt.localPrefix
|
||||
t.process("test.com", "t.go", nil, nil, tt.want)
|
||||
t.env.LocalPrefix = tt.localPrefix
|
||||
t.assertProcessEquals("test.com", "t.go", nil, nil, tt.want)
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -1827,7 +1854,7 @@ func TestImportPathToNameGoPathParse(t *testing.T) {
|
||||
if strings.Contains(t.Name(), "GoPackages") {
|
||||
t.Skip("go/packages does not ignore package main")
|
||||
}
|
||||
r := t.fixEnv.getResolver()
|
||||
r := t.env.getResolver()
|
||||
srcDir := filepath.Dir(t.exported.File("example.net/pkg", "z.go"))
|
||||
names, err := r.loadPackageNames([]string{"example.net/pkg"}, srcDir)
|
||||
if err != nil {
|
||||
@ -2220,13 +2247,19 @@ func TestPkgIsCandidate(t *testing.T) {
|
||||
|
||||
// Issue 20941: this used to panic on Windows.
|
||||
func TestProcessStdin(t *testing.T) {
|
||||
got, err := Process("<standard input>", []byte("package main\nfunc main() {\n\tfmt.Println(123)\n}\n"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(got), `"fmt"`) {
|
||||
t.Errorf("expected fmt import; got: %s", got)
|
||||
}
|
||||
testConfig{
|
||||
module: packagestest.Module{
|
||||
Name: "foo.com",
|
||||
},
|
||||
}.test(t, func(t *goimportTest) {
|
||||
got, err := t.processNonModule("<standard input>", []byte("package main\nfunc main() {\n\tfmt.Println(123)\n}\n"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(string(got), `"fmt"`) {
|
||||
t.Errorf("expected fmt import; got: %s", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Tests LocalPackagePromotion when there is a local package that matches, it
|
||||
@ -2324,14 +2357,21 @@ import "bytes"
|
||||
|
||||
var _ = &bytes.Buffer{}
|
||||
`
|
||||
testConfig{
|
||||
goPackagesIncompatible: true,
|
||||
module: packagestest.Module{
|
||||
Name: "mycompany.net",
|
||||
},
|
||||
}.test(t, func(t *goimportTest) {
|
||||
buf, err := t.processNonModule("mycompany.net/tool/main.go", []byte(input), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() = %v", err)
|
||||
}
|
||||
if string(buf) != want {
|
||||
t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
|
||||
}
|
||||
})
|
||||
|
||||
buf, err := Process("mycompany.net/tool/main.go", []byte(input), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Process() = %v", err)
|
||||
}
|
||||
if string(buf) != want {
|
||||
t.Errorf("Got:\n%s\nWant:\n%s", buf, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensures a token as large as 500000 bytes can be handled
|
@ -6,14 +6,13 @@
|
||||
|
||||
// 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"
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
@ -27,8 +26,10 @@ import (
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Options specifies options for processing files.
|
||||
// Options is golang.org/x/tools/imports.Options with extra internal-only options.
|
||||
type Options struct {
|
||||
Env *ProcessEnv // The environment to use. Note: this contains the cached module and filesystem state.
|
||||
|
||||
Fragment bool // Accept fragment of a source file (no package statement)
|
||||
AllErrors bool // Report all errors (not just the first 10 on different lines)
|
||||
|
||||
@ -39,18 +40,8 @@ type Options struct {
|
||||
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.
|
||||
// Process implements golang.org/x/tools/imports.Process with explicit context in env.
|
||||
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
|
||||
env := &fixEnv{GOPATH: build.Default.GOPATH, GOROOT: build.Default.GOROOT}
|
||||
return process(filename, src, opt, env)
|
||||
}
|
||||
|
||||
func process(filename string, src []byte, opt *Options, env *fixEnv) ([]byte, error) {
|
||||
if opt == nil {
|
||||
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
|
||||
}
|
||||
@ -69,12 +60,12 @@ func process(filename string, src []byte, opt *Options, env *fixEnv) ([]byte, er
|
||||
}
|
||||
|
||||
if !opt.FormatOnly {
|
||||
if err := fixImports(fileSet, file, filename, env); err != nil {
|
||||
if err := fixImports(fileSet, file, filename, opt.Env); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sortImports(fileSet, file)
|
||||
sortImports(opt.Env, fileSet, file)
|
||||
imps := astutil.Imports(fileSet, file)
|
||||
var spacesBefore []string // import paths we need spaces before
|
||||
for _, impSection := range imps {
|
||||
@ -85,7 +76,7 @@ func process(filename string, src []byte, opt *Options, env *fixEnv) ([]byte, er
|
||||
lastGroup := -1
|
||||
for _, importSpec := range impSection {
|
||||
importPath, _ := strconv.Unquote(importSpec.Path.Value)
|
||||
groupNum := importGroup(importPath)
|
||||
groupNum := importGroup(opt.Env, importPath)
|
||||
if groupNum != lastGroup && lastGroup != -1 {
|
||||
spacesBefore = append(spacesBefore, importPath)
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
// 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
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bytes"
|
@ -3,7 +3,7 @@
|
||||
// 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
|
||||
package imports
|
||||
|
||||
import (
|
||||
"bufio"
|
@ -22,7 +22,7 @@ import (
|
||||
// moduleResolver implements resolver for modules using the go command as little
|
||||
// as feasible.
|
||||
type moduleResolver struct {
|
||||
env *fixEnv
|
||||
env *ProcessEnv
|
||||
|
||||
initialized bool
|
||||
main *moduleJSON
|
||||
@ -62,7 +62,7 @@ func (r *moduleResolver) init() error {
|
||||
return err
|
||||
}
|
||||
if mod.Dir == "" {
|
||||
if Debug {
|
||||
if r.env.Debug {
|
||||
log.Printf("module %v has not been downloaded and will be ignored", mod.Path)
|
||||
}
|
||||
// Can't do anything with a module that's not downloaded.
|
||||
@ -253,7 +253,7 @@ func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
|
||||
matches := modCacheRegexp.FindStringSubmatch(subdir)
|
||||
modPath, err := module.DecodePath(filepath.ToSlash(matches[1]))
|
||||
if err != nil {
|
||||
if Debug {
|
||||
if r.env.Debug {
|
||||
log.Printf("decoding module cache path %q: %v", subdir, err)
|
||||
}
|
||||
return
|
||||
@ -303,7 +303,7 @@ func (r *moduleResolver) scan(_ references) ([]*pkg, error) {
|
||||
importPathShort: VendorlessPath(importPath),
|
||||
dir: dir,
|
||||
})
|
||||
}, gopathwalk.Options{Debug: Debug, ModulesEnabled: true})
|
||||
}, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true})
|
||||
return result, nil
|
||||
}
|
||||
|
@ -485,7 +485,7 @@ var proxyDir string
|
||||
|
||||
type modTest struct {
|
||||
*testing.T
|
||||
env *fixEnv
|
||||
env *ProcessEnv
|
||||
resolver *moduleResolver
|
||||
cleanup func()
|
||||
}
|
||||
@ -515,7 +515,7 @@ func setup(t *testing.T, main, wd string) *modTest {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
env := &fixEnv{
|
||||
env := &ProcessEnv{
|
||||
GOROOT: build.Default.GOROOT,
|
||||
GOPATH: filepath.Join(dir, "gopath"),
|
||||
GO111MODULE: "on",
|
@ -15,7 +15,7 @@ import (
|
||||
|
||||
// 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) {
|
||||
func sortImports(env *ProcessEnv, fset *token.FileSet, f *ast.File) {
|
||||
for i, d := range f.Decls {
|
||||
d, ok := d.(*ast.GenDecl)
|
||||
if !ok || d.Tok != token.IMPORT {
|
||||
@ -40,11 +40,11 @@ func sortImports(fset *token.FileSet, f *ast.File) {
|
||||
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])...)
|
||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:j])...)
|
||||
i = j
|
||||
}
|
||||
}
|
||||
specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
|
||||
specs = append(specs, sortSpecs(env, fset, f, d.Specs[i:])...)
|
||||
d.Specs = specs
|
||||
|
||||
// Deduping can leave a blank line before the rparen; clean that up.
|
||||
@ -95,7 +95,7 @@ type posSpan struct {
|
||||
End token.Pos
|
||||
}
|
||||
|
||||
func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
func sortSpecs(env *ProcessEnv, 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.
|
||||
@ -144,7 +144,7 @@ func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
// 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))
|
||||
sort.Sort(byImportSpec{env, specs})
|
||||
|
||||
// Dedup. Thanks to our sorting, we can just consider
|
||||
// adjacent pairs of imports.
|
||||
@ -197,16 +197,19 @@ func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
|
||||
return specs
|
||||
}
|
||||
|
||||
type byImportSpec []ast.Spec // slice of *ast.ImportSpec
|
||||
type byImportSpec struct {
|
||||
env *ProcessEnv
|
||||
specs []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) Len() int { return len(x.specs) }
|
||||
func (x byImportSpec) Swap(i, j int) { x.specs[i], x.specs[j] = x.specs[j], x.specs[i] }
|
||||
func (x byImportSpec) Less(i, j int) bool {
|
||||
ipath := importPath(x[i])
|
||||
jpath := importPath(x[j])
|
||||
ipath := importPath(x.specs[i])
|
||||
jpath := importPath(x.specs[j])
|
||||
|
||||
igroup := importGroup(ipath)
|
||||
jgroup := importGroup(jpath)
|
||||
igroup := importGroup(x.env, ipath)
|
||||
jgroup := importGroup(x.env, jpath)
|
||||
if igroup != jgroup {
|
||||
return igroup < jgroup
|
||||
}
|
||||
@ -214,13 +217,13 @@ func (x byImportSpec) Less(i, j int) bool {
|
||||
if ipath != jpath {
|
||||
return ipath < jpath
|
||||
}
|
||||
iname := importName(x[i])
|
||||
jname := importName(x[j])
|
||||
iname := importName(x.specs[i])
|
||||
jname := importName(x.specs[j])
|
||||
|
||||
if iname != jname {
|
||||
return iname < jname
|
||||
}
|
||||
return importComment(x[i]) < importComment(x[j])
|
||||
return importComment(x.specs[i]) < importComment(x.specs[j])
|
||||
}
|
||||
|
||||
type byCommentPos []*ast.CommentGroup
|
Loading…
Reference in New Issue
Block a user