cmd/eg: use go/packages
Rewrite eg to use go/packages, so that it works with Go modules.
There are two known regressions:
* lost build tag support
* you can't use a template to add a new dependency
These can be re-added at a future point as needed.
Change-Id: I2b59bb5e101c738a4d4c40e84a12c5b3a19bcd18
Reviewed-on: https://go-review.googlesource.com/c/tools/+/258304
Trust: Josh Bleecher Snyder <josharian@gmail.com>
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/cmd/eg/eg.go b/cmd/eg/eg.go
index a5473ad..c178a48 100644
--- a/cmd/eg/eg.go
+++ b/cmd/eg/eg.go
@@ -10,16 +10,17 @@
import (
"flag"
"fmt"
- "go/build"
+ "go/ast"
"go/format"
"go/parser"
"go/token"
- exec "golang.org/x/sys/execabs"
+ "go/types"
+ "io/ioutil"
"os"
"strings"
- "golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/go/loader"
+ exec "golang.org/x/sys/execabs"
+ "golang.org/x/tools/go/packages"
"golang.org/x/tools/refactor/eg"
)
@@ -32,13 +33,9 @@
verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics")
)
-func init() {
- flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
-}
-
const usage = `eg: an example-based refactoring tool.
-Usage: eg -t template.go [-w] [-transitive] <args>...
+Usage: eg -t template.go [-w] [-transitive] <packages>
-help show detailed help message
-t template.go specifies the template file (use -help to see explanation)
@@ -47,7 +44,7 @@
-v show verbose matcher diagnostics
-beforeedit cmd a command to exec before each file is modified.
"{}" represents the name of the file.
-` + loader.FromArgsUsage
+`
func main() {
if err := doMain(); err != nil {
@@ -74,51 +71,65 @@
return fmt.Errorf("no -t template.go file specified")
}
- conf := loader.Config{
- Fset: token.NewFileSet(),
- ParserMode: parser.ParseComments,
- }
-
- // The first Created package is the template.
- conf.CreateFromFilenames("template", *templateFlag)
-
- if _, err := conf.FromArgs(args, true); err != nil {
+ template, err := ioutil.ReadFile(*templateFlag)
+ if err != nil {
return err
}
- // Load, parse and type-check the whole program.
- iprog, err := conf.Load()
+ cfg := &packages.Config{
+ Fset: token.NewFileSet(),
+ Mode: packages.NeedTypesInfo | packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps | packages.NeedCompiledGoFiles,
+ Tests: true,
+ }
+
+ pkgs, err := packages.Load(cfg, args...)
+ if err != nil {
+ return err
+ }
+
+ tFile, err := parser.ParseFile(cfg.Fset, *templateFlag, template, parser.ParseComments)
+ if err != nil {
+ return err
+ }
+
+ // Type-check the template.
+ tInfo := types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ Implicits: make(map[ast.Node]types.Object),
+ Selections: make(map[*ast.SelectorExpr]*types.Selection),
+ Scopes: make(map[ast.Node]*types.Scope),
+ }
+ conf := types.Config{
+ Importer: pkgsImporter(pkgs),
+ }
+ tPkg, err := conf.Check("egtemplate", cfg.Fset, []*ast.File{tFile}, &tInfo)
if err != nil {
return err
}
// Analyze the template.
- template := iprog.Created[0]
- xform, err := eg.NewTransformer(iprog.Fset, template.Pkg, template.Files[0], &template.Info, *verboseFlag)
+ xform, err := eg.NewTransformer(cfg.Fset, tPkg, tFile, &tInfo, *verboseFlag)
if err != nil {
return err
}
// Apply it to the input packages.
- var pkgs []*loader.PackageInfo
+ var all []*packages.Package
if *transitiveFlag {
- for _, info := range iprog.AllPackages {
- pkgs = append(pkgs, info)
- }
+ packages.Visit(pkgs, nil, func(p *packages.Package) { all = append(all, p) })
} else {
- pkgs = iprog.InitialPackages()
+ all = pkgs
}
var hadErrors bool
for _, pkg := range pkgs {
- if pkg == template {
- continue
- }
- for _, file := range pkg.Files {
- n := xform.Transform(&pkg.Info, pkg.Pkg, file)
+ for i, filename := range pkg.CompiledGoFiles {
+ file := pkg.Syntax[i]
+ n := xform.Transform(pkg.TypesInfo, pkg.Types, file)
if n == 0 {
continue
}
- filename := iprog.Fset.File(file.Pos()).Name()
fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n)
if *writeFlag {
// Run the before-edit command (e.g. "chmod +w", "checkout") if any.
@@ -138,17 +149,34 @@
args, err)
}
}
- if err := eg.WriteAST(iprog.Fset, filename, file); err != nil {
+ if err := eg.WriteAST(cfg.Fset, filename, file); err != nil {
fmt.Fprintf(os.Stderr, "eg: %s\n", err)
hadErrors = true
}
} else {
- format.Node(os.Stdout, iprog.Fset, file)
+ format.Node(os.Stdout, cfg.Fset, file)
}
}
}
if hadErrors {
os.Exit(1)
}
+
return nil
}
+
+type pkgsImporter []*packages.Package
+
+func (p pkgsImporter) Import(path string) (tpkg *types.Package, err error) {
+ packages.Visit([]*packages.Package(p), func(pkg *packages.Package) bool {
+ if pkg.PkgPath == path {
+ tpkg = pkg.Types
+ return false
+ }
+ return true
+ }, nil)
+ if tpkg != nil {
+ return tpkg, nil
+ }
+ return nil, fmt.Errorf("package %q not found", path)
+}