]> err.no Git - varnish/commitdiff
Overhaul cross reference checks in vcc compiler
authorphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Sat, 31 Mar 2007 08:36:31 +0000 (08:36 +0000)
committerphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Sat, 31 Mar 2007 08:36:31 +0000 (08:36 +0000)
Move and isolate cross reference stuff to it's own source file
(vcc_xref.c) and use vcc_ prefix as originally intended.

Also warn about multiple definitions of objects.

git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@1291 d4fa192b-c00b-0410-8231-f00ffab90ce4

varnish-cache/lib/libvcl/Makefile.am
varnish-cache/lib/libvcl/flint.lnt
varnish-cache/lib/libvcl/vcc_acl.c
varnish-cache/lib/libvcl/vcc_compile.c
varnish-cache/lib/libvcl/vcc_compile.h
varnish-cache/lib/libvcl/vcc_parse.c
varnish-cache/lib/libvcl/vcc_xref.c [new file with mode: 0644]

index a8184b6a518fd0d754dd155953dfcffecf6b1bd6..c5afed45245852cc2d71437b0b9d8a3156882101 100644 (file)
@@ -14,6 +14,7 @@ libvcl_la_SOURCES = \
        vcc_parse.c \
        vcc_fixed_token.c \
        vcc_obj.c \
-       vcc_token.c
+       vcc_token.c \
+       vcc_xref.c
 
 libvcl_la_CFLAGS = -include config.h
index d04ad898677c67a7d673504780cb2c0ff5e7d82b..94b2c7b219b99166da1b6705628235ff7de847ce 100644 (file)
@@ -18,7 +18,7 @@
 -e763  // Redundant declaration for symbol '...' previously declared
 
 
--e737  //  Loss of sign in promotion from int to unsigned int
+-e737  // Loss of sign in promotion from int to unsigned int
 -e715  // Symbol 'arg' (line 43) not referenced
 -e818  // Pointer parameter '...' could be declared as pointing to const
 
index 33e98ad56f51351147688bd52fde7fc0e431372f..c2c7c5285fbe576a6536319141ed828598b209b7 100644 (file)
@@ -60,7 +60,7 @@ vcc_Cond_Ip(struct var *vp, struct tokenlist *tl)
        case '~':
                vcc_NextToken(tl);
                ExpectErr(tl, ID);
-               AddRef(tl, tl->t, R_ACL);
+               vcc_AddRef(tl, tl->t, R_ACL);
                Fc(tl, 1, "VRT_acl_match(sp, \"%.*s\", acl_%.*s)\n",
                    PF(tl->t), PF(tl->t));
                vcc_NextToken(tl);
@@ -87,7 +87,7 @@ vcc_Acl(struct tokenlist *tl)
        an = tl->t;
        vcc_NextToken(tl);
 
-       AddDef(tl, an, R_ACL);
+       vcc_AddDef(tl, an, R_ACL);
        Fh(tl, 0, "static struct vrt_acl acl_%.*s[];\n", PF(an));
        Fc(tl, 1, "static struct vrt_acl acl_%.*s[] = {\n", PF(an));
 
index 6adc872388282dc2e690908164d5521d98ce8073..425b125789bd252c5f2c7b0890d7b816b2dd3edd 100644 (file)
@@ -85,7 +85,7 @@
 #include "vrt.h"
 #include "libvcl.h"
 
