Skip to content

Commit

Permalink
Fix handling of looping structs
Browse files Browse the repository at this point in the history
* tests: add case for looping structure (stack overflow)

* Add tracking for already seen structs

Fixes #8

Co-authored-by: Lucas Bremgartner <lucas@bremis.ch>
  • Loading branch information
markus-wa and breml authored Feb 3, 2022
1 parent b5e48c9 commit a1a85f0
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 7 deletions.
19 changes: 12 additions & 7 deletions errchkjson.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (e *errchkjson) handleJSONMarshal(pass *analysis.Pass, ce *ast.CallExpr, fn
t = t.(*types.Pointer).Elem()
}

err := e.jsonSafe(t, 0)
err := e.jsonSafe(t, 0, map[types.Type]struct{}{})
if err != nil {
if _, ok := err.(unsupported); ok {
pass.Reportf(ce.Pos(), "`%s` for %v", fnName, err)
Expand Down Expand Up @@ -149,7 +149,11 @@ const (
unsupportedBasicTypes = types.IsComplex
)

func (e *errchkjson) jsonSafe(t types.Type, level int) error {
func (e *errchkjson) jsonSafe(t types.Type, level int, seenTypes map[types.Type]struct{}) error {
if _, ok := seenTypes[t]; ok {
return nil
}

if types.Implements(t, textMarshalerInterface()) {
return fmt.Errorf("unsafe type `%s` found", t.String())
}
Expand All @@ -176,20 +180,21 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
}

case *types.Array:
err := e.jsonSafe(ut.Elem(), level+1)
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
if err != nil {
return err
}
return nil

case *types.Slice:
err := e.jsonSafe(ut.Elem(), level+1)
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
if err != nil {
return err
}
return nil

case *types.Struct:
seenTypes[t] = struct{}{}
exported := 0
for i := 0; i < ut.NumFields(); i++ {
if !ut.Field(i).Exported() {
Expand All @@ -202,7 +207,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
continue
}
}
err := e.jsonSafe(ut.Field(i).Type(), level+1)
err := e.jsonSafe(ut.Field(i).Type(), level+1, seenTypes)
if err != nil {
return err
}
Expand All @@ -214,7 +219,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
return nil

case *types.Pointer:
err := e.jsonSafe(ut.Elem(), level+1)
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
if err != nil {
return err
}
Expand All @@ -225,7 +230,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
if err != nil {
return err
}
err = e.jsonSafe(ut.Elem(), level+1)
err = e.jsonSafe(ut.Elem(), level+1, seenTypes)
if err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions errchkjson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,10 @@ func TestNoExportedField(t *testing.T) {
testdata := analysistest.TestData()
analysistest.Run(t, testdata, errchkjson, "noexport")
}

func TestLoop(t *testing.T) {
errchkjson := errchkjson.NewAnalyzer()

testdata := analysistest.TestData()
analysistest.Run(t, testdata, errchkjson, "loop")
}
18 changes: 18 additions & 0 deletions testdata/src/loop/a.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package example

import (
"encoding/json"
)

type entry struct {
Name string `json:"name"`
Fields schema `json:"fields"`
}

type schema []entry

// JSONMarshalStructWithLoop contains a struct with a loop.
func JSONMarshalStructWithLoop() {
var structWithLoop schema
_, _ = json.Marshal(structWithLoop)
}

0 comments on commit a1a85f0

Please sign in to comment.