Skip to content

Commit cb89afa

Browse files
committed
imports: drop anything after a non identifier rune in package names
A package name cannot contain a '.' anyway, so this is a mostly likely internal package name in the presence of a '.' in the import path. This stops goimports from adding a local alias in places where it is not needed, for instance in gopkg.in conventions. Fixes golang/go#29556 Change-Id: I0ab11f2852d7f1dae14457995692760077201c8e Reviewed-on: https://go-review.googlesource.com/c/157357 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
1 parent 1c35819 commit cb89afa

File tree

2 files changed

+48
-13
lines changed

2 files changed

+48
-13
lines changed

imports/fix.go

+27-7
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import (
2323
"strings"
2424
"sync"
2525
"time"
26+
"unicode"
27+
"unicode/utf8"
2628

2729
"golang.org/x/tools/go/ast/astutil"
2830
"golang.org/x/tools/go/packages"
@@ -289,7 +291,7 @@ func (p *pass) importIdentifier(imp *importInfo) string {
289291
if known != nil && known.name != "" {
290292
return known.name
291293
}
292-
return importPathToNameBasic(imp.importPath, p.srcDir)
294+
return importPathToAssumedName(imp.importPath)
293295
}
294296

295297
// load reads in everything necessary to run a pass, and reports whether the
@@ -389,7 +391,7 @@ func (p *pass) fix() bool {
389391
}
390392
path := strings.Trim(imp.Path.Value, `""`)
391393
ident := p.importIdentifier(&importInfo{importPath: path})
392-
if ident != importPathToNameBasic(path, p.srcDir) {
394+
if ident != importPathToAssumedName(path) {
393395
imp.Name = &ast.Ident{Name: ident, NamePos: imp.Pos()}
394396
}
395397
}
@@ -648,7 +650,7 @@ func (r *goPackagesResolver) loadPackageNames(importPaths []string, srcDir strin
648650
if _, ok := names[path]; ok {
649651
continue
650652
}
651-
names[path] = importPathToNameBasic(path, srcDir)
653+
names[path] = importPathToAssumedName(path)
652654
}
653655
return names, nil
654656

@@ -741,18 +743,36 @@ func addExternalCandidates(pass *pass, refs references, filename string) error {
741743
return firstErr
742744
}
743745

744-
// importPathToNameBasic assumes the package name is the base of import path,
745-
// except that if the path ends in foo/vN, it assumes the package name is foo.
746-
func importPathToNameBasic(importPath, srcDir string) (packageName string) {
746+
// notIdentifier reports whether ch is an invalid identifier character.
747+
func notIdentifier(ch rune) bool {
748+
return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' ||
749+
'0' <= ch && ch <= '9' ||
750+
ch == '_' ||
751+
ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch)))
752+
}
753+
754+
// importPathToAssumedName returns the assumed package name of an import path.
755+
// It does this using only string parsing of the import path.
756+
// It picks the last element of the path that does not look like a major
757+
// version, and then picks the valid identifier off the start of that element.
758+
// It is used to determine if a local rename should be added to an import for
759+
// clarity.
760+
// This function could be moved to a standard package and exported if we want
761+
// for use in other tools.
762+
func importPathToAssumedName(importPath string) string {
747763
base := path.Base(importPath)
748764
if strings.HasPrefix(base, "v") {
749765
if _, err := strconv.Atoi(base[1:]); err == nil {
750766
dir := path.Dir(importPath)
751767
if dir != "." {
752-
return path.Base(dir)
768+
base = path.Base(dir)
753769
}
754770
}
755771
}
772+
base = strings.TrimPrefix(base, "go-")
773+
if i := strings.IndexFunc(base, notIdentifier); i >= 0 {
774+
base = base[:i]
775+
}
756776
return base
757777
}
758778

imports/fix_test.go

+21-6
Original file line numberDiff line numberDiff line change
@@ -1373,13 +1373,13 @@ var (
13731373

13741374
// Test for correctly identifying the name of a vendored package when it
13751375
// differs from its directory name. In this test, the import line
1376-
// "mypkg.com/mypkg.v1" would be removed if goimports wasn't able to detect
1376+
// "mypkg.com/mypkg_v1" would be removed if goimports wasn't able to detect
13771377
// that the package name is "mypkg".
13781378
func TestVendorPackage(t *testing.T) {
13791379
const input = `package p
13801380
import (
13811381
"fmt"
1382-
"mypkg.com/mypkg.v1"
1382+
"mypkg.com/mypkg_v1"
13831383
)
13841384
var _, _ = fmt.Print, mypkg.Foo
13851385
`
@@ -1389,7 +1389,7 @@ var _, _ = fmt.Print, mypkg.Foo
13891389
import (
13901390
"fmt"
13911391
1392-
mypkg "mypkg.com/mypkg.v1"
1392+
mypkg "mypkg.com/mypkg_v1"
13931393
)
13941394
13951395
var _, _ = fmt.Print, mypkg.Foo
@@ -1400,7 +1400,7 @@ var _, _ = fmt.Print, mypkg.Foo
14001400
module: packagestest.Module{
14011401
Name: "mypkg.com/outpkg",
14021402
Files: fm{
1403-
"vendor/mypkg.com/mypkg.v1/f.go": "package mypkg\nvar Foo = 123\n",
1403+
"vendor/mypkg.com/mypkg_v1/f.go": "package mypkg\nvar Foo = 123\n",
14041404
"toformat.go": input,
14051405
},
14061406
},
@@ -1626,28 +1626,43 @@ func TestAddNameToMismatchedImport(t *testing.T) {
16261626
const input = `package main
16271627
16281628
import (
1629+
"foo.com/a.thing"
16291630
"foo.com/surprise"
16301631
"foo.com/v1"
1632+
"foo.com/other/v2"
1633+
"foo.com/other/v3"
1634+
"foo.com/go-thing"
1635+
"foo.com/go-wrong"
16311636
)
16321637
1633-
var _, _ = bar.X, v1.Y`
1638+
var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}`
16341639

16351640
const want = `package main
16361641
16371642
import (
1643+
"foo.com/a.thing"
1644+
"foo.com/go-thing"
1645+
gow "foo.com/go-wrong"
1646+
v2 "foo.com/other/v2"
1647+
"foo.com/other/v3"
16381648
bar "foo.com/surprise"
16391649
v1 "foo.com/v1"
16401650
)
16411651
1642-
var _, _ = bar.X, v1.Y
1652+
var _ = []interface{}{bar.X, v1.Y, a.A, v2.V2, other.V3, thing.Thing, gow.Wrong}
16431653
`
16441654

16451655
testConfig{
16461656
module: packagestest.Module{
16471657
Name: "foo.com",
16481658
Files: fm{
1659+
"a.thing/a.go": "package a \n const A = 1",
16491660
"surprise/x.go": "package bar \n const X = 1",
16501661
"v1/x.go": "package v1 \n const Y = 1",
1662+
"other/v2/y.go": "package v2 \n const V2 = 1",
1663+
"other/v3/z.go": "package other \n const V3 = 1",
1664+
"go-thing/b.go": "package thing \n const Thing = 1",
1665+
"go-wrong/b.go": "package gow \n const Wrong = 1",
16511666
"test/t.go": input,
16521667
},
16531668
},

0 commit comments

Comments
 (0)