diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-09-04 19:52:41 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-09-04 20:25:22 +0200 |
| commit | f29b943c0b672ff7f02c1046416f1b85d7344bfb (patch) | |
| tree | ecd4f70e01322d26aadc3b43cdc612a547cf2eb8 /pkg/compiler/compiler.go | |
| parent | f400a0da0fcd3e4d27d915b57c54f504813ef1d3 (diff) | |
pkg/compiler: detect recursive struct declarations
Update #217
Diffstat (limited to 'pkg/compiler/compiler.go')
| -rw-r--r-- | pkg/compiler/compiler.go | 117 |
1 files changed, 95 insertions, 22 deletions
diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index 57b9dd951..a192ed8d7 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -108,31 +108,12 @@ func (comp *compiler) warning(pos ast.Pos, msg string, args ...interface{}) { func (comp *compiler) check() { // TODO: check len in syscall arguments referring to parent. // TODO: incorrect name is referenced in len type - // TODO: infinite recursion via struct pointers (e.g. a linked list) // TODO: no constructor for a resource comp.checkNames() comp.checkFields() - - for _, decl := range comp.desc.Nodes { - switch n := decl.(type) { - case *ast.Resource: - comp.checkType(n.Base, false, true) - comp.checkResource(n) - case *ast.Struct: - for _, f := range n.Fields { - comp.checkType(f.Type, false, false) - } - comp.checkStruct(n) - case *ast.Call: - for _, a := range n.Args { - comp.checkType(a.Type, true, false) - } - if n.Ret != nil { - comp.checkType(n.Ret, true, false) - } - } - } + comp.checkTypes() + comp.checkRecursion() } func (comp *compiler) checkNames() { @@ -238,7 +219,40 @@ func (comp *compiler) checkFields() { } } -func (comp *compiler) checkResource(n *ast.Resource) { +func (comp *compiler) checkTypes() { + for _, decl := range comp.desc.Nodes { + switch n := decl.(type) { + case *ast.Resource: + comp.checkType(n.Base, false, true) + case *ast.Struct: + for _, f := range n.Fields { + comp.checkType(f.Type, false, false) + } + comp.checkStruct(n) + case *ast.Call: + for _, a := range n.Args { + comp.checkType(a.Type, true, false) + } + if n.Ret != nil { + comp.checkType(n.Ret, true, false) + } + } + } +} + +func (comp *compiler) checkRecursion() { + checked := make(map[string]bool) + for _, decl := range comp.desc.Nodes { + switch n := decl.(type) { + case *ast.Resource: + comp.checkResourceRecursion(n) + case *ast.Struct: + comp.checkStructRecursion(checked, n) + } + } +} + +func (comp *compiler) checkResourceRecursion(n *ast.Resource) { var seen []string for n != nil { if arrayContains(seen, n.Name.Name) { @@ -255,6 +269,65 @@ func (comp *compiler) checkResource(n *ast.Resource) { } } +type pathElem struct { + Pos ast.Pos + Struct string + Field string +} + +func (comp *compiler) checkStructRecursion(checked map[string]bool, n *ast.Struct) { + var path []pathElem + comp.checkStructRecursion1(checked, n, path) +} + +func (comp *compiler) checkStructRecursion1(checked map[string]bool, n *ast.Struct, path []pathElem) { + name := n.Name.Name + if checked[name] { + return + } + for i, elem := range path { + if elem.Struct != name { + continue + } + path = path[i:] + str := "" + for _, elem := range path { + str += fmt.Sprintf("%v.%v -> ", elem.Struct, elem.Field) + } + str += name + comp.error(path[0].Pos, "recursive declaration: %v (mark some pointers as opt)", str) + checked[name] = true + return + } + for _, f := range n.Fields { + path = append(path, pathElem{ + Pos: f.Pos, + Struct: name, + Field: f.Name.Name, + }) + comp.recurseField(checked, f.Type, path) + path = path[:len(path)-1] + } + checked[name] = true +} + +func (comp *compiler) recurseField(checked map[string]bool, t *ast.Type, path []pathElem) { + desc := comp.getTypeDesc(t) + if desc == typeStruct { + comp.checkStructRecursion1(checked, comp.structs[t.Ident], path) + return + } + _, args, base := comp.getArgsBase(t, "", sys.DirIn, false) + if desc == typePtr && base.IsOptional { + return // optional pointers prune recursion + } + for i, arg := range args { + if desc.Args[i].Type == typeArgType { + comp.recurseField(checked, arg, path) + } + } +} + func (comp *compiler) checkStruct(n *ast.Struct) { if n.IsUnion { comp.parseUnionAttrs(n) |
