cmd/stringer: revert back to source importer

Roll back my two recent changes. Stringer is now very slow again,
but works in most use cases.

My git foo is insufficient to do this as a revert, but it is a by-hand
reversion of CLs

	https://go-review.googlesource.com/121884
	https://go-review.googlesource.com/121995

See the issue for a long conversation about the general problem.

Update golang/go#10249
Update golang/go#25650

Change-Id: I7b6ce352a4c7ebf0977883509e9d7189aaac1251
Reviewed-on: https://go-review.googlesource.com/122535
Run-TryBot: Rob Pike <r@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Rob Pike 2018-07-07 08:11:50 +10:00
parent 435878328f
commit 827133af57
6 changed files with 50 additions and 110 deletions

View File

@ -9,7 +9,6 @@
package main
import (
"bytes"
"fmt"
"go/build"
"io"
@ -27,8 +26,17 @@ import (
// binary panics if the String method for X is not correct, including for error cases.
func TestEndToEnd(t *testing.T) {
dir, stringer := buildStringer(t)
dir, err := ioutil.TempDir("", "stringer")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
// Create stringer in temporary directory.
stringer := filepath.Join(dir, "stringer.exe")
err = run("go", "build", "-o", stringer)
if err != nil {
t.Fatalf("building stringer: %s", err)
}
// Read the testdata directory.
fd, err := os.Open("testdata")
if err != nil {
@ -45,10 +53,6 @@ func TestEndToEnd(t *testing.T) {
t.Errorf("%s is not a Go file", name)
continue
}
if strings.HasPrefix(name, "tag_") {
// This file is used for tag processing in TestTags, below.
continue
}
if name == "cgo.go" && !build.Default.CgoEnabled {
t.Logf("cgo is not enabled for %s", name)
continue
@ -59,69 +63,9 @@ func TestEndToEnd(t *testing.T) {
}
}
// TestTags verifies that the -tags flag works as advertised.
func TestTags(t *testing.T) {
dir, stringer := buildStringer(t)
defer os.RemoveAll(dir)
var (
protectedConst = []byte("TagProtected")
output = filepath.Join(dir, "const_string.go")
)
for _, file := range []string{"tag_main.go", "tag_tag.go"} {
err := copy(filepath.Join(dir, file), filepath.Join("testdata", file))
if err != nil {
t.Fatal(err)
}
}
err := run(stringer, "-type", "Const", dir)
if err != nil {
t.Fatal(err)
}
result, err := ioutil.ReadFile(output)
if err != nil {
t.Fatal(err)
}
if bytes.Contains(result, protectedConst) {
t.Fatal("tagged variable appears in untagged run")
}
err = os.Remove(output)
if err != nil {
t.Fatal(err)
}
err = run(stringer, "-type", "Const", "-tags", "tag", dir)
if err != nil {
t.Fatal(err)
}
result, err = ioutil.ReadFile(output)
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(result, protectedConst) {
t.Fatal("tagged variable does not appear in tagged run")
}
}
// buildStringer creates a temporary directory and installs stringer there.
func buildStringer(t *testing.T) (dir string, stringer string) {
t.Helper()
dir, err := ioutil.TempDir("", "stringer")
if err != nil {
t.Fatal(err)
}
stringer = filepath.Join(dir, "stringer.exe")
err = run("go", "build", "-o", stringer, "stringer.go")
if err != nil {
t.Fatalf("building stringer: %s", err)
}
return dir, stringer
}
// stringerCompileAndRun runs stringer for the named file and compiles and
// runs the target binary in directory dir. That binary will panic if the String method is incorrect.
func stringerCompileAndRun(t *testing.T, dir, stringer, typeName, fileName string) {
t.Helper()
t.Logf("run: %s %s\n", fileName, typeName)
source := filepath.Join(dir, fileName)
err := copy(source, filepath.Join("testdata", fileName))

View File

@ -0,0 +1,16 @@
// Copyright 2017 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 !go1.9
package main
import (
"go/importer"
"go/types"
)
func defaultImporter() types.Importer {
return importer.Default()
}

View File

@ -0,0 +1,16 @@
// Copyright 2017 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 go1.9
package main
import (
"go/importer"
"go/types"
)
func defaultImporter() types.Importer {
return importer.For("source", nil)
}

View File

@ -66,7 +66,6 @@ import (
"go/build"
exact "go/constant"
"go/format"
"go/importer"
"go/parser"
"go/token"
"go/types"
@ -83,7 +82,6 @@ var (
output = flag.String("output", "", "output file name; default srcdir/<type>_string.go")
trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names")
linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present")
buildTags = flag.String("tags", "", "comma-separated list of build tags to apply")
)
// Usage is a replacement usage function for the flags package.
@ -107,10 +105,6 @@ func main() {
os.Exit(2)
}
types := strings.Split(*typeNames, ",")
var tags []string
if len(*buildTags) > 0 {
tags = strings.Split(*buildTags, ",")
}
// We accept either one directory or a list of files. Which do we have?
args := flag.Args()
@ -127,11 +121,8 @@ func main() {
}
if len(args) == 1 && isDirectory(args[0]) {
dir = args[0]
g.parsePackageDir(args[0], tags)
g.parsePackageDir(args[0])
} else {
if len(tags) != 0 {
log.Fatal("-tags option applies only to directories, not when files are specified")
}
dir = filepath.Dir(args[0])
g.parsePackageFiles(args)
}
@ -206,15 +197,9 @@ type Package struct {
typesPkg *types.Package
}
func buildContext(tags []string) *build.Context {
ctx := build.Default
ctx.BuildTags = tags
return &ctx
}
// parsePackageDir parses the package residing in the directory.
func (g *Generator) parsePackageDir(directory string, tags []string) {
pkg, err := buildContext(tags).ImportDir(directory, 0)
func (g *Generator) parsePackageDir(directory string) {
pkg, err := build.Default.ImportDir(directory, 0)
if err != nil {
log.Fatalf("cannot process directory %s: %s", directory, err)
}
@ -276,17 +261,14 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac
g.pkg.name = astFiles[0].Name.Name
g.pkg.files = files
g.pkg.dir = directory
g.pkg.typeCheck(fs, astFiles)
// Type check the package.
g.pkg.check(fs, astFiles)
}
// check type-checks the package so we can evaluate contants whose values we are printing.
func (pkg *Package) typeCheck(fs *token.FileSet, astFiles []*ast.File) {
// check type-checks the package. The package must be OK to proceed.
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) {
pkg.defs = make(map[*ast.Ident]types.Object)
config := types.Config{
IgnoreFuncBodies: true, // We only need to evaluate constants.
Importer: importer.Default(),
FakeImportC: true,
}
config := types.Config{Importer: defaultImporter(), FakeImportC: true}
info := &types.Info{
Defs: pkg.defs,
}

View File

@ -1,11 +0,0 @@
// No build tag in this file.
package main
type Const int
const (
A Const = iota
B
C
)

View File

@ -1,7 +0,0 @@
// This file has a build tag "tag"
// +build tag
package main
const TagProtected Const = C + 1