internal/refactor/inline: T{} is duplicable for struct/array
This change makes empty composite literals of aggregate
(struct/array) types duplicable. Nonempty literals remain
nonduplicable on grounds of verbosity; and map and slice
literals are nonduplicable because they allocate a new
variable.
Change-Id: I9e2d778e004fb4743fd242c4e81d00e55830a6bd
Reviewed-on: https://go-review.googlesource.com/c/tools/+/534397
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/internal/refactor/inline/inline.go b/internal/refactor/inline/inline.go
index 1882c12..618b179 100644
--- a/internal/refactor/inline/inline.go
+++ b/internal/refactor/inline/inline.go
@@ -1998,10 +1998,28 @@
case *ast.UnaryExpr: // e.g. +1, -1
return (e.Op == token.ADD || e.Op == token.SUB) && duplicable(info, e.X)
+ case *ast.CompositeLit:
+ // Empty struct or array literals T{} are duplicable.
+ // (Non-empty literals are too verbose, and slice/map
+ // literals allocate indirect variables.)
+ if len(e.Elts) == 0 {
+ switch info.TypeOf(e).Underlying().(type) {
+ case *types.Struct, *types.Array:
+ return true
+ }
+ }
+ return false
+
case *ast.CallExpr:
// Don't treat a conversion T(x) as duplicable even
// if x is duplicable because it could duplicate
- // allocations. There may be cases to tease apart here.
+ // allocations.
+ //
+ // TODO(adonovan): there are cases to tease apart here:
+ // duplicating string([]byte) conversions increases
+ // allocation but doesn't change behavior, but the
+ // reverse, []byte(string), allocates a distinct array,
+ // which is observable
return false
case *ast.SelectorExpr:
diff --git a/internal/refactor/inline/inline_test.go b/internal/refactor/inline/inline_test.go
index 319ea54..189ac3f 100644
--- a/internal/refactor/inline/inline_test.go
+++ b/internal/refactor/inline/inline_test.go
@@ -391,6 +391,56 @@
})
}
+func TestDuplicable(t *testing.T) {
+ runTests(t, []testcase{
+ {
+ "Empty strings are duplicable.",
+ `func f(s string) { print(s, s) }`,
+ `func _() { f("") }`,
+ `func _() { print("", "") }`,
+ },
+ {
+ "Non-empty string literals are not duplicable.",
+ `func f(s string) { print(s, s) }`,
+ `func _() { f("hi") }`,
+ `func _() {
+ var s string = "hi"
+ print(s, s)
+}`,
+ },
+ {
+ "Empty array literals are duplicable.",
+ `func f(a [2]int) { print(a, a) }`,
+ `func _() { f([2]int{}) }`,
+ `func _() { print([2]int{}, [2]int{}) }`,
+ },
+ {
+ "Non-empty array literals are not duplicable.",
+ `func f(a [2]int) { print(a, a) }`,
+ `func _() { f([2]int{1, 2}) }`,
+ `func _() {
+ var a [2]int = [2]int{1, 2}
+ print(a, a)
+}`,
+ },
+ {
+ "Empty struct literals are duplicable.",
+ `func f(s S) { print(s, s) }; type S struct { x int }`,
+ `func _() { f(S{}) }`,
+ `func _() { print(S{}, S{}) }`,
+ },
+ {
+ "Non-empty struct literals are not duplicable.",
+ `func f(s S) { print(s, s) }; type S struct { x int }`,
+ `func _() { f(S{x: 1}) }`,
+ `func _() {
+ var s S = S{x: 1}
+ print(s, s)
+}`,
+ },
+ })
+}
+
func TestExprStmtReduction(t *testing.T) {
runTests(t, []testcase{
{