cmd/cgo: don't replace newlines with semicolons in composite literals

Fixes #29748

Change-Id: I2b19165bdb3c99df5b79574390b5d5f6d40462dc
Reviewed-on: https://go-review.googlesource.com/c/157961
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/misc/cgo/test/issue29748.go b/misc/cgo/test/issue29748.go
new file mode 100644
index 0000000..8229b3b
--- /dev/null
+++ b/misc/cgo/test/issue29748.go
@@ -0,0 +1,22 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Error handling a struct initializer that requires pointer checking.
+// Compilation test only, nothing to run.
+
+package cgotest
+
+// typedef struct { char **p; } S29748;
+// static int f29748(S29748 *p) { return 0; }
+import "C"
+
+var Vissue29748 = C.f29748(&C.S29748{
+	nil,
+})
+
+func Fissue299748() {
+	C.f29748(&C.S29748{
+		nil,
+	})
+}
diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go
index 9c763a2..c0cd8e0 100644
--- a/src/cmd/cgo/godefs.go
+++ b/src/cmd/cgo/godefs.go
@@ -127,8 +127,21 @@
 	return gofmtBuf.String()
 }
 
+// gofmtLineReplacer is used to put a gofmt-formatted string for an
+// AST expression onto a single line. The lexer normally inserts a
+// semicolon at each newline, so we can replace newline with semicolon.
+// However, we can't do that in cases where the lexer would not insert
+// a semicolon. Fortunately we only have to worry about cases that
+// can occur in an expression passed through gofmt, which just means
+// composite literals.
+var gofmtLineReplacer = strings.NewReplacer(
+	"{\n", "{",
+	",\n", ",",
+	"\n", ";",
+)
+
 // gofmtLine returns the gofmt-formatted string for an AST node,
 // ensuring that it is on a single line.
 func gofmtLine(n interface{}) string {
-	return strings.Replace(gofmt(n), "\n", ";", -1)
+	return gofmtLineReplacer.Replace(gofmt(n))
 }