1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:54:40 -07:00

cmd/bundle: Support renaming imports.

Document a risk of generating invalid code due to shadowing between
identifiers in code files and the imported package names.

This risk was present before for any package with more than 1 .go file,
but it's increased when some files have renamed imports (since they're
typically used to resolve shadowing conflicts).

Resolves TODO(adonovan): support renaming imports.

Change-Id: Ie0e702345790fd2059c229623fb99fe645d688a4
Reviewed-on: https://go-review.googlesource.com/23785
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
Dmitri Shuralyov 2016-06-04 23:20:26 -07:00 committed by Alan Donovan
parent 6e2a0ce36e
commit 5cb1c80a83
4 changed files with 66 additions and 27 deletions

View File

@ -96,6 +96,7 @@ import (
"log"
"os"
"path"
"strconv"
"strings"
"golang.org/x/tools/go/loader"
@ -257,46 +258,61 @@ func bundle(src, dst, dstpkg, prefix string) ([]byte, error) {
fmt.Fprintf(&out, "package %s\n\n", dstpkg)
// Print a single declaration that imports all necessary packages.
// TODO(adonovan):
// - support renaming imports.
// BUG(adonovan,shurcooL): bundle may generate incorrect code
// due to shadowing between identifiers and imported package names.
//
// The generated code will either fail to compile or
// (unlikely) compile successfully but have different behavior
// than the original package. The risk of this happening is higher
// when the original package has renamed imports (they're typically
// renamed in order to resolve a shadow inside that particular .go file).
// TODO(adonovan,shurcooL):
// - detect shadowing issues, and either return error or resolve them
// - preserve comments from the original import declarations.
// pkgStd and pkgExt are sets of printed import specs. This is done
// to deduplicate instances of the same import name and path.
var pkgStd = make(map[string]bool)
var pkgExt = make(map[string]bool)
for _, f := range info.Files {
for _, imp := range f.Imports {
if imp.Name != nil {
return nil, fmt.Errorf("%s: renaming imports not supported",
lprog.Fset.Position(imp.Pos()))
path, err := strconv.Unquote(imp.Path.Value)
if err != nil {
log.Fatalf("invalid import path string: %v", err) // Shouldn't happen here since conf.Load succeeded.
}
}
}
var pkgStd, pkgExt []string
for _, p := range info.Pkg.Imports() {
if p.Path() == dst {
if path == dst {
continue
}
x, ok := importMap[p.Path()]
if !ok {
x = p.Path()
if newPath, ok := importMap[path]; ok {
path = newPath
}
if isStandardImportPath(x) {
pkgStd = append(pkgStd, x)
var name string
if imp.Name != nil {
name = imp.Name.Name
}
spec := fmt.Sprintf("%s %q", name, path)
if isStandardImportPath(path) {
pkgStd[spec] = true
} else {
pkgExt = append(pkgExt, x)
pkgExt[spec] = true
}
}
}
// Print a single declaration that imports all necessary packages.
fmt.Fprintln(&out, "import (")
for _, p := range pkgStd {
fmt.Fprintf(&out, "\t%q\n", p)
for p := range pkgStd {
fmt.Fprintf(&out, "\t%s\n", p)
}
if len(pkgExt) > 0 {
fmt.Fprintln(&out)
for _, p := range pkgExt {
fmt.Fprintf(&out, "\t%q\n", p)
}
for p := range pkgExt {
fmt.Fprintf(&out, "\t%s\n", p)
}
fmt.Fprintln(&out, ")\n")
fmt.Fprint(&out, ")\n\n")
// Modify and print each file.
for _, f := range info.Files {

View File

@ -28,6 +28,7 @@ func TestBundle(t *testing.T) {
"initial": {
"a.go": load("testdata/src/initial/a.go"),
"b.go": load("testdata/src/initial/b.go"),
"c.go": load("testdata/src/initial/c.go"),
},
"domain.name/importdecl": {
"p.go": load("testdata/src/domain.name/importdecl/p.go"),

View File

@ -8,6 +8,10 @@ package dest
import (
"fmt"
. "fmt"
_ "fmt"
renamedfmt "fmt"
renamedfmt2 "fmt"
"domain.name/importdecl"
)
@ -31,3 +35,9 @@ const prefixc = 1
func prefixfoo() {
fmt.Println(importdecl.F())
}
func prefixbaz() {
renamedfmt.Println()
renamedfmt2.Println()
Println()
}

12
cmd/bundle/testdata/src/initial/c.go vendored Normal file
View File

@ -0,0 +1,12 @@
package initial
import _ "fmt"
import renamedfmt "fmt"
import renamedfmt2 "fmt"
import . "fmt"
func baz() {
renamedfmt.Println()
renamedfmt2.Println()
Println()
}