-static struct method method_tab[] = {
+struct method method_tab[] = {
 #define VCL_RET_MAC(l,U,b,n)
 #define VCL_MET_MAC(l,U,m)     { "vcl_"#l, m },
 #include "vcl_returns.h"
@@ -237,46 +237,6 @@ EncToken(struct vsb *sb, struct token *t)
        EncString(sb, t->dec, NULL, 0);
 }
 
-/*--------------------------------------------------------------------
- * Keep track of definitions and references
- */
-
-static struct ref *
-FindRef(struct tokenlist *tl, struct token *t, enum ref_type type)
-{
-       struct ref *r;
-
-       TAILQ_FOREACH(r, &tl->refs, list) {
-               if (r->type != type)
-                       continue;
-               if (vcc_Teq(r->name, t))
-                       return (r);
-       }
-       r = TlAlloc(tl, sizeof *r);
-       assert(r != NULL);
-       r->name = t;
-       r->type = type;
-       TAILQ_INSERT_TAIL(&tl->refs, r, list);
-       return (r);
-}
-
-void
-AddRef(struct tokenlist *tl, struct token *t, enum ref_type type)
-{
-
-       FindRef(tl, t, type)->refcnt++;
-}
-
-void
-AddDef(struct tokenlist *tl, struct token *t, enum ref_type type)
-{
-       struct ref *r;
-
-       r = FindRef(tl, t, type);
-       r->defcnt++;
-       r->name = t;
-}
-
 /*--------------------------------------------------------------------*/
 
 static struct var *
@@ -333,182 +293,6 @@ FindVar(struct tokenlist *tl, struct token *t, struct var *vl)
        return (NULL);
 }
 
