Skip to content

Commit

Permalink
Handle types that implement json.Marshaler interface
Browse files Browse the repository at this point in the history
Fixes #9
  • Loading branch information
breml committed Feb 8, 2022
1 parent a1a85f0 commit 68d82d6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
26 changes: 23 additions & 3 deletions errchkjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int, seenTypes map[types.Type]
return nil
}

if types.Implements(t, textMarshalerInterface()) {
if types.Implements(t, textMarshalerInterface()) || types.Implements(t, jsonMarshalerInterface()) {
return fmt.Errorf("unsafe type `%s` found", t.String())
}

Expand Down Expand Up @@ -247,7 +247,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int, seenTypes map[types.Type]
}

func jsonSafeMapKey(t types.Type) error {
if types.Implements(t, textMarshalerInterface()) {
if types.Implements(t, textMarshalerInterface()) || types.Implements(t, jsonMarshalerInterface()) {
return fmt.Errorf("unsafe type `%s` as map key found", t.String())
}
switch ut := t.Underlying().(type) {
Expand All @@ -268,7 +268,7 @@ func jsonSafeMapKey(t types.Type) error {
}
}

// Construct *types.Interface for interface TextMarshaler
// Construct *types.Interface for interface encoding.TextMarshaler
// type TextMarshaler interface {
// MarshalText() (text []byte, err error)
// }
Expand All @@ -287,3 +287,23 @@ func textMarshalerInterface() *types.Interface {

return textMarshalerInterface
}

// Construct *types.Interface for interface json.Marshaler
// type Marshaler interface {
// MarshalJSON() ([]byte, error)
// }
//
func jsonMarshalerInterface() *types.Interface {
textMarshalerInterface := types.NewInterfaceType([]*types.Func{
types.NewFunc(token.NoPos, nil, "MarshalJSON", types.NewSignature(
nil, nil, types.NewTuple(
types.NewVar(token.NoPos, nil, "",
types.NewSlice(
types.Universe.Lookup("byte").Type())),
types.NewVar(token.NoPos, nil, "", types.Universe.Lookup("error").Type())),
false)),
}, nil)
textMarshalerInterface.Complete()

return textMarshalerInterface
}
26 changes: 25 additions & 1 deletion testdata/src/nosafe/a.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ import (

type marshalText struct{}

func (mt marshalText) MarshalText() ([]byte, error) {
func (_ marshalText) MarshalText() ([]byte, error) {
return []byte(`mt`), nil
}

var _ encoding.TextMarshaler = marshalText(struct{}{})

type marshalJSON struct{}

func (_ marshalJSON) MarshalJSON() ([]byte, error) {
return []byte(`mj`), nil
}

var _ json.Marshaler = marshalJSON(struct{}{})

// JSONMarshalSafeTypesWithNoSafe contains a multitude of test cases to marshal different combinations of types to JSON,
// that are safe, that is, they will never return an error, if these types are marshaled to JSON.
func JSONMarshalSafeTypesWithNoSafe() {
Expand Down Expand Up @@ -266,6 +274,8 @@ type (
Stringer fmt.Stringer
Mt marshalText
MapMarshalTextString map[marshalText]string
Mj marshalJSON
MapMarshalJSONString map[marshalJSON]string

C128 complex128
C128Ptr *complex128
Expand Down Expand Up @@ -301,6 +311,8 @@ func JSONMarshalSafeStructWithUnexportedFieldsWithNoSafe() {
stringer fmt.Stringer // unsafe unexported
mt marshalText // unsafe unexported
mapMarshalTextString map[marshalText]string // unsafe unexported
mj marshalJSON // unsafe unexported
mapMarshalJSONString map[marshalJSON]string // unsafe unexported
unexportedStruct ExportedUnsafeAndInvalidStruct // unsafe unexported
unexportedStructPtr *ExportedUnsafeAndInvalidStruct // unsafe unexported

Expand Down Expand Up @@ -366,6 +378,8 @@ func JSONMarshalSafeStructWithOmittedFieldsWithNoSafe() {
Stringer fmt.Stringer `json:"-"` // unsafe exported but omitted
Mt marshalText `json:"-"` // unsafe exported but omitted
MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but omitted
Mj marshalJSON `json:"-"` // unsafe exported but omitted
MapMarshalJSONString map[marshalJSON]string `json:"-"` // unsafe exported but omitted
ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted
ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted

Expand Down Expand Up @@ -506,6 +520,16 @@ func JSONMarshalUnsafeTypes() {
_, _ = json.Marshal(mapMarshalTextString) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `nosafe.marshalText` as map key found"
_, err = json.Marshal(mapMarshalTextString) // err is checked
_ = err

var mj marshalJSON
_, _ = json.Marshal(mj) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `nosafe.marshalJSON` found"
_, err = json.Marshal(mj) // err is checked
_ = err

var mapMarshalJSONString map[marshalJSON]string
_, _ = json.Marshal(mapMarshalJSONString) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `nosafe.marshalJSON` as map key found"
_, err = json.Marshal(mapMarshalJSONString) // err is checked
_ = err
}

// JSONMarshalInvalidTypes contains a multitude of test cases to marshal different combinations of types to JSON,
Expand Down
26 changes: 25 additions & 1 deletion testdata/src/standard/a.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,20 @@ import (

type marshalText struct{}

func (mt marshalText) MarshalText() ([]byte, error) {
func (_ marshalText) MarshalText() ([]byte, error) {
return []byte(`mt`), nil
}

var _ encoding.TextMarshaler = marshalText(struct{}{})

type marshalJSON struct{}

func (_ marshalJSON) MarshalJSON() ([]byte, error) {
return []byte(`mj`), nil
}

var _ json.Marshaler = marshalJSON(struct{}{})

// JSONMarshalSafeTypes contains a multitude of test cases to marshal different combinations of types to JSON,
// that are safe, that is, they will never return an error, if these types are marshaled to JSON.
func JSONMarshalSafeTypes() {
Expand Down Expand Up @@ -266,6 +274,8 @@ type (
Stringer fmt.Stringer
Mt marshalText
MapMarshalTextString map[marshalText]string
Mj marshalJSON
MapMarshalJSONString map[marshalJSON]string

C128 complex128
C128Ptr *complex128
Expand Down Expand Up @@ -301,6 +311,8 @@ func JSONMarshalSafeStructWithUnexportedFields() {
stringer fmt.Stringer // unsafe unexported
mt marshalText // unsafe unexported
mapMarshalTextString map[marshalText]string // unsafe unexported
mj marshalJSON // unsafe unexported
mapMarshalJSONString map[marshalJSON]string // unsafe unexported
unexportedStruct ExportedUnsafeAndInvalidStruct // unsafe unexported
unexportedStructPtr *ExportedUnsafeAndInvalidStruct // unsafe unexported

Expand Down Expand Up @@ -366,6 +378,8 @@ func JSONMarshalSafeStructWithOmittedFields() {
Stringer fmt.Stringer `json:"-"` // unsafe exported but omitted
Mt marshalText `json:"-"` // unsafe exported but omitted
MapMarshalTextString map[marshalText]string `json:"-"` // unsafe exported but omitted
Mj marshalJSON `json:"-"` // unsafe exported but omitted
MapMarshalJSONString map[marshalJSON]string `json:"-"` // unsafe exported but omitted
ExportedStruct ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted
ExportedStructPtr *ExportedUnsafeAndInvalidStruct `json:"-"` // unsafe exported but omitted

Expand Down Expand Up @@ -506,6 +520,16 @@ func JSONMarshalUnsafeTypes() {
_, _ = json.Marshal(mapMarshalTextString) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `standard.marshalText` as map key found"
_, err = json.Marshal(mapMarshalTextString) // err is checked
_ = err

var mj marshalJSON
_, _ = json.Marshal(mj) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `standard.marshalJSON` found"
_, err = json.Marshal(mj) // err is checked
_ = err

var mapMarshalJSONString map[marshalJSON]string
_, _ = json.Marshal(mapMarshalJSONString) // want "Error return value of `encoding/json.Marshal` is not checked: unsafe type `standard.marshalJSON` as map key found"
_, err = json.Marshal(mapMarshalJSONString) // err is checked
_ = err
}

// JSONMarshalInvalidTypes contains a multitude of test cases to marshal different combinations of types to JSON,
Expand Down

0 comments on commit 68d82d6

Please sign in to comment.