diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a9871e63..cb5a7007 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -13,12 +13,13 @@ jobs: uses: actions/checkout@v3 - name: build run: docker-compose run go-json + test: name: Test strategy: matrix: os: [ "ubuntu-latest", "macos-latest", "windows-latest" ] - go-version: [ "1.18", "1.19", "1.20" ] + go-version: [ "1.19", "1.20", "1.21" ] runs-on: ${{ matrix.os }} steps: - name: setup Go ${{ matrix.go-version }} @@ -35,6 +36,20 @@ jobs: GOGC: 1 - name: test with race detector run: go test -v -race ./... -count=1 + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v3 + - name: setup Go + uses: actions/setup-go@v3 + with: + go-version: '1.21' + - name: lint + run: | + make lint bench: name: Benchmark runs-on: ubuntu-latest @@ -42,7 +57,7 @@ jobs: - name: setup Go uses: actions/setup-go@v3 with: - go-version: '1.20' + go-version: '1.21' - name: checkout ( feature ) uses: actions/checkout@v3 - name: run benchmark ( feature ) @@ -57,19 +72,21 @@ jobs: run: cd benchmarks && go test -bench GoJson | tee $HOME/old.txt - name: compare benchmark results run: benchstat $HOME/old.txt $HOME/new.txt + coverage: name: Coverage runs-on: ubuntu-latest steps: + - name: checkout + uses: actions/checkout@v3 - name: setup Go uses: actions/setup-go@v3 with: - go-version: '1.20' - - name: checkout - uses: actions/checkout@v3 + go-version: '1.21' - name: measure coverage run: make cover - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 with: fail_ci_if_error: true verbose: true + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index aa73ee31..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: lint -on: - push: - branches: - - master - pull_request: -jobs: - golangci: - name: lint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: golangci/golangci-lint-action@v3 - with: - version: v1.45.2 - args: --timeout=5m diff --git a/.golangci.yml b/.golangci.yml index 57ae5a52..977accaa 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -56,6 +56,9 @@ linters: - cyclop - containedctx - revive + - nosnakecase + - exhaustruct + - depguard issues: exclude-rules: diff --git a/Makefile b/Makefile index 5bbfc4c9..c030577d 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ golangci-lint: | $(BIN_DIR) GOLANGCI_LINT_TMP_DIR=$$(mktemp -d); \ cd $$GOLANGCI_LINT_TMP_DIR; \ go mod init tmp; \ - GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.48.0; \ + GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2; \ rm -rf $$GOLANGCI_LINT_TMP_DIR; \ } diff --git a/benchmarks/bench_test.go b/benchmarks/bench_test.go index d671811f..d45331be 100644 --- a/benchmarks/bench_test.go +++ b/benchmarks/bench_test.go @@ -15,7 +15,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "os" "strings" "testing" @@ -56,7 +55,7 @@ func codeInit() { if err != nil { panic(err) } - data, err := ioutil.ReadAll(gz) + data, err := io.ReadAll(gz) if err != nil { panic(err) } @@ -104,7 +103,7 @@ func Benchmark_EncodeBigData_GoJson(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := json.NewEncoder(ioutil.Discard) + enc := json.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -122,7 +121,7 @@ func Benchmark_EncodeBigData_EncodingJson(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := stdjson.NewEncoder(ioutil.Discard) + enc := stdjson.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -141,7 +140,7 @@ func Benchmark_EncodeBigData_JsonIter(b *testing.B) { } var json = jsoniter.ConfigCompatibleWithStandardLibrary b.RunParallel(func(pb *testing.PB) { - enc := json.NewEncoder(ioutil.Discard) + enc := json.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -159,7 +158,7 @@ func Benchmark_EncodeBigData_SegmentioJson(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := segmentiojson.NewEncoder(ioutil.Discard) + enc := segmentiojson.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -316,7 +315,7 @@ func Benchmark_EncodeRawMessage_EncodingJson(b *testing.B) { }{} b.RunParallel(func(pb *testing.PB) { - enc := stdjson.NewEncoder(ioutil.Discard) + enc := stdjson.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&m); err != nil { b.Fatal("Encode:", err) @@ -336,7 +335,7 @@ func Benchmark_EncodeRawMessage_JsonIter(b *testing.B) { var json = jsoniter.ConfigCompatibleWithStandardLibrary b.RunParallel(func(pb *testing.PB) { - enc := json.NewEncoder(ioutil.Discard) + enc := json.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&m); err != nil { b.Fatal("Encode:", err) @@ -353,7 +352,7 @@ func Benchmark_EncodeRawMessage_GoJson(b *testing.B) { B json.RawMessage }{} b.RunParallel(func(pb *testing.PB) { - enc := json.NewEncoder(ioutil.Discard) + enc := json.NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&m); err != nil { b.Fatal("Encode:", err) diff --git a/benchmarks/go.mod b/benchmarks/go.mod index 73ca9db9..6fd858ef 100644 --- a/benchmarks/go.mod +++ b/benchmarks/go.mod @@ -1,6 +1,6 @@ module benchmark -go 1.12 +go 1.19 require ( github.com/francoispqt/gojay v1.2.13 @@ -9,9 +9,14 @@ require ( github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 github.com/segmentio/encoding v0.2.4 - github.com/stretchr/testify v1.7.0 // indirect github.com/valyala/fastjson v1.6.3 github.com/wI2L/jettison v0.7.1 ) +require ( + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/stretchr/testify v1.7.0 // indirect +) + replace github.com/goccy/go-json => ../ diff --git a/benchmarks/go.sum b/benchmarks/go.sum index fd8aa9b3..9c5f8ff4 100644 --- a/benchmarks/go.sum +++ b/benchmarks/go.sum @@ -56,7 +56,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= @@ -71,7 +70,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -182,7 +180,6 @@ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/decode_test.go b/decode_test.go index df914eda..8f32cedb 100644 --- a/decode_test.go +++ b/decode_test.go @@ -197,6 +197,13 @@ func Test_Decoder(t *testing.T) { assertEq(t, "interface{}", v.F, nil) assertEq(t, "nilfunc", true, v.G == nil) }) + t.Run("struct.pointer must be nil", func(t *testing.T) { + var v struct { + A *int + } + json.Unmarshal([]byte(`{"a": "alpha"}`), &v) + assertEq(t, "struct.A", v.A, (*int)(nil)) + }) }) t.Run("interface", func(t *testing.T) { t.Run("number", func(t *testing.T) { diff --git a/encode.go b/encode.go index 4bd899f3..c5173825 100644 --- a/encode.go +++ b/encode.go @@ -52,7 +52,7 @@ func (e *Encoder) EncodeContext(ctx context.Context, v interface{}, optFuncs ... rctx.Option.Flag |= encoder.ContextOption rctx.Option.Context = ctx - err := e.encodeWithOption(rctx, v, optFuncs...) + err := e.encodeWithOption(rctx, v, optFuncs...) //nolint: contextcheck encoder.ReleaseRuntimeContext(rctx) return err @@ -120,7 +120,7 @@ func marshalContext(ctx context.Context, v interface{}, optFuncs ...EncodeOption optFunc(rctx.Option) } - buf, err := encode(rctx, v) + buf, err := encode(rctx, v) //nolint: contextcheck if err != nil { encoder.ReleaseRuntimeContext(rctx) return nil, err diff --git a/go.mod b/go.mod index 58a14881..f5b57ff7 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/goccy/go-json -go 1.12 +go 1.19 diff --git a/internal/cmd/generator/main.go b/internal/cmd/generator/main.go index 0712cd03..326f5273 100644 --- a/internal/cmd/generator/main.go +++ b/internal/cmd/generator/main.go @@ -7,7 +7,7 @@ import ( "go/parser" "go/printer" "go/token" - "io/ioutil" + "os" "path/filepath" "runtime" "strings" @@ -273,11 +273,11 @@ func (t OpType) FieldToOmitEmptyField() OpType { if err != nil { return err } - return ioutil.WriteFile(path, buf, 0644) + return os.WriteFile(path, buf, 0644) } func generateVM() error { - file, err := ioutil.ReadFile("vm.go.tmpl") + file, err := os.ReadFile("vm.go.tmpl") if err != nil { return err } @@ -295,7 +295,7 @@ func generateVM() error { if err != nil { return err } - if err := ioutil.WriteFile(path, source, 0644); err != nil { + if err := os.WriteFile(path, source, 0644); err != nil { return err } } diff --git a/internal/decoder/ptr.go b/internal/decoder/ptr.go index de12e105..ae229946 100644 --- a/internal/decoder/ptr.go +++ b/internal/decoder/ptr.go @@ -85,6 +85,7 @@ func (d *ptrDecoder) Decode(ctx *RuntimeContext, cursor, depth int64, p unsafe.P } c, err := d.dec.Decode(ctx, cursor, depth, newptr) if err != nil { + *(*unsafe.Pointer)(p) = nil return 0, err } cursor = c diff --git a/internal/decoder/unmarshal_text.go b/internal/decoder/unmarshal_text.go index 6d37993f..d711d0f8 100644 --- a/internal/decoder/unmarshal_text.go +++ b/internal/decoder/unmarshal_text.go @@ -147,7 +147,7 @@ func (d *unmarshalTextDecoder) DecodePath(ctx *RuntimeContext, cursor, depth int return nil, 0, fmt.Errorf("json: unmarshal text decoder does not support decode path") } -func unquoteBytes(s []byte) (t []byte, ok bool) { +func unquoteBytes(s []byte) (t []byte, ok bool) { //nolint: nonamedreturns length := len(s) if length < 2 || s[0] != '"' || s[length-1] != '"' { return diff --git a/internal/encoder/compact.go b/internal/encoder/compact.go index 0eb9545d..e287a6c0 100644 --- a/internal/encoder/compact.go +++ b/internal/encoder/compact.go @@ -213,8 +213,8 @@ func compactString(dst, src []byte, cursor int64, escape bool) ([]byte, int64, e dst = append(dst, src[start:cursor]...) dst = append(dst, `\u202`...) dst = append(dst, hex[src[cursor+2]&0xF]) - cursor += 2 start = cursor + 3 + cursor += 2 } } switch c { diff --git a/internal/encoder/compiler.go b/internal/encoder/compiler.go index 3ae39ba8..37b7aa38 100644 --- a/internal/encoder/compiler.go +++ b/internal/encoder/compiler.go @@ -480,7 +480,7 @@ func (c *Compiler) mapCode(typ *runtime.Type) (*MapCode, error) { func (c *Compiler) listElemCode(typ *runtime.Type) (Code, error) { switch { - case c.isPtrMarshalJSONType(typ): + case c.implementsMarshalJSONType(typ) || c.implementsMarshalJSONType(runtime.PtrTo(typ)): return c.marshalJSONCode(typ) case !typ.Implements(marshalTextType) && runtime.PtrTo(typ).Implements(marshalTextType): return c.marshalTextCode(typ) diff --git a/internal/encoder/int.go b/internal/encoder/int.go index 85f07960..8b5febea 100644 --- a/internal/encoder/int.go +++ b/internal/encoder/int.go @@ -1,3 +1,27 @@ +// This files's processing codes are inspired by https://github.com/segmentio/encoding. +// The license notation is as follows. +// +// # MIT License +// +// Copyright (c) 2019 Segment.io, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. package encoder import ( diff --git a/internal/encoder/string.go b/internal/encoder/string.go index e4152b27..4abb8416 100644 --- a/internal/encoder/string.go +++ b/internal/encoder/string.go @@ -1,3 +1,27 @@ +// This files's string processing codes are inspired by https://github.com/segmentio/encoding. +// The license notation is as follows. +// +// # MIT License +// +// Copyright (c) 2019 Segment.io, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. package encoder import ( diff --git a/internal/runtime/rtype.go b/internal/runtime/rtype.go index 4db10deb..37cfe35a 100644 --- a/internal/runtime/rtype.go +++ b/internal/runtime/rtype.go @@ -252,7 +252,6 @@ func IfaceIndir(*Type) bool //go:noescape func RType2Type(t *Type) reflect.Type -//go:nolint structcheck type emptyInterface struct { _ *Type ptr unsafe.Pointer diff --git a/json.go b/json.go index 413cb20b..fb18065a 100644 --- a/json.go +++ b/json.go @@ -89,31 +89,31 @@ type UnmarshalerContext interface { // // Examples of struct field tags and their meanings: // -// // Field appears in JSON as key "myName". -// Field int `json:"myName"` +// // Field appears in JSON as key "myName". +// Field int `json:"myName"` // -// // Field appears in JSON as key "myName" and -// // the field is omitted from the object if its value is empty, -// // as defined above. -// Field int `json:"myName,omitempty"` +// // Field appears in JSON as key "myName" and +// // the field is omitted from the object if its value is empty, +// // as defined above. +// Field int `json:"myName,omitempty"` // -// // Field appears in JSON as key "Field" (the default), but -// // the field is skipped if empty. -// // Note the leading comma. -// Field int `json:",omitempty"` +// // Field appears in JSON as key "Field" (the default), but +// // the field is skipped if empty. +// // Note the leading comma. +// Field int `json:",omitempty"` // -// // Field is ignored by this package. -// Field int `json:"-"` +// // Field is ignored by this package. +// Field int `json:"-"` // -// // Field appears in JSON as key "-". -// Field int `json:"-,"` +// // Field appears in JSON as key "-". +// Field int `json:"-,"` // // The "string" option signals that a field is stored as JSON inside a // JSON-encoded string. It applies only to fields of string, floating point, // integer, or boolean types. This extra level of encoding is sometimes used // when communicating with JavaScript programs: // -// Int64String int64 `json:",string"` +// Int64String int64 `json:",string"` // // The key name will be used if it's a non-empty string consisting of // only Unicode letters, digits, and ASCII punctuation except quotation @@ -166,7 +166,6 @@ type UnmarshalerContext interface { // JSON cannot represent cyclic data structures and Marshal does not // handle them. Passing cyclic structures to Marshal will result in // an infinite recursion. -// func Marshal(v interface{}) ([]byte, error) { return MarshalWithOption(v) } @@ -264,14 +263,13 @@ func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...E // // The JSON null value unmarshals into an interface, map, pointer, or slice // by setting that Go value to nil. Because null is often used in JSON to mean -// ``not present,'' unmarshaling a JSON null into any other Go type has no effect +// “not present,” unmarshaling a JSON null into any other Go type has no effect // on the value and produces no error. // // When unmarshaling quoted strings, invalid UTF-8 or // invalid UTF-16 surrogate pairs are not treated as an error. // Instead, they are replaced by the Unicode replacement // character U+FFFD. -// func Unmarshal(data []byte, v interface{}) error { return unmarshal(data, v) } @@ -299,7 +297,6 @@ func UnmarshalNoEscape(data []byte, v interface{}, optFuncs ...DecodeOptionFunc) // Number, for JSON numbers // string, for JSON string literals // nil, for JSON null -// type Token = json.Token // A Number represents a JSON number literal. diff --git a/stream_test.go b/stream_test.go index edb680cc..7b32df86 100644 --- a/stream_test.go +++ b/stream_test.go @@ -9,7 +9,6 @@ import ( "compress/gzip" "fmt" "io" - "io/ioutil" "log" "net" "net/http" @@ -234,7 +233,7 @@ func TestDecoderBuffered(t *testing.T) { if m.Name != "Gopher" { t.Errorf("Name = %q; want Gopher", m.Name) } - rest, err := ioutil.ReadAll(d.Buffered()) + rest, err := io.ReadAll(d.Buffered()) if err != nil { t.Fatal(err) } diff --git a/test/cover/cover_slice_test.go b/test/cover/cover_slice_test.go index 61de9a5d..70a03063 100644 --- a/test/cover/cover_slice_test.go +++ b/test/cover/cover_slice_test.go @@ -16,6 +16,18 @@ func (coverSliceMarshalJSON) MarshalJSON() ([]byte, error) { return []byte(`"hello"`), nil } +type coverSliceMarshalJSONMap map[string]any + +func (c coverSliceMarshalJSONMap) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any(c)) +} + +type coverSliceMarshalJSONMapPtr map[string]any + +func (c *coverSliceMarshalJSONMapPtr) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]any(*c)) +} + type coverSliceMarshalText struct { A int } @@ -152,6 +164,22 @@ func TestCoverSlice(t *testing.T) { name: "SliceMarshalJSON", data: []coverSliceMarshalJSON{{A: 1}, {A: 2}}, }, + { + name: "SliceMarshalJSONMap", + data: []coverSliceMarshalJSONMap{{"foo": "bar"}, {"some": 1}}, + }, + { + name: "SliceMarshalJSONMap", + data: []*coverSliceMarshalJSONMap{{"foo": "bar"}, {"some": 1}}, + }, + { + name: "SliceMarshalJSONMap", + data: []coverSliceMarshalJSONMapPtr{{"foo": "bar"}, {"some": 1}}, + }, + { + name: "SliceMarshalJSONMap", + data: []*coverSliceMarshalJSONMapPtr{{"foo": "bar"}, {"some": 1}}, + }, { name: "SliceMarshalText", data: []coverSliceMarshalText{{A: 1}, {A: 2}},