@@ -224,7 +224,7 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
224
224
if ipath == "C" {
225
225
break
226
226
}
227
- local := path . Base (ipath )
227
+ local := importPathToName (ipath , srcDir )
228
228
decls [local ] = v
229
229
case * ast.SelectorExpr :
230
230
xident , ok := v .X .(* ast.Ident )
@@ -363,6 +363,98 @@ func fixImports(fset *token.FileSet, f *ast.File, filename string) (added []stri
363
363
return added , nil
364
364
}
365
365
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
+
366
458
var stdImportPackage = map [string ]string {} // "net/http" => "http"
367
459
368
460
func init () {
@@ -680,34 +772,14 @@ func findImportGoPath(ctx context.Context, pkgName string, symbols map[string]bo
680
772
if pkg == nil {
681
773
continue
682
774
}
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 ,
684
776
// 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
691
778
return pkg .importPathShort , needsRename , nil
692
779
}
693
780
return "" , false , nil
694
781
}
695
782
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
-
711
783
// pkgIsCandidate reports whether pkg is a candidate for satisfying the
712
784
// finding which package pkgIdent in the file named by filename is trying
713
785
// to refer to.
0 commit comments