Skip to content

Commit 3901733

Browse files
findleyrgopherbot
authored andcommitted
internal/refactor/inline: substitute groups of dependent arguments
In change signature refactoring, the wrapper was very likely to run into a particular unidiomatic behavior of the inliner, where arguments were detected as unsubstitutable because they have free variables shadowed by other parameters. But if those other parameters are going to be substituted, this shadowing is irrelevant. Fix this by keeping track of shadowing from other parameters in a new shadowMap type, and then analyzing these relationships during substitution to detect cases where closed subgraphs of parameters can be substituted simultaneously. Also: fix a formatting bug (golang/go#67335) that was reproduced by one of the new tests: we need to clear positions when using nodes from the callee in the caller's binding decl. For golang/go#70599 Fixes golang/go#67335 Change-Id: I08970a1cea8a82ea108084394569cffbc5975235 Reviewed-on: https://go-review.googlesource.com/c/tools/+/633256 Reviewed-by: Alan Donovan <[email protected]> Auto-Submit: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 61b2408 commit 3901733

File tree

6 files changed

+478
-67
lines changed

6 files changed

+478
-67
lines changed

gopls/internal/test/marker/testdata/codeaction/issue64558.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ go 1.18
88
package a
99

1010
func _() {
11-
f(1, 2) //@ diag("2", re"too many arguments"), codeaction("f", "refactor.inline.call", end=")", err=re`inlining failed \("args/params mismatch"\), likely because inputs were ill-typed`)
11+
f(1, 2) //@ diag("2", re"too many arguments"), codeaction("f", "refactor.inline.call", end=")", err=re`inlining failed \("too many arguments"\), likely because inputs were ill-typed`)
1212
}
1313

1414
func f(int) {}

gopls/internal/test/marker/testdata/codeaction/removeparam_satisfies.txt

+1-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ var _ Fooer = rm.T(0)
4242

4343
func _() {
4444
var x rm.T
45-
var t rm.T = x
46-
t.Foo()
45+
x.Foo()
4746
}
4847
-- use/use.go --
4948
package use

internal/refactor/inline/callee.go

+61-20
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ type object struct {
7373
PkgName string // name of object's package (or imported package if kind="pkgname")
7474
// TODO(rfindley): should we also track LocalPkgName here? Do we want to
7575
// preserve the local package name?
76-
ValidPos bool // Object.Pos().IsValid()
77-
Shadow map[string]bool // names shadowed at one of the object's refs
76+
ValidPos bool // Object.Pos().IsValid()
77+
Shadow shadowMap // shadowing info for the object's refs
7878
}
7979

8080
// AnalyzeCallee analyzes a function that is a candidate for inlining
@@ -125,6 +125,7 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa
125125
// Record the location of all free references in the FuncDecl.
126126
// (Parameters are not free by this definition.)
127127
var (
128+
fieldObjs = fieldObjs(sig)
128129
freeObjIndex = make(map[types.Object]int)
129130
freeObjs []object
130131
freeRefs []freeRef // free refs that may need renaming
@@ -221,7 +222,7 @@ func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Pa
221222
freeObjIndex[obj] = objidx
222223
}
223224

224-
freeObjs[objidx].Shadow = addShadows(freeObjs[objidx].Shadow, info, obj.Name(), stack)
225+
freeObjs[objidx].Shadow = freeObjs[objidx].Shadow.add(info, fieldObjs, obj.Name(), stack)
225226

226227
freeRefs = append(freeRefs, freeRef{
227228
Offset: int(n.Pos() - decl.Pos()),
@@ -383,15 +384,15 @@ func parseCompact(content []byte) (*token.FileSet, *ast.FuncDecl, error) {
383384

384385
// A paramInfo records information about a callee receiver, parameter, or result variable.
385386
type paramInfo struct {
386-
Name string // parameter name (may be blank, or even "")
387-
Index int // index within signature
388-
IsResult bool // false for receiver or parameter, true for result variable
389-
IsInterface bool // parameter has a (non-type parameter) interface type
390-
Assigned bool // parameter appears on left side of an assignment statement
391-
Escapes bool // parameter has its address taken
392-
Refs []refInfo // information about references to parameter within body
393-
Shadow map[string]bool // names shadowed at one of the above refs
394-
FalconType string // name of this parameter's type (if basic) in the falcon system
387+
Name string // parameter name (may be blank, or even "")
388+
Index int // index within signature
389+
IsResult bool // false for receiver or parameter, true for result variable
390+
IsInterface bool // parameter has a (non-type parameter) interface type
391+
Assigned bool // parameter appears on left side of an assignment statement
392+
Escapes bool // parameter has its address taken
393+
Refs []refInfo // information about references to parameter within body
394+
Shadow shadowMap // shadowing info for the above refs; see [shadowMap]
395+
FalconType string // name of this parameter's type (if basic) in the falcon system
395396
}
396397

397398
type refInfo struct {
@@ -419,10 +420,10 @@ func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.I
419420
panic(fmt.Sprintf("%s: no func object for %q",
420421
fset.PositionFor(decl.Name.Pos(), false), decl.Name)) // ill-typed?
421422
}
423+
sig := fnobj.Type().(*types.Signature)
422424

423425
paramInfos := make(map[*types.Var]*paramInfo)
424426
{
425-
sig := fnobj.Type().(*types.Signature)
426427
newParamInfo := func(param *types.Var, isResult bool) *paramInfo {
427428
info := &paramInfo{
428429
Name: param.Name(),
@@ -461,6 +462,7 @@ func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.I
461462
//
462463
// TODO(adonovan): combine this traversal with the one that computes
463464
// FreeRefs. The tricky part is that calleefx needs this one first.
465+
fieldObjs := fieldObjs(sig)
464466
var stack []ast.Node
465467
stack = append(stack, decl.Type) // for scope of function itself
466468
ast.Inspect(decl.Body, func(n ast.Node) bool {
@@ -493,7 +495,7 @@ func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.I
493495
IsSelectionOperand: isSelectionOperand(stack),
494496
}
495497
pinfo.Refs = append(pinfo.Refs, ref)
496-
pinfo.Shadow = addShadows(pinfo.Shadow, info, pinfo.Name, stack)
498+
pinfo.Shadow = pinfo.Shadow.add(info, fieldObjs, pinfo.Name, stack)
497499
}
498500
}
499501
}
@@ -746,27 +748,66 @@ func isSelectionOperand(stack []ast.Node) bool {
746748
return ok && sel.X == expr
747749
}
748750

749-
// addShadows returns the shadows set augmented by the set of names
751+
// A shadowMap records information about shadowing at any of the parameter's
752+
// references within the callee decl.
753+
//
754+
// For each name shadowed at a reference to the parameter within the callee
755+
// body, shadow map records the 1-based index of the callee decl parameter
756+
// causing the shadowing, or -1, if the shadowing is not due to a callee decl.
757+
// A value of zero (or missing) indicates no shadowing. By convention,
758+
// self-shadowing is excluded from the map.
759+
//
760+
// For example, in the following callee
761+
//
762+
// func f(a, b int) int {
763+
// c := 2 + b
764+
// return a + c
765+
// }
766+
//
767+
// the shadow map of a is {b: 2, c: -1}, because b is shadowed by the 2nd
768+
// parameter. The shadow map of b is {a: 1}, because c is not shadowed at the
769+
// use of b.
770+
type shadowMap map[string]int
771+
772+
// addShadows returns the [shadowMap] augmented by the set of names
750773
// locally shadowed at the location of the reference in the callee
751774
// (identified by the stack). The name of the reference itself is
752775
// excluded.
753776
//
754777
// These shadowed names may not be used in a replacement expression
755778
// for the reference.
756-
func addShadows(shadows map[string]bool, info *types.Info, exclude string, stack []ast.Node) map[string]bool {
779+
func (s shadowMap) add(info *types.Info, paramIndexes map[types.Object]int, exclude string, stack []ast.Node) shadowMap {
757780
for _, n := range stack {
758781
if scope := scopeFor(info, n); scope != nil {
759782
for _, name := range scope.Names() {
760783
if name != exclude {
761-
if shadows == nil {
762-
shadows = make(map[string]bool)
784+
if s == nil {
785+
s = make(shadowMap)
786+
}
787+
obj := scope.Lookup(name)
788+
if idx, ok := paramIndexes[obj]; ok {
789+
s[name] = idx + 1
790+
} else {
791+
s[name] = -1
763792
}
764-
shadows[name] = true
765793
}
766794
}
767795
}
768796
}
769-
return shadows
797+
return s
798+
}
799+
800+
// fieldObjs returns a map of each types.Object defined by the given signature
801+
// to its index in the parameter list. Parameters with missing or blank name
802+
// are skipped.
803+
func fieldObjs(sig *types.Signature) map[types.Object]int {
804+
m := make(map[types.Object]int)
805+
for i := range sig.Params().Len() {
806+
if p := sig.Params().At(i); p.Name() != "" && p.Name() != "_" {
807+
m[p] = i
808+
}
809+
}
810+
return m
770811
}
771812

772813
func isField(obj types.Object) bool {

0 commit comments

Comments
 (0)