Skip to content

Commit a28dfb4

Browse files
heschibradfitz
authored andcommitted
revert "imports: create named imports for name/path mismatches"
This reverts CL 145699 (commit 864069c) Reason for revert: If the mismatch is bad enough that goimports can't find it again, then the import is just removed, even if the user adds it back again. Fixes #28645. Change-Id: I6c8fc5434c2b56c73b260bcec2c12d8746fac4ad Reviewed-on: https://go-review.googlesource.com/c/148237 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 4ca4995 commit a28dfb4

File tree

2 files changed

+139
-48
lines changed

2 files changed

+139
-48
lines changed

imports/fix.go

Lines changed: 95 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
224224
if ipath == "C" {
225225
break
226226
}
227-
local := path.Base(ipath)
227+
local := importPathToName(ipath, srcDir)
228228
decls[local] = v
229229
case *ast.SelectorExpr:
230230
xident, ok := v.X.(*ast.Ident)
@@ -363,6 +363,98 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
363363
return added, nil
364364
}
365365

366+
// importPathToName returns the package name for the given import path.
367+
var importPathToName func(importPath, srcDir string) (packageName string) = importPathToNameGoPath
368+
369+
// importPathToNameBasic assumes the package name is the base of import path,
370+
// except that if the path ends in foo/vN, it assumes the package name is foo.
371+
func importPathToNameBasic(importPath, srcDir string) (packageName string) {
372+
base := path.Base(importPath)
373+
if strings.HasPrefix(base, "v") {
374+
if _, err := strconv.Atoi(base[1:]); err == nil {
375+
dir := path.Dir(importPath)
376+
if dir != "." {
377+
return path.Base(dir)
378+
}
379+
}
380+
}
381+
return base
382+
}
383+
384+
// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
385+
// If there's a problem, it falls back to using importPathToNameBasic.
386+
func importPathToNameGoPath(importPath, srcDir string) (packageName string) {
387+
// Fast path for standard library without going to disk.
388+
if pkg, ok := stdImportPackage[importPath]; ok {
389+
return pkg
390+
}
391+
392+
pkgName, err := importPathToNameGoPathParse(importPath, srcDir)
393+
if Debug {
394+
log.Printf("importPathToNameGoPathParse(%q, srcDir=%q) = %q, %v", importPath, srcDir, pkgName, err)
395+
}
396+
if err == nil {
397+
return pkgName
398+
}
399+
return importPathToNameBasic(importPath, srcDir)
400+
}
401+
402+
// importPathToNameGoPathParse is a faster version of build.Import if
403+
// the only thing desired is the package name. It uses build.FindOnly
404+
// to find the directory and then only parses one file in the package,
405+
// trusting that the files in the directory are consistent.
406+
func importPathToNameGoPathParse(importPath, srcDir string) (packageName string, err error) {
407+
buildPkg, err := build.Import(importPath, srcDir, build.FindOnly)
408+
if err != nil {
409+
return "", err
410+
}
411+
d, err := os.Open(buildPkg.Dir)
412+
if err != nil {
413+
return "", err
414+
}
415+
names, err := d.Readdirnames(-1)
416+
d.Close()
417+
if err != nil {
418+
return "", err
419+
}
420+
sort.Strings(names) // to have predictable behavior
421+
var lastErr error
422+
var nfile int
423+
for _, name := range names {
424+
if !strings.HasSuffix(name, ".go") {
425+
continue
426+
}
427+
if strings.HasSuffix(name, "_test.go") {
428+
continue
429+
}
430+
nfile++
431+
fullFile := filepath.Join(buildPkg.Dir, name)
432+
433+
fset := token.NewFileSet()
434+
f, err := parser.ParseFile(fset, fullFile, nil, parser.PackageClauseOnly)
435+
if err != nil {
436+
lastErr = err
437+
continue
438+
}
439+
pkgName := f.Name.Name
440+
if pkgName == "documentation" {
441+
// Special case from go/build.ImportDir, not
442+
// handled by ctx.MatchFile.
443+
continue
444+
}
445+
if pkgName == "main" {
446+
// Also skip package main, assuming it's a +build ignore generator or example.
447+
// Since you can't import a package main anyway, there's no harm here.
448+
continue
449+
}
450+
return pkgName, nil
451+
}
452+
if lastErr != nil {
453+
return "", lastErr
454+
}
455+
return "", fmt.Errorf("no importable package found in %d Go files", nfile)
456+
}
457+
366458
var stdImportPackage = map[string]string{} // "net/http" => "http"
367459

368460
func init() {
@@ -680,34 +772,14 @@ func findImportGoPath(ctx context.Context, pkgName string, symbols map[string]bo
680772
if pkg == nil {
681773
continue
682774
}
683-
// If the package name in the source doesn't match the import path,
775+
// If the package name in the source doesn't match the import path's base,
684776
// return true so the rewriter adds a name (import foo "github.com/bar/go-foo")
685-
// Module-style version suffixes are allowed.
686-
lastSeg := path.Base(pkg.importPath)
687-
if isVersionSuffix(lastSeg) {
688-
lastSeg = path.Base(path.Dir(pkg.importPath))
689-
}
690-
needsRename := lastSeg != pkgName
777+
needsRename := path.Base(pkg.importPath) != pkgName
691778
return pkg.importPathShort, needsRename, nil
692779
}
693780
return "", false, nil
694781
}
695782

