Commit aa545dbd1e

Loris Cro <kappaloris@gmail.com>
2022-05-22 16:21:49
autodoc: improve frontend rendering
1 parent cf685c1
Changed files (2)
lib
docs
src
lib/docs/main.js
@@ -32,6 +32,13 @@
    } TypeKind
 */
 
+/**
+ * @typedef {{
+     typeRef: Expr?,
+     expr: Expr,
+   }} WalkResult
+*/
+
 /**
  * @typedef {{
      void: {},
@@ -40,20 +47,20 @@
      type: number,
      comptimeExpr: number,
      call: number,
-     int: { typeRef: WalkResult; value: number },
-     float: { typeRef: WalkResult; value: number },
+     int: number,
+     float: number,
      bool: boolean,
      undefined: WalkResult,
      null: WalkResult,
      typeOf: WalkResult,
-     compileError: string,
+     compileError: string
      string: string,
-     struct: Struct,
-     refPath: WalkResult[],
+     struct: Expr[],
+     refPath: Expr[],
      declRef: number,
      array: ZigArray,
      enumLiteral: string,
-   }} WalkResult
+   }} Expr
 */
 
 /**
@@ -128,7 +135,7 @@
 * @typedef {{
      kind: number,
      name: string,
-     child: WalkResult
+     child: Expr,
 }} OptionalType
 */
 
@@ -148,9 +155,9 @@
 
 /**
  * @typedef {{
-       func: WalkResult,
-       args: WalkResult[],
-       ret: WalkResult,
+       func: Expr,
+       args: Expr[],
+       ret: Expr,
    }} Call
 */
 
@@ -188,13 +195,6 @@
    }} Package
 */
 
-/**
- * @typedef {{
-     typeRef: WalkResult,
-     fieldVals: WalkResult[],
-   }} Struct
-*/
-
 /**
  * @typedef {{
       typeRef: WalkResult,
@@ -391,13 +391,13 @@ var zigAnalysis;
         return isType(x) && typeKindIsContainer(/** @type {Type} */(x).kind) ;
     }
 