-/*--------------------------------------------------------------------
- * Consistency check
- */
-
-struct proc *
-AddProc(struct tokenlist *tl, struct token *t, int def)
-{
-       struct proc *p;
-
-       TAILQ_FOREACH(p, &tl->procs, list) {
-               if (!vcc_Teq(p->name, t))
-                       continue;
-               if (def)
-                       p->name = t;
-               return (p);
-       }
-       p = TlAlloc(tl, sizeof *p);
-       assert(p != NULL);
-       p->name = t;
-       TAILQ_INIT(&p->calls);
-       TAILQ_INSERT_TAIL(&tl->procs, p, list);
-       return (p);
-}
-
-void
-AddCall(struct tokenlist *tl, struct token *t)
-{
-       struct proccall *pc;
-       struct proc *p;
-
-       p = AddProc(tl, t, 0);
-       TAILQ_FOREACH(pc, &tl->curproc->calls, list) {
-               if (pc->p == p)
-                       return;
-       }
-       pc = TlAlloc(tl, sizeof *pc);
-       assert(pc != NULL);
-       pc->p = p;
-       pc->t = t;
-       TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list);
-}
-
-static int
-Consist_Decend(struct tokenlist *tl, struct proc *p, unsigned returns)
-{
-       unsigned u;
-       struct proccall *pc;
-
-       if (!p->exists) {
-               vsb_printf(tl->sb, "Function %.*s does not exist\n",
-                   PF(p->name));
-               return (1);
-       }
-       if (p->active) {
-               vsb_printf(tl->sb, "Function recurses on\n");
-               vcc_ErrWhere(tl, p->name);
-               return (1);
-       }
-       u = p->returns & ~returns;
-       if (u) {
-#define VCL_RET_MAC(a, b, c, d) \
-               if (u & VCL_RET_##b) { \
-                       vsb_printf(tl->sb, "Illegal action \"%s\"\n", #a); \
-                       vcc_ErrWhere(tl, p->returnt[d]); \
-               }
-#include "vcl_returns.h"
-#undef VCL_RET_MAC
-               vsb_printf(tl->sb, "\n...in function \"%.*s\"\n", PF(p->name));
-               vcc_ErrWhere(tl, p->name);
-               return (1);
-       }
-       p->active = 1;
-       TAILQ_FOREACH(pc, &p->calls, list) {
-               if (Consist_Decend(tl, pc->p, returns)) {
-                       vsb_printf(tl->sb, "\n...called from \"%.*s\"\n",
-                           PF(p->name));
-                       vcc_ErrWhere(tl, pc->t);
-                       return (1);
-               }
-       }
-       p->active = 0;
-       p->called++;
-       return (0);
-}
-
-static int
-Consistency(struct tokenlist *tl)
-{
-       struct proc *p;
-       struct method *m;
-
-       TAILQ_FOREACH(p, &tl->procs, list) {
-               for(m = method_tab; m->name != NULL; m++) {
-                       if (vcc_IdIs(p->name, m->name))
-                               break;
-               }
-               if (m->name == NULL)
-                       continue;
-               if (Consist_Decend(tl, p, m->returns)) {
-                       vsb_printf(tl->sb,
-                           "\n...which is the \"%s\" method\n", m->name);
-                       vsb_printf(tl->sb, "Legal actions are:");
-#define VCL_RET_MAC(a, b, c, d) \
-                       if (m->returns & c) \
-                               vsb_printf(tl->sb, " \"%s\"", #a);
-#define VCL_RET_MAC_E(a, b, c, d) VCL_RET_MAC(a, b, c, d)
-#include "vcl_returns.h"
-#undef VCL_RET_MAC
-#undef VCL_RET_MAC_E
-                       vsb_printf(tl->sb, "\n");
-                       return (1);
-               }
-       }
-       TAILQ_FOREACH(p, &tl->procs, list) {
-               if (p->called)
-                       continue;
-               vsb_printf(tl->sb, "Function unused\n");
-               vcc_ErrWhere(tl, p->name);
-               return (1);
-       }
-       return (0);
-}
-
-/*--------------------------------------------------------------------*/
-
-static int
-CheckRefs(struct tokenlist *tl)
-{
-       struct ref *r;
-       const char *type;
-       int nerr = 0;
-
-       TAILQ_FOREACH(r, &tl->refs, list) {
-               if (r->defcnt != 0 && r->refcnt != 0)
-                       continue;
-               nerr++;
-
-               switch(r->type) {
-               case R_FUNC:
-                       type = "function";
-                       break;
-               case R_ACL:
-                       type = "acl";
-                       break;
-               case R_BACKEND:
-                       type = "backend";
-                       break;
-               default:
-                       ErrInternal(tl);
-                       vsb_printf(tl->sb, "Ref ");
-                       vcc_ErrToken(tl, r->name);
-                       vsb_printf(tl->sb, " has unknown type %d\n",
-                           r->type);
-                       continue;
-               }
-               if (r->defcnt == 0 && r->name->tok == METHOD) {
-                       vsb_printf(tl->sb,
-                           "No definition for method %.*s\n", PF(r->name));
-                       continue;
-               }
-
-               if (r->defcnt == 0) {
-                       vsb_printf(tl->sb,
-                           "Undefined %s %.*s, first reference:\n",
-                           type, PF(r->name));
-                       vcc_ErrWhere(tl, r->name);
-                       continue;
-               }
-
-               vsb_printf(tl->sb, "Unused %s %.*s, defined:\n",
-                   type, PF(r->name));
-               vcc_ErrWhere(tl, r->name);
-       }
-       return (nerr);
-}
-
 /*--------------------------------------------------------------------
  * Output the location/profiling table.  For each counted token, we
  * record source+line+charpos for the first character in the token.
@@ -939,13 +723,13 @@ vcc_CompileSource(struct vsb *sb, struct source *sp)
        if (tl->err)
                return (vcc_DestroyTokenList(tl, NULL));
 
-       /* Perform consistency checks */
-       Consistency(tl);
-       if (tl->err)
+       /* Check for orphans */
+       if (vcc_CheckReferences(tl))
                return (vcc_DestroyTokenList(tl, NULL));
 
-       /* Check for orphans */
-       if (CheckRefs(tl))
+       /* Check that all action returns are legal */
+       vcc_CheckAction(tl);
+       if (tl->err)
                return (vcc_DestroyTokenList(tl, NULL));
 
        Ff(tl, 0, "\tVRT_free_backends(&VCL_conf);\n");
index af3504429c61402c6290521185df00ed3877f7ea..6fcfa985a11d22bb289b54e3bae20e56ebcb7bc7 100644 (file)
@@ -120,26 +120,10 @@ struct var {
 
 struct method {
        const char              *name;
-       unsigned                returns;
-};
-
-struct proccall {
-       TAILQ_ENTRY(proccall)   list;
-       struct proc             *p;
-       struct token            *t;
-};
-
-struct proc {
-       TAILQ_ENTRY(proc)       list;
-       TAILQ_HEAD(,proccall)   calls;
-       struct token            *name;
-       unsigned                returns;
-       unsigned                exists;
-       unsigned                called;
-       unsigned                active;
-       struct token            *returnt[VCL_RET_MAX];
+       unsigned                actions;
 };
 
+struct proc;
 
 /*--------------------------------------------------------------------*/
 
@@ -149,18 +133,15 @@ void vcc_Acl(struct tokenlist *tl);
 void vcc_Cond_Ip(struct var *vp, struct tokenlist *tl);
 
 /* vcc_compile.c */
+extern struct method method_tab[];
 void Fh(struct tokenlist *tl, int indent, const char *fmt, ...);
 void Fc(struct tokenlist *tl, int indent, const char *fmt, ...);
 void Fb(struct tokenlist *tl, int indent, const char *fmt, ...);
 void Fi(struct tokenlist *tl, int indent, const char *fmt, ...);
 void Ff(struct tokenlist *tl, int indent, const char *fmt, ...);
 unsigned UintVal(struct tokenlist *tl);
-void AddDef(struct tokenlist *tl, struct token *t, enum ref_type type);
-void AddRef(struct tokenlist *tl, struct token *t, enum ref_type type);
 void EncToken(struct vsb *sb, struct token *t);
 struct var *FindVar(struct tokenlist *tl, struct token *t, struct var *vl);
-void AddCall(struct tokenlist *tl, struct token *t);
-struct proc *AddProc(struct tokenlist *tl, struct token *t, int def);
 int IsMethod(struct token *t);
 void *TlAlloc(struct tokenlist *tl, unsigned len);
 
@@ -183,6 +164,16 @@ void vcc__ErrInternal(struct tokenlist *tl, const char *func, unsigned line);
 void vcc_AddToken(struct tokenlist *tl, unsigned tok, const char *b, const char *e);
 void vcc_FreeToken(struct token *t);
 
+/* vcc_expr.c */
+void vcc_AddDef(struct tokenlist *tl, struct token *t, enum ref_type type);
+void vcc_AddRef(struct tokenlist *tl, struct token *t, enum ref_type type);
+int vcc_CheckReferences(struct tokenlist *tl);
+
+void vcc_AddCall(struct tokenlist *tl, struct token *t);
+struct proc *vcc_AddProc(struct tokenlist *tl, struct token *t);
+void vcc_ProcAction(struct proc *p, unsigned action, struct token *t);
+int vcc_CheckAction(struct tokenlist *tl);
+
 #define ERRCHK(tl)      do { if ((tl)->err) return; } while (0)
 #define ErrInternal(tl) vcc__ErrInternal(tl, __func__, __LINE__)
 #define Expect(a, b) vcc__Expect(a, b, __LINE__)
index 6bfe344f6f9f090363199522b7742077df1c946e..9331475ede668bf086c02dbae75c725fcfccf748 100644 (file)
@@ -527,8 +527,7 @@ Action(struct tokenlist *tl)
                return;
 #define VCL_RET_MAC(a,b,c,d) case T_##b: \
                Fb(tl, 1, "VRT_done(sp, VCL_RET_%s);\n", #b); \
-               tl->curproc->returns |= VCL_RET_##b; \
-               tl->curproc->returnt[d] = at; \
+               vcc_ProcAction(tl->curproc, d, at); \
                return;
 #include "vcl_returns.h"
 #undef VCL_RET_MAC
@@ -554,8 +553,8 @@ Action(struct tokenlist *tl)
                return;
        case T_CALL:
                ExpectErr(tl, ID);
-               AddCall(tl, tl->t);
-               AddRef(tl, tl->t, R_FUNC);
+               vcc_AddCall(tl, tl->t);
+               vcc_AddRef(tl, tl->t, R_FUNC);
                Fb(tl, 1, "if (VGC_function_%.*s(sp))\n", PF(tl->t));
                Fb(tl, 1, "\treturn (1);\n");
                vcc_NextToken(tl);
@@ -620,7 +619,7 @@ Action(struct tokenlist *tl)
                case BACKEND:
                        if (tl->t->tok == '=') {
                                vcc_NextToken(tl);
-                               AddRef(tl, tl->t, R_BACKEND);
+                               vcc_AddRef(tl, tl->t, R_BACKEND);
                                Fb(tl, 0, "VGC_backend_%.*s", PF(tl->t));
                                vcc_NextToken(tl);
                                Fb(tl, 0, ");\n");
@@ -717,9 +716,9 @@ Backend(struct tokenlist *tl)
        vcc_NextToken(tl);
        ExpectErr(tl, ID);
        t_be = tl->t;
-       AddDef(tl, tl->t, R_BACKEND);
+       vcc_AddDef(tl, tl->t, R_BACKEND);
        if (tl->nbackend == 0)
-               AddRef(tl, tl->t, R_BACKEND);
+               vcc_AddRef(tl, tl->t, R_BACKEND);
        Fh(tl, 1, "#define VGC_backend_%.*s (VCL_conf.backend[%d])\n",
            PF(tl->t), tl->nbackend);
        Fc(tl, 0, "\n");
@@ -837,17 +836,15 @@ Function(struct tokenlist *tl)
                assert(m < N_METHODS);
                tl->fb = tl->fm[m];
                if (tl->mprocs[m] == NULL) {
-                       tl->mprocs[m] = AddProc(tl, tl->t, 1);
-                       tl->mprocs[m]->exists++;
-                       AddDef(tl, tl->t, R_FUNC);
-                       AddRef(tl, tl->t, R_FUNC);
+                       tl->mprocs[m] = vcc_AddProc(tl, tl->t);
+                       vcc_AddDef(tl, tl->t, R_FUNC);
+                       vcc_AddRef(tl, tl->t, R_FUNC);
                }
                tl->curproc = tl->mprocs[m];
        } else {
                tl->fb = tl->fc;
-               tl->curproc = AddProc(tl, tl->t, 1);
-               tl->curproc->exists++;
-               AddDef(tl, tl->t, R_FUNC);
+               tl->curproc = vcc_AddProc(tl, tl->t);
+               vcc_AddDef(tl, tl->t, R_FUNC);
                Fh(tl, 0, "static int VGC_function_%.*s (struct sess *sp);\n",
                    PF(tl->t));
                Fc(tl, 1, "static int\n");
diff --git a/varnish-cache/lib/libvcl/vcc_xref.c b/varnish-cache/lib/libvcl/vcc_xref.c
new file mode 100644 (file)
index 0000000..4cbd151
--- /dev/null
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2006 Verdens Gang AS
+ * Copyright (c) 2006 Linpro AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ * This fine contains code for two cross-reference or consistency checks.
+ *
+ * The first check is simply that all functions, acls and backends are
+ * both defined and referenced.  Complaints about referenced but undefined
+ * or defined but unreferenced objects will be emitted.
+ *
+ * The second check recursively decends through function calls to make
+ * sure that action actions are correct for the methods through which
+ * they are called.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "vsb.h"
+
+#include "vcc_priv.h"
+#include "vcc_compile.h"
+
+/*--------------------------------------------------------------------*/
+
+struct proccall {
+       TAILQ_ENTRY(proccall)   list;
+       struct proc             *p;
+       struct token            *t;
+};
+
+struct proc {
+       TAILQ_ENTRY(proc)       list;
+       TAILQ_HEAD(,proccall)   calls;
+       struct token            *name;
+       unsigned                actions;
+       unsigned                exists;
+       unsigned                called;
+       unsigned                active;
+       struct token            *action_tok[VCL_RET_MAX];
+};
+
+/*--------------------------------------------------------------------*/
+
+static const char *
+vcc_typename(struct tokenlist *tl, const struct ref *r)
+{
+       switch (r->type) {
+       case R_FUNC: return ("function");
+       case R_ACL: return ("acl");
+       case R_BACKEND: return ("backend");
+       default:
+               ErrInternal(tl);
+               vsb_printf(tl->sb, "Ref ");
+               vcc_ErrToken(tl, r->name);
+               vsb_printf(tl->sb, " has unknown type %d\n",
+                   r->type);
+               return "???";
+       }
+}
+
+/*--------------------------------------------------------------------
+ * Keep track of definitions and references
+ */
+
+static struct ref *
+vcc_findref(struct tokenlist *tl, struct token *t, enum ref_type type)
+{
+       struct ref *r;
+
+       TAILQ_FOREACH(r, &tl->refs, list) {
+               if (r->type != type)
+                       continue;
+               if (vcc_Teq(r->name, t))
+                       return (r);
+       }
+       r = TlAlloc(tl, sizeof *r);
+       assert(r != NULL);
+       r->name = t;
+       r->type = type;
+       TAILQ_INSERT_TAIL(&tl->refs, r, list);
+       return (r);
+}
+
+void
+vcc_AddRef(struct tokenlist *tl, struct token *t, enum ref_type type)
+{
+
+       vcc_findref(tl, t, type)->refcnt++;
+}
+
+void
+vcc_AddDef(struct tokenlist *tl, struct token *t, enum ref_type type)
+{
+       struct ref *r;
+       const char *tp;
+
+       r = vcc_findref(tl, t, type);
+       if (r->defcnt > 0) {
+               tp = vcc_typename(tl, r);
+               vsb_printf(tl->sb, "Multiple definitions of %s \"%.*s\"\n",
+                   tp, PF(t));
+               vcc_ErrWhere(tl, r->name);
+               vsb_printf(tl->sb, "...and\n");
+               vcc_ErrWhere(tl, t);
+       }
+       r->defcnt++;
+       r->name = t;
+}
+
+/*--------------------------------------------------------------------*/
+
+int
+vcc_CheckReferences(struct tokenlist *tl)
+{
+       struct ref *r;
+       const char *type;
+       int nerr = 0;
+
+       TAILQ_FOREACH(r, &tl->refs, list) {
+               if (r->defcnt != 0 && r->refcnt != 0)
+                       continue;
+               nerr++;
+
+               type = vcc_typename(tl, r);
+               if (r->defcnt == 0 && r->name->tok == METHOD) {
+                       vsb_printf(tl->sb,
+                           "No definition for method %.*s\n", PF(r->name));
+                       continue;
+               }
+
+               if (r->defcnt == 0) {
+                       vsb_printf(tl->sb,
+                           "Undefined %s %.*s, first reference:\n",
+                           type, PF(r->name));
+                       vcc_ErrWhere(tl, r->name);
+                       continue;
+               }
+
+               vsb_printf(tl->sb, "Unused %s %.*s, defined:\n",
+                   type, PF(r->name));
+               vcc_ErrWhere(tl, r->name);
+       }
+       return (nerr);
+}
+
+/*--------------------------------------------------------------------
+ * Returned action checks
+ */
+
+static struct proc *
+vcc_findproc(struct tokenlist *tl, struct token *t)
+{
+       struct proc *p;
+
+       TAILQ_FOREACH(p, &tl->procs, list)
+               if (vcc_Teq(p->name, t))
+                       return (p);
+       p = TlAlloc(tl, sizeof *p);
+       assert(p != NULL);
+       TAILQ_INIT(&p->calls);
+       TAILQ_INSERT_TAIL(&tl->procs, p, list);
+       p->name = t;
+       return (p);
+}
+
+struct proc *
+vcc_AddProc(struct tokenlist *tl, struct token *t)
+{
+       struct proc *p;
+
+       p = vcc_findproc(tl, t);
+       p->name = t;    /* make sure the name matches the definition */
+       p->exists++;
+       return (p);
+}
+
+void
+vcc_AddCall(struct tokenlist *tl, struct token *t)
+{
+       struct proccall *pc;
+       struct proc *p;
+
+       p = vcc_findproc(tl, t);
+       pc = TlAlloc(tl, sizeof *pc);
+       assert(pc != NULL);
+       pc->p = p;
+       pc->t = t;
+       TAILQ_INSERT_TAIL(&tl->curproc->calls, pc, list);
+}
+
+void
+vcc_ProcAction(struct proc *p, unsigned action, struct token *t)
+{
+
+       p->actions |= (1 << action);
+       /* Record the first instance of this action */
+       if (p->action_tok[action] == NULL)
+               p->action_tok[action] = t;
+}
+
+static int
+vcc_CheckActionRecurse(struct tokenlist *tl, struct proc *p, unsigned actions)
+{
+       unsigned u;
+       struct proccall *pc;
+
+       if (!p->exists) {
+               vsb_printf(tl->sb, "Function %.*s does not exist\n",
+                   PF(p->name));
+               return (1);
+       }
+       if (p->active) {
+               vsb_printf(tl->sb, "Function recurses on\n");
+               vcc_ErrWhere(tl, p->name);
+               return (1);
+       }
+       u = p->actions & ~actions;
+       if (u) {
+#define VCL_RET_MAC(a, b, c, d) \
+               if (u & VCL_RET_##b) { \
+                       vsb_printf(tl->sb, "Illegal action \"%s\"\n", #a); \
+                       vcc_ErrWhere(tl, p->action_tok[d]); \
+               }
+#include "vcl_returns.h"
+#undef VCL_RET_MAC
+               vsb_printf(tl->sb, "\n...in function \"%.*s\"\n", PF(p->name));
+               vcc_ErrWhere(tl, p->name);
+               return (1);
+       }
+       p->active = 1;
+       TAILQ_FOREACH(pc, &p->calls, list) {
+               if (vcc_CheckActionRecurse(tl, pc->p, actions)) {
+                       vsb_printf(tl->sb, "\n...called from \"%.*s\"\n",
+                           PF(p->name));
+                       vcc_ErrWhere(tl, pc->t);
+                       return (1);
+               }
+       }
+       p->active = 0;
+       p->called++;
+       return (0);
+}
+
+int
+vcc_CheckAction(struct tokenlist *tl)
+{
+       struct proc *p;
+       struct method *m;
+       int i;
+
+       TAILQ_FOREACH(p, &tl->procs, list) {
+               i = IsMethod(p->name);
+               if (i < 0)
+                       continue;
+               m = method_tab + i;
+               if (vcc_CheckActionRecurse(tl, p, m->actions)) {
+                       vsb_printf(tl->sb,
+                           "\n...which is the \"%s\" method\n", m->name);
+                       vsb_printf(tl->sb, "Legal actions are:");
+#define VCL_RET_MAC(a, b, c, d) \
+                       if (m->actions & c) \
+                               vsb_printf(tl->sb, " \"%s\"", #a);
+#define VCL_RET_MAC_E(a, b, c, d) VCL_RET_MAC(a, b, c, d)
+#include "vcl_returns.h"
+#undef VCL_RET_MAC
+#undef VCL_RET_MAC_E
+                       vsb_printf(tl->sb, "\n");
+                       return (1);
+               }
+       }
+       TAILQ_FOREACH(p, &tl->procs, list) {
+               if (p->called)
+                       continue;
+               vsb_printf(tl->sb, "Function unused\n");
+               vcc_ErrWhere(tl, p->name);
+               return (1);
+       }
+       return (0);
+}
+