]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: fix Indent trailing whitespace regression in goexperiment.jsonv2
authorJoe Tsai <joetsai@digital-static.net>
Thu, 31 Jul 2025 23:21:41 +0000 (16:21 -0700)
committerGopher Robot <gobot@golang.org>
Mon, 11 Aug 2025 19:17:50 +0000 (12:17 -0700)
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>

src/encoding/json/scanner_test.go
src/encoding/json/v2_indent.go
src/encoding/json/v2_scanner_test.go

index fb64463599625e900cd442c97f2ae4d0892f20b7..a062e91243e4779f59eaaf5bbbc0504e81f9abab 100644 (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))
                        }
                })
index 2655942b128b32f186267d4231274d771612cefc..b2e8518471ba6708d2915212bcc3f08533703005 100644 (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
 }
index bec55212745b01ff168942e56eb1a41ebb4e4778..8885520e6d890ae2ef797e5cce9ca52426312106 100644 (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))
                        }
                })