diff --git a/musttag.go b/musttag.go index 121777b..4268b4f 100644 --- a/musttag.go +++ b/musttag.go @@ -131,6 +131,7 @@ func run(pass *analysis.Pass, funcs map[string]Func) (any, error) { walk := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) filter := []ast.Node{(*ast.CallExpr)(nil)} + visited := make(map[string]struct{}) walk.Preorder(filter, func(n ast.Node) { call, ok := n.(*ast.CallExpr) @@ -171,7 +172,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, visited) if ok { return // nothing to report. } @@ -222,7 +223,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 @@ -238,7 +240,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 007328c..7a2013f 100644 --- a/testdata/src/tests/tests.go +++ b/testdata/src/tests/tests.go @@ -468,3 +468,14 @@ func nonStructArgument() { custom.Marshal(0) custom.Unmarshal(nil, &[]int{}) } + +// 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) +}