aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/compiler/compiler.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-04 19:52:41 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-04 20:25:22 +0200
commitf29b943c0b672ff7f02c1046416f1b85d7344bfb (patch)
treeecd4f70e01322d26aadc3b43cdc612a547cf2eb8 /pkg/compiler/compiler.go
parentf400a0da0fcd3e4d27d915b57c54f504813ef1d3 (diff)
pkg/compiler: detect recursive struct declarations
Update #217
Diffstat (limited to 'pkg/compiler/compiler.go')
-rw-r--r--pkg/compiler/compiler.go117
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)