encoding/json: fix Indent trailing whitespace regression in goexperiment.jsonv2

The Indent function preserves trailing whitespace,
while the v1 emulation under v2 implementation accidentally dropped it.

There was prior logic that attempted to preserve it,
but it did not work correctly since it ran in a defer and
accidentally mutated the dst input argument rather than the output argument.
Move the logic to the end and avoid a defer.
Also, add a test to both v1 and v1in2 to codify this behavior.

This only modifies code that is compiled in under goexperiment.jsonv2.

Updates #13520
Fixes #74806

Change-Id: I22b1a8da5185eb969e2a8a111b625d3752cfcbe8
Reviewed-on: https://go-review.googlesource.com/c/go/+/692195
Reviewed-by: Sean Liao <sean@liao.dev>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Sean Liao <sean@liao.dev>
This commit is contained in:
Joe Tsai 2025-07-31 16:21:41 -07:00 committed by Gopher Robot
parent 925149da20
commit 691af6ca28
3 changed files with 10 additions and 12 deletions

View file

@ -74,6 +74,7 @@ func TestCompactAndIndent(t *testing.T) {
-5e+2
]`},
{Name(""), "{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
{Name(""), `null`, "null \n\r\t"}, // See golang.org/issue/13520 and golang.org/issue/74806
}
var buf bytes.Buffer
for _, tt := range tests {
@ -102,7 +103,7 @@ func TestCompactAndIndent(t *testing.T) {
buf.Reset()
if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
t.Errorf("%s: Indent error: %v", tt.Where, err)
} else if got := buf.String(); got != tt.indent {
} else if got := buf.String(); got != strings.TrimRight(tt.indent, " \n\r\t") {
t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
}
})

View file

@ -88,17 +88,8 @@ func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
}
func appendIndent(dst, src []byte, prefix, indent string) ([]byte, error) {
// In v2, trailing whitespace is discarded, while v1 preserved it.
dstLen := len(dst)
if n := len(src) - len(bytes.TrimRight(src, " \n\r\t")); n > 0 {
// Append the trailing whitespace afterwards.
defer func() {
if len(dst) > dstLen {
dst = append(dst, src[len(src)-n:]...)
}
}()
}
// In v2, only spaces and tabs are allowed, while v1 allowed any character.
dstLen := len(dst)
if len(strings.Trim(prefix, " \t"))+len(strings.Trim(indent, " \t")) > 0 {
// Use placeholder spaces of correct length, and replace afterwards.
invalidPrefix, invalidIndent := prefix, indent
@ -129,5 +120,10 @@ func appendIndent(dst, src []byte, prefix, indent string) ([]byte, error) {
if err != nil {
return dst[:dstLen], transformSyntacticError(err)
}
// In v2, trailing whitespace is discarded, while v1 preserved it.
if n := len(src) - len(bytes.TrimRight(src, " \n\r\t")); n > 0 {
dst = append(dst, src[len(src)-n:]...)
}
return dst, nil
}

View file

@ -74,6 +74,7 @@ func TestCompactAndIndent(t *testing.T) {
-5e+2
]`},
{Name(""), "{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
{Name(""), `null`, "null \n\r\t"}, // See golang.org/issue/13520 and golang.org/issue/74806
}
var buf bytes.Buffer
for _, tt := range tests {
@ -102,7 +103,7 @@ func TestCompactAndIndent(t *testing.T) {
buf.Reset()
if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
t.Errorf("%s: Indent error: %v", tt.Where, err)
} else if got := buf.String(); got != tt.indent {
} else if got := buf.String(); got != strings.TrimRight(tt.indent, " \n\r\t") {
t.Errorf("%s: Compact:\n\tgot: %s\n\twant: %s", tt.Where, indentNewlines(got), indentNewlines(tt.indent))
}
})