diff --git a/go/packages/packagestest/modules.go b/go/packages/packagestest/modules.go index 0a660bb1..2e06612c 100644 --- a/go/packages/packagestest/modules.go +++ b/go/packages/packagestest/modules.go @@ -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 { diff --git a/imports/forward.go b/imports/forward.go new file mode 100644 index 00000000..bb661d59 --- /dev/null +++ b/imports/forward.go @@ -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) +} diff --git a/imports/fix.go b/internal/imports/fix.go similarity index 94% rename from imports/fix.go rename to internal/imports/fix.go index 777d28cc..3af3a142 100644 --- a/imports/fix.go +++ b/internal/imports/fix.go @@ -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 diff --git a/imports/fix_test.go b/internal/imports/fix_test.go similarity index 94% rename from imports/fix_test.go rename to internal/imports/fix_test.go index 101978c1..4f106969 100644 --- a/imports/fix_test.go +++ b/internal/imports/fix_test.go @@ -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("", []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("", []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 diff --git a/imports/imports.go b/internal/imports/imports.go similarity index 91% rename from imports/imports.go rename to internal/imports/imports.go index 07101cb8..8b63908c 100644 --- a/imports/imports.go +++ b/internal/imports/imports.go @@ -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) } diff --git a/imports/mkindex.go b/internal/imports/mkindex.go similarity index 99% rename from imports/mkindex.go rename to internal/imports/mkindex.go index 755e2394..ef8c0d28 100644 --- a/imports/mkindex.go +++ b/internal/imports/mkindex.go @@ -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" diff --git a/imports/mkstdlib.go b/internal/imports/mkstdlib.go similarity index 99% rename from imports/mkstdlib.go rename to internal/imports/mkstdlib.go index c8865e55..f67b5c1e 100644 --- a/imports/mkstdlib.go +++ b/internal/imports/mkstdlib.go @@ -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" diff --git a/imports/mod.go b/internal/imports/mod.go similarity index 98% rename from imports/mod.go rename to internal/imports/mod.go index 018c43ce..a072214e 100644 --- a/imports/mod.go +++ b/internal/imports/mod.go @@ -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 } diff --git a/imports/mod_112_test.go b/internal/imports/mod_112_test.go similarity index 100% rename from imports/mod_112_test.go rename to internal/imports/mod_112_test.go diff --git a/imports/mod_test.go b/internal/imports/mod_test.go similarity index 99% rename from imports/mod_test.go rename to internal/imports/mod_test.go index f2fb76e2..cda41470 100644 --- a/imports/mod_test.go +++ b/internal/imports/mod_test.go @@ -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", diff --git a/imports/proxy_112_test.go b/internal/imports/proxy_112_test.go similarity index 100% rename from imports/proxy_112_test.go rename to internal/imports/proxy_112_test.go diff --git a/imports/proxy_113_test.go b/internal/imports/proxy_113_test.go similarity index 100% rename from imports/proxy_113_test.go rename to internal/imports/proxy_113_test.go diff --git a/imports/sortimports.go b/internal/imports/sortimports.go similarity index 85% rename from imports/sortimports.go rename to internal/imports/sortimports.go index f3dd56c7..0a156fe2 100644 --- a/imports/sortimports.go +++ b/internal/imports/sortimports.go @@ -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 diff --git a/imports/testdata/mod/example.com_v1.0.0.txt b/internal/imports/testdata/mod/example.com_v1.0.0.txt similarity index 100% rename from imports/testdata/mod/example.com_v1.0.0.txt rename to internal/imports/testdata/mod/example.com_v1.0.0.txt diff --git a/imports/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt b/internal/imports/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt similarity index 100% rename from imports/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt rename to internal/imports/testdata/mod/golang.org_x_text_v0.0.0-20170915032832-14c0d48ead0c.txt diff --git a/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt b/internal/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt similarity index 100% rename from imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt rename to internal/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.2.txt diff --git a/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt b/internal/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt similarity index 100% rename from imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt rename to internal/imports/testdata/mod/rsc.io_!q!u!o!t!e_v1.5.3-!p!r!e.txt diff --git a/imports/testdata/mod/rsc.io_quote_v1.5.1.txt b/internal/imports/testdata/mod/rsc.io_quote_v1.5.1.txt similarity index 100% rename from imports/testdata/mod/rsc.io_quote_v1.5.1.txt rename to internal/imports/testdata/mod/rsc.io_quote_v1.5.1.txt diff --git a/imports/testdata/mod/rsc.io_quote_v1.5.2.txt b/internal/imports/testdata/mod/rsc.io_quote_v1.5.2.txt similarity index 100% rename from imports/testdata/mod/rsc.io_quote_v1.5.2.txt rename to internal/imports/testdata/mod/rsc.io_quote_v1.5.2.txt diff --git a/imports/testdata/mod/rsc.io_quote_v2_v2.0.1.txt b/internal/imports/testdata/mod/rsc.io_quote_v2_v2.0.1.txt similarity index 100% rename from imports/testdata/mod/rsc.io_quote_v2_v2.0.1.txt rename to internal/imports/testdata/mod/rsc.io_quote_v2_v2.0.1.txt diff --git a/imports/testdata/mod/rsc.io_quote_v3_v3.0.0.txt b/internal/imports/testdata/mod/rsc.io_quote_v3_v3.0.0.txt similarity index 100% rename from imports/testdata/mod/rsc.io_quote_v3_v3.0.0.txt rename to internal/imports/testdata/mod/rsc.io_quote_v3_v3.0.0.txt diff --git a/imports/testdata/mod/rsc.io_sampler_v1.3.0.txt b/internal/imports/testdata/mod/rsc.io_sampler_v1.3.0.txt similarity index 100% rename from imports/testdata/mod/rsc.io_sampler_v1.3.0.txt rename to internal/imports/testdata/mod/rsc.io_sampler_v1.3.0.txt diff --git a/imports/testdata/mod/rsc.io_sampler_v1.3.1.txt b/internal/imports/testdata/mod/rsc.io_sampler_v1.3.1.txt similarity index 100% rename from imports/testdata/mod/rsc.io_sampler_v1.3.1.txt rename to internal/imports/testdata/mod/rsc.io_sampler_v1.3.1.txt diff --git a/imports/zstdlib.go b/internal/imports/zstdlib.go similarity index 100% rename from imports/zstdlib.go rename to internal/imports/zstdlib.go