diff --git a/musttag.go b/musttag.go index 1c57e14..4344022 100644 --- a/musttag.go +++ b/musttag.go @@ -174,7 +174,7 @@ func run(pass *analysis.Pass, funcs map[string]Func) (any, error) { return // not a struct argument. } - reportPos, ok := checkStruct(s, fn.Tag) + reportPos, ok := checkStruct(s, fn.Tag, make(map[string]struct{})) if ok { return // nothing to report. } @@ -225,7 +225,8 @@ func parseStruct(t types.Type, pos token.Pos) (*structInfo, bool) { // checkStruct recursively checks the given struct and returns the position for report, // in case one of its fields is missing the tag. -func checkStruct(s *structInfo, tag string) (token.Pos, bool) { +func checkStruct(s *structInfo, tag string, visited map[string]struct{}) (token.Pos, bool) { + visited[s.String()] = struct{}{} for i := 0; i < s.NumFields(); i++ { if !s.Field(i).Exported() { continue @@ -241,7 +242,10 @@ func checkStruct(s *structInfo, tag string) (token.Pos, bool) { if !ok { continue } - if pos, ok := checkStruct(nested, tag); !ok { + if _, ok := visited[nested.String()]; ok { + continue + } + if pos, ok := checkStruct(nested, tag, visited); !ok { return pos, false } } diff --git a/testdata/src/tests/tests.go b/testdata/src/tests/tests.go index 7db8eef..43faac5 100644 --- a/testdata/src/tests/tests.go +++ b/testdata/src/tests/tests.go @@ -473,3 +473,14 @@ func nonStructArgument() { func nilObject() { json.Marshal(nil) } + +// test for stack overflow issue: https://github.com/junk1tm/musttag/issues/16 +func selfType() { + type Human struct { + Mom *Human `json:"mom"` + Dad *Human `json:"dad"` + Children []*Human `json:"children"` + } + var v *Human + json.Marshal(v) +}