696-
// isVersionSuffix reports whether the path segment seg is a semantic import
697-
// versioning style major version suffix.
698-
func isVersionSuffix(seg string) bool {
699-
if seg == "" {
700-
return false
701-
}
702-
if seg[0] != 'v' {
703-
return false
704-
}
705-
if _, err := strconv.Atoi(seg[1:]); err != nil {
706-
return false
707-
}
708-
return true
709-
}
710-
711783
// pkgIsCandidate reports whether pkg is a candidate for satisfying the
712784
// finding which package pkgIdent in the file named by filename is trying
713785
// to refer to.

imports/fix_test.go

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package imports
77
import (
88
"fmt"
99
"go/build"
10+
"path/filepath"
1011
"runtime"
1112
"strings"
1213
"sync"
@@ -1349,15 +1350,9 @@ var (
13491350
`
13501351

13511352
testConfig{
1352-
modules: []packagestest.Module{
1353-
{
1354-
Name: "mypkg.com/outpkg",
1355-
Files: fm{"toformat.go": input},
1356-
},
1357-
{
1358-
Name: "github.com/foo/v2",
1359-
Files: fm{"x.go": "package foo\n func Foo(){}\n"},
1360-
},
1353+
module: packagestest.Module{
1354+
Name: "mypkg.com/outpkg",
1355+
Files: fm{"toformat.go": input},
13611356
},
13621357
}.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, input)
13631358
}
@@ -1368,24 +1363,18 @@ var (
13681363
// that the package name is "mypkg".
13691364
func TestVendorPackage(t *testing.T) {
13701365
const input = `package p
1371-
import (
1372-
"fmt"
1373-
"mypkg.com/mypkg.v1"
1374-
)
1375-
var _, _ = fmt.Print, mypkg.Foo
1376-
`
1377-
1378-
const want = `package p
13791366
13801367
import (
13811368
"fmt"
13821369
1383-
mypkg "mypkg.com/mypkg.v1"
1370+
"mypkg.com/mypkg.v1"
13841371
)
13851372
1386-
var _, _ = fmt.Print, mypkg.Foo
1373+
var (
1374+
_ = fmt.Print
1375+
_ = mypkg.Foo
1376+
)
13871377
`
1388-
13891378
testConfig{
13901379
gopathOnly: true,
13911380
module: packagestest.Module{
@@ -1395,7 +1384,7 @@ var _, _ = fmt.Print, mypkg.Foo
13951384
"toformat.go": input,
13961385
},
13971386
},
1398-
}.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, want)
1387+
}.processTest(t, "mypkg.com/outpkg", "toformat.go", nil, nil, input)
13991388
}
14001389

14011390
func TestInternal(t *testing.T) {
@@ -1573,23 +1562,25 @@ func (t *goimportTest) process(module, file string, contents []byte, opts *Optio
15731562
}
15741563

15751564
// Tests that added imports are renamed when the import path's base doesn't
1576-
// match its package name.
1565+
// match its package name. For example, we want to generate:
1566+
//
1567+
// import cloudbilling "google.golang.org/api/cloudbilling/v1"
15771568
func TestRenameWhenPackageNameMismatch(t *testing.T) {
15781569
const input = `package main
15791570
const Y = bar.X`
15801571

15811572
const want = `package main
15821573
1583-
import bar "foo.com/foo/bar/baz"
1574+
import bar "foo.com/foo/bar/v1"
15841575
15851576
const Y = bar.X
15861577
`
15871578
testConfig{
15881579
module: packagestest.Module{
15891580
Name: "foo.com",
15901581
Files: fm{
1591-
"foo/bar/baz/x.go": "package bar \n const X = 1",
1592-
"test/t.go": input,
1582+
"foo/bar/v1/x.go": "package bar \n const X = 1",
1583+
"test/t.go": input,
15931584
},
15941585
},
15951586
}.processTest(t, "foo.com", "test/t.go", nil, nil, want)
@@ -1734,6 +1725,34 @@ const Y = foo.X
17341725
}.processTest(t, "foo.com", "x/x.go", nil, nil, want)
17351726
}
17361727

1728+
// Tests importPathToNameGoPathParse and in particular that it stops
1729+
// after finding the first non-documentation package name, not
1730+
// reporting an error on inconsistent package names (since it should
1731+
// never make it that far).
1732+
func TestImportPathToNameGoPathParse(t *testing.T) {
1733+
testConfig{
1734+
gopathOnly: true,
1735+
module: packagestest.Module{
1736+
Name: "example.net/pkg",
1737+
Files: fm{
1738+
"doc.go": "package documentation\n", // ignored
1739+
"gen.go": "package main\n", // also ignored
1740+
"pkg.go": "package the_pkg_name_to_find\n and this syntax error is ignored because of parser.PackageClauseOnly",
1741+
"z.go": "package inconsistent\n", // inconsistent but ignored
1742+
},
1743+
},
1744+
}.test(t, func(t *goimportTest) {
1745+
got, err := importPathToNameGoPathParse("example.net/pkg", filepath.Join(t.gopath, "src", "other.net"))
1746+
if err != nil {
1747+
t.Fatal(err)
1748+
}
1749+
const want = "the_pkg_name_to_find"
1750+
if got != want {
1751+
t.Errorf("importPathToNameGoPathParse(..) = %q; want %q", got, want)
1752+
}
1753+
})
1754+
}
1755+
17371756
func TestIgnoreConfiguration(t *testing.T) {
17381757
const input = `package x
17391758

0 commit comments

Comments
 (0)