Skip to content

Commit

Permalink
Fix endless recursion when traversing recursive types.
Browse files Browse the repository at this point in the history
Simply keep a slice of already visited types and exit early.

Fixes #47

Signed-off-by: Jakub Ciolek <jakub@ciolek.dev>
  • Loading branch information
jake-ciolek committed Oct 12, 2022
1 parent 806c0b0 commit 3997423
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 6 deletions.
2 changes: 1 addition & 1 deletion internal/method/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func NewResolveReferences(traverser *xptypes.Traverser, receiver, clientPath, re
Field: refProcessor,
Named: xptypes.NamedProcessorChain{},
}
if err := traverser.Traverse(n, cfg); err != nil {
if err := traverser.Traverse(n, cfg, []*types.Named{}); err != nil {
panic(errors.Wrapf(err, "cannot traverse the type tree of %s", n.Obj().Name()))
}
refs := refProcessor.GetReferences()
Expand Down
16 changes: 11 additions & 5 deletions internal/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,14 @@ type Traverser struct {
// constructing an error. But we keep that for future type and field processors.

// Traverse traverser given type recursively and runs given processors.
func (t *Traverser) Traverse(n *types.Named, cfg *ProcessorConfig, parentFields ...string) error { // nolint:gocyclo
func (t *Traverser) Traverse(n *types.Named, cfg *ProcessorConfig, visited []*types.Named, parentFields ...string) error { // nolint:gocyclo
// NOTE(muvaf): gocyclo is disabled due to repeated type checks.
for _, v := range visited {
if n == v {
return nil
}
}
visited = append(visited, n)
if err := cfg.Named.Process(n, t.comments.For(n.Obj())); err != nil {
return errors.Wrapf(err, "type processors failed to run for type %s", n.Obj().Name())
}
Expand All @@ -103,24 +109,24 @@ func (t *Traverser) Traverse(n *types.Named, cfg *ProcessorConfig, parentFields
}
switch ft := field.Type().(type) {
case *types.Named:
if err := t.Traverse(ft, cfg, append(parentFields, field.Name())...); err != nil {
if err := t.Traverse(ft, cfg, visited, append(parentFields, field.Name())...); err != nil {
return errors.Wrapf(err, "failed to traverse type of field %s", field.Name())
}
case *types.Pointer:
if elemType, ok := ft.Elem().(*types.Named); ok {
if err := t.Traverse(elemType, cfg, append(parentFields, "*"+field.Name())...); err != nil {
if err := t.Traverse(elemType, cfg, visited, append(parentFields, "*"+field.Name())...); err != nil {
return errors.Wrapf(err, "failed to traverse type of field %s", field.Name())
}
}
case *types.Slice:
switch elemType := ft.Elem().(type) {
case *types.Named:
if err := t.Traverse(elemType, cfg, append(parentFields, "[]"+field.Name())...); err != nil {
if err := t.Traverse(elemType, cfg, visited, append(parentFields, "[]"+field.Name())...); err != nil {
return errors.Wrapf(err, "failed to traverse type of field %s", field.Name())
}
case *types.Pointer:
if elemElemType, ok := elemType.Elem().(*types.Named); ok {
if err := t.Traverse(elemElemType, cfg, append(parentFields, "[]"+"*"+field.Name())...); err != nil {
if err := t.Traverse(elemElemType, cfg, visited, append(parentFields, "[]"+"*"+field.Name())...); err != nil {
return errors.Wrapf(err, "failed to traverse type of field %s", field.Name())
}
}
Expand Down

0 comments on commit 3997423

Please sign in to comment.