@@ -470,6 +470,156 @@ type newImport struct {
470
470
spec * ast.ImportSpec
471
471
}
472
472
473
+ // importState tracks information about imports.
474
+ type importState struct {
475
+ logf func (string , ... any )
476
+ caller * Caller
477
+ importMap map [string ][]string // from package paths in the caller's file to local names
478
+ newImports []newImport // for references to free names in callee; to be added to the file
479
+ oldImports []oldImport // referenced only by caller.Call.Fun; to be removed from the file
480
+ }
481
+
482
+ // newImportState returns an importState with initial information about the caller's imports.
483
+ func newImportState (logf func (string , ... any ), caller * Caller , callee * gobCallee ) * importState {
484
+ // For simplicity we ignore existing dot imports, so that a qualified
485
+ // identifier (QI) in the callee is always represented by a QI in the caller,
486
+ // allowing us to treat a QI like a selection on a package name.
487
+ is := & importState {
488
+ logf : logf ,
489
+ caller : caller ,
490
+ importMap : make (map [string ][]string ),
491
+ }
492
+
493
+ for _ , imp := range caller .File .Imports {
494
+ if pkgName , ok := importedPkgName (caller .Info , imp ); ok &&
495
+ pkgName .Name () != "." &&
496
+ pkgName .Name () != "_" {
497
+
498
+ // If the import's sole use is in caller.Call.Fun of the form p.F(...),
499
+ // where p.F is a qualified identifier, the p import may not be
500
+ // necessary.
501
+ //
502
+ // Only the qualified identifier case matters, as other references to
503
+ // imported package names in the Call.Fun expression (e.g.
504
+ // x.after(3*time.Second).f() or time.Second.String()) will remain after
505
+ // inlining, as arguments.
506
+ //
507
+ // If that is the case, proactively check if any of the callee FreeObjs
508
+ // need this import. Doing so eagerly simplifies the resulting logic.
509
+ needed := true
510
+ sel , ok := ast .Unparen (caller .Call .Fun ).(* ast.SelectorExpr )
511
+ if ok && soleUse (caller .Info , pkgName ) == sel .X {
512
+ needed = false // no longer needed by caller
513
+ // Check to see if any of the inlined free objects need this package.
514
+ for _ , obj := range callee .FreeObjs {
515
+ if obj .PkgPath == pkgName .Imported ().Path () && obj .Shadow [pkgName .Name ()] == 0 {
516
+ needed = true // needed by callee
517
+ break
518
+ }
519
+ }
520
+ }
521
+
522
+ // Exclude imports not needed by the caller or callee after inlining; the second
523
+ // return value holds these.
524
+ if needed {
525
+ path := pkgName .Imported ().Path ()
526
+ is .importMap [path ] = append (is .importMap [path ], pkgName .Name ())
527
+ } else {
528
+ is .oldImports = append (is .oldImports , oldImport {pkgName : pkgName , spec : imp })
529
+ }
530
+ }
531
+ }
532
+ return is
533
+ }
534
+
535
+ // importName finds an existing import name to use in a particular shadowing
536
+ // context. It is used to determine the set of new imports in
537
+ // getOrMakeImportName, and is also used for writing out names in inlining
538
+ // strategies below.
539
+ func (i * importState ) importName (pkgPath string , shadow shadowMap ) string {
540
+ for _ , name := range i .importMap [pkgPath ] {
541
+ // Check that either the import preexisted, or that it was newly added
542
+ // (no PkgName) but is not shadowed, either in the callee (shadows) or
543
+ // caller (caller.lookup).
544
+ if shadow [name ] == 0 {
545
+ found := i .caller .lookup (name )
546
+ if is [* types.PkgName ](found ) || found == nil {
547
+ return name
548
+ }
549
+ }
550
+ }
551
+ return ""
552
+ }
553
+
554
+ // localName returns the local name for a given imported package path,
555
+ // adding one if it doesn't exists.
556
+ func (i * importState ) localName (pkgPath , pkgName string , shadow shadowMap ) string {
557
+ // Does an import already exist that works in this shadowing context?
558
+ if name := i .importName (pkgPath , shadow ); name != "" {
559
+ return name
560
+ }
561
+
562
+ newlyAdded := func (name string ) bool {
563
+ for _ , new := range i .newImports {
564
+ if new .pkgName == name {
565
+ return true
566
+ }
567
+ }
568
+ return false
569
+ }
570
+
571
+ // shadowedInCaller reports whether a candidate package name
572
+ // already refers to a declaration in the caller.
573
+ shadowedInCaller := func (name string ) bool {
574
+ obj := i .caller .lookup (name )
575
+ if obj == nil {
576
+ return false
577
+ }
578
+ // If obj will be removed, the name is available.
579
+ for _ , old := range i .oldImports {
580
+ if old .pkgName == obj {
581
+ return false
582
+ }
583
+ }
584
+ return true
585
+ }
586
+
587
+ // import added by callee
588
+ //
589
+ // Choose local PkgName based on last segment of
590
+ // package path plus, if needed, a numeric suffix to
591
+ // ensure uniqueness.
592
+ //
593
+ // "init" is not a legal PkgName.
594
+ //
595
+ // TODO(rfindley): is it worth preserving local package names for callee
596
+ // imports? Are they likely to be better or worse than the name we choose
597
+ // here?
598
+ base := pkgName
599
+ name := base
600
+ for n := 0 ; shadow [name ] != 0 || shadowedInCaller (name ) || newlyAdded (name ) || name == "init" ; n ++ {
601
+ name = fmt .Sprintf ("%s%d" , base , n )
602
+ }
603
+ i .logf ("adding import %s %q" , name , pkgPath )
604
+ spec := & ast.ImportSpec {
605
+ Path : & ast.BasicLit {
606
+ Kind : token .STRING ,
607
+ Value : strconv .Quote (pkgPath ),
608
+ },
609
+ }
610
+ // Use explicit pkgname (out of necessity) when it differs from the declared name,
611
+ // or (for good style) when it differs from base(pkgpath).
612
+ if name != pkgName || name != pathpkg .Base (pkgPath ) {
613
+ spec .Name = makeIdent (name )
614
+ }
615
+ i .newImports = append (i .newImports , newImport {
616
+ pkgName : name ,
617
+ spec : spec ,
618
+ })
619
+ i .importMap [pkgPath ] = append (i .importMap [pkgPath ], name )
620
+ return name
621
+ }
622
+
473
623
type inlineCallResult struct {
474
624
newImports []newImport // to add
475
625
oldImports []oldImport // to remove
@@ -586,102 +736,8 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
586
736
assign1 = func (v * types.Var ) bool { return ! updatedLocals [v ] }
587
737
}
588
738
589
- // Build a map, initially populated with caller imports, and updated below
590
- // with new imports necessary to reference free symbols in the callee.
591
- // oldImports are caller imports that won't be needed after inlining.
592
- importMap , oldImports := callerImportMap (caller , callee )
593
-
594
- // importName finds an existing import name to use in a particular shadowing
595
- // context. It is used to determine the set of new imports in
596
- // getOrMakeImportName, and is also used for writing out names in inlining
597
- // strategies below.
598
- importName := func (pkgPath string , shadow shadowMap ) string {
599
- for _ , name := range importMap [pkgPath ] {
600
- // Check that either the import preexisted, or that it was newly added
601
- // (no PkgName) but is not shadowed, either in the callee (shadows) or
602
- // caller (caller.lookup).
603
- if shadow [name ] == 0 {
604
- found := caller .lookup (name )
605
- if is [* types.PkgName ](found ) || found == nil {
606
- return name
607
- }
608
- }
609
- }
610
- return ""
611
- }
612
-
613
- // keep track of new imports that are necessary to reference any free names
614
- // in the callee.
615
- var newImports []newImport
616
-
617
- // getOrMakeImportName returns the local name for a given imported package path,
618
- // adding one if it doesn't exists.
619
- getOrMakeImportName := func (pkgPath , pkgName string , shadow shadowMap ) string {
620
- // Does an import already exist that works in this shadowing context?
621
- if name := importName (pkgPath , shadow ); name != "" {
622
- return name
623
- }
624
-
625
- newlyAdded := func (name string ) bool {
626
- for _ , new := range newImports {
627
- if new .pkgName == name {
628
- return true
629
- }
630
- }
631
- return false
632
- }
633
-
634
- // shadowedInCaller reports whether a candidate package name
635
- // already refers to a declaration in the caller.
636
- shadowedInCaller := func (name string ) bool {
637
- obj := caller .lookup (name )
638
- if obj == nil {
639
- return false
640
- }
641
- // If obj will be removed, the name is available.
642
- for _ , old := range oldImports {
643
- if old .pkgName == obj {
644
- return false
645
- }
646
- }
647
- return true
648
- }
649
-
650
- // import added by callee
651
- //
652
- // Choose local PkgName based on last segment of
653
- // package path plus, if needed, a numeric suffix to
654
- // ensure uniqueness.
655
- //
656
- // "init" is not a legal PkgName.
657
- //
658
- // TODO(rfindley): is it worth preserving local package names for callee
659
- // imports? Are they likely to be better or worse than the name we choose
660
- // here?
661
- base := pkgName
662
- name := base
663
- for n := 0 ; shadow [name ] != 0 || shadowedInCaller (name ) || newlyAdded (name ) || name == "init" ; n ++ {
664
- name = fmt .Sprintf ("%s%d" , base , n )
665
- }
666
- logf ("adding import %s %q" , name , pkgPath )
667
- spec := & ast.ImportSpec {
668
- Path : & ast.BasicLit {
669
- Kind : token .STRING ,
670
- Value : strconv .Quote (pkgPath ),
671
- },
672
- }
673
- // Use explicit pkgname (out of necessity) when it differs from the declared name,
674
- // or (for good style) when it differs from base(pkgpath).
675
- if name != pkgName || name != pathpkg .Base (pkgPath ) {
676
- spec .Name = makeIdent (name )
677
- }
678
- newImports = append (newImports , newImport {
679
- pkgName : name ,
680
- spec : spec ,
681
- })
682
- importMap [pkgPath ] = append (importMap [pkgPath ], name )
683
- return name
684
- }
739
+ // Extract information about the caller's imports.
740
+ istate := newImportState (logf , caller , callee )
685
741
686
742
// Compute the renaming of the callee's free identifiers.
687
743
objRenames := make ([]ast.Expr , len (callee .FreeObjs )) // nil => no change
@@ -709,7 +765,7 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
709
765
var newName ast.Expr
710
766
if obj .Kind == "pkgname" {
711
767
// Use locally appropriate import, creating as needed.
712
- n := getOrMakeImportName (obj .PkgPath , obj .PkgName , obj .Shadow )
768
+ n := istate . localName (obj .PkgPath , obj .PkgName , obj .Shadow )
713
769
newName = makeIdent (n ) // imported package
714
770
} else if ! obj .ValidPos {
715
771
// Built-in function, type, or value (e.g. nil, zero):
@@ -754,7 +810,7 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
754
810
755
811
// Form a qualified identifier, pkg.Name.
756
812
if qualify {
757
- pkgName := getOrMakeImportName (obj .PkgPath , obj .PkgName , obj .Shadow )
813
+ pkgName := istate . localName (obj .PkgPath , obj .PkgName , obj .Shadow )
758
814
newName = & ast.SelectorExpr {
759
815
X : makeIdent (pkgName ),
760
816
Sel : makeIdent (obj .Name ),
@@ -765,8 +821,8 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
765
821
}
766
822
767
823
res := & inlineCallResult {
768
- newImports : newImports ,
769
- oldImports : oldImports ,
824
+ newImports : istate . newImports ,
825
+ oldImports : istate . oldImports ,
770
826
}
771
827
772
828
// Parse callee function declaration.
@@ -1115,7 +1171,7 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
1115
1171
(! needBindingDecl || (bindingDecl != nil && len (bindingDecl .names ) == 0 )) {
1116
1172
1117
1173
// Reduces to: { var (bindings); lhs... := rhs... }
1118
- if newStmts , ok := st .assignStmts (stmt , results , importName ); ok {
1174
+ if newStmts , ok := st .assignStmts (stmt , results , istate . importName ); ok {
1119
1175
logf ("strategy: reduce assign-context call to { return exprs }" )
1120
1176
1121
1177
clearPositions (calleeDecl .Body )
@@ -1348,56 +1404,6 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
1348
1404
return res , nil
1349
1405
}
1350
1406
1351
- // callerImportMap returns a map from package paths in the caller's file to local names.
1352
- // The map excludes imports not needed by the caller or callee after inlining; the second
1353
- // return value holds these.
1354
- func callerImportMap (caller * Caller , callee * gobCallee ) (map [string ][]string , []oldImport ) {
1355
- // For simplicity we ignore existing dot imports, so that a qualified
1356
- // identifier (QI) in the callee is always represented by a QI in the caller,
1357
- // allowing us to treat a QI like a selection on a package name.
1358
- importMap := make (map [string ][]string ) // maps package path to local name(s)
1359
- var oldImports []oldImport // imports referenced only by caller.Call.Fun
1360
-
1361
- for _ , imp := range caller .File .Imports {
1362
- if pkgName , ok := importedPkgName (caller .Info , imp ); ok &&
1363
- pkgName .Name () != "." &&
1364
- pkgName .Name () != "_" {
1365
-
1366
- // If the import's sole use is in caller.Call.Fun of the form p.F(...),
1367
- // where p.F is a qualified identifier, the p import may not be
1368
- // necessary.
1369
- //
1370
- // Only the qualified identifier case matters, as other references to
1371
- // imported package names in the Call.Fun expression (e.g.
1372
- // x.after(3*time.Second).f() or time.Second.String()) will remain after
1373
- // inlining, as arguments.
1374
- //
1375
- // If that is the case, proactively check if any of the callee FreeObjs
1376
- // need this import. Doing so eagerly simplifies the resulting logic.
1377
- needed := true
1378
- sel , ok := ast .Unparen (caller .Call .Fun ).(* ast.SelectorExpr )
1379
- if ok && soleUse (caller .Info , pkgName ) == sel .X {
1380
- needed = false // no longer needed by caller
1381
- // Check to see if any of the inlined free objects need this package.
1382
- for _ , obj := range callee .FreeObjs {
1383
- if obj .PkgPath == pkgName .Imported ().Path () && obj .Shadow [pkgName .Name ()] == 0 {
1384
- needed = true // needed by callee
1385
- break
1386
- }
1387
- }
1388
- }
1389
-
1390
- if needed {
1391
- path := pkgName .Imported ().Path ()
1392
- importMap [path ] = append (importMap [path ], pkgName .Name ())
1393
- } else {
1394
- oldImports = append (oldImports , oldImport {pkgName : pkgName , spec : imp })
1395
- }
1396
- }
1397
- }
1398
- return importMap , oldImports
1399
- }
1400
-
1401
1407
type argument struct {
1402
1408
expr ast.Expr
1403
1409
typ types.Type // may be tuple for sole non-receiver arg in spread call
0 commit comments