Skip to content

Commit b93a72a

Browse files
committed
internal/refactor/inline: fix spurious caller mutation
A failing test in a later CL was rooted in a bug allowing the inliner to mutate the caller syntax: during literalization we were clearing positions after inserting the binding decl, and the binding decl may refer to syntax in the caller. This was not caught by tests, because the deepHash utility itself had a bug: binary.Write is a no op for ints or uints, because they are not fixed width (this was rather surprising behavior). This is fixed by adding handling for Int and Uint values, and delegating to binary.Write only in the cases that are known to be supported. Change-Id: Icdf03f4d2cbc91b347b876cc39d8a48484c90c7c Reviewed-on: https://go-review.googlesource.com/c/tools/+/631295 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 41f04a0 commit b93a72a

File tree

2 files changed

+44
-7
lines changed

2 files changed

+44
-7
lines changed

internal/refactor/inline/inline.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,10 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
13541354
Type: calleeDecl.Type,
13551355
Body: calleeDecl.Body,
13561356
}
1357+
// clear positions before prepending the binding decl below, since the
1358+
// binding decl contains syntax from the caller and we must not mutate the
1359+
// caller. (This was a prior bug.)
1360+
clearPositions(funcLit)
13571361

13581362
// Literalization can still make use of a binding
13591363
// decl as it gives a more natural reading order:
@@ -1375,7 +1379,6 @@ func (st *state) inlineCall() (*inlineCallResult, error) {
13751379
Ellipsis: token.NoPos, // f(slice...) is always simplified
13761380
Args: remainingArgs,
13771381
}
1378-
clearPositions(newCall.Fun)
13791382
res.old = caller.Call
13801383
res.new = newCall
13811384
return res, nil

internal/refactor/inline/inline_test.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,12 @@ func TestSubstitutionPreservesParameterType(t *testing.T) {
14861486
`func _() { T.f(1) }`,
14871487
`func _() { T(1).g() }`,
14881488
},
1489+
{
1490+
"Implicit dereference is made explicit", // TODO(rfindley): fix unnecessary literalization.
1491+
`type T struct{ field int }; func (x T) f(y T) bool { return x == y && x.field == x.g() }; func (x T) g() int { return 0 }`,
1492+
`func _() { var t *T; var y T; _ = t.f(y) }`,
1493+
`func _() { var t *T; var y T; _ = func() bool { var x T = *t; return x == y && x.field == x.g() }() }`,
1494+
},
14891495
{
14901496
"Check for shadowing error on type used in the conversion.",
14911497
`func f(x T) { _ = &x == (*T)(nil) }; type T int16`,
@@ -1845,14 +1851,22 @@ func deepHash(n ast.Node) any {
18451851
visit(v.Elem())
18461852
}
18471853

1848-
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer:
1849-
panic(v) // unreachable in AST
1854+
case reflect.String:
1855+
writeUint64(uint64(v.Len()))
1856+
hasher.Write([]byte(v.String()))
18501857

1851-
default: // bool, string, number
1852-
if v.Kind() == reflect.String { // proper framing
1853-
writeUint64(uint64(v.Len()))
1854-
}
1858+
case reflect.Int:
1859+
writeUint64(uint64(v.Int()))
1860+
1861+
case reflect.Uint:
1862+
writeUint64(uint64(v.Uint()))
1863+
1864+
case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
1865+
// Bools and fixed width numbers can be handled by binary.Write.
18551866
binary.Write(hasher, le, v.Interface())
1867+
1868+
default: // reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer, reflect.Uintptr
1869+
panic(v) // unreachable in AST
18561870
}
18571871
}
18581872
visit(reflect.ValueOf(n))
@@ -1861,3 +1875,23 @@ func deepHash(n ast.Node) any {
18611875
hasher.Sum(hash[:0])
18621876
return hash
18631877
}
1878+
1879+
func TestDeepHash(t *testing.T) {
1880+
// This test reproduces a bug in DeepHash that was encountered during work on
1881+
// the inliner.
1882+
//
1883+
// TODO(rfindley): consider replacing this with a fuzz test.
1884+
id := &ast.Ident{
1885+
NamePos: 2,
1886+
Name: "t",
1887+
}
1888+
c := &ast.CallExpr{
1889+
Fun: id,
1890+
}
1891+
h1 := deepHash(c)
1892+
id.NamePos = 1
1893+
h2 := deepHash(c)
1894+
if h1 == h2 {
1895+
t.Fatal("bad")
1896+
}
1897+
}

0 commit comments

Comments
 (0)