Skip to content

Commit a1a85f0

Browse files
markus-wabreml
andauthored
Fix handling of looping structs
* tests: add case for looping structure (stack overflow) * Add tracking for already seen structs Fixes #8 Co-authored-by: Lucas Bremgartner <[email protected]>
1 parent b5e48c9 commit a1a85f0

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

errchkjson.go

+12-7
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ func (e *errchkjson) handleJSONMarshal(pass *analysis.Pass, ce *ast.CallExpr, fn
120120
t = t.(*types.Pointer).Elem()
121121
}
122122

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

152-
func (e *errchkjson) jsonSafe(t types.Type, level int) error {
152+
func (e *errchkjson) jsonSafe(t types.Type, level int, seenTypes map[types.Type]struct{}) error {
153+
if _, ok := seenTypes[t]; ok {
154+
return nil
155+
}
156+
153157
if types.Implements(t, textMarshalerInterface()) {
154158
return fmt.Errorf("unsafe type `%s` found", t.String())
155159
}
@@ -176,20 +180,21 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
176180
}
177181

178182
case *types.Array:
179-
err := e.jsonSafe(ut.Elem(), level+1)
183+
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
180184
if err != nil {
181185
return err
182186
}
183187
return nil
184188

185189
case *types.Slice:
186-
err := e.jsonSafe(ut.Elem(), level+1)
190+
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
187191
if err != nil {
188192
return err
189193
}
190194
return nil
191195

192196
case *types.Struct:
197+
seenTypes[t] = struct{}{}
193198
exported := 0
194199
for i := 0; i < ut.NumFields(); i++ {
195200
if !ut.Field(i).Exported() {
@@ -202,7 +207,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
202207
continue
203208
}
204209
}
205-
err := e.jsonSafe(ut.Field(i).Type(), level+1)
210+
err := e.jsonSafe(ut.Field(i).Type(), level+1, seenTypes)
206211
if err != nil {
207212
return err
208213
}
@@ -214,7 +219,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
214219
return nil
215220

216221
case *types.Pointer:
217-
err := e.jsonSafe(ut.Elem(), level+1)
222+
err := e.jsonSafe(ut.Elem(), level+1, seenTypes)
218223
if err != nil {
219224
return err
220225
}
@@ -225,7 +230,7 @@ func (e *errchkjson) jsonSafe(t types.Type, level int) error {
225230
if err != nil {
226231
return err
227232
}
228-
err = e.jsonSafe(ut.Elem(), level+1)
233+
err = e.jsonSafe(ut.Elem(), level+1, seenTypes)
229234
if err != nil {
230235
return err
231236
}

errchkjson_test.go

+7
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ func TestNoExportedField(t *testing.T) {
3939
testdata := analysistest.TestData()
4040
analysistest.Run(t, testdata, errchkjson, "noexport")
4141
}
42+
43+
func TestLoop(t *testing.T) {
44+
errchkjson := errchkjson.NewAnalyzer()
45+
46+
testdata := analysistest.TestData()
47+
analysistest.Run(t, testdata, errchkjson, "loop")
48+
}

testdata/src/loop/a.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package example
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
type entry struct {
8+
Name string `json:"name"`
9+
Fields schema `json:"fields"`
10+
}
11+
12+
type schema []entry
13+
14+
// JSONMarshalStructWithLoop contains a struct with a loop.
15+
func JSONMarshalStructWithLoop() {
16+
var structWithLoop schema
17+
_, _ = json.Marshal(structWithLoop)
18+
}

0 commit comments

Comments
 (0)