-    /** @param {WalkResult} wr */
-    function typeShorthandName(wr) {
-        let resolvedWr = resolveValue(wr);
-        if (!("type" in resolvedWr)) {
+    /** @param {Expr} expr */
+    function typeShorthandName(expr) {
+        let resolvedExpr = resolveValue({expr: expr});
+        if (!("type" in resolvedExpr)) {
             return null;
         }
-        let type = /** @type {Type} */(zigAnalysis.types[resolvedWr.type]);
+        let type = /** @type {Type} */(zigAnalysis.types[resolvedExpr.type]);
 
         outer: for (let i = 0; i < 10000; i += 1) {
             switch (type.kind) {
@@ -472,16 +472,24 @@ var zigAnalysis;
         while(i < 1000) {
             i += 1;
 
-            if ("refPath" in value) {
-                value = value.refPath[value.refPath.length -1];
+            if ("refPath" in value.expr) {
+                value = {expr: value.expr.refPath[value.expr.refPath.length -1]};
                 continue;
             }
 
-            if ("declRef" in value) {
-                value = zigAnalysis.decls[value.declRef].value;
+            if ("declRef" in value.expr) {
+                value = zigAnalysis.decls[value.expr.declRef].value;
                 continue;
             }
 
+//            if ("as" in value.expr) {
+//                value = {
+//                  typeRef: zigAnalysis.exprs[value.expr.as.typeRefArg],
+//                  expr: zigAnalysis.exprs[value.expr.as.exprArg],
+//                };
+//                continue;
+//            }
+
             return value;
 
         }
@@ -493,89 +501,89 @@ var zigAnalysis;
         * @param {Decl} decl
         * @return {WalkResult}
     */
-    function typeOfDecl(decl){
-        return decl.value.typeRef;
-
-        let i = 0;
-        while(i < 1000) {
-            i += 1;
-            console.assert(isDecl(decl));
-            if ("type" in decl.value) {
-                return /** @type {WalkResult} */({ type: typeTypeId });
-            }
-
-//            if ("string" in decl.value) {
-//                return /** @type {WalkResult} */({ type: {
-//                  kind: typeKinds.Pointer,
-//                  size: pointerSizeEnum.One,
-//                  child: });
+//    function typeOfDecl(decl){
+//        return decl.value.typeRef;
+//
+//        let i = 0;
+//        while(i < 1000) {
+//            i += 1;
+//            console.assert(isDecl(decl));
+//            if ("type" in decl.value) {
+//                return /** @type {WalkResult} */({ type: typeTypeId });
 //            }
-
-            if ("refPath" in decl.value) {
-                decl =  /** @type {Decl} */({
-                  value: decl.value.refPath[decl.value.refPath.length -1]
-                });
-                continue;
-            }
-
-            if ("declRef" in decl.value) {
-                decl = zigAnalysis.decls[decl.value.declRef];
-                continue;
-            }
-
-            if ("int" in decl.value) {
-                return decl.value.int.typeRef;
-            }
-
-            if ("float" in decl.value) {
-                return decl.value.float.typeRef;
-            }
-
-            if ("array" in decl.value) {
-                return decl.value.array.typeRef;
-            }
-
-            if ("struct" in decl.value) {
-                return decl.value.struct.typeRef;
-            }
-
-            if ("comptimeExpr" in decl.value) {
-                const cte = zigAnalysis.comptimeExprs[decl.value.comptimeExpr];
-                return cte.typeRef;
-            }
-
-            if ("call" in decl.value) {
-                const fn_call = zigAnalysis.calls[decl.value.call];
-                let fn_decl = undefined;
-                if ("declRef" in fn_call.func) {
-                    fn_decl = zigAnalysis.decls[fn_call.func.declRef];
-                } else if ("refPath" in fn_call.func) {
-                    console.assert("declRef" in fn_call.func.refPath[fn_call.func.refPath.length -1]);
-                    fn_decl = zigAnalysis.decls[fn_call.func.refPath[fn_call.func.refPath.length -1].declRef];
-                } else throw {};
-
-                const fn_decl_value = resolveValue(fn_decl.value);
-                console.assert("type" in fn_decl_value); //TODO handle comptimeExpr
-                const fn_type = /** @type {Fn} */(zigAnalysis.types[fn_decl_value.type]);
-                console.assert(fn_type.kind === typeKinds.Fn);
-                return fn_type.ret;
-            }
-
-            if ("void" in decl.value) {
-                return /** @type {WalkResult} */({ type: typeTypeId });
-            }
-
-            if ("bool" in decl.value) {
-                return /** @type {WalkResult} */({ type: typeKinds.Bool });
-            }
-
-            console.log("TODO: handle in `typeOfDecl` more cases: ", decl);
-            console.assert(false);
-            throw {};
-        }
-        console.assert(false);
-        return /** @type {WalkResult} */({});
-    }
+//
+////            if ("string" in decl.value) {
+////                return /** @type {WalkResult} */({ type: {
+////                  kind: typeKinds.Pointer,
+////                  size: pointerSizeEnum.One,
+////                  child: });
+////            }
+//
+//            if ("refPath" in decl.value) {
+//                decl =  /** @type {Decl} */({
+//                  value: decl.value.refPath[decl.value.refPath.length -1]
+//                });
+//                continue;
+//            }
+//
+//            if ("declRef" in decl.value) {
+//                decl = zigAnalysis.decls[decl.value.declRef];
+//                continue;
+//            }
+//
+//            if ("int" in decl.value) {
+//                return decl.value.int.typeRef;
+//            }
+//
+//            if ("float" in decl.value) {
+//                return decl.value.float.typeRef;
+//            }
+//
+//            if ("array" in decl.value) {
+//                return decl.value.array.typeRef;
+//            }
+//
+//            if ("struct" in decl.value) {
+//                return decl.value.struct.typeRef;
+//            }
+//
+//            if ("comptimeExpr" in decl.value) {
+//                const cte = zigAnalysis.comptimeExprs[decl.value.comptimeExpr];
+//                return cte.typeRef;
+//            }
+//
+//            if ("call" in decl.value) {
+//                const fn_call = zigAnalysis.calls[decl.value.call];
+//                let fn_decl = undefined;
+//                if ("declRef" in fn_call.func) {
+//                    fn_decl = zigAnalysis.decls[fn_call.func.declRef];
+//                } else if ("refPath" in fn_call.func) {
+//                    console.assert("declRef" in fn_call.func.refPath[fn_call.func.refPath.length -1]);
+//                    fn_decl = zigAnalysis.decls[fn_call.func.refPath[fn_call.func.refPath.length -1].declRef];
+//                } else throw {};
+//
+//                const fn_decl_value = resolveValue(fn_decl.value);
+//                console.assert("type" in fn_decl_value); //TODO handle comptimeExpr
+//                const fn_type = /** @type {Fn} */(zigAnalysis.types[fn_decl_value.type]);
+//                console.assert(fn_type.kind === typeKinds.Fn);
+//                return fn_type.ret;
+//            }
+//
+//            if ("void" in decl.value) {
+//                return /** @type {WalkResult} */({ type: typeTypeId });
+//            }
+//
+//            if ("bool" in decl.value) {
+//                return /** @type {WalkResult} */({ type: typeKinds.Bool });
+//            }
+//
+//            console.log("TODO: handle in `typeOfDecl` more cases: ", decl);
+//            console.assert(false);
+//            throw {};
+//        }
+//        console.assert(false);
+//        return /** @type {WalkResult} */({});
+//    }
 
     function render() {
         domStatus.classList.add("hidden");
@@ -637,7 +645,7 @@ var zigAnalysis;
                 return render404();
             }
 
-            let childDeclValue = resolveValue(/** @type {Decl} */(childDecl).value);
+            let childDeclValue = resolveValue(/** @type {Decl} */(childDecl).value).expr;
             if ("type" in childDeclValue) {
 
                 const t = zigAnalysis.types[childDeclValue.type];
@@ -674,7 +682,7 @@ var zigAnalysis;
         }
 
         if (lastIsDecl && last.kind === 'const') {
-            let typeObj = zigAnalysis.types[resolveValue(/** @type {Decl} */(last).value).type];
+            let typeObj = zigAnalysis.types[resolveValue(/** @type {Decl} */(last).value).expr.type];
             if (typeObj && typeObj.kind === typeKinds.Fn) {
                 return renderFn(/** @type {Decl} */(last));
             }
@@ -721,11 +729,22 @@ var zigAnalysis;
 
     /** @param {Decl} fnDecl */
     function renderFn(fnDecl) {
+        if ("refPath" in fnDecl.value.expr) {
+            let last = fnDecl.value.expr.refPath.length - 1;
+            let lastExpr = fnDecl.value.expr.refPath[last];
+            console.assert("declRef" in lastExpr);
+            fnDecl = zigAnalysis.decls[lastExpr.declRef];
+        }
+
         let value = resolveValue(fnDecl.value);
-        console.assert("type" in value);
-        let typeObj = /** @type {Fn} */(zigAnalysis.types[value.type]);
+        console.assert("type" in value.expr);
+        let typeObj = /** @type {Fn} */(zigAnalysis.types[value.expr.type]);
 
-        domFnProtoCode.innerHTML = typeValueName(value, true, true, fnDecl);
+        domFnProtoCode.innerHTML = exprName(value.expr, {
+          wantHtml: true,
+          wantLink: true,
+          fnDecl,
+        });
 
         let docsSource = null;
         let srcNode = zigAnalysis.astNodes[fnDecl.src];
@@ -733,23 +752,26 @@ var zigAnalysis;
             docsSource = srcNode.docs;
         }
 
-        let retIndex = resolveValue(typeObj.ret).type;
         renderFnParamDocs(fnDecl, typeObj);
 
-        let errSetTypeIndex = /** @type {number | null} */(null);
-        let retType = zigAnalysis.types[retIndex];
-        if (retType.kind === typeKinds.ErrorSet) {
-            errSetTypeIndex = retIndex;
-        } else if (retType.kind === typeKinds.ErrorUnion) {
-            errSetTypeIndex = /** @type {ErrUnionType} */(retType).err.type;
-        }
-        if (errSetTypeIndex != null) {
-            let errSetType = /** @type {ErrSetType} */(zigAnalysis.types[errSetTypeIndex]);
-            renderErrorSet(errSetType);
+        let retExpr = resolveValue({expr:typeObj.ret}).expr;
+        if ("type" in retExpr) {
+            let retIndex = retExpr.type;
+            let errSetTypeIndex = /** @type {number | null} */(null);
+            let retType = zigAnalysis.types[retIndex];
+            if (retType.kind === typeKinds.ErrorSet) {
+                errSetTypeIndex = retIndex;
+            } else if (retType.kind === typeKinds.ErrorUnion) {
+                errSetTypeIndex = /** @type {ErrUnionType} */(retType).err.type;
+            }
+            if (errSetTypeIndex != null) {
+                let errSetType = /** @type {ErrSetType} */(zigAnalysis.types[errSetTypeIndex]);
+                renderErrorSet(errSetType);
+            }
         }
 
         let protoSrcIndex = fnDecl.src;
-        if (typeIsGenericFn(value.type)) {
+        if (typeIsGenericFn(value.expr.type)) {
             throw "TODO";
             // let instantiations = nodesToFnsMap[protoSrcIndex];
             // let calls = nodesToCallsMap[protoSrcIndex];
@@ -827,7 +849,7 @@ var zigAnalysis;
             if (isVarArgs && i === typeObj.params.length - 1) {
                 html += '...';
             } else {
-                let name = typeValueName(value, false, false);
+                let name = exprName(value, {wantHtml: false, wantLink: false});
                 html += '<span class="tok-kw">' + name + '</span>';
             }
 
@@ -1017,19 +1039,72 @@ var zigAnalysis;
             listDom.removeChild(listDom.lastChild);
         }
     }
-
      /**
-    * @typedef {{
-        wantHtml: boolean,
-    }} RenderWrOptions
-    * @param {WalkResult} wr,
-    * @param {RenderWrOptions} opts,
-    * @return {string}
+      * @param {WalkResult} wr,
+      * @return {Expr}
+    */
+    function walkResultTypeRef(wr) {
+      if (wr.typeRef) return wr.typeRef;
+      return walkResultTypeRef(resolveValue(wr));
+    }
+     /**
+      * @typedef {{
+          wantHtml: boolean,
+      }} RenderWrOptions
+      * @param {Expr} expr,
+      * @param {RenderWrOptions} opts,
+      * @return {string}
     */
-
     function exprName(expr, opts) {
-        const activeField = Object.keys(expr)[0];
-        switch (activeField) {
+        switch (Object.keys(expr)[0]) {
+          default: throw "oh no";
+          case "array": {
+            let payloadHtml = ".{";
+            for (let i = 0; i < expr.array.length; i++) {
+                if (i != 0) payloadHtml += ", ";
+                let elem = zigAnalysis.exprs[expr.array[i]];
+                payloadHtml += exprName(elem);
+            }
+            return payloadHtml + "}";
+          }
+          case "comptimeExpr": {
+              return "[ComptimeExpr]";
+          }
+          case "call": {
+              let call = zigAnalysis.calls[expr.call];
+              let payloadHtml = "";
+
+
+              switch(Object.keys(call.func)[0]){
+                default: throw "TODO";
+                case "declRef":
+                case "refPath": {
+                    payloadHtml += exprName(call.func);
+                    break;
+                }
+              }
+              payloadHtml += "(";
+
+              for (let i = 0; i < call.args.length; i++) {
+                  if (i != 0) payloadHtml += ", ";
+                  payloadHtml += exprName(call.args[i]);
+              }
+
+              payloadHtml += ")";
+              return payloadHtml;
+          }
+          case "as": {
+              const typeRefArg = zigAnalysis.exprs[expr.as.typeRefArg];
+              const exprArg = zigAnalysis.exprs[expr.as.exprArg];
+              return "@as(" + exprName(typeRefArg, opts) +
+                ", " + exprName(exprArg, opts) + ")";
+          }
+          case "declRef": {
+            return zigAnalysis.decls[expr.declRef].name;
+          }
+          case "refPath": {
+            return expr.refPath.map(x => exprName(x, opts)).join(".");
+          }
           case "int": {
               return "" + expr.int;
           }
@@ -1047,8 +1122,12 @@ var zigAnalysis;
 
           case "type": {
               let name = "";
-              const typeObj = zigAnalysis.types[expr.type];
+
+              let typeObj = expr.type;
+              if (typeof typeObj === 'number') typeObj = zigAnalysis.types[typeObj];
+
               switch (typeObj.kind) {
+                  default: throw "TODO";
                   case typeKinds.Array:
                   {
                       let arrayObj = /** @type {ArrayType} */(typeObj);
@@ -1064,8 +1143,7 @@ var zigAnalysis;
                       return name;
                   }
                   case typeKinds.Optional:
-
-                      return "?" + typeValueName(/**@type {OptionalType} */(typeObj).child, wantHtml, wantSubLink, fnDecl, linkFnNameDecl);
+                      return "?" + exprName(/**@type {OptionalType} */(typeObj).child, opts);
                   case typeKinds.Pointer:
                   {
                       let ptrObj = /** @type {PointerType} */(typeObj);
@@ -1135,7 +1213,7 @@ var zigAnalysis;
                   {
                       let floatObj = /** @type {NumberType} */ (typeObj);
 
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">' + floatObj.name + '</span>';
                       } else {
                           return floatObj.name;
@@ -1152,43 +1230,43 @@ var zigAnalysis;
                       }
                   }
                   case typeKinds.ComptimeInt:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">comptime_int</span>';
                       } else {
                           return "comptime_int";
                       }
                   case typeKinds.ComptimeFloat:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">comptime_float</span>';
                       } else {
                           return "comptime_float";
                       }
                   case typeKinds.Type:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">type</span>';
                       } else {
                           return "type";
                       }
                   case typeKinds.Bool:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">bool</span>';
                       } else {
                           return "bool";
                       }
                   case typeKinds.Void:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">void</span>';
                       } else {
                           return "void";
                       }
                   case typeKinds.EnumLiteral:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">(enum literal)</span>';
                       } else {
                           return "(enum literal)";
                       }
                   case typeKinds.NoReturn:
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           return '<span class="tok-type">noreturn</span>';
                       } else {
                           return "noreturn";
@@ -1230,20 +1308,20 @@ var zigAnalysis;
                   {
                       let fnObj = /** @type {Fn} */(typeObj);
                       let payloadHtml = "";
-                      if (wantHtml) {
+                      if (opts.wantHtml) {
                           payloadHtml += '<span class="tok-kw">fn</span>';
-                          if (fnDecl != null) {
+                          if (opts.fnDecl) {
                               payloadHtml += ' <span class="tok-fn">';
-                              if (linkFnNameDecl != null) {
-                                  payloadHtml += '<a href="' + linkFnNameDecl + '">' +
-                                      escapeHtml(fnDecl.name) + '</a>';
+                              if (opts.linkFnNameDecl) {
+                                  payloadHtml += '<a href="' + opts.linkFnNameDecl + '">' +
+                                      escapeHtml(opts.fnDecl.name) + '</a>';
                               } else {
-                                  payloadHtml += escapeHtml(fnDecl.name);
+                                  payloadHtml += escapeHtml(opts.fnDecl.name);
                               }
                               payloadHtml += '</span>';
                           }
                       } else {
-                          payloadHtml += 'fn'
+                          payloadHtml += 'fn ';
                       }
                       payloadHtml += '(';
                           if (fnObj.params) {
@@ -1259,7 +1337,7 @@ var zigAnalysis;
                                   }
 
                                   let value = fnObj.params[i];
-                                  let paramValue = resolveValue(value);
+                                  let paramValue = resolveValue({expr: value});
 
                                   if (fields != null) {
                                       let paramNode = zigAnalysis.astNodes[fields[i]];
@@ -1270,7 +1348,7 @@ var zigAnalysis;
                                       }
 
                                       if (paramNode.noalias) {
-                                          if (wantHtml) {
+                                          if (opts.wantHtml) {
                                               payloadHtml += '<span class="tok-kw">noalias</span> ';
                                           } else {
                                               payloadHtml += 'noalias ';
@@ -1278,7 +1356,7 @@ var zigAnalysis;
                                       }
 
                                       if (paramNode.comptime) {
-                                          if (wantHtml) {
+                                          if (opts.wantHtml) {
                                               payloadHtml += '<span class="tok-kw">comptime</span> ';
                                           } else {
                                               payloadHtml += 'comptime ';
@@ -1297,35 +1375,48 @@ var zigAnalysis;
                                   if (isVarArgs && i === fnObj.params.length - 1) {
                                       payloadHtml += '...';
                                   } else if ("refPath" in value) {
-                                      payloadHtml += '<a href="">';
-                                      payloadHtml += '<span class="tok-kw" style="color:lightblue;">[Ref Path]</span>';
-                                      payloadHtml += '</a>';
+                                      if (opts.wantHtml) {
+                                          payloadHtml += '<a href="">';
+                                          payloadHtml +=
+                                            '<span class="tok-kw" style="color:lightblue;">'
+                                              + exprName(value, opts) + '</span>';
+                                          payloadHtml += '</a>';
+                                      } else {
+                                          payloadHtml += exprName(value, opts);
+                                      }
 
                                   } else if ("type" in value) {
-                                      let name = typeValueName(value, false, false, fnDecl, linkFnNameDecl);
+                                      let name = exprName(value, {
+                                        wantHtml: false,
+                                        wantLink: false,
+                                        fnDecl: opts.fnDecl,
+                                        linkFnNameDecl: opts.linkFnNameDecl,
+                                      });
                                       payloadHtml += '<span class="tok-kw">' + escapeHtml(name) + '</span>';
                                   } else if ("comptimeExpr" in value) {
-                                      payloadHtml += '<span class="tok-kw">[ComptimeExpr]</span>';
-                                  } else if (wantHtml) {
-                                      payloadHtml += '<span class="tok-kw">var</span>';
+                                      if (opts.wantHtml) {
+                                        payloadHtml += '<span class="tok-kw">[ComptimeExpr]</span>';
+                                      } else {
+                                        payloadHtml += "[ComptimeExpr]";
+                                      }
+                                  } else if (opts.wantHtml) {
+                                      payloadHtml += '<span class="tok-kw">anytype</span>';
                                   } else {
-                                      payloadHtml += 'var';
+                                      payloadHtml += 'anytype';
                                   }
                               }
                           }
 
                       payloadHtml += ') ';
                       if (fnObj.ret != null) {
-                          payloadHtml += typeValueName(fnObj.ret, wantHtml, wantSubLink, fnDecl);
-                      } else if (wantHtml) {
+                          payloadHtml += exprName(fnObj.ret, opts);
+                      } else if (opts.wantHtml) {
                           payloadHtml += '<span class="tok-kw">anytype</span>';
                       } else {
                           payloadHtml += 'anytype';
                       }
                       return payloadHtml;
                   }
-                  default:
-                      throw "TODO";
                       // if (wantHtml) {
                       //     return escapeHtml(typeObj.name);
                       // } else {
@@ -1334,104 +1425,16 @@ var zigAnalysis;
               }
         }
 
-          default: throw "oh no";
         }
     }
 
 
     /**
-    * @param {WalkResult} typeValue,
-    * @param {boolean} wantHtml,
-    * @param {boolean} wantLink,
-    * @param {Decl | null} [fnDecl],
-    * @param {string} [linkFnNameDecl],
-    * @return {string}
-    */
-    function typeValueName(typeValue, wantHtml, wantLink, fnDecl, linkFnNameDecl) {
-
-        if ("int" in typeValue) {
-            return "" + typeValue.int.value;
-        }
-        if ("call" in typeValue) {
-            let result = "";
-            let call = zigAnalysis.calls[typeValue.call];
-            let functionName = typeValueName(call.func, wantHtml, wantLink, fnDecl, linkFnNameDecl);
-            result += functionName + "(";
-            for (let j = 0; j < call.args.length; j += 1) {
-              result += typeValueName(call.args[j], wantHtml, wantLink, fnDecl, linkFnNameDecl);
-              if (j != call.args.length -1) result += ",";
-            }
-
-            return result + ")";
-        }
-        if ("comptimeExpr" in typeValue) {
-            return "[ComptimeExpr]";
-        }
-        if ("refPath" in typeValue) {
-              let result = "";
-              for (let j = 0; j < typeValue.refPath.length; j++) {
-
-                  let name = "[RefPath]";
-                  if (wantHtml) {
-                      //result += '<a href="'+navLinkDecl(decl.name)+'">';
-                      result += '<a href="">';
-                      result += '<span class="tok-kw" style="color:lightblue;">' +
-                           name + '</span>';
-                      result += '</a>';
-                  } else {
-                      result += name;
-                  }
-
-                  if (j != 0) result += ".";
-            }
-
-            return result;
-        }
-
-        if ("declRef" in typeValue) {
-            return zigAnalysis.decls[typeValue.declRef].name;
-        }
-
-        if ("string" in typeValue) {
-            return typeValue.string + " (string)";
-        }
-
-        if ("anytype" in typeValue) {
-            return "anytype";
-        }
-
-        if ("this" in typeValue) {
-            return "this";
-        }
-
-        console.assert("type" in typeValue)
-        let typeIndex = typeValue.type;
-        let typeObj = zigAnalysis.types[typeIndex];
-        if (wantLink) {
-            let declIndex = getCanonTypeDecl(typeIndex);
-            let declPath = getCanonDeclPath(declIndex);
-            if (declPath == null) {
-                return typeName(typeObj, wantHtml, wantLink, fnDecl, linkFnNameDecl);
-            }
-            let name = (wantLink && declCanRepresentTypeKind(typeObj.kind)) ?
-                declPath.declNames[declPath.declNames.length - 1] :
-                typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl);
-            if (wantLink && wantHtml) {
-                return '<a href="' + navLink(declPath.pkgNames, declPath.declNames) + '">' + name + '</a>';
-            } else {
-                return name;
-            }
-        } else {
-            return typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl);
-        }
-    }
-
-    /**
-    * @param {WalkResult} typeRef
+    * @param {Expr} typeRef
     * @param {string} paramName
     */
     function shouldSkipParamName(typeRef, paramName) {
-        let resolvedTypeRef = resolveValue(typeRef);
+        let resolvedTypeRef = resolveValue({expr: typeRef});
         if ("type" in resolvedTypeRef) {
             let typeObj = zigAnalysis.types[resolvedTypeRef.type];
             if (typeObj.kind === typeKinds.Pointer){
@@ -1450,69 +1453,13 @@ var zigAnalysis;
         return (typeObj.size == null) ? pointerSizeEnum.One : typeObj.size;
     }
 
-    // function getCallHtml(fnDecl, callIndex) {
-    //     let callObj = zigAnalysis.calls[callIndex];
-
-    //     // TODO make these links work
-    //     //let html = '<a href="' + navLinkCall(callObj) + '">' + escapeHtml(fnDecl.name) + '</a>(';
-    //         let html = escapeHtml(fnDecl.name) + '(';
-    //             for (let arg_i = 0; arg_i < callObj.args.length; arg_i += 1) {
-    //                 if (arg_i !== 0) html += ', ';
-    //                 let argObj = callObj.args[arg_i];
-    //                 html += getValueText(argObj, argObj.value, true, true);
-    //             }
-    //             html += ')';
-    //         return html;
-    //     }
-
-   //  /**
-   //  * @param {WalkResult} typeRef
-   //  * @param {any} value
-   //  * @param {boolean} wantHtml
-   //  * @param {boolean} wantLink
-   //  */
-   //  function getValueText(typeRef, value, wantHtml, wantLink) {
-   //      let resolvedTypeRef = resolveValue(typeRef);
-   //      if ("comptimeExpr" in resolvedTypeRef) {
-   //          return "[ComptimeExpr]";
-   //      }
-   //      console.assert("type" in resolvedTypeRef);
-   //      let typeObj = zigAnalysis.types[typeRef.type];
-   //      switch (typeObj.kind) {
-   //          case typeKinds.Type:
-   //              return typeValueName(value, wantHtml, wantLink);
-   //          case typeKinds.Fn:
-   //              let fnObj = zigAnalysis.fns[value];
-   //              return typeName(fnObj, wantHtml, wantLink);
-   //          case typeKinds.Int:
-   //              if (wantHtml) {
-   //                  return '<span class="tok-number">' + value + '</span>';
-   //              } else {
-   //                  return value + "";
-   //              }
-   //          default:
-   //              console.trace("TODO implement getValueText for this type:", zigAnalysis.typeKinds[typeObj.kind]);
-   //      }
-   //  }
-
-    /**
-    * @param {Type} typeObj,
-    * @param {boolean} wantHtml,
-    * @param {boolean} wantSubLink,
-    * @param {Decl | null} [fnDecl],
-    * @param {string} [linkFnNameDecl],
-    * @return {string}
-    */
-    function typeName(typeObj, wantHtml, wantSubLink, fnDecl, linkFnNameDecl) {
-    }
-
     /** @param {Type} typeObj */
     function renderType(typeObj) {
         let name;
         if (rootIsStd && typeObj === zigAnalysis.types[zigAnalysis.packages[zigAnalysis.rootPkg].main]) {
             name = "std";
         } else {
-            name = typeName(typeObj, false, false);
+            name = exprName({type:typeObj}, false, false);
         }
         if (name != null && name != "") {
             domHdrName.innerText = name + " (" + zigAnalysis.typeKinds[typeObj.kind] + ")";
@@ -1626,54 +1573,14 @@ var zigAnalysis;
 //     }
 
 
-    // function mergeDecls(declObj, nextDeclIndex, firstTypeObj, typeObj) {
-    //     let nextDeclObj = zigAnalysis.decls[nextDeclIndex];
-    //     if (declObj.type != null && nextDeclObj.type != null && declObj.type !== nextDeclObj.type) {
-    //         if (typeof(declObj.type) !== 'object') {
-    //             let prevType = declObj.type;
-    //             declObj.type = {};
-    //             declObj.type[prevType] = firstTypeObj;
-    //             declObj.value = null;
-    //         }
-    //         declObj.type[nextDeclObj.type] = typeObj;
-    //     } else if (declObj.type == null && nextDeclObj != null) {
-    //         declObj.type = nextDeclObj.type;
-    //     }
-    //     if (declObj.value != null && nextDeclObj.value != null && declObj.value !== nextDeclObj.value) {
-    //         if (typeof(declObj.value) !== 'object') {
-    //             let prevValue = declObj.value;
-    //             declObj.value = {};
-    //             declObj.value[prevValue] = firstTypeObj;
-    //         }
-    //         declObj.value[nextDeclObj.value] = typeObj;
-    //     } else if (declObj.value == null && nextDeclObj.value != null) {
-    //         declObj.value = nextDeclObj.value;
-    //     }
-    // }
 
     /** @param {Decl} decl */
     function renderValue(decl) {
-
-        let declTypeRef = decl.value.typeRef;
-        let declValueText = exprName(decl.value.expr);
-//        switch(Object.keys(decl.value)[0]) {
-//            case "int":
-//                declValueText += /** @type {{int: {value: number}}} */(decl.value).int.value;
-//                break;
-//            case "float":
-//                declValueText += /** @type {{float: {value: number}}} */(decl.value).float.value;
-//                break;
-//            case "comptimeExpr":
-//                declValueText += "[ComptimeExpr]";
-//                break;
-//            default:
-//                console.log("TODO: renderValue for ", Object.keys(decl.value)[0]);
-//                declValueText += "#TODO#";
-//        }
+        let resolvedValue = resolveValue(decl.value)
 
         domFnProtoCode.innerHTML = '<span class="tok-kw">const</span> ' +
-            escapeHtml(decl.name) + ': ' + exprName(declTypeRef, {wantHtml: true}) +
-            " = " + declValueText;
+            escapeHtml(decl.name) + ': ' + exprName(resolvedValue.typeRef, {wantHtml: true, wantLink:true}) +
+            " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";";
 
         let docs = zigAnalysis.astNodes[decl.src].docs;
         if (docs != null) {
@@ -1729,43 +1636,36 @@ var zigAnalysis;
             }
 
             if (decl.kind === 'const') {
-                if ("call" in declValue) {
-                    let c  = zigAnalysis.calls[declValue.call];
-                    console.assert("comptimeExpr" in c.ret);
-                    let fDecl = resolveValue(c.func);
-                    if ("type" in fDecl) {
-                        console.assert("type" in fDecl);
-                        let fType = /** @type {Fn} */(zigAnalysis.types[fDecl.type]);
-                        console.assert("type" in fType.ret);
-                        if (fType.ret.type === typeTypeId) {
-                            typesList.push(decl);
-                        } else {
-                            valsList.push(decl);
-                        }
-                    } else {
-                        valsList.push(decl);
-                    }
-                } else if (!("type" in declValue)){
-                    valsList.push(decl);
-                } else {
-                    let value = zigAnalysis.types[declValue.type];
-                    let kind = value.kind;
-                    if (kind === typeKinds.Fn) {
-                        // TODO: handle CTE return types when we know their type.
-                        const resVal = resolveValue(/** @type {Fn} */(value).ret);
-                        if ("type" in resVal && resVal.type == typeTypeId) {
-                            typesList.push(decl);
+                if ("type" in declValue.expr) {
+                    // We have the actual type expression at hand.
+                    const typeExpr = zigAnalysis.types[declValue.expr.type];
+                    if (typeExpr.kind == typeKinds.Fn) {
+                        const funcRetExpr = resolveValue({
+                            expr: /** @type {Fn} */(typeExpr).ret
+                        });
+                        if ("type" in funcRetExpr && funcRetExpr.type != typeTypeId) {
+                            if (typeIsErrSet(declValue.expr.type)) {
+                                errSetsList.push(decl);
+                            } else if (typeIsStructWithNoFields(declValue.expr.type)) {
+                                namespacesList.push(decl);
+                            } else {
+                                typesList.push(decl);
+                            }
                         } else {
                             fnsList.push(decl);
                         }
-
-                    } else  if (typeIsErrSet(declValue.type)) {
-                        errSetsList.push(decl);
-                    } else if (typeIsStructWithNoFields(declValue.type)) {
-                        namespacesList.push(decl);
-                    } else {
+                     } else {
                         typesList.push(decl);
-                    }
+                     }
+                 } else if ("typeRef" in declValue) {
+                      if ("type" in declValue.typeRef && declValue.typeRef == typeTypeId) {
+                          // We don't know what the type expression is, but we know it's a type.
+                          typesList.push(decl);
+                      } else {
+                          valsList.push(decl);
+                      }
+                } else {
+                    valsList.push(decl);
                 }
             }
         }
@@ -1859,9 +1759,14 @@ var zigAnalysis;
                 let tdDesc = trDom.children[1];
 
                 let declType = resolveValue(decl.value);
-                console.assert("type" in declType);
+                console.assert("type" in declType.expr);
 
-                tdFnCode.innerHTML = typeValueName(declType, true, true, decl, navLinkDecl(decl.name));
+                tdFnCode.innerHTML = exprName(declType.expr,{
+                  wantHtml: true,
+                  wantLink: true,
+                  fnDecl: decl,
+                  linkFnNameDecl: navLinkDecl(decl.name),
+                });
 
                 let docs = zigAnalysis.astNodes[decl.src].docs;
                 if (docs != null) {
@@ -1874,7 +1779,7 @@ var zigAnalysis;
         }
 
         let containerNode = zigAnalysis.astNodes[container.src];
-        if (containerNode.fields) {
+        if (containerNode.fields && containerNode.fields.length > 0) {
             resizeDomList(domListFields, containerNode.fields.length, '<div></div>');
 
             for (let i = 0; i < containerNode.fields.length; i += 1) {
@@ -1887,11 +1792,11 @@ var zigAnalysis;
                 if (container.kind === typeKinds.Enum) {
                     html += ' = <span class="tok-number">' + fieldName + '</span>';
                 } else {
-                    let fieldTypeWr = container.fields[i];
+                    let fieldTypeExpr = container.fields[i];
                     html += ": ";
-                    let name = typeValueName(fieldTypeWr, false, false);
+                    let name = exprName(fieldTypeExpr, false, false);
                     html += '<span class="tok-kw">'+ name +'</span>';
-                    let tsn = typeShorthandName(fieldTypeWr);
+                    let tsn = typeShorthandName(fieldTypeExpr);
                     if (tsn) {
                         html += '<span> ('+ tsn +')</span>';
 
@@ -1951,7 +1856,8 @@ var zigAnalysis;
                 tdNameA.setAttribute('href', navLinkDecl(decl.name));
                 tdNameA.textContent = decl.name;
 
-                tdType.innerHTML = typeValueName(typeOfDecl(decl), true, true);
+                tdType.innerHTML = exprName(walkResultTypeRef(decl.value),
+                  {wantHtml:true, wantLink:true});
 
                 let docs = zigAnalysis.astNodes[decl.src].docs;
                 if (docs != null) {
@@ -1978,7 +1884,8 @@ var zigAnalysis;
                 tdNameA.setAttribute('href', navLinkDecl(decl.name));
                 tdNameA.textContent = decl.name;
 
-                tdType.innerHTML = typeValueName(typeOfDecl(decl), true, true);
+                tdType.innerHTML = exprName(walkResultTypeRef(decl.value),
+                  {wantHtml:true, wantLink:true});
 
                 let docs = zigAnalysis.astNodes[decl.src].docs;
                 if (docs != null) {
src/Autodoc.zig
@@ -542,13 +542,14 @@ const DocData = struct {
         call: usize, // index in `calls`
         enumLiteral: []const u8, // direct value
         typeOf: usize, // index in `exprs`
-        as: struct {
-            typeRefArg: ?usize, // index in `exprs`
-            exprArg: usize, // index in `exprs`
-        },
+        as: As,
         sizeOf: usize, // index in `exprs`
         compileError: []const u8,
         string: []const u8, // direct value
+        const As = struct {
+            typeRefArg: ?usize, // index in `exprs`
+            exprArg: usize, // index in `exprs`
+        };
         const FieldRef = struct {
             type: usize, // index in `types`
             index: usize, // index in type.fields
@@ -592,12 +593,16 @@ const DocData = struct {
                     , .{v});
                 },
                 .sizeOf => |v| try std.json.stringify(v, options, w),
-                .as => |v| try std.json.stringify(v, options, w),
                 .fieldRef => |v| try std.json.stringify(
                     struct { fieldRef: FieldRef }{ .fieldRef = v },
                     options,
                     w,
                 ),
+                .as => |v| try std.json.stringify(
+                    struct { as: As }{ .as = v },
+                    options,
+                    w,
+                ),
                 .@"struct" => |v| try std.json.stringify(
                     struct { @"struct": []FieldVal }{ .@"struct" = v },
                     options,
@@ -774,7 +779,12 @@ fn walkInstruction(
             );
 
             return DocData.WalkResult{
-                .expr = .{ .compileError = operand.expr.string },
+                .expr = .{
+                    .compileError = switch (operand.expr) {
+                        .string => |s| s,
+                        else => "TODO: non-string @compileError arguments",
+                    },
+                },
             };
         },
         .enum_literal => {
@@ -1010,6 +1020,9 @@ fn walkInstruction(
         .decl_val, .decl_ref => {
             const str_tok = data[inst_index].str_tok;
             const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
+            // While it would make sense to grab the original decl's typeRef info,
+            // that decl might not have been analyzed yet! The frontend will have
+            // to navigate through all declRefs to find the underlying type.
             return DocData.WalkResult{ .expr = .{ .declRef = decls_slot_index } };
         },
         .field_val, .field_call_bind, .field_ptr, .field_type => {