diff --git a/CMakeLists.txt b/CMakeLists.txt index b1e34b6068..b046152bdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -340,7 +340,6 @@ set(STAGE1_SOURCES "${CMAKE_SOURCE_DIR}/src/stage1/bigint.cpp" "${CMAKE_SOURCE_DIR}/src/stage1/buffer.cpp" "${CMAKE_SOURCE_DIR}/src/stage1/codegen.cpp" - "${CMAKE_SOURCE_DIR}/src/stage1/dump_analysis.cpp" "${CMAKE_SOURCE_DIR}/src/stage1/errmsg.cpp" "${CMAKE_SOURCE_DIR}/src/stage1/error.cpp" "${CMAKE_SOURCE_DIR}/src/stage1/heap.cpp" diff --git a/build.zig b/build.zig index cd29b5b4fd..9cfa57223c 100644 --- a/build.zig +++ b/build.zig @@ -936,7 +936,6 @@ const stage1_sources = [_][]const u8{ "src/stage1/bigint.cpp", "src/stage1/buffer.cpp", "src/stage1/codegen.cpp", - "src/stage1/dump_analysis.cpp", "src/stage1/errmsg.cpp", "src/stage1/error.cpp", "src/stage1/heap.cpp", diff --git a/ci/azure/build.zig b/ci/azure/build.zig index 5215262cb9..3fec555321 100644 --- a/ci/azure/build.zig +++ b/ci/azure/build.zig @@ -745,7 +745,6 @@ const stage1_sources = [_][]const u8{ "src/stage1/bigint.cpp", "src/stage1/buffer.cpp", "src/stage1/codegen.cpp", - "src/stage1/dump_analysis.cpp", "src/stage1/errmsg.cpp", "src/stage1/error.cpp", "src/stage1/heap.cpp", diff --git a/lib/docs/index.html b/lib/docs/index.html index 61e3bb4ed2..03579d975e 100644 --- a/lib/docs/index.html +++ b/lib/docs/index.html @@ -26,25 +26,23 @@ --search-sh-color: rgba(0, 0, 0, 0.18); --help-sh-color: rgba(0, 0, 0, 0.75); } - + html, body { margin: 0; padding:0; height: 100%; } a { text-decoration: none; } - + a:hover { text-decoration: underline; } - + .hidden { display: none; } /* layout */ .canvas { - display: flex; - flex-direction: column; width: 100vw; height: 100vh; overflow: hidden; @@ -55,21 +53,12 @@ background-color: var(--bg-color); } - .banner { - background-color: darkred; - text-align: center; - color: white; - padding: 15px 5px; - } - - .banner a { - color: bisque; - text-decoration: underline; - } - .flex-main { display: flex; - overflow-y: hidden; + width: 100%; + height: 100%; + justify-content: center; + z-index: 100; } @@ -87,7 +76,7 @@ overflow-wrap: break-word; flex-shrink: 0; flex-grow: 0; - + z-index: 300; } @@ -97,7 +86,7 @@ -webkit-overflow-scrolling: touch; flex-grow: 1; flex-shrink: 1; - + z-index: 200; } @@ -106,44 +95,44 @@ max-width: 85vw; flex-shrink: 1; } - + .help-modal { z-index: 400; } - + /* sidebar */ .sidebar { font-size: 1rem; background-color: var(--bg-color); box-shadow: 0 0 1rem var(--sidebar-sh-color); } - + .sidebar .logo { padding: 1rem 0.35rem 0.35rem 0.35rem; } - + .sidebar .logo > svg { display: block; overflow: visible; } - + .sidebar h2 { margin: 0.5rem; padding: 0; font-size: 1.2rem; } - + .sidebar h2 > span { border-bottom: 0.125rem dotted var(--tx-color); } - + .sidebar .packages { list-style-type: none; margin: 0; padding: 0; background-color: var(--sidebar-pkg-bg-color); } - + .sidebar .packages > li > a { display: block; padding: 0.5rem 1rem; @@ -151,17 +140,17 @@ background-color: var(--sidebar-pkglnk-bg-color); text-decoration: none; } - + .sidebar .packages > li > a:hover { color: var(--sidebar-pkglnk-tx-color-hover); background-color: var(--sidebar-pkglnk-bg-color-hover); } - + .sidebar .packages > li > a.active { color: var(--sidebar-pkglnk-tx-color-active); background-color: var(--sidebar-pkglnk-bg-color-active); } - + .sidebar p.str { margin: 0.5rem; font-family: var(--mono); @@ -194,28 +183,28 @@ border-radius: 0; -webkit-appearance: none; } - + .docs .search:focus { background-color: var(--search-bg-color-focus); border-bottom-color: #ffbb4d; box-shadow: 0 0.3em 1em 0.125em var(--search-sh-color); } - + .docs .search::placeholder { font-size: 1rem; font-family: var(--ui); color: var(--tx-color); opacity: 0.5; } - + .docs a { color: var(--link-color); } - + .docs p { margin: 0.8rem 0; } - + .docs pre { font-family: var(--mono); font-size:1em; @@ -223,19 +212,19 @@ padding:1em; overflow-x: auto; } - + .docs code { font-family: var(--mono); font-size: 1em; } - + .docs h1 { font-size: 1.4em; margin: 0.8em 0; padding: 0; border-bottom: 0.0625rem dashed; } - + .docs h2 { font-size: 1.3em; margin: 0.5em 0; @@ -275,7 +264,7 @@ #tableFnErrors dt { font-weight: bold; } - + .examples { list-style-type: none; margin: 0; @@ -286,7 +275,7 @@ white-space: nowrap; overflow-x: auto; } - + .docs td { margin: 0; padding: 0.5em; @@ -294,7 +283,7 @@ text-overflow: ellipsis; overflow-x: hidden; } - + /* help dialog */ .help-modal { display: flex; @@ -319,23 +308,23 @@ border: 0.125rem solid #000; box-shadow: 0 0.5rem 2.5rem 0.3rem var(--help-sh-color); } - + .help-modal h1 { margin: 0.75em 2.5em 1em 2.5em; font-size: 1.5em; text-align: center; } - + .help-modal dt, .help-modal dd { display: inline; margin: 0 0.2em; } - + .help-modal dl { margin-left: 0.5em; margin-right: 0.5em; } - + .help-modal kbd { display: inline-block; padding: 0.3em 0.2em; @@ -352,7 +341,14 @@ box-shadow: inset 0 -0.0625em 0 #c6cbd1; cursor: default; } - + + #listFns dt { + font-family: var(--mono); + } + .argBreaker { + display: none; + } + /* tokens */ .tok-kw { color: #333; @@ -382,10 +378,10 @@ color: #458; font-weight: bold; } - + /* dark mode */ @media (prefers-color-scheme: dark) { - + :root { --tx-color: #bbb; --bg-color: #111; @@ -403,7 +399,7 @@ --search-sh-color: rgba(255, 255, 255, 0.28); --help-sh-color: rgba(142, 142, 142, 0.5); } - + .docs pre { background-color:#2A2A2A; } @@ -451,7 +447,7 @@ .tok-type { color: #68f; } - + } @media only screen and (max-width: 750px) { @@ -523,34 +519,87 @@ min-width: calc(100% - 2.8rem); } } + .banner { + background-color: orange; + text-align: center; + color: black; + padding: 5px 5px; + } + .banner a { + color: black; + text-decoration: underline; + } - +
@@ -588,7 +637,6 @@
diff --git a/lib/docs/main.js b/lib/docs/main.js index fa209e51cf..8be263e57f 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1,73 +1,83 @@ +'use strict'; + +var zigAnalysis; + (function() { - var domStatus = document.getElementById("status"); - var domSectNav = document.getElementById("sectNav"); - var domListNav = document.getElementById("listNav"); - var domSectPkgs = document.getElementById("sectPkgs"); - var domListPkgs = document.getElementById("listPkgs"); - var domSectTypes = document.getElementById("sectTypes"); - var domListTypes = document.getElementById("listTypes"); - var domSectNamespaces = document.getElementById("sectNamespaces"); - var domListNamespaces = document.getElementById("listNamespaces"); - var domSectErrSets = document.getElementById("sectErrSets"); - var domListErrSets = document.getElementById("listErrSets"); - var domSectFns = document.getElementById("sectFns"); - var domListFns = document.getElementById("listFns"); - var domSectFields = document.getElementById("sectFields"); - var domListFields = document.getElementById("listFields"); - var domSectGlobalVars = document.getElementById("sectGlobalVars"); - var domListGlobalVars = document.getElementById("listGlobalVars"); - var domSectValues = document.getElementById("sectValues"); - var domListValues = document.getElementById("listValues"); - var domFnProto = document.getElementById("fnProto"); - var domFnProtoCode = document.getElementById("fnProtoCode"); - var domSectParams = document.getElementById("sectParams"); - var domListParams = document.getElementById("listParams"); - var domTldDocs = document.getElementById("tldDocs"); - var domSectFnErrors = document.getElementById("sectFnErrors"); - var domListFnErrors = document.getElementById("listFnErrors"); - var domTableFnErrors = document.getElementById("tableFnErrors"); - var domFnErrorsAnyError = document.getElementById("fnErrorsAnyError"); - var domFnExamples = document.getElementById("fnExamples"); - var domListFnExamples = document.getElementById("listFnExamples"); - var domFnNoExamples = document.getElementById("fnNoExamples"); - var domDeclNoRef = document.getElementById("declNoRef"); - var domSearch = document.getElementById("search"); - var domSectSearchResults = document.getElementById("sectSearchResults"); - var domListSearchResults = document.getElementById("listSearchResults"); - var domSectSearchNoResults = document.getElementById("sectSearchNoResults"); - var domSectSearchAllResultsLink = document.getElementById("sectSearchAllResultsLink"); - var domSectInfo = document.getElementById("sectInfo"); - var domTdTarget = document.getElementById("tdTarget"); - var domTdZigVer = document.getElementById("tdZigVer"); - var domHdrName = document.getElementById("hdrName"); - var domHelpModal = document.getElementById("helpDialog"); + let domStatus = (document.getElementById("status")); + let domSectNav = (document.getElementById("sectNav")); + let domListNav = (document.getElementById("listNav")); + let domSectMainPkg = (document.getElementById("sectMainPkg")); + let domSectPkgs = (document.getElementById("sectPkgs")); + let domListPkgs = (document.getElementById("listPkgs")); + let domSectTypes = (document.getElementById("sectTypes")); + let domListTypes = (document.getElementById("listTypes")); + let domSectTests = (document.getElementById("sectTests")); + let domListTests = (document.getElementById("listTests")); + let domSectNamespaces = (document.getElementById("sectNamespaces")); + let domListNamespaces = (document.getElementById("listNamespaces")); + let domSectErrSets = (document.getElementById("sectErrSets")); + let domListErrSets = (document.getElementById("listErrSets")); + let domSectFns = (document.getElementById("sectFns")); + let domListFns = (document.getElementById("listFns")); + let domSectFields = (document.getElementById("sectFields")); + let domListFields = (document.getElementById("listFields")); + let domSectGlobalVars = (document.getElementById("sectGlobalVars")); + let domListGlobalVars = (document.getElementById("listGlobalVars")); + let domSectValues = (document.getElementById("sectValues")); + let domListValues = (document.getElementById("listValues")); + let domFnProto = (document.getElementById("fnProto")); + let domFnProtoCode = (document.getElementById("fnProtoCode")); + let domSectParams = (document.getElementById("sectParams")); + let domListParams = (document.getElementById("listParams")); + let domTldDocs = (document.getElementById("tldDocs")); + let domSectFnErrors = (document.getElementById("sectFnErrors")); + let domListFnErrors = (document.getElementById("listFnErrors")); + let domTableFnErrors =(document.getElementById("tableFnErrors")); + let domFnErrorsAnyError = (document.getElementById("fnErrorsAnyError")); + let domFnExamples = (document.getElementById("fnExamples")); + // let domListFnExamples = (document.getElementById("listFnExamples")); + let domFnNoExamples = (document.getElementById("fnNoExamples")); + let domDeclNoRef = (document.getElementById("declNoRef")); + let domSearch = (document.getElementById("search")); + let domSectSearchResults = (document.getElementById("sectSearchResults")); - var searchTimer = null; - var searchTrimResults = true; - var escapeHtmlReplacements = { "&": "&", '"': """, "<": "<", ">": ">" }; + let domListSearchResults = (document.getElementById("listSearchResults")); + let domSectSearchNoResults = (document.getElementById("sectSearchNoResults")); + let domSectInfo = (document.getElementById("sectInfo")); + // let domTdTarget = (document.getElementById("tdTarget")); + let domPrivDeclsBox = (document.getElementById("privDeclsBox")); + let domTdZigVer = (document.getElementById("tdZigVer")); + let domHdrName = (document.getElementById("hdrName")); + let domHelpModal = (document.getElementById("helpDialog")); - var typeKinds = indexTypeKinds(); - var typeTypeId = findTypeTypeId(); - var pointerSizeEnum = { One: 0, Many: 1, Slice: 2, C: 3 }; - var tokenKinds = { - Keyword: 'tok-kw', - String: 'tok-str', - Builtin: 'tok-builtin', - Comment: 'tok-comment', - Function: 'tok-fn', - Null: 'tok-null', - Number: 'tok-number', - Type: 'tok-type', - }; + + let searchTimer = null; + + + let escapeHtmlReplacements = { "&": "&", '"': """, "<": "<", ">": ">" }; + + let typeKinds = (indexTypeKinds()); + let typeTypeId = (findTypeTypeId()); + let pointerSizeEnum = { One: 0, Many: 1, Slice: 2, C: 3 }; // for each package, is an array with packages to get to this one - var canonPkgPaths = computeCanonicalPackagePaths(); - // for each decl, is an array with {declNames, pkgNames} to get to this one - var canonDeclPaths = null; // lazy; use getCanonDeclPath - // for each type, is an array with {declNames, pkgNames} to get to this one - var canonTypeDecls = null; // lazy; use getCanonTypeDecl + let canonPkgPaths = computeCanonicalPackagePaths(); - var curNav = { + + + // for each decl, is an array with {declNames, pkgNames} to get to this one + + let canonDeclPaths = null; // lazy; use getCanonDeclPath + + // for each type, is an array with {declNames, pkgNames} to get to this one + + let canonTypeDecls = null; // lazy; use getCanonTypeDecl + + + + + let curNav = { + showPrivDecls: false, // each element is a package name, e.g. @import("a") then within there @import("b") // starting implicitly from root package pkgNames: [], @@ -82,27 +92,43 @@ // (a, b, c, d) comptime call; result is the value the docs refer to callName: null, }; - var curNavSearch = ""; - var curSearchIndex = -1; - var imFeelingLucky = false; - var rootIsStd = detectRootIsStd(); + let curNavSearch = ""; + let curSearchIndex = -1; + let imFeelingLucky = false; + + let rootIsStd = detectRootIsStd(); // map of decl index to list of non-generic fn indexes - var nodesToFnsMap = indexNodesToFns(); + // let nodesToFnsMap = indexNodesToFns(); // map of decl index to list of comptime fn calls - var nodesToCallsMap = indexNodesToCalls(); + // let nodesToCallsMap = indexNodesToCalls(); domSearch.addEventListener('keydown', onSearchKeyDown, false); - domSectSearchAllResultsLink.addEventListener('click', onClickSearchShowAllResults, false); - + domPrivDeclsBox.addEventListener('change', function() { + if (this.checked != curNav.showPrivDecls) { + if (this.checked && location.hash.length > 1 && location.hash[1] != '*'){ + location.hash = "#*" + location.hash.substring(1); + return; + } + if (!this.checked && location.hash.length > 1 && location.hash[1] == '*') { + location.hash = "#" + location.hash.substring(2); + return; + } + } + }, false); + + if (location.hash == "") { + location.hash = "#root"; + } + window.addEventListener('hashchange', onHashChange, false); window.addEventListener('keydown', onWindowKeyDown, false); onHashChange(); function renderTitle() { - var list = curNav.pkgNames.concat(curNav.declNames); - var suffix = " - Zig"; + let list = curNav.pkgNames.concat(curNav.declNames); + let suffix = " - Zig"; if (list.length === 0) { if (rootIsStd) { document.title = "std" + suffix; @@ -114,20 +140,221 @@ } } + + function isDecl(x) { + return "value" in x; + } + + + function isType(x) { + return "kind" in x && !("value" in x); + } + + + function isContainerType(x) { + return isType(x) && typeKindIsContainer((x).kind) ; + } + + + function typeShorthandName(expr) { + let resolvedExpr = resolveValue({expr: expr}); + if (!("type" in resolvedExpr)) { + return null; + } + let type = (zigAnalysis.types[resolvedExpr.type]); + + outer: for (let i = 0; i < 10000; i += 1) { + switch (type.kind) { + case typeKinds.Optional: + case typeKinds.Pointer: + let child = (type).child; + let resolvedChild = resolveValue(child); + if ("type" in resolvedChild) { + type = zigAnalysis.types[resolvedChild.type]; + continue; + } else { + return null; + } + default: + break outer; + } + + if (i == 9999) throw "Exhausted typeShorthandName quota"; + } + + + + let name = undefined; + if (type.kind === typeKinds.Struct) { + name = "struct"; + } else if (type.kind === typeKinds.Enum) { + name = "enum"; + } else if (type.kind === typeKinds.Union) { + name = "union"; + } else { + console.log("TODO: unhalndled case in typeShortName"); + return null; + } + + return escapeHtml(name); + } + + + function typeKindIsContainer(typeKind) { + return typeKind === typeKinds.Struct || + typeKind === typeKinds.Union || + typeKind === typeKinds.Enum; + } + + + function declCanRepresentTypeKind(typeKind) { + return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind); + } + + // + // function findCteInRefPath(path) { + // for (let i = path.length - 1; i >= 0; i -= 1) { + // const ref = path[i]; + // if ("string" in ref) continue; + // if ("comptimeExpr" in ref) return ref; + // if ("refPath" in ref) return findCteInRefPath(ref.refPath); + // return null; + // } + + // return null; + // } + + + function resolveValue(value) { + let i = 0; + while(i < 1000) { + i += 1; + + if ("refPath" in value.expr) { + value = {expr: value.expr.refPath[value.expr.refPath.length -1]}; + continue; + } + + 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; + + } + console.assert(false); + return ({}); + } + + +// 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: typeTypeId }); +// } +// +//// if ("string" in decl.value) { +//// return ({ type: { +//// kind: typeKinds.Pointer, +//// size: pointerSizeEnum.One, +//// child: }); +//// } +// +// if ("refPath" in decl.value) { +// 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 = (zigAnalysis.types[fn_decl_value.type]); +// console.assert(fn_type.kind === typeKinds.Fn); +// return fn_type.ret; +// } +// +// if ("void" in decl.value) { +// return ({ type: typeTypeId }); +// } +// +// if ("bool" in decl.value) { +// return ({ type: typeKinds.Bool }); +// } +// +// console.log("TODO: handle in `typeOfDecl` more cases: ", decl); +// console.assert(false); +// throw {}; +// } +// console.assert(false); +// return ({}); +// } + function render() { domStatus.classList.add("hidden"); domFnProto.classList.add("hidden"); domSectParams.classList.add("hidden"); domTldDocs.classList.add("hidden"); + domSectMainPkg.classList.add("hidden"); domSectPkgs.classList.add("hidden"); domSectTypes.classList.add("hidden"); + domSectTests.classList.add("hidden"); domSectNamespaces.classList.add("hidden"); domSectErrSets.classList.add("hidden"); domSectFns.classList.add("hidden"); domSectFields.classList.add("hidden"); domSectSearchResults.classList.add("hidden"); domSectSearchNoResults.classList.add("hidden"); - domSectSearchAllResultsLink.classList.add("hidden"); domSectInfo.classList.add("hidden"); domHdrName.classList.add("hidden"); domSectNav.classList.add("hidden"); @@ -144,15 +371,17 @@ renderInfo(); renderPkgList(); + domPrivDeclsBox.checked = curNav.showPrivDecls; + if (curNavSearch !== "") { return renderSearch(); } - var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; - var pkg = rootPkg; + let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + let pkg = rootPkg; curNav.pkgObjs = [pkg]; - for (var i = 0; i < curNav.pkgNames.length; i += 1) { - var childPkg = zigAnalysis.packages[pkg.table[curNav.pkgNames[i]]]; + for (let i = 0; i < curNav.pkgNames.length; i += 1) { + let childPkg = zigAnalysis.packages[pkg.table[curNav.pkgNames[i]]]; if (childPkg == null) { return render404(); } @@ -160,52 +389,68 @@ curNav.pkgObjs.push(pkg); } - var decl = zigAnalysis.types[pkg.main]; - curNav.declObjs = [decl]; - for (var i = 0; i < curNav.declNames.length; i += 1) { - var childDecl = findSubDecl(decl, curNav.declNames[i]); + + let currentType = zigAnalysis.types[pkg.main]; + curNav.declObjs = [currentType]; + for (let i = 0; i < curNav.declNames.length; i += 1) { + + + let childDecl = findSubDecl((currentType), curNav.declNames[i]); if (childDecl == null) { return render404(); } - var container = getDeclContainerType(childDecl); - if (container == null) { - if (i + 1 === curNav.declNames.length) { - curNav.declObjs.push(childDecl); - break; - } else { - return render404(); + + let childDeclValue = resolveValue((childDecl).value).expr; + if ("type" in childDeclValue) { + + const t = zigAnalysis.types[childDeclValue.type]; + if (t.kind != typeKinds.Fn) { + childDecl = t; } } - decl = container; - curNav.declObjs.push(decl); + + currentType = (childDecl); + curNav.declObjs.push(currentType); } renderNav(); - var lastDecl = curNav.declObjs[curNav.declObjs.length - 1]; - if (lastDecl.pubDecls != null) { - renderContainer(lastDecl); + let last = curNav.declObjs[curNav.declObjs.length - 1]; + let lastIsDecl = isDecl(last); + let lastIsType = isType(last); + let lastIsContainerType = isContainerType(last); + + if (lastIsContainerType) { + return renderContainer((last)); } - if (lastDecl.kind == null) { - return renderUnknownDecl(lastDecl); - } else if (lastDecl.kind === 'var') { - return renderVar(lastDecl); - } else if (lastDecl.kind === 'const' && lastDecl.type != null) { - var typeObj = zigAnalysis.types[lastDecl.type]; - if (typeObj.kind === typeKinds.Fn) { - return renderFn(lastDecl); - } else { - return renderValue(lastDecl); + + if (!lastIsDecl && !lastIsType) { + return renderUnknownDecl((last)); + } + + if (lastIsType) { + return renderType((last)); + } + + if (lastIsDecl && last.kind === 'var') { + return renderVar((last)); + } + + if (lastIsDecl && last.kind === 'const') { + let typeObj = zigAnalysis.types[resolveValue((last).value).expr.type]; + if (typeObj && typeObj.kind === typeKinds.Fn) { + return renderFn((last)); } - } else { - renderType(lastDecl); + + return renderValue((last)); } } + function renderUnknownDecl(decl) { domDeclNoRef.classList.remove("hidden"); - var docs = zigAnalysis.astNodes[decl.src].docs; + let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { domTldDocs.innerHTML = markdown(docs); } else { @@ -214,85 +459,130 @@ domTldDocs.classList.remove("hidden"); } + function typeIsErrSet(typeIndex) { - var typeObj = zigAnalysis.types[typeIndex]; + let typeObj = zigAnalysis.types[typeIndex]; return typeObj.kind === typeKinds.ErrorSet; } + function typeIsStructWithNoFields(typeIndex) { - var typeObj = zigAnalysis.types[typeIndex]; + let typeObj = zigAnalysis.types[typeIndex]; if (typeObj.kind !== typeKinds.Struct) return false; - return typeObj.fields == null || typeObj.fields.length === 0; + return (typeObj).fields.length == 0; } + function typeIsGenericFn(typeIndex) { - var typeObj = zigAnalysis.types[typeIndex]; + let typeObj = zigAnalysis.types[typeIndex]; if (typeObj.kind !== typeKinds.Fn) { return false; } - return typeObj.generic; + return (typeObj).generic_ret != null; } + function renderFn(fnDecl) { - domFnProtoCode.innerHTML = typeIndexName(fnDecl.type, true, true, 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]; + } - var docsSource = null; - var srcNode = zigAnalysis.astNodes[fnDecl.src]; + let value = resolveValue(fnDecl.value); + console.assert("type" in value.expr); + let typeObj = (zigAnalysis.types[value.expr.type]); + + domFnProtoCode.innerHTML = exprName(value.expr, { + wantHtml: true, + wantLink: true, + fnDecl, + }); + + let docsSource = null; + let srcNode = zigAnalysis.astNodes[fnDecl.src]; if (srcNode.docs != null) { docsSource = srcNode.docs; } - var typeObj = zigAnalysis.types[fnDecl.type]; renderFnParamDocs(fnDecl, typeObj); - var errSetTypeIndex = null; - if (typeObj.ret != null) { - var retType = zigAnalysis.types[typeObj.ret]; + let retExpr = resolveValue({expr:typeObj.ret}).expr; + if ("type" in retExpr) { + let retIndex = retExpr.type; + let errSetTypeIndex = (null); + let retType = zigAnalysis.types[retIndex]; if (retType.kind === typeKinds.ErrorSet) { - errSetTypeIndex = typeObj.ret; + errSetTypeIndex = retIndex; } else if (retType.kind === typeKinds.ErrorUnion) { - errSetTypeIndex = retType.err; + errSetTypeIndex = (retType).err.type; + } + if (errSetTypeIndex != null) { + let errSetType = (zigAnalysis.types[errSetTypeIndex]); + renderErrorSet(errSetType); } } - if (errSetTypeIndex != null) { - var errSetType = zigAnalysis.types[errSetTypeIndex]; - renderErrorSet(errSetType); - } - var fnObj = zigAnalysis.fns[fnDecl.value]; - var protoSrcIndex = fnObj.src; - if (typeIsGenericFn(fnDecl.type)) { - var instantiations = nodesToFnsMap[protoSrcIndex]; - var calls = nodesToCallsMap[protoSrcIndex]; - if (instantiations == null && calls == null) { - domFnNoExamples.classList.remove("hidden"); - } else if (calls != null) { - if (fnObj.combined === undefined) fnObj.combined = allCompTimeFnCallsResult(calls); - if (fnObj.combined != null) { - renderContainer(fnObj.combined, calls.map(function (call) { return zigAnalysis.calls[call].result.value })); - } - - var domListFnExamplesFragment = createDomListFragment(calls.length, '
  • '); - - for (var callI = 0; callI < calls.length; callI += 1) { - var liDom = domListFnExamplesFragment.children[callI]; - liDom.innerHTML = getCallHtml(fnDecl, calls[callI]); - } - - domListFnExamples.innerHTML = ""; - domListFnExamples.appendChild(domListFnExamplesFragment); - domFnExamples.classList.remove("hidden"); - } else if (instantiations != null) { - // TODO + let protoSrcIndex = fnDecl.src; + if (typeIsGenericFn(value.expr.type)) { + // does the generic_ret contain a container? + var resolvedGenericRet = resolveValue({expr: typeObj.generic_ret}); + + if ("call" in resolvedGenericRet.expr){ + let call = zigAnalysis.calls[resolvedGenericRet.expr.call]; + let resolvedFunc = resolveValue({expr: call.func}); + if (!("type" in resolvedFunc.expr)) return; + let callee = zigAnalysis.types[resolvedFunc.expr.type]; + if (!callee.generic_ret) return; + resolvedGenericRet = resolveValue({expr: callee.generic_ret}); } + + // TODO: see if unwrapping the `as` here is a good idea or not. + if ("as" in resolvedGenericRet.expr) { + resolvedGenericRet = { + expr: zigAnalysis.exprs[resolvedGenericRet.expr.as.exprArg] + }; + } + + if (!("type" in resolvedGenericRet.expr)) return; + const genericType = zigAnalysis.types[resolvedGenericRet.expr.type]; + if (isContainerType(genericType)) { + renderContainer(genericType) + } + + + + + + // old code + // let instantiations = nodesToFnsMap[protoSrcIndex]; + // let calls = nodesToCallsMap[protoSrcIndex]; + // if (instantiations == null && calls == null) { + // domFnNoExamples.classList.remove("hidden"); + // } else if (calls != null) { + // // if (fnObj.combined === undefined) fnObj.combined = allCompTimeFnCallsResult(calls); + // if (fnObj.combined != null) renderContainer(fnObj.combined); + + // resizeDomList(domListFnExamples, calls.length, '
  • '); + + // for (let callI = 0; callI < calls.length; callI += 1) { + // let liDom = domListFnExamples.children[callI]; + // liDom.innerHTML = getCallHtml(fnDecl, calls[callI]); + // } + + // domFnExamples.classList.remove("hidden"); + // } else if (instantiations != null) { + // // TODO + // } } else { domFnExamples.classList.add("hidden"); domFnNoExamples.classList.add("hidden"); } - var protoSrcNode = zigAnalysis.astNodes[protoSrcIndex]; + let protoSrcNode = zigAnalysis.astNodes[protoSrcIndex]; if (docsSource == null && protoSrcNode != null && protoSrcNode.docs != null) { docsSource = protoSrcNode.docs; } @@ -303,17 +593,17 @@ domFnProto.classList.remove("hidden"); } + function renderFnParamDocs(fnDecl, typeObj) { - var docCount = 0; + let docCount = 0; - var fnObj = zigAnalysis.fns[fnDecl.value]; - var fnNode = zigAnalysis.astNodes[fnObj.src]; - var fields = fnNode.fields; - var isVarArgs = fnNode.varArgs; + let fnNode = zigAnalysis.astNodes[fnDecl.src]; + let fields = (fnNode.fields); + let isVarArgs = fnNode.varArgs; - for (var i = 0; i < fields.length; i += 1) { - var field = fields[i]; - var fieldNode = zigAnalysis.astNodes[field]; + for (let i = 0; i < fields.length; i += 1) { + let field = fields[i]; + let fieldNode = zigAnalysis.astNodes[field]; if (fieldNode.docs != null) { docCount += 1; } @@ -322,56 +612,55 @@ return; } - var domListParamsFragment = createDomListFragment(docCount, '
    '); - var domIndex = 0; + resizeDomList(domListParams, docCount, '
    '); + let domIndex = 0; - for (var i = 0; i < fields.length; i += 1) { - var field = fields[i]; - var fieldNode = zigAnalysis.astNodes[field]; + for (let i = 0; i < fields.length; i += 1) { + let field = fields[i]; + let fieldNode = zigAnalysis.astNodes[field]; if (fieldNode.docs == null) { continue; } - var divDom = domListParamsFragment.children[domIndex]; + let divDom = domListParams.children[domIndex]; domIndex += 1; - var argTypeIndex = typeObj.args[i]; - var html = '
    ' + escapeHtml(fieldNode.name) + ": ";
    -            if (isVarArgs && i === typeObj.args.length - 1) {
    +
    +            let value = typeObj.params[i];
    +            let html = '
    ' + escapeHtml((fieldNode.name)) + ": ";
    +            if (isVarArgs && i === typeObj.params.length - 1) {
                     html += '...';
    -            } else if (argTypeIndex != null) {
    -                html += typeIndexName(argTypeIndex, true, true);
                 } else {
    -                html += 'var';
    +                let name = exprName(value, {wantHtml: false, wantLink: false});
    +                html += '' + name + '';
                 }
     
                 html += ',
    '; - var docs = fieldNode.docs; + let docs = fieldNode.docs; if (docs != null) { html += markdown(docs); } divDom.innerHTML = html; } - - domListParams.innerHTML = ""; - domListParams.appendChild(domListParamsFragment); domSectParams.classList.remove("hidden"); } function renderNav() { - var len = curNav.pkgNames.length + curNav.declNames.length; - var domListNavFragment = createDomListFragment(len, '
  • '); - var list = []; - var hrefPkgNames = []; - var hrefDeclNames = []; - for (var i = 0; i < curNav.pkgNames.length; i += 1) { + let len = curNav.pkgNames.length + curNav.declNames.length; + resizeDomList(domListNav, len, '
  • '); + let list = []; + let hrefPkgNames = []; + let hrefDeclNames = ([]); + for (let i = 0; i < curNav.pkgNames.length; i += 1) { hrefPkgNames.push(curNav.pkgNames[i]); + let name = curNav.pkgNames[i]; + if (name == "root") name = zigAnalysis.rootPkgName; list.push({ - name: curNav.pkgNames[i], + name: name, link: navLink(hrefPkgNames, hrefDeclNames), }); } - for (var i = 0; i < curNav.declNames.length; i += 1) { + for (let i = 0; i < curNav.declNames.length; i += 1) { hrefDeclNames.push(curNav.declNames[i]); list.push({ name: curNav.declNames[i], @@ -379,9 +668,9 @@ }); } - for (var i = 0; i < list.length; i += 1) { - var liDom = domListNavFragment.children[i]; - var aDom = liDom.children[0]; + for (let i = 0; i < list.length; i += 1) { + let liDom = domListNav.children[i]; + let aDom = liDom.children[0]; aDom.textContent = list[i].name; aDom.setAttribute('href', list[i].link); if (i + 1 == list.length) { @@ -391,14 +680,12 @@ } } - domListNav.innerHTML = ""; - domListNav.appendChild(domListNavFragment); domSectNav.classList.remove("hidden"); } function renderInfo() { domTdZigVer.textContent = zigAnalysis.params.zigVersion; - domTdTarget.textContent = zigAnalysis.params.builds[0].target; + //domTdTarget.textContent = zigAnalysis.params.builds[0].target; domSectInfo.classList.remove("hidden"); } @@ -409,26 +696,39 @@ } function renderPkgList() { - var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; - var list = []; - for (var key in rootPkg.table) { - if (key === "root" && rootIsStd) continue; - var pkgIndex = rootPkg.table[key]; + let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + let list = []; + for (let key in rootPkg.table) { + let pkgIndex = rootPkg.table[key]; if (zigAnalysis.packages[pkgIndex] == null) continue; + if (key == zigAnalysis.params.rootName) continue; list.push({ name: key, pkg: pkgIndex, }); } + + { + let aDom = domSectMainPkg.children[1].children[0].children[0]; + aDom.textContent = zigAnalysis.rootPkgName; + aDom.setAttribute('href', navLinkPkg(zigAnalysis.rootPkg)); + if (zigAnalysis.params.rootName === curNav.pkgNames[0]) { + aDom.classList.add("active"); + } else { + aDom.classList.remove("active"); + } + domSectMainPkg.classList.remove("hidden"); + } + list.sort(function(a, b) { return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase()); }); if (list.length !== 0) { - var domListPkgsFragment = createDomListFragment(list.length, '
  • '); - for (var i = 0; i < list.length; i += 1) { - var liDom = domListPkgsFragment.children[i]; - var aDom = liDom.children[0]; + resizeDomList(domListPkgs, list.length, '
  • '); + for (let i = 0; i < list.length; i += 1) { + let liDom = domListPkgs.children[i]; + let aDom = liDom.children[0]; aDom.textContent = list[i].name; aDom.setAttribute('href', navLinkPkg(list[i].pkg)); if (list[i].name === curNav.pkgNames[0]) { @@ -438,488 +738,1338 @@ } } - domListPkgs.innerHTML = ""; - domListPkgs.appendChild(domListPkgsFragment); domSectPkgs.classList.remove("hidden"); } } + + function navLink(pkgNames, declNames, callName) { + let base = '#'; + if (curNav.showPrivDecls) { + base += "*"; + } + if (pkgNames.length === 0 && declNames.length === 0) { - return '#'; + return base; } else if (declNames.length === 0 && callName == null) { - return '#' + pkgNames.join('.'); + return base + pkgNames.join('.'); } else if (callName == null) { - return '#' + pkgNames.join('.') + ';' + declNames.join('.'); + return base + pkgNames.join('.') + ';' + declNames.join('.'); } else { - return '#' + pkgNames.join('.') + ';' + declNames.join('.') + ';' + callName; + return base + pkgNames.join('.') + ';' + declNames.join('.') + ';' + callName; } } + function navLinkPkg(pkgIndex) { return navLink(canonPkgPaths[pkgIndex], []); } + function navLinkDecl(childName) { return navLink(curNav.pkgNames, curNav.declNames.concat([childName])); } - function navLinkCall(callObj) { - var declNamesCopy = curNav.declNames.concat([]); - var callName = declNamesCopy.pop(); + // + // function navLinkCall(callObj) { + // let declNamesCopy = curNav.declNames.concat([]); + // let callName = (declNamesCopy.pop()); - callName += '('; - for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { - if (arg_i !== 0) callName += ','; - var argObj = callObj.args[arg_i]; - callName += getValueText(argObj.type, argObj.value, false, false); + // callName += '('; + // for (let arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { + // if (arg_i !== 0) callName += ','; + // let argObj = callObj.args[arg_i]; + // callName += getValueText(argObj, argObj, false, false); + // } + // callName += ')'; + + // declNamesCopy.push(callName); + // return navLink(curNav.pkgNames, declNamesCopy); + // } + + + function resizeDomListDl(dlDom, desiredLen) { + // add the missing dom entries + for (let i = dlDom.childElementCount / 2; i < desiredLen; i += 1) { + dlDom.insertAdjacentHTML('beforeend', '
    '); + } + // remove extra dom entries + while (desiredLen < dlDom.childElementCount / 2) { + dlDom.removeChild(dlDom.lastChild); + dlDom.removeChild(dlDom.lastChild); } - callName += ')'; - - declNamesCopy.push(callName); - return navLink(curNav.pkgNames, declNamesCopy); } - function createDomListFragment(desiredLen, templateHtml) { - var domTemplate = document.createElement("template"); - domTemplate.innerHTML = templateHtml.repeat(desiredLen); - return domTemplate.content; - } - - function typeIndexName(typeIndex, wantHtml, wantLink, fnDecl, linkFnNameDecl, thisTypes) { - if(thisTypes && thisTypes.includes(typeIndex)){ - return token('@This', tokenKinds.Builtin, wantHtml) + '()'; + + function resizeDomList(listDom, desiredLen, templateHtml) { + // add the missing dom entries + for (let i = listDom.childElementCount; i < desiredLen; i += 1) { + listDom.insertAdjacentHTML('beforeend', templateHtml); } - var typeObj = zigAnalysis.types[typeIndex]; - var declNameOk = declCanRepresentTypeKind(typeObj.kind); - if (wantLink) { - var declIndex = getCanonTypeDecl(typeIndex); - var declPath = getCanonDeclPath(declIndex); - if (declPath == null) { - return typeName(typeObj, wantHtml, wantLink, fnDecl, linkFnNameDecl, thisTypes); + // remove extra dom entries + while (desiredLen < listDom.childElementCount) { + listDom.removeChild(listDom.lastChild); + } + } + + function walkResultTypeRef(wr) { + if (wr.typeRef) return wr.typeRef; + let resolved = resolveValue(wr); + if (wr === resolved) { + return {type: 0}; + } + return walkResultTypeRef(resolved); + } + + function exprName(expr, opts) { + switch (Object.keys(expr)[0]) { + default: throw "oh no"; + case "&": { + return "&" + exprName(zigAnalysis.exprs[expr["&"]]); + } + case "compileError": { + let compileError = expr.compileError; + return compileError; + } + case "enumLiteral": { + let literal = expr.enumLiteral; + return "." + literal; + } + case "void": { + return "void"; + } + case "slice":{ + let payloadHtml = ""; + const lhsExpr = zigAnalysis.exprs[expr.slice.lhs]; + const startExpr = zigAnalysis.exprs[expr.slice.start]; + let decl = exprName(lhsExpr); + let start = exprName(startExpr); + let end = ""; + let sentinel = ""; + if (expr.slice['end']) { + const endExpr = zigAnalysis.exprs[expr.slice.end]; + let end_ = exprName(endExpr); + end += end_; } - var name = (wantLink && declCanRepresentTypeKind(typeObj.kind)) ? - declPath.declNames[declPath.declNames.length - 1] : - typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl, thisTypes); - if (wantLink && wantHtml) { - return '' + name + ''; + if (expr.slice['sentinel']) { + const sentinelExpr = zigAnalysis.exprs[expr.slice.sentinel]; + let sentinel_ = exprName(sentinelExpr); + sentinel += " :" + sentinel_; + } + payloadHtml += decl + "["+ start + ".." + end + sentinel + "]"; + return payloadHtml; + } + case "sliceIndex": { + const sliceIndex = zigAnalysis.exprs[expr.sliceIndex]; + return exprName(sliceIndex, opts); + } + case "cmpxchg":{ + const typeIndex = zigAnalysis.exprs[expr.cmpxchg.type]; + const ptrIndex = zigAnalysis.exprs[expr.cmpxchg.ptr]; + const expectedValueIndex = zigAnalysis.exprs[expr.cmpxchg.expected_value]; + const newValueIndex = zigAnalysis.exprs[expr.cmpxchg.new_value]; + const successOrderIndex = zigAnalysis.exprs[expr.cmpxchg.success_order]; + const failureOrderIndex = zigAnalysis.exprs[expr.cmpxchg.failure_order]; + + const type = exprName(typeIndex, opts); + const ptr = exprName(ptrIndex, opts); + const expectedValue = exprName(expectedValueIndex, opts); + const newValue = exprName(newValueIndex, opts); + const successOrder = exprName(successOrderIndex, opts); + const failureOrder = exprName(failureOrderIndex, opts); + + let fnName = "@"; + + switch (expr.cmpxchg.name) { + case "cmpxchg_strong": { + fnName += "cmpxchgStrong" + break; + } + case "cmpxchg_weak": { + fnName += "cmpxchgWeak" + break; + } + default: { + console.log("There's only cmpxchg_strong and cmpxchg_weak"); + } + } + + return fnName + "(" + type + ", " + ptr + ", " + expectedValue + ", "+ newValue + ", "+"." +successOrder + ", "+ "." +failureOrder + ")"; + } + case "cmpxchgIndex": { + const cmpxchgIndex = zigAnalysis.exprs[expr.cmpxchgIndex]; + return exprName(cmpxchgIndex, opts); + } + case "switchOp":{ + let condExpr = zigAnalysis.exprs[expr.switchOp.cond_index]; + let ast = zigAnalysis.astNodes[expr.switchOp.ast]; + let file_name = expr.switchOp.file_name; + let outer_decl_index = expr.switchOp.outer_decl; + let outer_decl = zigAnalysis.types[outer_decl_index]; + let line = 0; + // console.log(expr.switchOp) + // console.log(outer_decl) + while (outer_decl_index !== 0 && outer_decl.line_number > 0) { + line += outer_decl.line_number; + outer_decl_index = outer_decl.outer_decl; + outer_decl = zigAnalysis.types[outer_decl_index]; + // console.log(outer_decl) + } + line += ast.line + 1; + let payloadHtml = ""; + let cond = exprName(condExpr, opts); + + payloadHtml += "
    " + "node_name: " + ast.name + "
    " + "file: " + file_name + "
    " + "line: " + line + "
    "; + payloadHtml += "switch(" + cond + ") {" + "" +"..." + "}"; + return payloadHtml; + } + case "switchIndex": { + const switchIndex = zigAnalysis.exprs[expr.switchIndex]; + return exprName(switchIndex, opts); + } + case "refPath" : { + const declRef = expr.refPath[0].declRef; + let name = zigAnalysis.decls[declRef].name; + for (let i = 1; i < expr.refPath.length; i++) { + let component = undefined; + if ("string" in expr.refPath[i]) { + component = expr.refPath[i].string; + } else { + component = exprName(expr.refPath[i]); + } + name += "." + component; + } + return name; + } + case "fieldRef" : { + const enumObj = exprName({"type":expr.fieldRef.type} ,opts); + const field = zigAnalysis.astNodes[enumObj.ast].fields[expr.fieldRef.index]; + const name = zigAnalysis.astNodes[field].name; + return name + } + case "enumToInt" : { + const enumToInt = zigAnalysis.exprs[expr.enumToInt]; + return "@enumToInt(" + exprName(enumToInt, opts) + ")"; + } + case "bitSizeOf" : { + const bitSizeOf = zigAnalysis.exprs[expr.bitSizeOf]; + return "@bitSizeOf(" + exprName(bitSizeOf, opts) + ")"; + } + case "sizeOf" : { + const sizeOf = zigAnalysis.exprs[expr.sizeOf]; + return "@sizeOf(" + exprName(sizeOf, opts) + ")"; + } + case "builtinIndex" : { + const builtinIndex = zigAnalysis.exprs[expr.builtinIndex]; + return exprName(builtinIndex, opts); + } + case "builtin": { + const param_expr = zigAnalysis.exprs[expr.builtin.param]; + let param = exprName(param_expr, opts); + + + let payloadHtml = "@"; + switch (expr.builtin.name) { + case "align_of": { + payloadHtml += "alignOf"; + break; + } + case "bool_to_int": { + payloadHtml += "boolToInt"; + break; + } + case "embed_file": { + payloadHtml += "embedFile"; + break; + } + case "error_name": { + payloadHtml += "errorName"; + break; + } + case "panic": { + payloadHtml += "panic"; + break; + } + case "set_cold": { + payloadHtml += "setCold"; + break; + } + case "set_runtime_safety": { + payloadHtml += "setRuntimeSafety"; + break; + } + case "sqrt": { + payloadHtml += "sqrt"; + break; + } + case "sin": { + payloadHtml += "sin"; + break; + } + case "cos": { + payloadHtml += "cos"; + break; + } + case "tan": { + payloadHtml += "tan"; + break; + } + case "exp": { + payloadHtml += "exp"; + break; + } + case "exp2": { + payloadHtml += "exp2"; + break; + } + case "log": { + payloadHtml += "log"; + break; + } + case "log2": { + payloadHtml += "log2"; + break; + } + case "log10": { + payloadHtml += "log10"; + break; + } + case "fabs": { + payloadHtml += "fabs"; + break; + } + case "floor": { + payloadHtml += "floor"; + break; + } + case "ceil": { + payloadHtml += "ceil"; + break; + } + case "trunc": { + payloadHtml += "trunc"; + break; + } + case "round": { + payloadHtml += "round"; + break; + } + case "tag_name": { + payloadHtml += "tagName"; + break; + } + case "reify": { + payloadHtml += "Type"; + break; + } + case "type_name": { + payloadHtml += "typeName"; + break; + } + case "frame_type": { + payloadHtml += "Frame"; + break; + } + case "frame_size": { + payloadHtml += "frameSize"; + break; + } + case "ptr_to_int": { + payloadHtml += "ptrToInt"; + break; + } + case "error_to_int": { + payloadHtml += "errorToInt"; + break; + } + case "int_to_error": { + payloadHtml += "intToError"; + break; + } + case "maximum": { + payloadHtml += "maximum"; + break; + } + case "minimum": { + payloadHtml += "minimum"; + break; + } + case "bit_not": { + return "~" + param; + } + case "clz": { + return "@clz(T" + ", " + param + ")"; + } + case "ctz": { + return "@ctz(T" + ", " + param + ")"; + } + case "pop_count": { + return "@popCount(T" + ", " + param + ")"; + } + case "byte_swap": { + return "@byteSwap(T" + ", " + param + ")"; + } + case "bit_reverse": { + return "@bitReverse(T" + ", " + param + ")"; + } + default: console.log("builtin function not handled yet or doesn't exist!"); + }; + return payloadHtml + "(" + param + ")"; + + } + case "builtinBinIndex" : { + const builtinBinIndex = zigAnalysis.exprs[expr.builtinBinIndex]; + return exprName(builtinBinIndex, opts); + } + case "builtinBin": { + const lhsOp = zigAnalysis.exprs[expr.builtinBin.lhs]; + const rhsOp = zigAnalysis.exprs[expr.builtinBin.rhs]; + let lhs = exprName(lhsOp, opts); + let rhs = exprName(rhsOp, opts); + + let payloadHtml = "@"; + switch (expr.builtinBin.name) { + case "float_to_int": { + payloadHtml += "floatToInt"; + break; + } + case "int_to_float": { + payloadHtml += "intToFloat"; + break; + } + case "int_to_ptr": { + payloadHtml += "intToPtr"; + break; + } + case "int_to_enum": { + payloadHtml += "intToEnum"; + break; + } + case "float_cast": { + payloadHtml += "floatCast"; + break; + } + case "int_cast": { + payloadHtml += "intCast"; + break; + } + case "ptr_cast": { + payloadHtml += "ptrCast"; + break; + } + case "truncate": { + payloadHtml += "truncate"; + break; + } + case "align_cast": { + payloadHtml += "alignCast"; + break; + } + case "has_decl": { + payloadHtml += "hasDecl"; + break; + } + case "has_field": { + payloadHtml += "hasField"; + break; + } + case "bit_reverse": { + payloadHtml += "bitReverse"; + break; + } + case "div_exact": { + payloadHtml += "divExact"; + break; + } + case "div_floor": { + payloadHtml += "divFloor"; + break; + } + case "div_trunc": { + payloadHtml += "divTrunc"; + break; + } + case "mod": { + payloadHtml += "mod"; + break; + } + case "rem": { + payloadHtml += "rem"; + break; + } + case "mod_rem": { + payloadHtml += "rem"; + break; + } + case "shl_exact": { + payloadHtml += "shlExact"; + break; + } + case "shr_exact": { + payloadHtml += "shrExact"; + break; + } + case "bitcast" : { + payloadHtml += "bitCast"; + break; + } + case "align_cast" : { + payloadHtml += "alignCast"; + break; + } + case "vector_type" : { + payloadHtml += "Vector"; + break; + } + case "reduce": { + payloadHtml += "reduce"; + break; + } + case "splat": { + payloadHtml += "splat"; + break; + } + case "offset_of": { + payloadHtml += "offsetOf"; + break; + } + case "bit_offset_of": { + payloadHtml += "bitOffsetOf"; + break; + } + default: console.log("builtin function not handled yet or doesn't exist!"); + }; + return payloadHtml + "(" + lhs + ", " + rhs + ")"; + + } + case "binOpIndex" : { + const binOpIndex = zigAnalysis.exprs[expr.binOpIndex]; + return exprName(binOpIndex, opts); + } + case "binOp": { + const lhsOp = zigAnalysis.exprs[expr.binOp.lhs]; + const rhsOp = zigAnalysis.exprs[expr.binOp.rhs]; + let lhs = exprName(lhsOp, opts); + let rhs = exprName(rhsOp, opts); + + let print_lhs = ""; + let print_rhs = ""; + + if (lhsOp['binOpIndex']) { + print_lhs = "(" + lhs + ")"; } else { - return name; + print_lhs = lhs; + } + if (rhsOp['binOpIndex']) { + print_rhs = "(" + rhs + ")"; + } else { + print_rhs = rhs; } - } else { - return typeName(typeObj, wantHtml, false, fnDecl, linkFnNameDecl, thisTypes); - } - } - function shouldSkipParamName(typeIndex, paramName) { - var typeObj = zigAnalysis.types[typeIndex]; - if (typeObj.kind === typeKinds.Pointer && getPtrSize(typeObj) === pointerSizeEnum.One) { - typeIndex = typeObj.elem; - } - return typeIndexName(typeIndex, false, true).toLowerCase() === paramName; - } + let operator = ""; - function getPtrSize(typeObj) { - return (typeObj.len == null) ? pointerSizeEnum.One : typeObj.len; - } + switch (expr.binOp.name) { + case "add": { + operator += "+"; + break; + } + case "addwrap": { + operator += "+%"; + break; + } + case "add_sat": { + operator += "+|"; + break; + } + case "sub": { + operator += "-"; + break; + } + case "subwrap": { + operator += "-%"; + break; + } + case "sub_sat": { + operator += "-|"; + break; + } + case "mul": { + operator += "*"; + break; + } + case "mulwrap": { + operator += "*%"; + break; + } + case "mul_sat": { + operator += "*|"; + break; + } + case "div": { + operator += "/"; + break; + } + case "shl": { + operator += "<<"; + break; + } + case "shl_sat": { + operator += "<<|"; + break; + } + case "shr": { + operator += ">>"; + break; + } + case "bit_or" : { + operator += "|"; + break; + } + case "bit_and" : { + operator += "&"; + break; + } + case "array_cat" : { + operator += "++"; + break; + } + case "array_mul" : { + operator += "**"; + break; + } + default: console.log("operator not handled yet or doesn't exist!"); + }; - function getCallHtml(fnDecl, callIndex) { - var callObj = zigAnalysis.calls[callIndex]; + return print_lhs + " " + operator + " " + print_rhs; - // TODO make these links work - //var html = '' + escapeHtml(fnDecl.name) + '('; - var html = escapeHtml(fnDecl.name) + '('; - for (var arg_i = 0; arg_i < callObj.args.length; arg_i += 1) { - if (arg_i !== 0) html += ', '; - var argObj = callObj.args[arg_i]; - html += getValueText(argObj.type, argObj.value, true, true); - } - html += ')'; - return html; - } + } + case "errorSets": { + const errUnionObj = zigAnalysis.types[expr.errorSets]; + let lhs = exprName(errUnionObj.lhs, opts); + let rhs = exprName(errUnionObj.rhs, opts); + return lhs + " || " + rhs; - function getValueText(typeIndex, value, wantHtml, wantLink) { - var typeObj = zigAnalysis.types[typeIndex]; - switch (typeObj.kind) { - case typeKinds.Type: - return typeIndexName(value, wantHtml, wantLink); - case typeKinds.Fn: - var fnObj = zigAnalysis.fns[value]; - var declPath = fnObj.decl && getCanonDeclPath(fnObj.decl); - var fnName = declPath ? declPath.declNames.join('.') : '(unknown)'; + } + case "errorUnion": { + const errUnionObj = zigAnalysis.types[expr.errorUnion]; + let lhs = exprName(errUnionObj.lhs, opts); + let rhs = exprName(errUnionObj.rhs, opts); + return lhs + "!" + rhs; - if (!wantHtml) { - return fnName; + } + case "struct": { + const struct_name = zigAnalysis.decls[expr.struct[0].val.typeRef.refPath[0].declRef].name; + let struct_body = ""; + struct_body += struct_name + "{ "; + for (let i = 0; i < expr.struct.length; i++) { + const val = expr.struct[i].name + const exprArg = zigAnalysis.exprs[expr.struct[i].val.expr.as.exprArg]; + let value_field = exprArg[Object.keys(exprArg)[0]]; + if (value_field instanceof Object) { + value_field = zigAnalysis.decls[value_field[0].val.typeRef.refPath[0].declRef].name; + }; + struct_body += "." + val + " = " + value_field; + if (i !== expr.struct.length - 1) { + struct_body += ", "; + } else { + struct_body += " "; + } + } + struct_body += "}"; + return struct_body; + } + case "typeOf_peer": { + let payloadHtml = "@TypeOf(" + for (let i = 0; i < expr.typeOf_peer.length; i++) { + let elem = zigAnalysis.exprs[expr.typeOf_peer[i]]; + payloadHtml += exprName(elem, {wantHtml: true, wantLink:true}); + if (i !== expr.typeOf_peer.length - 1) { + payloadHtml += ", "; + } + } + payloadHtml += ")"; + return payloadHtml; + + } + case "alignOf": { + const alignRefArg = zigAnalysis.exprs[expr.alignOf]; + let payloadHtml = "@alignOf(" + exprName(alignRefArg, {wantHtml: true, wantLink:true}) + ")"; + return payloadHtml; + } + case "typeOf": { + const typeRefArg = zigAnalysis.exprs[expr.typeOf]; + let payloadHtml = "@TypeOf(" + exprName(typeRefArg, {wantHtml: true, wantLink:true}) + ")"; + return payloadHtml; + } + case "null": { + return "null"; + } + 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, opts); + } + return payloadHtml + "}"; + } + case "comptimeExpr": { + return zigAnalysis.comptimeExprs[expr.comptimeExpr].code; + } + 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, opts); + break; } + } + payloadHtml += "("; - var str = ''; - if (wantLink && declPath != null) { - str += ''; - str += escapeHtml(fnName); - str += ''; - } else { - str += escapeHtml(fnName); - } - str += ''; - return str; - case typeKinds.Int: - return token(value, tokenKinds.Number, wantHtml); - case typeKinds.Optional: - if(value === 'null'){ - return token(value, tokenKinds.Null, wantHtml); - } else { - console.trace("TODO non-null optional value printing"); - return "TODO"; - } - case typeKinds.Bool: - return token(value, tokenKinds.Null, wantHtml); - default: - console.trace("TODO implement getValueText for this type:", zigAnalysis.typeKinds[typeObj.kind]); - return "TODO"; - } - } + for (let i = 0; i < call.args.length; i++) { + if (i != 0) payloadHtml += ", "; + payloadHtml += exprName(call.args[i], opts); + } - function typeName(typeObj, wantHtml, wantSubLink, fnDecl, linkFnNameDecl, thisTypes) { - switch (typeObj.kind) { - case typeKinds.Array: - var name = "["; - name += token(typeObj.len, tokenKinds.Number, wantHtml); - name += "]"; - name += typeIndexName(typeObj.elem, wantHtml, wantSubLink, null, null, thisTypes); - return name; - case typeKinds.Vector: - var name = "Vector("; - name += token(typeObj.len, tokenKinds.Number, wantHtml); - name += ", "; - name += typeIndexName(typeObj.elem, wantHtml, wantSubLink, null, null, thisTypes); - name += ")"; - return name; - case typeKinds.Optional: - return "?" + typeIndexName(typeObj.child, wantHtml, wantSubLink, fnDecl, linkFnNameDecl, thisTypes); - case typeKinds.Pointer: - var name = ""; - switch (typeObj.len) { - case pointerSizeEnum.One: - default: - name += "*"; - break; - case pointerSizeEnum.Many: - name += "[*]"; - break; - case pointerSizeEnum.Slice: - name += "[]"; - break; - case pointerSizeEnum.C: - name += "[*c]"; - break; - } - if (typeObj['const']) { - name += token('const', tokenKinds.Keyword, wantHtml) + ' '; - } - if (typeObj['volatile']) { - name += token('volatile', tokenKinds.Keyword, wantHtml) + ' '; - } - if (typeObj.align != null) { - name += token('align', tokenKinds.Keyword, wantHtml) + '('; - name += token(typeObj.align, tokenKinds.Number, wantHtml); + payloadHtml += ")"; + return payloadHtml; + } + case "as": { + // @Check : this should be done in backend because there are legit @as() calls + // const typeRefArg = zigAnalysis.exprs[expr.as.typeRefArg]; + const exprArg = zigAnalysis.exprs[expr.as.exprArg]; + // return "@as(" + exprName(typeRefArg, opts) + + // ", " + exprName(exprArg, opts) + ")"; + return 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; + } + case "float": { + return "" + expr.float.toFixed(2); + } + case "float128": { + return "" + expr.float128.toFixed(2); + } + case "undefined": { + return "undefined"; + } + case "string": { + return "\"" + escapeHtml(expr.string) + "\""; + } - if (typeObj.hostIntBytes != null) { - name += ":"; - name += token(typeObj.bitOffsetInHost, tokenKinds.Number, wantHtml); - name += ":"; - name += token(typeObj.hostIntBytes, tokenKinds.Number, wantHtml); - } - name += ") "; - } - name += typeIndexName(typeObj.elem, wantHtml, wantSubLink, null, null, thisTypes); - return name; - case typeKinds.Float: - return token('f' + typeObj.bits, tokenKinds.Type, wantHtml); - case typeKinds.Int: - var signed = (typeObj.i != null) ? 'i' : 'u'; - var bits = typeObj[signed]; - return token(signed + bits, tokenKinds.Type, wantHtml); - case typeKinds.ComptimeInt: - return token('comptime_int', tokenKinds.Type, wantHtml); - case typeKinds.ComptimeFloat: - return token('comptime_float', tokenKinds.Type, wantHtml); - case typeKinds.Type: - return token('type', tokenKinds.Type, wantHtml); - case typeKinds.Bool: - return token('bool', tokenKinds.Type, wantHtml); - case typeKinds.Void: - return token('void', tokenKinds.Type, wantHtml); - case typeKinds.EnumLiteral: - return token('(enum literal)', tokenKinds.Type, wantHtml); - case typeKinds.NoReturn: - return token('noreturn', tokenKinds.Type, wantHtml); - case typeKinds.ErrorSet: - if (typeObj.errors == null) { - return token('anyerror', tokenKinds.Type, wantHtml); - } else { - if (wantHtml) { - return escapeHtml(typeObj.name); + case "anytype": { + return "anytype"; + } + + case "this":{ + return "@This()"; + } + + case "type": { + let name = ""; + + let typeObj = expr.type; + if (typeof typeObj === 'number') typeObj = zigAnalysis.types[typeObj]; + switch (typeObj.kind) { + default: throw "TODO"; + case typeKinds.Struct: + { + let structObj = (typeObj); + return structObj; + } + case typeKinds.Enum: + { + let enumObj = (typeObj); + return enumObj; + } + case typeKinds.Opaque: + { + let opaqueObj = (typeObj); + + return opaqueObj.name; + } + case typeKinds.ComptimeExpr: + { + return "anyopaque"; + } + case typeKinds.Array: + { + let arrayObj = typeObj; + let name = "["; + let lenName = exprName(arrayObj.len, opts); + let sentinel = arrayObj.sentinel ? ":"+exprName(arrayObj.sentinel, opts) : ""; + // let is_mutable = arrayObj.is_multable ? "const " : ""; + + if (opts.wantHtml) { + name += + '' + lenName + sentinel + ""; } else { - return typeObj.name; + name += lenName + sentinel; } - } - case typeKinds.ErrorUnion: - var errSetTypeObj = zigAnalysis.types[typeObj.err]; - var payloadHtml = typeIndexName(typeObj.payload, wantHtml, wantSubLink, null, null, thisTypes); - if (fnDecl != null && errSetTypeObj.fn === fnDecl.value) { - // function index parameter supplied and this is the inferred error set of it - return "!" + payloadHtml; - } else { - return typeIndexName(typeObj.err, wantHtml, wantSubLink, null, null, thisTypes) + "!" + payloadHtml; - } - case typeKinds.Fn: - var payloadHtml = ""; - if (wantHtml) { - payloadHtml += 'fn'; - if (fnDecl != null) { - payloadHtml += ' '; - if (linkFnNameDecl != null) { - payloadHtml += '' + - escapeHtml(fnDecl.name) + ''; - } else { - payloadHtml += escapeHtml(fnDecl.name); - } - payloadHtml += ''; + name += "]"; + // name += is_mutable; + name += exprName(arrayObj.child, opts); + return name; + } + case typeKinds.Optional: + return "?" + exprName((typeObj).child, opts); + case typeKinds.Pointer: + { + let ptrObj = (typeObj); + let sentinel = ptrObj.sentinel ? ":"+exprName(ptrObj.sentinel, opts) : ""; + let is_mutable = !ptrObj.is_mutable ? "const " : ""; + let name = ""; + switch (ptrObj.size) { + default: + console.log("TODO: implement unhandled pointer size case"); + case pointerSizeEnum.One: + name += "*"; + name += is_mutable; + break; + case pointerSizeEnum.Many: + name += "[*"; + name += sentinel; + name += "]"; + name += is_mutable; + break; + case pointerSizeEnum.Slice: + if (ptrObj.is_ref) { + name += "*"; + } + name += "["; + name += sentinel; + name += "]"; + name += is_mutable; + break; + case pointerSizeEnum.C: + name += "[*c"; + name += sentinel; + name += "]"; + name += is_mutable; + break; + } + // @check: after the major changes in arrays the consts are came from switch above + // if (!ptrObj.is_mutable) { + // if (opts.wantHtml) { + // name += 'const '; + // } else { + // name += "const "; + // } + // } + if (ptrObj.is_allowzero) { + name += "allowzero "; + } + if (ptrObj.is_volatile) { + name += "volatile "; + } + if (ptrObj.has_addrspace) { + name += "addrspace("; + name += "." + ""; + name += ") "; + } + if (ptrObj.has_align) { + let align = exprName(ptrObj.align, opts); + if (opts.wantHtml) { + name += 'align('; + } else { + name += "align("; + } + if (opts.wantHtml) { + name += '' + align + ''; + } else { + name += align; + } + if (ptrObj.hostIntBytes != null) { + name += ":"; + if (opts.wantHtml) { + name += '' + ptrObj.bitOffsetInHost + ''; + } else { + name += ptrObj.bitOffsetInHost; + } + name += ":"; + if (opts.wantHtml) { + name += '' + ptrObj.hostIntBytes + ''; + } else { + name += ptrObj.hostIntBytes; + } + } + name += ") "; + } + //name += typeValueName(ptrObj.child, wantHtml, wantSubLink, null); + name += exprName(ptrObj.child, opts); + return name; + } + case typeKinds.Float: + { + let floatObj = (typeObj); + + if (opts.wantHtml) { + return '' + floatObj.name + ''; + } else { + return floatObj.name; + } + } + case typeKinds.Int: + { + let intObj = (typeObj); + let name = intObj.name; + if (opts.wantHtml) { + return '' + name + ''; + } else { + return name; + } + } + case typeKinds.ComptimeInt: + if (opts.wantHtml) { + return 'comptime_int'; + } else { + return "comptime_int"; + } + case typeKinds.ComptimeFloat: + if (opts.wantHtml) { + return 'comptime_float'; + } else { + return "comptime_float"; + } + case typeKinds.Type: + if (opts.wantHtml) { + return 'type'; + } else { + return "type"; + } + case typeKinds.Bool: + if (opts.wantHtml) { + return 'bool'; + } else { + return "bool"; + } + case typeKinds.Void: + if (opts.wantHtml) { + return 'void'; + } else { + return "void"; + } + case typeKinds.EnumLiteral: + if (opts.wantHtml) { + return '(enum literal)'; + } else { + return "(enum literal)"; + } + case typeKinds.NoReturn: + if (opts.wantHtml) { + return 'noreturn'; + } else { + return "noreturn"; + } + case typeKinds.ErrorSet: + { + let errSetObj = (typeObj); + if (errSetObj.fields == null) { + return 'anyerror'; + } else { + // throw "TODO"; + let html = "error{" + errSetObj.fields[0].name + "}"; + return html; + } + } + + case typeKinds.ErrorUnion: + { + let errUnionObj = (typeObj); + let lhs = exprName(errUnionObj.lhs, opts); + let rhs = exprName(errUnionObj.rhs, opts); + return lhs + "!" + rhs; + } + case typeKinds.Fn: + { + let fnObj = (typeObj); + let payloadHtml = ""; + if (opts.wantHtml) { + if (fnObj.is_extern) { + payloadHtml += "pub extern "; + } + if (fnObj.has_lib_name) { + payloadHtml += "\"" + fnObj.lib_name +"\" "; + } + payloadHtml += 'fn'; + if (opts.fnDecl) { + payloadHtml += ' '; + if (opts.linkFnNameDecl) { + payloadHtml += '' + + escapeHtml(opts.fnDecl.name) + ''; + } else { + payloadHtml += escapeHtml(opts.fnDecl.name); + } + payloadHtml += ''; + } + } else { + payloadHtml += 'fn '; + } + payloadHtml += '('; + if (fnObj.params) { + let fields = null; + let isVarArgs = false; + let fnNode = zigAnalysis.astNodes[fnObj.src]; + fields = fnNode.fields; + isVarArgs = fnNode.varArgs; + + for (let i = 0; i < fnObj.params.length; i += 1) { + if (i != 0) { + payloadHtml += ', '; + } + + payloadHtml += "
        
    " + let value = fnObj.params[i]; + let paramValue = resolveValue({expr: value}); + + if (fields != null) { + let paramNode = zigAnalysis.astNodes[fields[i]]; + + if (paramNode.varArgs) { + payloadHtml += '...'; + continue; + } + + if (paramNode.noalias) { + if (opts.wantHtml) { + payloadHtml += 'noalias '; + } else { + payloadHtml += 'noalias '; + } + } + + if (paramNode.comptime) { + if (opts.wantHtml) { + payloadHtml += 'comptime '; + } else { + payloadHtml += 'comptime '; + } + } + + let paramName = paramNode.name; + if (paramName != null) { + // skip if it matches the type name + if (!shouldSkipParamName(paramValue, paramName)) { + payloadHtml += paramName + ': '; + } + } + } + + if (isVarArgs && i === fnObj.params.length - 1) { + payloadHtml += '...'; + } + else if ("alignOf" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + + } + else if ("typeOf" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + + } + else if ("typeOf_peer" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + + } + else if ("declRef" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + + } + else if ("call" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + } + else if ("refPath" in value) { + if (opts.wantHtml) { + payloadHtml += ''; + payloadHtml += + '' + + exprName(value, opts) + ''; + payloadHtml += ''; + } else { + payloadHtml += exprName(value, opts); + } + } else if ("type" in value) { + let name = exprName(value, { + wantHtml: false, + wantLink: false, + fnDecl: opts.fnDecl, + linkFnNameDecl: opts.linkFnNameDecl, + }); + payloadHtml += '' + name + ''; + } else if ("binOpIndex" in value) { + payloadHtml += exprName(value, opts); + }else if ("comptimeExpr" in value) { + let comptimeExpr = zigAnalysis.comptimeExprs[value.comptimeExpr].code; + if (opts.wantHtml) { + payloadHtml += '' + comptimeExpr + ''; + } else { + payloadHtml += comptimeExpr; + } + } else if (opts.wantHtml) { + payloadHtml += 'anytype'; + } else { + payloadHtml += 'anytype'; + } + } + } + + payloadHtml += ",
    " + payloadHtml += ') '; + + if (fnObj.has_align) { + let align = zigAnalysis.exprs[fnObj.align] + payloadHtml += "align(" + exprName(align, opts) + ") "; } - } else { - payloadHtml += 'fn' - } - payloadHtml += '('; - if (typeObj.args != null) { - var fields = null; - var isVarArgs = false; - if (fnDecl != null) { - var fnObj = zigAnalysis.fns[fnDecl.value]; - var fnNode = zigAnalysis.astNodes[fnObj.src]; - fields = fnNode.fields; - isVarArgs = fnNode.varArgs; + if (fnObj.has_cc) { + let cc = zigAnalysis.exprs[fnObj.cc] + if (cc) { + payloadHtml += "callconv(." + cc.enumLiteral + ") "; + } } - for (var i = 0; i < typeObj.args.length; i += 1) { - if (i != 0) { - payloadHtml += ', '; - } + if (fnObj.is_inferred_error) { + payloadHtml += "!"; + } + if (fnObj.ret != null) { + payloadHtml += exprName(fnObj.ret, opts); + } else if (opts.wantHtml) { + payloadHtml += 'anytype'; + } else { + payloadHtml += 'anytype'; + } + return payloadHtml; + } + // if (wantHtml) { + // return escapeHtml(typeObj.name); + // } else { + // return typeObj.name; + // } + } + } - var argTypeIndex = typeObj.args[i]; - - if (fields != null) { - var paramNode = zigAnalysis.astNodes[fields[i]]; - - if (paramNode.varArgs) { - payloadHtml += '...'; - continue; - } - - if (paramNode.noalias) { - payloadHtml += token('noalias', tokenKinds.Keyword, wantHtml) + ' '; - } - - if (paramNode.comptime) { - payloadHtml += token('comptime', tokenKinds.Keyword, wantHtml) + ' '; - } - - var paramName = paramNode.name; - if (paramName != null) { - // skip if it matches the type name - if (argTypeIndex == null || !shouldSkipParamName(argTypeIndex, paramName)) { - payloadHtml += paramName + ': '; - } - } - } - - if (isVarArgs && i === typeObj.args.length - 1) { - payloadHtml += '...'; - } else if (argTypeIndex != null) { - payloadHtml += typeIndexName(argTypeIndex, wantHtml, wantSubLink, null, null, thisTypes); - } else { - payloadHtml += token('anytype', tokenKinds.Keyword, wantHtml); - } - } - } - - payloadHtml += ') '; - if (typeObj.ret != null) { - payloadHtml += typeIndexName(typeObj.ret, wantHtml, wantSubLink, fnDecl, null, thisTypes); - } else { - payloadHtml += token('anytype', tokenKinds.Keyword, wantHtml); - } - return payloadHtml; - case typeKinds.Frame: - var fnObj = zigAnalysis.fns[typeObj.fn]; - return '@Frame(' + getValueText(fnObj.type, typeObj.fn, wantHtml, wantSubLink) + ')'; - case typeKinds.AnyFrame: - var name = token('anyframe', tokenKinds.Keyword, wantHtml); - if (typeObj.result) { - name += "->"; - name += typeIndexName(typeObj.result, wantHtml, wantSubLink, null, null, thisTypes); - } - return name; - default: - if (wantHtml) { - return escapeHtml(typeObj.name); - } else { - return typeObj.name; - } } } + + + function shouldSkipParamName(typeRef, paramName) { + let resolvedTypeRef = resolveValue({expr: typeRef}); + if ("type" in resolvedTypeRef) { + let typeObj = zigAnalysis.types[resolvedTypeRef.type]; + if (typeObj.kind === typeKinds.Pointer){ + let ptrObj = (typeObj); + if (getPtrSize(ptrObj) === pointerSizeEnum.One) { + const value = resolveValue(ptrObj.child); + return typeValueName(value, false, true).toLowerCase() === paramName; + } + } + } + return false; + } + + + function getPtrSize(typeObj) { + return (typeObj.size == null) ? pointerSizeEnum.One : typeObj.size; + } + + function renderType(typeObj) { - var name; + 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] + ")"; domHdrName.classList.remove("hidden"); } if (typeObj.kind == typeKinds.ErrorSet) { - renderErrorSet(typeObj); + renderErrorSet((typeObj)); } } + function renderErrorSet(errSetType) { - if (errSetType.errors == null) { + if (errSetType.fields == null) { domFnErrorsAnyError.classList.remove("hidden"); } else { - var errorList = []; - for (var i = 0; i < errSetType.errors.length; i += 1) { - var errObj = zigAnalysis.errors[errSetType.errors[i]]; - var srcObj = zigAnalysis.astNodes[errObj.src]; - errorList.push({ - err: errObj, - docs: srcObj.docs, - }); + let errorList = []; + for (let i = 0; i < errSetType.fields.length; i += 1) { + let errObj = errSetType.fields[i]; + //let srcObj = zigAnalysis.astNodes[errObj.src]; + errorList.push(errObj); } errorList.sort(function(a, b) { - return operatorCompare(a.err.name.toLowerCase(), b.err.name.toLowerCase()); + return operatorCompare(a.name.toLowerCase(), b.name.toLowerCase()); }); - var domListFnErrorsFragment = createDomListFragment(errorList.length, "
    "); - for (var i = 0; i < errorList.length; i += 1) { - var nameTdDom = domListFnErrorsFragment.children[i * 2 + 0]; - var descTdDom = domListFnErrorsFragment.children[i * 2 + 1]; - nameTdDom.textContent = errorList[i].err.name; - var docs = errorList[i].docs; + resizeDomListDl(domListFnErrors, errorList.length); + for (let i = 0; i < errorList.length; i += 1) { + let nameTdDom = domListFnErrors.children[i * 2 + 0]; + let descTdDom = domListFnErrors.children[i * 2 + 1]; + nameTdDom.textContent = errorList[i].name; + let docs = errorList[i].docs; if (docs != null) { descTdDom.innerHTML = markdown(docs); } else { descTdDom.textContent = ""; } } - domListFnErrors.innerHTML = ""; - domListFnErrors.appendChild(domListFnErrorsFragment); domTableFnErrors.classList.remove("hidden"); } domSectFnErrors.classList.remove("hidden"); } - function allCompTimeFnCallsHaveTypeResult(typeIndex, value) { - var srcIndex = zigAnalysis.fns[value].src; - var calls = nodesToCallsMap[srcIndex]; - if (calls == null) return false; - for (var i = 0; i < calls.length; i += 1) { - var call = zigAnalysis.calls[calls[i]]; - if (call.result.type !== typeTypeId) return false; - } - return true; - } +// function allCompTimeFnCallsHaveTypeResult(typeIndex, value) { +// let srcIndex = zigAnalysis.fns[value].src; +// let calls = nodesToCallsMap[srcIndex]; +// if (calls == null) return false; +// for (let i = 0; i < calls.length; i += 1) { +// let call = zigAnalysis.calls[calls[i]]; +// if (call.result.type !== typeTypeId) return false; +// } +// return true; +// } +// +// function allCompTimeFnCallsResult(calls) { +// let firstTypeObj = null; +// let containerObj = { +// privDecls: [], +// }; +// for (let callI = 0; callI < calls.length; callI += 1) { +// let call = zigAnalysis.calls[calls[callI]]; +// if (call.result.type !== typeTypeId) return null; +// let typeObj = zigAnalysis.types[call.result.value]; +// if (!typeKindIsContainer(typeObj.kind)) return null; +// if (firstTypeObj == null) { +// firstTypeObj = typeObj; +// containerObj.src = typeObj.src; +// } else if (firstTypeObj.src !== typeObj.src) { +// return null; +// } +// +// if (containerObj.fields == null) { +// containerObj.fields = (typeObj.fields || []).concat([]); +// } else for (let fieldI = 0; fieldI < typeObj.fields.length; fieldI += 1) { +// let prev = containerObj.fields[fieldI]; +// let next = typeObj.fields[fieldI]; +// if (prev === next) continue; +// if (typeof(prev) === 'object') { +// if (prev[next] == null) prev[next] = typeObj; +// } else { +// containerObj.fields[fieldI] = {}; +// containerObj.fields[fieldI][prev] = firstTypeObj; +// containerObj.fields[fieldI][next] = typeObj; +// } +// } +// +// if (containerObj.pubDecls == null) { +// containerObj.pubDecls = (typeObj.pubDecls || []).concat([]); +// } else for (let declI = 0; declI < typeObj.pubDecls.length; declI += 1) { +// let prev = containerObj.pubDecls[declI]; +// let next = typeObj.pubDecls[declI]; +// if (prev === next) continue; +// // TODO instead of showing "examples" as the public declarations, +// // do logic like this: +// //if (typeof(prev) !== 'object') { +// // let newDeclId = zigAnalysis.decls.length; +// // prev = clone(zigAnalysis.decls[prev]); +// // prev.id = newDeclId; +// // zigAnalysis.decls.push(prev); +// // containerObj.pubDecls[declI] = prev; +// //} +// //mergeDecls(prev, next, firstTypeObj, typeObj); +// } +// } +// for (let declI = 0; declI < containerObj.pubDecls.length; declI += 1) { +// let decl = containerObj.pubDecls[declI]; +// if (typeof(decl) === 'object') { +// containerObj.pubDecls[declI] = containerObj.pubDecls[declI].id; +// } +// } +// return containerObj; +// } - function allCompTimeFnCallsResult(calls) { - var firstTypeObj = null; - var containerObj = { - privDecls: [], - }; - for (var callI = 0; callI < calls.length; callI += 1) { - var call = zigAnalysis.calls[calls[callI]]; - if (call.result.type !== typeTypeId) return null; - var typeObj = zigAnalysis.types[call.result.value]; - if (!typeKindIsContainer(typeObj.kind)) return null; - if (firstTypeObj == null) { - firstTypeObj = typeObj; - containerObj.src = typeObj.src; - } else if (firstTypeObj.src !== typeObj.src) { - return null; - } - if (containerObj.fields == null) { - containerObj.fields = (typeObj.fields || []).concat([]); - } else for (var fieldI = 0; fieldI < typeObj.fields.length; fieldI += 1) { - var prev = containerObj.fields[fieldI]; - var next = typeObj.fields[fieldI]; - if (prev === next) continue; - if (typeof(prev) === 'object') { - if (prev[next] == null) prev[next] = typeObj; - } else { - containerObj.fields[fieldI] = {}; - containerObj.fields[fieldI][prev] = firstTypeObj; - containerObj.fields[fieldI][next] = typeObj; - } - } - - if (containerObj.pubDecls == null) { - containerObj.pubDecls = (typeObj.pubDecls || []).concat([]); - } else for (var declI = 0; declI < typeObj.pubDecls.length; declI += 1) { - var prev = containerObj.pubDecls[declI]; - var next = typeObj.pubDecls[declI]; - if (prev === next) continue; - // TODO instead of showing "examples" as the public declarations, - // do logic like this: - //if (typeof(prev) !== 'object') { - // var newDeclId = zigAnalysis.decls.length; - // prev = clone(zigAnalysis.decls[prev]); - // prev.id = newDeclId; - // zigAnalysis.decls.push(prev); - // containerObj.pubDecls[declI] = prev; - //} - //mergeDecls(prev, next, firstTypeObj, typeObj); - } - } - for (var declI = 0; declI < containerObj.pubDecls.length; declI += 1) { - var decl = containerObj.pubDecls[declI]; - if (typeof(decl) === 'object') { - containerObj.pubDecls[declI] = containerObj.pubDecls[declI].id; - } - } - return containerObj; - } - - function mergeDecls(declObj, nextDeclIndex, firstTypeObj, typeObj) { - var nextDeclObj = zigAnalysis.decls[nextDeclIndex]; - if (declObj.type != null && nextDeclObj.type != null && declObj.type !== nextDeclObj.type) { - if (typeof(declObj.type) !== 'object') { - var 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') { - var 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; - } - } + function renderValue(decl) { + let resolvedValue = resolveValue(decl.value) + + if (resolvedValue.expr.fieldRef) { + const declRef = decl.value.expr.refPath[0].declRef; + const type = zigAnalysis.decls[declRef]; domFnProtoCode.innerHTML = 'const ' + - escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true); + escapeHtml(decl.name) + ': ' + type.name + + " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";"; + } else if (resolvedValue.expr.string !== undefined || resolvedValue.expr.call !== undefined || resolvedValue.expr.comptimeExpr) { + domFnProtoCode.innerHTML = 'const ' + + escapeHtml(decl.name) + ': ' + exprName(resolvedValue.expr, {wantHtml: true, wantLink:true}) + + " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";"; + } else if (resolvedValue.expr.compileError) { + domFnProtoCode.innerHTML = 'const ' + + escapeHtml(decl.name) + " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";"; + } + else { + domFnProtoCode.innerHTML = 'const ' + + escapeHtml(decl.name) + ': ' + exprName(resolvedValue.typeRef, {wantHtml: true, wantLink:true}) + + " = " + exprName(decl.value.expr, {wantHtml: true, wantLink:true}) + ";"; + } - var docs = zigAnalysis.astNodes[decl.src].docs; + let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { domTldDocs.innerHTML = markdown(docs); domTldDocs.classList.remove("hidden"); @@ -928,11 +2078,13 @@ domFnProto.classList.remove("hidden"); } + function renderVar(decl) { + let declTypeRef = typeOfDecl(decl); domFnProtoCode.innerHTML = 'var ' + - escapeHtml(decl.name) + ': ' + typeIndexName(decl.type, true, true); + escapeHtml(decl.name) + ': ' + typeValueName(declTypeRef, true, true); - var docs = zigAnalysis.astNodes[decl.src].docs; + let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { domTldDocs.innerHTML = markdown(docs); domTldDocs.classList.remove("hidden"); @@ -941,52 +2093,103 @@ domFnProto.classList.remove("hidden"); } - function renderContainer(container, thisTypes) { - var typesList = []; - var namespacesList = []; - var errSetsList = []; - var fnsList = []; - var varsList = []; - var valsList = []; - for (var i = 0; i < container.pubDecls.length; i += 1) { - var decl = zigAnalysis.decls[container.pubDecls[i]]; + + function categorizeDecls(decls, + typesList, namespacesList, errSetsList, + fnsList, varsList, valsList, testsList) { + + for (let i = 0; i < decls.length; i += 1) { + let decl = zigAnalysis.decls[decls[i]]; + let declValue = resolveValue(decl.value); + + if (decl.isTest) { + testsList.push(decl); + continue; + } if (decl.kind === 'var') { varsList.push(decl); continue; - } else if (decl.kind === 'const' && decl.type != null) { - if (decl.type === typeTypeId) { - if (typeIsErrSet(decl.value)) { - errSetsList.push(decl); - } else if (typeIsStructWithNoFields(decl.value)) { - namespacesList.push(decl); - } else { - typesList.push(decl); - } - } else { - var typeKind = zigAnalysis.types[decl.type].kind; - if (typeKind === typeKinds.Fn) { - if (allCompTimeFnCallsHaveTypeResult(decl.type, decl.value)) { - typesList.push(decl); + } + + if (decl.kind === 'const') { + 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: (typeExpr).ret + }); + if ("type" in funcRetExpr.expr && funcRetExpr.expr.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 { - valsList.push(decl); + if (typeIsErrSet(declValue.expr.type)) { + errSetsList.push(decl); + } else if (typeIsStructWithNoFields(declValue.expr.type)) { + namespacesList.push(decl); + } 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); } } } + } + + + function renderContainer(container) { + + let typesList = []; + + let namespacesList = []; + + let errSetsList = []; + + let fnsList = []; + + let varsList = []; + + let valsList = []; + + let testsList = []; + + categorizeDecls(container.pubDecls, + typesList, namespacesList, errSetsList, + fnsList, varsList, valsList, testsList); + if (curNav.showPrivDecls) categorizeDecls(container.privDecls, + typesList, namespacesList, errSetsList, + fnsList, varsList, valsList, testsList); + + typesList.sort(byNameProperty); namespacesList.sort(byNameProperty); errSetsList.sort(byNameProperty); fnsList.sort(byNameProperty); varsList.sort(byNameProperty); valsList.sort(byNameProperty); + testsList.sort(byNameProperty); if (container.src != null) { - var docs = zigAnalysis.astNodes[container.src].docs; + let docs = zigAnalysis.astNodes[container.src].docs; if (docs != null) { domTldDocs.innerHTML = markdown(docs); domTldDocs.classList.remove("hidden"); @@ -994,163 +2197,193 @@ } if (typesList.length !== 0) { - var domListTypesFragment = createDomListFragment(typesList.length, '
  • '); - for (var i = 0; i < typesList.length; i += 1) { - var liDom = domListTypesFragment.children[i]; - var aDom = liDom.children[0]; - var decl = typesList[i]; + resizeDomList(domListTypes, typesList.length, '
  • '); + for (let i = 0; i < typesList.length; i += 1) { + let liDom = domListTypes.children[i]; + let aDom = liDom.children[0]; + let decl = typesList[i]; aDom.textContent = decl.name; aDom.setAttribute('href', navLinkDecl(decl.name)); } - domListTypes.innerHTML = ""; - domListTypes.appendChild(domListTypesFragment); domSectTypes.classList.remove("hidden"); } if (namespacesList.length !== 0) { - var domListNamespacesFragment = createDomListFragment(namespacesList.length, '
  • '); - for (var i = 0; i < namespacesList.length; i += 1) { - var liDom = domListNamespacesFragment.children[i]; - var aDom = liDom.children[0]; - var decl = namespacesList[i]; + resizeDomList(domListNamespaces, namespacesList.length, '
  • '); + for (let i = 0; i < namespacesList.length; i += 1) { + let liDom = domListNamespaces.children[i]; + let aDom = liDom.children[0]; + let decl = namespacesList[i]; aDom.textContent = decl.name; aDom.setAttribute('href', navLinkDecl(decl.name)); } - domListNamespaces.innerHTML = ""; - domListNamespaces.appendChild(domListNamespacesFragment); domSectNamespaces.classList.remove("hidden"); } if (errSetsList.length !== 0) { - var domListErrSetsFragment = createDomListFragment(errSetsList.length, '
  • '); - for (var i = 0; i < errSetsList.length; i += 1) { - var liDom = domListErrSetsFragment.children[i]; - var aDom = liDom.children[0]; - var decl = errSetsList[i]; + resizeDomList(domListErrSets, errSetsList.length, '
  • '); + for (let i = 0; i < errSetsList.length; i += 1) { + let liDom = domListErrSets.children[i]; + let aDom = liDom.children[0]; + let decl = errSetsList[i]; aDom.textContent = decl.name; aDom.setAttribute('href', navLinkDecl(decl.name)); } - domListErrSets.innerHTML = ""; - domListErrSets.appendChild(domListErrSetsFragment); domSectErrSets.classList.remove("hidden"); } if (fnsList.length !== 0) { - var domListFnsFragment = createDomListFragment(fnsList.length, ''); - for (var i = 0; i < fnsList.length; i += 1) { - var decl = fnsList[i]; - var trDom = domListFnsFragment.children[i]; + resizeDomList(domListFns, fnsList.length, '
    '); - var tdFnCode = trDom.children[0]; - var tdDesc = trDom.children[1]; + window.x = domListFns; + for (let i = 0; i < fnsList.length; i += 1) { + let decl = fnsList[i]; + let trDom = domListFns.children[i]; - tdFnCode.innerHTML = typeIndexName(decl.type, true, true, decl, navLinkDecl(decl.name), thisTypes); + let tdFnCode = trDom.children[0]; + let tdDesc = trDom.children[1]; - var docs = zigAnalysis.astNodes[decl.src].docs; + let declType = resolveValue(decl.value); + console.assert("type" in declType.expr); + + 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) { tdDesc.innerHTML = shortDescMarkdown(docs); } else { tdDesc.textContent = ""; } } - domListFns.innerHTML = ""; - domListFns.appendChild(domListFnsFragment); domSectFns.classList.remove("hidden"); } - if (container.fields != null && container.fields.length !== 0) { - var domListFieldsFragment = createDomListFragment(container.fields.length, '
    '); + let containerNode = zigAnalysis.astNodes[container.src]; + if (containerNode.fields && containerNode.fields.length > 0) { + resizeDomList(domListFields, containerNode.fields.length, '
    '); - var containerNode = zigAnalysis.astNodes[container.src]; - for (var i = 0; i < container.fields.length; i += 1) { - var field = container.fields[i]; - var fieldNode = zigAnalysis.astNodes[containerNode.fields[i]]; - var divDom = domListFieldsFragment.children[i]; + for (let i = 0; i < containerNode.fields.length; i += 1) { + let fieldNode = zigAnalysis.astNodes[containerNode.fields[i]]; + let divDom = domListFields.children[i]; + let fieldName = (fieldNode.name); - var html = '
    ' + escapeHtml(fieldNode.name);
    +                let html = '
    ' + escapeHtml(fieldName);
     
                     if (container.kind === typeKinds.Enum) {
    -                    html += ' = ' + field + '';
    +                    html += ' = ' + fieldName + '';
                     } else {
    +                    let fieldTypeExpr = container.fields[i];
                         html += ": ";
    -                    if (typeof(field) === 'object') {
    -                        html += 'var';
    -                    } else {
    -                        html += typeIndexName(field, true, true);
    +                    let name = exprName(fieldTypeExpr, false, false);
    +                    html += ''+ name +'';
    +                    let tsn = typeShorthandName(fieldTypeExpr);
    +                    if (tsn) {
    +                        html += ' ('+ tsn +')';
    +
                         }
                     }
     
                     html += ',
    '; - var docs = fieldNode.docs; + let docs = fieldNode.docs; if (docs != null) { html += markdown(docs); } divDom.innerHTML = html; } - domListFields.innerHTML = ""; - domListFields.appendChild(domListFieldsFragment); domSectFields.classList.remove("hidden"); } if (varsList.length !== 0) { - var domListGlobalVarsFragment = createDomListFragment(varsList.length, + resizeDomList(domListGlobalVars, varsList.length, ''); - for (var i = 0; i < varsList.length; i += 1) { - var decl = varsList[i]; - var trDom = domListGlobalVarsFragment.children[i]; + for (let i = 0; i < varsList.length; i += 1) { + let decl = varsList[i]; + let trDom = domListGlobalVars.children[i]; - var tdName = trDom.children[0]; - var tdNameA = tdName.children[0]; - var tdType = trDom.children[1]; - var tdDesc = trDom.children[2]; + let tdName = trDom.children[0]; + let tdNameA = tdName.children[0]; + let tdType = trDom.children[1]; + let tdDesc = trDom.children[2]; tdNameA.setAttribute('href', navLinkDecl(decl.name)); tdNameA.textContent = decl.name; - tdType.innerHTML = typeIndexName(decl.type, true, true); + tdType.innerHTML = typeValueName(typeOfDecl(decl), true, true); - var docs = zigAnalysis.astNodes[decl.src].docs; + let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { tdDesc.innerHTML = shortDescMarkdown(docs); } else { tdDesc.textContent = ""; } } - domListGlobalVars.innerHTML = ""; - domListGlobalVars.appendChild(domListGlobalVarsFragment); domSectGlobalVars.classList.remove("hidden"); } if (valsList.length !== 0) { - var domListValuesFragment = createDomListFragment(valsList.length, + resizeDomList(domListValues, valsList.length, ''); - for (var i = 0; i < valsList.length; i += 1) { - var decl = valsList[i]; - var trDom = domListValuesFragment.children[i]; + for (let i = 0; i < valsList.length; i += 1) { + let decl = valsList[i]; + let trDom = domListValues.children[i]; - var tdName = trDom.children[0]; - var tdNameA = tdName.children[0]; - var tdType = trDom.children[1]; - var tdDesc = trDom.children[2]; + let tdName = trDom.children[0]; + let tdNameA = tdName.children[0]; + let tdType = trDom.children[1]; + let tdDesc = trDom.children[2]; tdNameA.setAttribute('href', navLinkDecl(decl.name)); tdNameA.textContent = decl.name; - tdType.innerHTML = typeIndexName(decl.type, true, true); + tdType.innerHTML = exprName(walkResultTypeRef(decl.value), + {wantHtml:true, wantLink:true}); - var docs = zigAnalysis.astNodes[decl.src].docs; + let docs = zigAnalysis.astNodes[decl.src].docs; if (docs != null) { tdDesc.innerHTML = shortDescMarkdown(docs); } else { tdDesc.textContent = ""; } } - domListValues.innerHTML = ""; - domListValues.appendChild(domListValuesFragment); domSectValues.classList.remove("hidden"); } + + if (testsList.length !== 0) { + resizeDomList(domListTests, testsList.length, + ''); + for (let i = 0; i < testsList.length; i += 1) { + let decl = testsList[i]; + let trDom = domListTests.children[i]; + + let tdName = trDom.children[0]; + let tdNameA = tdName.children[0]; + let tdType = trDom.children[1]; + let tdDesc = trDom.children[2]; + + tdNameA.setAttribute('href', navLinkDecl(decl.name)); + tdNameA.textContent = decl.name; + + tdType.innerHTML = exprName(walkResultTypeRef(decl.value), + {wantHtml:true, wantLink:true}); + + let docs = zigAnalysis.astNodes[decl.src].docs; + if (docs != null) { + tdDesc.innerHTML = shortDescMarkdown(docs); + } else { + tdDesc.textContent = ""; + } + } + domSectTests.classList.remove("hidden"); + } } + + function operatorCompare(a, b) { if (a === b) { return 0; @@ -1162,33 +2395,33 @@ } function detectRootIsStd() { - var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; if (rootPkg.table["std"] == null) { // no std mapped into the root package return false; } - var stdPkg = zigAnalysis.packages[rootPkg.table["std"]]; + let stdPkg = zigAnalysis.packages[rootPkg.table["std"]]; if (stdPkg == null) return false; return rootPkg.file === stdPkg.file; } function indexTypeKinds() { - var map = {}; - for (var i = 0; i < zigAnalysis.typeKinds.length; i += 1) { + let map = ({}); + for (let i = 0; i < zigAnalysis.typeKinds.length; i += 1) { map[zigAnalysis.typeKinds[i]] = i; } // This is just for debugging purposes, not needed to function - var assertList = ["Type","Void","Bool","NoReturn","Int","Float","Pointer","Array","Struct", + let assertList = ["Type","Void","Bool","NoReturn","Int","Float","Pointer","Array","Struct", "ComptimeFloat","ComptimeInt","Undefined","Null","Optional","ErrorUnion","ErrorSet","Enum", "Union","Fn","BoundFn","Opaque","Frame","AnyFrame","Vector","EnumLiteral"]; - for (var i = 0; i < assertList.length; i += 1) { + for (let i = 0; i < assertList.length; i += 1) { if (map[assertList[i]] == null) throw new Error("No type kind '" + assertList[i] + "' found"); } return map; } function findTypeTypeId() { - for (var i = 0; i < zigAnalysis.types.length; i += 1) { + for (let i = 0; i < zigAnalysis.types.length; i += 1) { if (zigAnalysis.types[i].kind == typeKinds.Type) { return i; } @@ -1197,17 +2430,26 @@ } function updateCurNav() { + curNav = { + showPrivDecls: false, pkgNames: [], pkgObjs: [], declNames: [], declObjs: [], + callName: null, }; curNavSearch = ""; if (location.hash[0] === '#' && location.hash.length > 1) { - var query = location.hash.substring(1); - var qpos = query.indexOf("?"); + let query = location.hash.substring(1); + if (query[0] === '*') { + curNav.showPrivDecls = true; + query = query.substring(1); + } + + let qpos = query.indexOf("?"); + let nonSearchPart; if (qpos === -1) { nonSearchPart = query; } else { @@ -1215,16 +2457,12 @@ curNavSearch = decodeURIComponent(query.substring(qpos + 1)); } - var parts = nonSearchPart.split(";"); + let parts = nonSearchPart.split(";"); curNav.pkgNames = decodeURIComponent(parts[0]).split("."); if (parts[1] != null) { curNav.declNames = decodeURIComponent(parts[1]).split("."); } } - - if (curNav.pkgNames.length === 0 && rootIsStd) { - curNav.pkgNames = ["std"]; - } } function onHashChange() { @@ -1239,11 +2477,37 @@ } } + function findSubDecl(parentType, childName) { - if (parentType.pubDecls == null) throw new Error("parent object has no public decls"); - for (var i = 0; i < parentType.pubDecls.length; i += 1) { - var declIndex = parentType.pubDecls[i]; - var childDecl = zigAnalysis.decls[declIndex]; + { + // Generic functions + if ("value" in parentType) { + const rv = resolveValue(parentType.value); + if ("type" in rv.expr) { + const t = zigAnalysis.types[rv.expr.type]; + if (t.kind == typeKinds.Fn && t.generic_ret != null) { + const rgr = resolveValue({expr: t.generic_ret}); + if ("type" in rgr.expr) { + parentType = zigAnalysis.types[rgr.expr.type]; + } + } + } + } + } + + + if (!parentType.pubDecls) return null; + for (let i = 0; i < parentType.pubDecls.length; i += 1) { + let declIndex = parentType.pubDecls[i]; + let childDecl = zigAnalysis.decls[declIndex]; + if (childDecl.name === childName) { + return childDecl; + } + } + if (!parentType.privDecls) return null; + for (let i = 0; i < parentType.privDecls.length; i += 1) { + let declIndex = parentType.privDecls[i]; + let childDecl = zigAnalysis.decls[declIndex]; if (childDecl.name === childName) { return childDecl; } @@ -1251,31 +2515,27 @@ return null; } - function getDeclContainerType(decl) { - if (decl.type === typeTypeId) { - return zigAnalysis.types[decl.value]; - } - return null; - } + + function computeCanonicalPackagePaths() { - var list = new Array(zigAnalysis.packages.length); + let list = new Array(zigAnalysis.packages.length); // Now we try to find all the packages from root. - var rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; + let rootPkg = zigAnalysis.packages[zigAnalysis.rootPkg]; // Breadth-first to keep the path shortest possible. - var stack = [{ - path: [], + let stack = [{ + path: ([]), pkg: rootPkg, }]; while (stack.length !== 0) { - var item = stack.shift(); - for (var key in item.pkg.table) { - var childPkgIndex = item.pkg.table[key]; + let item = (stack.shift()); + for (let key in item.pkg.table) { + let childPkgIndex = item.pkg.table[key]; if (list[childPkgIndex] != null) continue; - var childPkg = zigAnalysis.packages[childPkgIndex]; + let childPkg = zigAnalysis.packages[childPkgIndex]; if (childPkg == null) continue; - var newPath = item.path.concat([key]) + let newPath = item.path.concat([key]) list[childPkgIndex] = newPath; stack.push({ path: newPath, @@ -1286,53 +2546,68 @@ return list; } - function typeKindIsContainer(typeKind) { - return typeKind === typeKinds.Struct || - typeKind === typeKinds.Union || - typeKind === typeKinds.Enum; - } - - function declCanRepresentTypeKind(typeKind) { - return typeKind === typeKinds.ErrorSet || typeKindIsContainer(typeKind); - } + function computeCanonDeclPaths() { - var list = new Array(zigAnalysis.decls.length); + let list = new Array(zigAnalysis.decls.length); canonTypeDecls = new Array(zigAnalysis.types.length); - for (var pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { + for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue; - var pkg = zigAnalysis.packages[pkgI]; - var pkgNames = canonPkgPaths[pkgI]; - var stack = [{ - declNames: [], + let pkg = zigAnalysis.packages[pkgI]; + let pkgNames = canonPkgPaths[pkgI]; + if (pkgNames === undefined) continue; + + let stack = [{ + declNames: ([]), type: zigAnalysis.types[pkg.main], }]; while (stack.length !== 0) { - var item = stack.shift(); + let item = (stack.shift()); - if (item.type.pubDecls != null) { - for (var declI = 0; declI < item.type.pubDecls.length; declI += 1) { - var mainDeclIndex = item.type.pubDecls[declI]; + if (isContainerType(item.type)) { + let t = (item.type); + + let len = t.pubDecls ? t.pubDecls.length : 0; + for (let declI = 0; declI < len; declI += 1) { + let mainDeclIndex = t.pubDecls[declI]; if (list[mainDeclIndex] != null) continue; - var decl = zigAnalysis.decls[mainDeclIndex]; - if (decl.type === typeTypeId && - declCanRepresentTypeKind(zigAnalysis.types[decl.value].kind)) - { - canonTypeDecls[decl.value] = mainDeclIndex; - } - var declNames = item.declNames.concat([decl.name]); + let decl = zigAnalysis.decls[mainDeclIndex]; + let declVal = decl.value; //resolveValue(decl.value); + let declNames = item.declNames.concat([decl.name]); list[mainDeclIndex] = { pkgNames: pkgNames, declNames: declNames, }; - var containerType = getDeclContainerType(decl); - if (containerType != null) { - stack.push({ - declNames: declNames, - type: containerType, - }); + if ("type" in declVal.expr) { + let value = zigAnalysis.types[declVal.expr.type]; + if (declCanRepresentTypeKind(value.kind)) + { + canonTypeDecls[declVal.type] = mainDeclIndex; + } + + if (isContainerType(value)) { + stack.push({ + declNames: declNames, + type:value, + }); + } + + + // Generic function + if (value.kind == typeKinds.Fn && value.generic_ret != null) { + let resolvedVal = resolveValue({ expr: value.generic_ret}); + if ("type" in resolvedVal.expr) { + let generic_type = zigAnalysis.types[resolvedVal.expr.type]; + if (isContainerType(generic_type)){ + stack.push({ + declNames: declNames, + type: generic_type, + }); + } + } + } } } } @@ -1341,41 +2616,49 @@ return list; } + function getCanonDeclPath(index) { if (canonDeclPaths == null) { canonDeclPaths = computeCanonDeclPaths(); } + //let cd = (canonDeclPaths); return canonDeclPaths[index]; } + function getCanonTypeDecl(index) { getCanonDeclPath(0); + //let ct = (canonTypeDecls); return canonTypeDecls[index]; } + function escapeHtml(text) { return text.replace(/[&"<>]/g, function (m) { return escapeHtmlReplacements[m]; }); } + function shortDescMarkdown(docs) { - var parts = docs.trim().split("\n"); - var firstLine = parts[0]; + let parts = docs.trim().split("\n"); + let firstLine = parts[0]; return markdown(firstLine); } + function markdown(input) { const raw_lines = input.split('\n'); // zig allows no '\r', so we don't need to split on CR + const lines = []; // PHASE 1: // Dissect lines and determine the type for each line. - // Also computes indentation level and removes unnecessary whitespace + // Also computes indentation level and removes unnecessary whitespace - var is_reading_code = false; - var code_indent = 0; - for (var line_no = 0; line_no < raw_lines.length; line_no++) { + let is_reading_code = false; + let code_indent = 0; + for (let line_no = 0; line_no < raw_lines.length; line_no++) { const raw_line = raw_lines[line_no]; const line = { @@ -1383,6 +2666,7 @@ raw_text: raw_line, text: raw_line.trim(), type: "p", // p, h1 … h6, code, ul, ol, blockquote, skip, empty + ordered_number: -1, // NOTE: hack to make the type checker happy }; if (!is_reading_code) { @@ -1419,7 +2703,7 @@ line.text = line.text.substr(1); } else if (line.text.match(/^\d+\..*$/)) { // if line starts with {number}{dot} - const match = line.text.match(/(\d+)\./); + const match = (line.text.match(/(\d+)\./)); line.type = "ul"; line.text = line.text.substr(match[0].length); line.ordered_number = Number(match[1].length); @@ -1450,21 +2734,24 @@ // PHASE 2: // Render HTML from markdown lines. - // Look at each line and emit fitting HTML code + // Look at each line and emit fitting HTML code - function markdownInlines(innerText, stopChar) { + + function markdownInlines(innerText) { // inline types: // **{INLINE}** : - // __{INLINE}__ : - // ~~{INLINE}~~ : - // *{INLINE}* : - // _{INLINE}_ : - // `{TEXT}` : - // [{INLINE}]({URL}) : - // ![{TEXT}]({URL}) : - // [[std;format.fmt]] : (inner link) + // __{INLINE}__ : + // ~~{INLINE}~~ : + // *{INLINE}* : + // _{INLINE}_ : + // `{TEXT}` : + // [{INLINE}]({URL}) : + // ![{TEXT}]({URL}) : + // [[std;format.fmt]] : (inner link) + + const formats = [ { marker: "**", @@ -1484,26 +2771,11 @@ } ]; - // Links, images and inner links don't use the same marker to wrap their content. - const linksFormat = [ - { - prefix: "[", - regex: /\[([^\]]*)\]\(([^\)]*)\)/, - urlIndex: 2, // Index in the match that contains the link URL - textIndex: 1 // Index in the match that contains the link text - }, - { - prefix: "h", - regex: /http[s]?:\/\/[^\s]+/, - urlIndex: 0, - textIndex: 0 - } - ]; - + const stack = []; - var innerHTML = ""; - var currentRun = ""; + let innerHTML = ""; + let currentRun = ""; function flushRun() { if (currentRun != "") { @@ -1512,18 +2784,18 @@ currentRun = ""; } - var parsing_code = false; - var codetag = ""; - var in_code = false; + let parsing_code = false; + let codetag = ""; + let in_code = false; - for (var i = 0; i < innerText.length; i++) { + for (let i = 0; i < innerText.length; i++) { if (parsing_code && in_code) { if (innerText.substr(i, codetag.length) == codetag) { // remove leading and trailing whitespace if string both starts and ends with one. - if (currentRun[0] == " " && currentRun[currentRun.length - 1] == " ") { - currentRun = currentRun.substr(1, currentRun.length - 2); - } + if (currentRun[0] == " " && currentRun[currentRun.length - 1] == " ") { + currentRun = currentRun.substr(1, currentRun.length - 2); + } flushRun(); i += codetag.length - 1; in_code = false; @@ -1550,31 +2822,8 @@ currentRun += innerText[i]; in_code = true; } else { - var foundMatches = false; - - for (var j = 0; j < linksFormat.length; j++) { - const linkFmt = linksFormat[j]; - - if (linkFmt.prefix == innerText[i]) { - var remaining = innerText.substring(i); - var matches = remaining.match(linkFmt.regex); - - if (matches) { - flushRun(); - innerHTML += ' ' + matches[linkFmt.textIndex] + ' '; - i += matches[0].length; // Skip the fragment we just consumed - foundMatches = true; - break; - } - } - } - - if (foundMatches) { - continue; - } - - var any = false; - for (var idx = (stack.length > 0 ? -1 : 0); idx < formats.length; idx++) { + let any = false; + for (let idx = (stack.length > 0 ? -1 : 0); idx < formats.length; idx++) { const fmt = idx >= 0 ? formats[idx] : stack[stack.length - 1]; if (innerText.substr(i, fmt.marker.length) == fmt.marker) { flushRun(); @@ -1598,48 +2847,54 @@ flushRun(); while (stack.length > 0) { - const fmt = stack.pop(); + const fmt = (stack.pop()); innerHTML += ""; } return innerHTML; } - var html = ""; - for (var line_no = 0; line_no < lines.length; line_no++) { + + function previousLineIs(type, line_no) { + if (line_no > 0) { + return (lines[line_no - 1].type == type); + } else { + return false; + } + } + + + function nextLineIs(type, line_no) { + if (line_no < (lines.length - 1)) { + return (lines[line_no + 1].type == type); + } else { + return false; + } + } + + + function getPreviousLineIndent(line_no) { + if (line_no > 0) { + return lines[line_no - 1].indent; + } else { + return 0; + } + } + + + function getNextLineIndent(line_no) { + if (line_no < (lines.length - 1)) { + return lines[line_no + 1].indent; + } else { + return 0; + } + } + + let html = ""; + for (let line_no = 0; line_no < lines.length; line_no++) { const line = lines[line_no]; - function previousLineIs(type) { - if (line_no > 0) { - return (lines[line_no - 1].type == type); - } else { - return false; - } - } - function nextLineIs(type) { - if (line_no < (lines.length - 1)) { - return (lines[line_no + 1].type == type); - } else { - return false; - } - } - - function getPreviousLineIndent() { - if (line_no > 0) { - return lines[line_no - 1].indent; - } else { - return 0; - } - } - - function getNextLineIndent() { - if (line_no < (lines.length - 1)) { - return lines[line_no + 1].indent; - } else { - return 0; - } - } switch (line.type) { case "h1": @@ -1653,33 +2908,33 @@ case "ul": case "ol": - if (!previousLineIs("ul") || getPreviousLineIndent() < line.indent) { + if (!previousLineIs("ul", line_no) || getPreviousLineIndent(line_no) < line.indent) { html += "<" + line.type + ">\n"; } html += "
  • " + markdownInlines(line.text) + "
  • \n"; - if (!nextLineIs("ul") || getNextLineIndent() < line.indent) { + if (!nextLineIs("ul", line_no) || getNextLineIndent(line_no) < line.indent) { html += "\n"; } break; case "p": - if (!previousLineIs("p")) { + if (!previousLineIs("p", line_no)) { html += "

    \n"; } html += markdownInlines(line.text) + "\n"; - if (!nextLineIs("p")) { + if (!nextLineIs("p", line_no)) { html += "

    \n"; } break; case "code": - if (!previousLineIs("code")) { + if (!previousLineIs("code", line_no)) { html += "
    ";
                         }
                         html += escapeHtml(line.text) + "\n";
    -                    if (!nextLineIs("code")) {
    +                    if (!nextLineIs("code", line_no)) {
                             html += "
    \n"; } break; @@ -1694,26 +2949,27 @@ return; } - var liDom = domListSearchResults.children[curSearchIndex]; + let liDom = domListSearchResults.children[curSearchIndex]; if (liDom == null && domListSearchResults.children.length !== 0) { liDom = domListSearchResults.children[0]; } if (liDom != null) { - var aDom = liDom.children[0]; - location.href = aDom.getAttribute("href"); + let aDom = liDom.children[0]; + location.href = (aDom.getAttribute("href")); curSearchIndex = -1; } domSearch.blur(); } + function onSearchKeyDown(ev) { switch (getKeyString(ev)) { case "Enter": // detect if this search changes anything - var terms1 = getSearchTerms(); + let terms1 = getSearchTerms(); startSearch(); updateCurNav(); - var terms2 = getSearchTerms(); + let terms2 = getSearchTerms(); // we might have to wait for onHashChange to trigger imFeelingLucky = (terms1.join(' ') !== terms2.join(' ')); if (!imFeelingLucky) activateSelectedResult(); @@ -1749,6 +3005,8 @@ } } + + function moveSearchCursor(dir) { if (curSearchIndex < 0 || curSearchIndex >= domListSearchResults.children.length) { if (dir > 0) { @@ -1768,9 +3026,10 @@ renderSearchCursor(); } + function getKeyString(ev) { - var name; - var ignoreShift = false; + let name; + let ignoreShift = false; switch (ev.which) { case 13: name = "Enter"; @@ -1794,6 +3053,7 @@ return name; } + function onWindowKeyDown(ev) { switch (getKeyString(ev)) { case "Esc": @@ -1811,214 +3071,160 @@ startAsyncSearch(); break; case "?": - ev.preventDefault(); + ev.preventDefault(); ev.stopPropagation(); showHelpModal(); break; } } - function showHelpModal() { - domHelpModal.classList.remove("hidden"); - domHelpModal.style.left = (window.innerWidth / 2 - domHelpModal.clientWidth / 2) + "px"; - domHelpModal.style.top = (window.innerHeight / 2 - domHelpModal.clientHeight / 2) + "px"; - domHelpModal.focus(); - } +function showHelpModal() { + domHelpModal.classList.remove("hidden"); + domHelpModal.style.left = (window.innerWidth / 2 - domHelpModal.clientWidth / 2) + "px"; + domHelpModal.style.top = (window.innerHeight / 2 - domHelpModal.clientHeight / 2) + "px"; + domHelpModal.focus(); +} - function onClickSearchShowAllResults(ev) { - ev.preventDefault(); - ev.stopPropagation(); - searchTrimResults = false; - onHashChange(); +function clearAsyncSearch() { + if (searchTimer != null) { + clearTimeout(searchTimer); + searchTimer = null; } +} - function clearAsyncSearch() { - if (searchTimer != null) { - clearTimeout(searchTimer); - searchTimer = null; +function startAsyncSearch() { + clearAsyncSearch(); + searchTimer = setTimeout(startSearch, 100); +} +function startSearch() { + clearAsyncSearch(); + let oldHash = location.hash; + let parts = oldHash.split("?"); + let newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); + location.hash = (parts.length === 1) ? (oldHash + newPart2) : (parts[0] + newPart2); +} +function getSearchTerms() { + let list = curNavSearch.trim().split(/[ \r\n\t]+/); + list.sort(); + return list; +} +function renderSearch() { + let matchedItems = []; + let ignoreCase = (curNavSearch.toLowerCase() === curNavSearch); + let terms = getSearchTerms(); + + decl_loop: for (let declIndex = 0; declIndex < zigAnalysis.decls.length; declIndex += 1) { + let canonPath = getCanonDeclPath(declIndex); + if (canonPath == null) continue; + + let decl = zigAnalysis.decls[declIndex]; + let lastPkgName = canonPath.pkgNames[canonPath.pkgNames.length - 1]; + let fullPathSearchText = lastPkgName + "." + canonPath.declNames.join('.'); + let astNode = zigAnalysis.astNodes[decl.src]; + let fileAndDocs = "" //zigAnalysis.files[astNode.file]; + // TODO: understand what this piece of code is trying to achieve + // also right now `files` are expressed as a hashmap. + if (astNode.docs != null) { + fileAndDocs += "\n" + astNode.docs; } + let fullPathSearchTextLower = fullPathSearchText; + if (ignoreCase) { + fullPathSearchTextLower = fullPathSearchTextLower.toLowerCase(); + fileAndDocs = fileAndDocs.toLowerCase(); + } + + let points = 0; + for (let termIndex = 0; termIndex < terms.length; termIndex += 1) { + let term = terms[termIndex]; + + // exact, case sensitive match of full decl path + if (fullPathSearchText === term) { + points += 4; + continue; + } + // exact, case sensitive match of just decl name + if (decl.name == term) { + points += 3; + continue; + } + // substring, case insensitive match of full decl path + if (fullPathSearchTextLower.indexOf(term) >= 0) { + points += 2; + continue; + } + if (fileAndDocs.indexOf(term) >= 0) { + points += 1; + continue; + } + + continue decl_loop; + } + + matchedItems.push({ + decl: decl, + path: canonPath, + points: points, + }); } - function startAsyncSearch() { - clearAsyncSearch(); - searchTrimResults = true; - searchTimer = setTimeout(startSearch, 10); + if (matchedItems.length !== 0) { + resizeDomList(domListSearchResults, matchedItems.length, '
  • '); + + matchedItems.sort(function(a, b) { + let cmp = operatorCompare(b.points, a.points); + if (cmp != 0) return cmp; + return operatorCompare(a.decl.name, b.decl.name); + }); + + for (let i = 0; i < matchedItems.length; i += 1) { + let liDom = domListSearchResults.children[i]; + let aDom = liDom.children[0]; + let match = matchedItems[i]; + let lastPkgName = match.path.pkgNames[match.path.pkgNames.length - 1]; + aDom.textContent = lastPkgName + "." + match.path.declNames.join('.'); + aDom.setAttribute('href', navLink(match.path.pkgNames, match.path.declNames)); + } + renderSearchCursor(); + + domSectSearchResults.classList.remove("hidden"); + } else { + domSectSearchNoResults.classList.remove("hidden"); } - function startSearch() { - clearAsyncSearch(); - var oldHash = location.hash; - var parts = oldHash.split("?"); - var newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); - var newHash = (oldHash === "" ? "#" : parts[0]) + newPart2; - // create a history entry only once per search - if (parts.length === 1) { - location.assign(newHash); +} + +function renderSearchCursor() { + for (let i = 0; i < domListSearchResults.children.length; i += 1) { + let liDom = (domListSearchResults.children[i]); + if (curSearchIndex === i) { + liDom.classList.add("selected"); } else { - location.replace(newHash); + liDom.classList.remove("selected"); } } - function getSearchTerms() { - var list = curNavSearch.trim().split(/[ \r\n\t]+/); - list.sort(); - return list; - } - function renderSearch() { - var matchedItems = []; - var ignoreCase = (curNavSearch.toLowerCase() === curNavSearch); - var terms = getSearchTerms(); +} - decl_loop: for (var declIndex = 0; declIndex < zigAnalysis.decls.length; declIndex += 1) { - var canonPath = getCanonDeclPath(declIndex); - if (canonPath == null) continue; - var decl = zigAnalysis.decls[declIndex]; - var lastPkgName = canonPath.pkgNames[canonPath.pkgNames.length - 1]; - var fullPathSearchText = lastPkgName + "." + canonPath.declNames.join('.'); - var astNode = zigAnalysis.astNodes[decl.src]; - var fileAndDocs = zigAnalysis.files[astNode.file]; - if (astNode.docs != null) { - fileAndDocs += "\n" + astNode.docs; - } - var fullPathSearchTextLower = fullPathSearchText; - if (ignoreCase) { - fullPathSearchTextLower = fullPathSearchTextLower.toLowerCase(); - fileAndDocs = fileAndDocs.toLowerCase(); - } - var points = 0; - for (var termIndex = 0; termIndex < terms.length; termIndex += 1) { - var term = terms[termIndex]; +// function indexNodesToCalls() { +// let map = {}; +// for (let i = 0; i < zigAnalysis.calls.length; i += 1) { +// let call = zigAnalysis.calls[i]; +// let fn = zigAnalysis.fns[call.fn]; +// if (map[fn.src] == null) { +// map[fn.src] = [i]; +// } else { +// map[fn.src].push(i); +// } +// } +// return map; +// } - // exact, case sensitive match of full decl path - if (fullPathSearchText === term) { - points += 4; - continue; - } - // exact, case sensitive match of just decl name - if (decl.name == term) { - points += 3; - continue; - } - // substring, case insensitive match of full decl path - if (fullPathSearchTextLower.indexOf(term) >= 0) { - points += 2; - continue; - } - if (fileAndDocs.indexOf(term) >= 0) { - points += 1; - continue; - } - continue decl_loop; - } - matchedItems.push({ - decl: decl, - path: canonPath, - points: points, - }); - } +function byNameProperty(a, b) { + return operatorCompare(a.name, b.name); +} - if (matchedItems.length !== 0) { - matchedItems.sort(function(a, b) { - var cmp = operatorCompare(b.points, a.points); - if (cmp != 0) return cmp; - return operatorCompare(a.decl.name, b.decl.name); - }); - var searchTrimmed = false - var searchTrimResultsMaxItems = 200 - if (searchTrimResults && matchedItems.length > searchTrimResultsMaxItems) { - matchedItems = matchedItems.slice(0, searchTrimResultsMaxItems) - searchTrimmed = true - } - var domListSearchResultsFragment = createDomListFragment(matchedItems.length, '
  • '); - for (var i = 0; i < matchedItems.length; i += 1) { - var liDom = domListSearchResultsFragment.children[i]; - var aDom = liDom.children[0]; - var match = matchedItems[i]; - var lastPkgName = match.path.pkgNames[match.path.pkgNames.length - 1]; - aDom.textContent = lastPkgName + "." + match.path.declNames.join('.'); - aDom.setAttribute('href', navLink(match.path.pkgNames, match.path.declNames)); - } - - domListSearchResults.innerHTML = ""; - domListSearchResults.appendChild(domListSearchResultsFragment); - domSectSearchResults.classList.remove("hidden"); - - if (searchTrimmed) { - domSectSearchAllResultsLink.classList.remove("hidden"); - } - - renderSearchCursor(); - } else { - domSectSearchNoResults.classList.remove("hidden"); - } - } - - function renderSearchCursor() { - for (var i = 0; i < domListSearchResults.children.length; i += 1) { - var liDom = domListSearchResults.children[i]; - if (curSearchIndex === i) { - liDom.classList.add("selected"); - } else { - liDom.classList.remove("selected"); - } - } - } - - function indexNodesToFns() { - var map = {}; - for (var i = 0; i < zigAnalysis.fns.length; i += 1) { - var fn = zigAnalysis.fns[i]; - if (typeIsGenericFn(fn.type)) continue; - if (map[fn.src] == null) { - map[fn.src] = [i]; - } else { - map[fn.src].push(i); - } - } - return map; - } - - function indexNodesToCalls() { - var map = {}; - for (var i = 0; i < zigAnalysis.calls.length; i += 1) { - var call = zigAnalysis.calls[i]; - var fn = zigAnalysis.fns[call.fn]; - if (map[fn.src] == null) { - map[fn.src] = [i]; - } else { - map[fn.src].push(i); - } - } - return map; - } - - function byNameProperty(a, b) { - return operatorCompare(a.name, b.name); - } - - function clone(obj) { - var res = {}; - for (var key in obj) { - res[key] = obj[key]; - } - return res; - } - - function firstObjectKey(obj) { - for (var key in obj) { - return key; - } - } - - function token(value, tokenClass, wantHtml){ - if(wantHtml){ - return '' + value + ''; - } else { - return value + ''; - } - } })(); diff --git a/src/Autodoc.zig b/src/Autodoc.zig new file mode 100644 index 0000000000..4ac6873442 --- /dev/null +++ b/src/Autodoc.zig @@ -0,0 +1,3890 @@ +const std = @import("std"); +const build_options = @import("build_options"); +const Autodoc = @This(); +const Compilation = @import("Compilation.zig"); +const Module = @import("Module.zig"); +const File = Module.File; +const Package = @import("Package.zig"); +const Zir = @import("Zir.zig"); +const Ref = Zir.Inst.Ref; +const log = std.log.scoped(.autodoc); + +module: *Module, +doc_location: Compilation.EmitLoc, +arena: std.mem.Allocator, + +// The goal of autodoc is to fill up these arrays +// that will then be serialized as JSON and consumed +// by the JS frontend. +packages: std.AutoArrayHashMapUnmanaged(*Package, DocData.DocPackage) = .{}, +files: std.AutoArrayHashMapUnmanaged(*File, usize) = .{}, +calls: std.ArrayListUnmanaged(DocData.Call) = .{}, +types: std.ArrayListUnmanaged(DocData.Type) = .{}, +decls: std.ArrayListUnmanaged(DocData.Decl) = .{}, +exprs: std.ArrayListUnmanaged(DocData.Expr) = .{}, +ast_nodes: std.ArrayListUnmanaged(DocData.AstNode) = .{}, +comptime_exprs: std.ArrayListUnmanaged(DocData.ComptimeExpr) = .{}, + +// These fields hold temporary state of the analysis process +// and are mainly used by the decl path resolving algorithm. +pending_ref_paths: std.AutoHashMapUnmanaged( + *DocData.Expr, // pointer to declpath tail end (ie `&decl_path[decl_path.len - 1]`) + std.ArrayListUnmanaged(RefPathResumeInfo), +) = .{}, +ref_paths_pending_on_decls: std.AutoHashMapUnmanaged( + usize, + std.ArrayListUnmanaged(RefPathResumeInfo), +) = .{}, +ref_paths_pending_on_types: std.AutoHashMapUnmanaged( + usize, + std.ArrayListUnmanaged(RefPathResumeInfo), +) = .{}, + +const RefPathResumeInfo = struct { + file: *File, + ref_path: []DocData.Expr, +}; + +var arena_allocator: std.heap.ArenaAllocator = undefined; +pub fn init(m: *Module, doc_location: Compilation.EmitLoc) Autodoc { + arena_allocator = std.heap.ArenaAllocator.init(m.gpa); + return .{ + .module = m, + .doc_location = doc_location, + .arena = arena_allocator.allocator(), + }; +} + +pub fn deinit(_: *Autodoc) void { + arena_allocator.deinit(); +} + +/// The entry point of the Autodoc generation process. +pub fn generateZirData(self: *Autodoc) !void { + if (self.doc_location.directory) |dir| { + if (dir.path) |path| { + log.debug("path: {s}", .{path}); + } + } + + const root_src_dir = self.module.main_pkg.root_src_directory; + const root_src_path = self.module.main_pkg.root_src_path; + const joined_src_path = try root_src_dir.join(self.arena, &.{root_src_path}); + defer self.arena.free(joined_src_path); + + const abs_root_src_path = try std.fs.path.resolve(self.arena, &.{ ".", joined_src_path }); + defer self.arena.free(abs_root_src_path); + + const file = self.module.import_table.get(abs_root_src_path).?; // file is expected to be present in the import table + // Append all the types in Zir.Inst.Ref. + { + try self.types.append(self.arena, .{ + .ComptimeExpr = .{ .name = "ComptimeExpr" }, + }); + + // this skipts Ref.none but it's ok becuse we replaced it with ComptimeExpr + var i: u32 = 1; + while (i <= @enumToInt(Ref.anyerror_void_error_union_type)) : (i += 1) { + var tmpbuf = std.ArrayList(u8).init(self.arena); + try Ref.typed_value_map[i].val.fmtDebug().format("", .{}, tmpbuf.writer()); + try self.types.append( + self.arena, + switch (@intToEnum(Ref, i)) { + else => blk: { + // TODO: map the remaining refs to a correct type + // instead of just assinging "array" to them. + break :blk .{ + .Array = .{ + .len = .{ + .int = .{ + .value = 1, + .negated = false, + }, + }, + .child = .{ .type = 0 }, + }, + }; + }, + .u1_type, + .u8_type, + .i8_type, + .u16_type, + .i16_type, + .u32_type, + .i32_type, + .u64_type, + .i64_type, + .u128_type, + .i128_type, + .usize_type, + .isize_type, + .c_short_type, + .c_ushort_type, + .c_int_type, + .c_uint_type, + .c_long_type, + .c_ulong_type, + .c_longlong_type, + .c_ulonglong_type, + .c_longdouble_type, + => .{ + .Int = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .f16_type, + .f32_type, + .f64_type, + .f128_type, + => .{ + .Float = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .comptime_int_type => .{ + .ComptimeInt = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .comptime_float_type => .{ + .ComptimeFloat = .{ .name = tmpbuf.toOwnedSlice() }, + }, + + .anyopaque_type => .{ + .ComptimeExpr = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .bool_type => .{ + .Bool = .{ .name = tmpbuf.toOwnedSlice() }, + }, + + .noreturn_type => .{ + .NoReturn = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .void_type => .{ + .Void = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .type_type => .{ + .Type = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .anyerror_type => .{ + .ErrorSet = .{ .name = tmpbuf.toOwnedSlice() }, + }, + .calling_convention_inline, .calling_convention_c, .calling_convention_type => .{ + .EnumLiteral = .{ .name = tmpbuf.toOwnedSlice() }, + }, + }, + ); + } + } + + const main_type_index = self.types.items.len; + { + try self.packages.put(self.arena, self.module.main_pkg, .{ + .name = "root", + .main = main_type_index, + .table = .{}, + }); + try self.packages.entries.items(.value)[0].table.put( + self.arena, + self.module.main_pkg, + .{ + .name = "root", + .value = 0, + }, + ); + } + + var root_scope = Scope{ .parent = null, .enclosing_type = main_type_index }; + try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); + try self.files.put(self.arena, file, main_type_index); + _ = try self.walkInstruction(file, &root_scope, Zir.main_struct_inst, false); + + if (self.ref_paths_pending_on_decls.count() > 0) { + @panic("some decl paths were never fully analized (pending on decls)"); + } + + if (self.ref_paths_pending_on_types.count() > 0) { + @panic("some decl paths were never fully analized (pending on types)"); + } + + if (self.pending_ref_paths.count() > 0) { + @panic("some decl paths were never fully analized"); + } + + const rootName = blk: { + const rootName = std.fs.path.basename(self.module.main_pkg.root_src_path); + break :blk rootName[0 .. rootName.len - 4]; + }; + var data = DocData{ + .rootPkgName = rootName, + .params = .{ .rootName = "root" }, + .packages = self.packages.values(), + .files = self.files, + .calls = self.calls.items, + .types = self.types.items, + .decls = self.decls.items, + .exprs = self.exprs.items, + .astNodes = self.ast_nodes.items, + .comptimeExprs = self.comptime_exprs.items, + }; + + if (self.doc_location.directory) |d| { + d.handle.makeDir( + self.doc_location.basename, + ) catch |e| switch (e) { + error.PathAlreadyExists => {}, + else => |err| return err, + }; + } else { + self.module.zig_cache_artifact_directory.handle.makeDir( + self.doc_location.basename, + ) catch |e| switch (e) { + error.PathAlreadyExists => {}, + else => |err| return err, + }; + } + const output_dir = if (self.doc_location.directory) |d| + try d.handle.openDir(self.doc_location.basename, .{}) + else + try self.module.zig_cache_artifact_directory.handle.openDir(self.doc_location.basename, .{}); + { + const data_js_f = try output_dir.createFile("data.js", .{}); + defer data_js_f.close(); + var buffer = std.io.bufferedWriter(data_js_f.writer()); + + const out = buffer.writer(); + try out.print( + \\ /** @type {{DocData}} */ + \\ var zigAnalysis= + , .{}); + try std.json.stringify( + data, + .{ + .whitespace = .{}, + .emit_null_optional_fields = false, + }, + out, + ); + try out.print(";", .{}); + + // last thing (that can fail) that we do is flush + try buffer.flush(); + } + + // copy main.js, index.html + var docs_dir = try self.module.comp.zig_lib_directory.handle.openDir("docs", .{}); + defer docs_dir.close(); + try docs_dir.copyFile("main.js", output_dir, "main.js", .{}); + try docs_dir.copyFile("index.html", output_dir, "index.html", .{}); +} + +/// Represents a chain of scopes, used to resolve decl references to the +/// corresponding entry in `self.decls`. +const Scope = struct { + parent: ?*Scope, + map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls` + enclosing_type: usize, // index into `types` + + /// Assumes all decls in present scope and upper scopes have already + /// been either fully resolved or at least reserved. + pub fn resolveDeclName(self: Scope, string_table_idx: u32) usize { + var cur: ?*const Scope = &self; + return while (cur) |s| : (cur = s.parent) { + break s.map.get(string_table_idx) orelse continue; + } else unreachable; + } + + pub fn insertDeclRef( + self: *Scope, + arena: std.mem.Allocator, + decl_name_index: u32, // decl name + decls_slot_index: usize, + ) !void { + try self.map.put(arena, decl_name_index, decls_slot_index); + } +}; + +/// The output of our analysis process. +const DocData = struct { + typeKinds: []const []const u8 = std.meta.fieldNames(DocTypeKinds), + rootPkg: u32 = 0, + rootPkgName: []const u8, + params: struct { + zigId: []const u8 = "arst", + zigVersion: []const u8 = build_options.version, + target: []const u8 = "arst", + rootName: []const u8, + builds: []const struct { target: []const u8 } = &.{ + .{ .target = "arst" }, + }, + }, + packages: []const DocPackage, + errors: []struct {} = &.{}, + + // non-hardcoded stuff + astNodes: []AstNode, + calls: []Call, + files: std.AutoArrayHashMapUnmanaged(*File, usize), + types: []Type, + decls: []Decl, + exprs: []Expr, + comptimeExprs: []ComptimeExpr, + const Call = struct { + func: Expr, + args: []Expr, + ret: Expr, + }; + + pub fn jsonStringify( + self: DocData, + opts: std.json.StringifyOptions, + w: anytype, + ) !void { + var jsw = std.json.writeStream(w, 15); + try jsw.beginObject(); + inline for (comptime std.meta.tags(std.meta.FieldEnum(DocData))) |f| { + const f_name = @tagName(f); + try jsw.objectField(f_name); + switch (f) { + .files => try writeFileTableToJson(self.files, &jsw), + else => { + try std.json.stringify(@field(self, f_name), opts, w); + jsw.state_index -= 1; + }, + } + } + try jsw.endObject(); + } + /// All the type "families" as described by `std.builtin.TypeId` + /// plus a couple extra that are unique to our use case. + /// + /// `Unanalyzed` is used so that we can refer to types that have started + /// analysis but that haven't been fully analyzed yet (in case we find + /// self-referential stuff, like `@This()`). + /// + /// `ComptimeExpr` represents the result of a piece of comptime logic + /// that we weren't able to analyze fully. Examples of that are comptime + /// function calls and comptime if / switch / ... expressions. + const DocTypeKinds = @typeInfo(Type).Union.tag_type.?; + + const ComptimeExpr = struct { + code: []const u8, + }; + const DocPackage = struct { + name: []const u8 = "(root)", + file: usize = 0, // index into `files` + main: usize = 0, // index into `types` + table: std.AutoHashMapUnmanaged(*Package, TableEntry), + pub const TableEntry = struct { + name: []const u8, + value: usize, + }; + + pub fn jsonStringify( + self: DocPackage, + opts: std.json.StringifyOptions, + w: anytype, + ) !void { + var jsw = std.json.writeStream(w, 15); + try jsw.beginObject(); + inline for (comptime std.meta.tags(std.meta.FieldEnum(DocPackage))) |f| { + const f_name = @tagName(f); + try jsw.objectField(f_name); + switch (f) { + .table => try writePackageTableToJson(self.table, &jsw), + else => { + try std.json.stringify(@field(self, f_name), opts, w); + jsw.state_index -= 1; + }, + } + } + try jsw.endObject(); + } + }; + + const Decl = struct { + name: []const u8, + kind: []const u8, + isTest: bool, + src: usize, // index into astNodes + value: WalkResult, + // The index in astNodes of the `test declname { }` node + decltest: ?usize = null, + _analyzed: bool, // omitted in json data + }; + + const AstNode = struct { + file: usize = 0, // index into files + line: usize = 0, + col: usize = 0, + name: ?[]const u8 = null, + docs: ?[]const u8 = null, + fields: ?[]usize = null, // index into astNodes + @"comptime": bool = false, + }; + + const Type = union(enum) { + Unanalyzed: struct {}, + Type: struct { name: []const u8 }, + Void: struct { name: []const u8 }, + Bool: struct { name: []const u8 }, + NoReturn: struct { name: []const u8 }, + Int: struct { name: []const u8 }, + Float: struct { name: []const u8 }, + Pointer: struct { + size: std.builtin.TypeInfo.Pointer.Size, + child: Expr, + sentinel: ?Expr = null, + @"align": ?Expr = null, + address_space: ?Expr = null, + bit_start: ?Expr = null, + host_size: ?Expr = null, + is_ref: bool = false, + is_allowzero: bool = false, + is_mutable: bool = false, + is_volatile: bool = false, + has_sentinel: bool = false, + has_align: bool = false, + has_addrspace: bool = false, + has_bit_range: bool = false, + }, + Array: struct { + len: Expr, + child: Expr, + sentinel: ?Expr = null, + }, + Struct: struct { + name: []const u8, + src: usize, // index into astNodes + privDecls: []usize = &.{}, // index into decls + pubDecls: []usize = &.{}, // index into decls + fields: ?[]Expr = null, // (use src->fields to find names) + line_number: usize, + outer_decl: usize, + ast: usize, + }, + ComptimeExpr: struct { name: []const u8 }, + ComptimeFloat: struct { name: []const u8 }, + ComptimeInt: struct { name: []const u8 }, + Undefined: struct { name: []const u8 }, + Null: struct { name: []const u8 }, + Optional: struct { + name: []const u8, + child: Expr, + }, + ErrorUnion: struct { lhs: Expr, rhs: Expr }, + // ErrorUnion: struct { name: []const u8 }, + ErrorSet: struct { + name: []const u8, + fields: ?[]const Field = null, + // TODO: fn field for inferred error sets? + }, + Enum: struct { + name: []const u8, + src: usize, // index into astNodes + privDecls: []usize = &.{}, // index into decls + pubDecls: []usize = &.{}, // index into decls + ast: usize, + // (use src->fields to find field names) + }, + Union: struct { + name: []const u8, + src: usize, // index into astNodes + privDecls: []usize = &.{}, // index into decls + pubDecls: []usize = &.{}, // index into decls + fields: []Expr = &.{}, // (use src->fields to find names) + ast: usize, + }, + Fn: struct { + name: []const u8, + src: ?usize = null, // index into `astNodes` + ret: Expr, + generic_ret: ?Expr = null, + params: ?[]Expr = null, // (use src->fields to find names) + lib_name: []const u8 = "", + is_var_args: bool = false, + is_inferred_error: bool = false, + has_lib_name: bool = false, + has_cc: bool = false, + cc: ?usize = null, + @"align": ?usize = null, + has_align: bool = false, + is_test: bool = false, + is_extern: bool = false, + }, + BoundFn: struct { name: []const u8 }, + Opaque: struct { name: []const u8 }, + Frame: struct { name: []const u8 }, + AnyFrame: struct { name: []const u8 }, + Vector: struct { name: []const u8 }, + EnumLiteral: struct { name: []const u8 }, + + const Field = struct { + name: []const u8, + docs: []const u8, + }; + + pub fn jsonStringify( + self: Type, + opts: std.json.StringifyOptions, + w: anytype, + ) !void { + const active_tag = std.meta.activeTag(self); + var jsw = std.json.writeStream(w, 15); + try jsw.beginObject(); + try jsw.objectField("kind"); + try jsw.emitNumber(@enumToInt(active_tag)); + inline for (comptime std.meta.fields(Type)) |case| { + if (@field(Type, case.name) == active_tag) { + const current_value = @field(self, case.name); + inline for (comptime std.meta.fields(case.field_type)) |f| { + try jsw.objectField(f.name); + if (f.field_type == std.builtin.TypeInfo.Pointer.Size) { + try jsw.emitNumber(@enumToInt(@field(current_value, f.name))); + } else { + try std.json.stringify(@field(current_value, f.name), opts, w); + jsw.state_index -= 1; + } + } + } + } + try jsw.endObject(); + } + }; + + /// An Expr represents the (untyped) result of analizing instructions. + /// The data is normalized, which means that an Expr that results in a + /// type definition will hold an index into `self.types`. + pub const Expr = union(enum) { + comptimeExpr: usize, // index in `comptimeExprs` + void: struct {}, + @"unreachable": struct {}, + @"null": struct {}, + @"undefined": struct {}, + @"struct": []FieldVal, + bool: bool, + @"anytype": struct {}, + @"&": usize, // index in `exprs` + type: usize, // index in `types` + this: usize, // index in `types` + declRef: usize, // index in `decls` + fieldRef: FieldRef, + refPath: []Expr, + int: struct { + value: u64, // direct value + negated: bool = false, + }, + int_big: struct { + value: []const u8, // direct value + negated: bool = false, + }, + float: f64, // direct value + float128: f128, // direct value + array: []usize, // index in `exprs` + call: usize, // index in `calls` + enumLiteral: []const u8, // direct value + alignOf: usize, // index in `exprs` + typeOf: usize, // index in `exprs` + typeOf_peer: []usize, + errorUnion: usize, // index in `exprs` + as: As, + sizeOf: usize, // index in `exprs` + bitSizeOf: usize, // index in `exprs` + enumToInt: usize, // index in `exprs` + compileError: []const u8, + errorSets: usize, + string: []const u8, // direct value + sliceIndex: usize, + slice: Slice, + cmpxchgIndex: usize, + cmpxchg: Cmpxchg, + builtin: Builtin, + builtinIndex: usize, + builtinBin: BuiltinBin, + builtinBinIndex: usize, + switchIndex: usize, // index in `exprs` + switchOp: SwitchOp, + binOp: BinOp, + binOpIndex: usize, + const BinOp = struct { + lhs: usize, // index in `exprs` + rhs: usize, // index in `exprs` + name: []const u8 = "", // tag name + }; + const SwitchOp = struct { + cond_index: usize, + file_name: []const u8, + ast: usize, + outer_decl: usize, // index in `types` + }; + const BuiltinBin = struct { + name: []const u8 = "", // fn name + lhs: usize, // index in `exprs` + rhs: usize, // index in `exprs` + }; + const Builtin = struct { + name: []const u8 = "", // fn name + param: usize, // index in `exprs` + }; + const Slice = struct { + lhs: usize, // index in `exprs` + start: usize, + end: ?usize = null, + sentinel: ?usize = null, // index in `exprs` + }; + const Cmpxchg = struct { name: []const u8, type: usize, ptr: usize, expected_value: usize, new_value: usize, success_order: usize, failure_order: usize }; + 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 + }; + + const FieldVal = struct { + name: []const u8, + val: WalkResult, + }; + + pub fn jsonStringify( + self: Expr, + opt: std.json.StringifyOptions, + w: anytype, + ) !void { + const active_tag = std.meta.activeTag(self); + var jsw = std.json.writeStream(w, 15); + try jsw.beginObject(); + try jsw.objectField(@tagName(active_tag)); + inline for (comptime std.meta.fields(Expr)) |case| { + if (@field(Expr, case.name) == active_tag) { + switch (active_tag) { + .int => { + if (self.int.negated) try w.writeAll("-"); + try jsw.emitNumber(self.int.value); + }, + .int_big => { + + //@panic("TODO: json serialization of big ints!"); + //if (v.negated) try w.writeAll("-"); + //try jsw.emitNumber(v.value); + }, + else => { + try std.json.stringify(@field(self, case.name), opt, w); + jsw.state_index -= 1; + // TODO: we should not reach into the state of the + // json writer, but alas, this is what's + // necessary with the current api. + // would be nice to have a proper integration + // between the json writer and the generic + // std.json.stringify implementation + }, + } + } + } + try jsw.endObject(); + } + }; + + /// A WalkResult represents the result of the analysis process done to a + /// a Zir instruction. Walk results carry type information either inferred + /// from the context (eg string literals are pointers to null-terminated + /// arrays), or because of @as() instructions. + /// Since the type information is only needed in certain contexts, the + /// underlying normalized data (Expr) is untyped. + const WalkResult = struct { + typeRef: ?Expr = null, // index in `exprs` + expr: Expr, // index in `exprs` + }; +}; + +const AutodocErrors = error{ + OutOfMemory, + CurrentWorkingDirectoryUnlinked, + Unexpected, +}; + +/// Called when we need to analyze a Zir instruction. +/// For example it gets called by `generateZirData` on instruction 0, +/// which represents the top-level struct corresponding to the root file. +/// Note that in some situations where we're analyzing code that only allows +/// for a limited subset of Zig syntax, we don't always resort to calling +/// `walkInstruction` and instead sometimes we handle Zir directly. +/// The best example of that are instructions corresponding to function +/// params, as those can only occur while analyzing a function definition. +fn walkInstruction( + self: *Autodoc, + file: *File, + parent_scope: *Scope, + inst_index: usize, + need_type: bool, // true if the caller needs us to provide also a typeRef +) AutodocErrors!DocData.WalkResult { + const tags = file.zir.instructions.items(.tag); + const data = file.zir.instructions.items(.data); + + // We assume that the topmost ast_node entry corresponds to our decl + const self_ast_node_index = self.ast_nodes.items.len - 1; + + switch (tags[inst_index]) { + else => { + printWithContext( + file, + inst_index, + "TODO: implement `{s}` for walkInstruction\n\n", + .{@tagName(tags[inst_index])}, + ); + return self.cteTodo(@tagName(tags[inst_index])); + }, + .import => { + const str_tok = data[inst_index].str_tok; + var path = str_tok.get(file.zir); + + const maybe_other_package: ?*Package = blk: { + if (self.module.main_pkg_in_std and std.mem.eql(u8, path, "std")) { + path = "root"; + break :blk self.module.main_pkg; + } else { + break :blk file.pkg.table.get(path); + } + }; + // importFile cannot error out since all files + // are already loaded at this point + if (maybe_other_package) |other_package| { + const result = try self.packages.getOrPut(self.arena, other_package); + + // Immediately add this package to the import table of our + // current package, regardless of wether it's new or not. + if (self.packages.getPtr(file.pkg)) |current_package| { + // TODO: apparently, in the stdlib a file gets analized before + // its package gets added. I guess we're importing a file + // that belongs to another package through its file path? + // (ie not through its package name). + // We're bailing for now, but maybe we shouldn't? + _ = try current_package.table.getOrPutValue( + self.arena, + other_package, + .{ + .name = path, + .value = self.packages.getIndex(other_package).?, + }, + ); + } + + if (result.found_existing) { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = result.value_ptr.main }, + }; + } + + // create a new package entry + const main_type_index = self.types.items.len; + result.value_ptr.* = .{ + .name = path, + .main = main_type_index, + .table = .{}, + }; + + // TODO: Add this package as a dependency to the current pakcage + // TODO: this seems something that could be done in bulk + // at the beginning or the end, or something. + const root_src_dir = other_package.root_src_directory; + const root_src_path = other_package.root_src_path; + const joined_src_path = try root_src_dir.join(self.arena, &.{root_src_path}); + defer self.arena.free(joined_src_path); + + const abs_root_src_path = try std.fs.path.resolve(self.arena, &.{ ".", joined_src_path }); + defer self.arena.free(abs_root_src_path); + + const new_file = self.module.import_table.get(abs_root_src_path).?; + + var root_scope = Scope{ .parent = null, .enclosing_type = main_type_index }; + try self.ast_nodes.append(self.arena, .{ .name = "(root)" }); + try self.files.put(self.arena, new_file, main_type_index); + return self.walkInstruction( + new_file, + &root_scope, + Zir.main_struct_inst, + false, + ); + } + + const new_file = self.module.importFile(file, path) catch unreachable; + const result = try self.files.getOrPut(self.arena, new_file.file); + if (result.found_existing) { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = result.value_ptr.* }, + }; + } + + result.value_ptr.* = self.types.items.len; + + var new_scope = Scope{ + .parent = null, + .enclosing_type = self.types.items.len, + }; + + return self.walkInstruction( + new_file.file, + &new_scope, + Zir.main_struct_inst, + need_type, + ); + }, + .ret_node => { + const un_node = data[inst_index].un_node; + return self.walkRef(file, parent_scope, un_node.operand, false); + }, + .ret_load => { + const un_node = data[inst_index].un_node; + const res_ptr_ref = un_node.operand; + const res_ptr_inst = @enumToInt(res_ptr_ref) - Ref.typed_value_map.len; + // TODO: this instruction doesn't let us know trivially if there's + // branching involved or not. For now here's the strat: + // We search backwarts until `ret_ptr` for `store_node`, + // if we find only one, then that's our value, if we find more + // than one, then it means that there's branching involved. + // Maybe. + + var i = inst_index - 1; + var result_ref: ?Ref = null; + while (i > res_ptr_inst) : (i -= 1) { + if (tags[i] == .store_node) { + const pl_node = data[i].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + if (extra.data.lhs == res_ptr_ref) { + // this store_load instruction is indeed pointing at + // the result location that we care about! + if (result_ref != null) return DocData.WalkResult{ + .expr = .{ .comptimeExpr = 0 }, + }; + result_ref = extra.data.rhs; + } + } + } + + if (result_ref) |rr| { + return self.walkRef(file, parent_scope, rr, need_type); + } + + return DocData.WalkResult{ + .expr = .{ .comptimeExpr = 0 }, + }; + }, + .closure_get => { + const inst_node = data[inst_index].inst_node; + return try self.walkInstruction(file, parent_scope, inst_node.inst, need_type); + }, + .closure_capture => { + const un_tok = data[inst_index].un_tok; + return try self.walkRef(file, parent_scope, un_tok.operand, need_type); + }, + .cmpxchg_strong, .cmpxchg_weak => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Cmpxchg, pl_node.payload_index); + + const last_type_index = self.exprs.items.len; + const last_type = self.exprs.items[last_type_index - 1]; + const type_index = self.exprs.items.len; + try self.exprs.append(self.arena, last_type); + + const ptr_index = self.exprs.items.len; + var ptr: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.ptr, + false, + ); + try self.exprs.append(self.arena, ptr.expr); + + const expected_value_index = self.exprs.items.len; + var expected_value: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.expected_value, + false, + ); + try self.exprs.append(self.arena, expected_value.expr); + + const new_value_index = self.exprs.items.len; + var new_value: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.new_value, + false, + ); + try self.exprs.append(self.arena, new_value.expr); + + const success_order_index = self.exprs.items.len; + var success_order: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.success_order, + false, + ); + try self.exprs.append(self.arena, success_order.expr); + + const failure_order_index = self.exprs.items.len; + var failure_order: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.failure_order, + false, + ); + try self.exprs.append(self.arena, failure_order.expr); + + const cmpxchg_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .cmpxchg = .{ .name = @tagName(tags[inst_index]), .type = type_index, .ptr = ptr_index, .expected_value = expected_value_index, .new_value = new_value_index, .success_order = success_order_index, .failure_order = failure_order_index } }); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .cmpxchgIndex = cmpxchg_index }, + }; + }, + .str => { + const str = data[inst_index].str.get(file.zir); + + const tRef: ?DocData.Expr = if (!need_type) null else blk: { + const arrTypeId = self.types.items.len; + try self.types.append(self.arena, .{ + .Array = .{ + .len = .{ .int = .{ .value = str.len } }, + .child = .{ .type = @enumToInt(Ref.u8_type) }, + .sentinel = .{ .int = .{ + .value = 0, + .negated = false, + } }, + }, + }); + // const sentinel: ?usize = if (ptr.flags.has_sentinel) 0 else null; + const ptrTypeId = self.types.items.len; + try self.types.append(self.arena, .{ + .Pointer = .{ + .size = .One, + .child = .{ .type = arrTypeId }, + .sentinel = .{ .int = .{ + .value = 0, + .negated = false, + } }, + .is_mutable = false, + }, + }); + break :blk .{ .type = ptrTypeId }; + }; + + return DocData.WalkResult{ + .typeRef = tRef, + .expr = .{ .string = str }, + }; + }, + .compile_error => { + const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + un_node.operand, + false, + ); + + return DocData.WalkResult{ + .expr = .{ + .compileError = switch (operand.expr) { + .string => |s| s, + else => "TODO: non-string @compileError arguments", + }, + }, + }; + }, + .enum_literal => { + const str_tok = data[inst_index].str_tok; + const literal = file.zir.nullTerminatedString(str_tok.start); + const type_index = self.types.items.len; + try self.types.append(self.arena, .{ + .EnumLiteral = .{ .name = "todo enum literal" }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = type_index }, + .expr = .{ .enumLiteral = literal }, + }; + }, + .int => { + const int = data[inst_index].int; + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = int } }, + }; + }, + .int_big => { + // @check + const str = data[inst_index].str.get(file.zir); + _ = str; + printWithContext( + file, + inst_index, + "TODO: implement `{s}` for walkInstruction\n\n", + .{@tagName(tags[inst_index])}, + ); + return self.cteTodo(@tagName(tags[inst_index])); + }, + + .slice_start => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.SliceStart, pl_node.payload_index); + + const slice_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var start: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.start, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const start_index = self.exprs.items.len; + try self.exprs.append(self.arena, start.expr); + self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } }; + + return DocData.WalkResult{ + .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, + .expr = .{ .sliceIndex = slice_index }, + }; + }, + .slice_end => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.SliceEnd, pl_node.payload_index); + + const slice_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var start: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.start, + false, + ); + var end: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.end, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const start_index = self.exprs.items.len; + try self.exprs.append(self.arena, start.expr); + const end_index = self.exprs.items.len; + try self.exprs.append(self.arena, end.expr); + self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } }; + + return DocData.WalkResult{ + .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, + .expr = .{ .sliceIndex = slice_index }, + }; + }, + .slice_sentinel => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.SliceSentinel, pl_node.payload_index); + + const slice_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var start: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.start, + false, + ); + var end: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.end, + false, + ); + var sentinel: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.sentinel, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const start_index = self.exprs.items.len; + try self.exprs.append(self.arena, start.expr); + const end_index = self.exprs.items.len; + try self.exprs.append(self.arena, end.expr); + const sentinel_index = self.exprs.items.len; + try self.exprs.append(self.arena, sentinel.expr); + self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } }; + + return DocData.WalkResult{ + .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, + .expr = .{ .sliceIndex = slice_index }, + }; + }, + + // @check array_cat and array_mul + .add, + .addwrap, + .add_sat, + .sub, + .subwrap, + .sub_sat, + .mul, + .mulwrap, + .mul_sat, + .div, + .shl, + .shl_sat, + .shr, + .bit_or, + .bit_and, + // @check still not working when applied in std + // .array_cat, + // .array_mul, + => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + + const binop_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .binOp = .{ .lhs = 0, .rhs = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.rhs, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const rhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, rhs.expr); + self.exprs.items[binop_index] = .{ .binOp = .{ + .name = @tagName(tags[inst_index]), + .lhs = lhs_index, + .rhs = rhs_index, + } }; + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .binOpIndex = binop_index }, + }; + }, + + // builtin functions + .align_of, + .bool_to_int, + .embed_file, + .error_name, + .panic, + .set_cold, // @check + .set_runtime_safety, // @check + .sqrt, + .sin, + .cos, + .tan, + .exp, + .exp2, + .log, + .log2, + .log10, + .fabs, + .floor, + .ceil, + .trunc, + .round, + .tag_name, + .reify, + .type_name, + .frame_type, + .frame_size, + .ptr_to_int, + .minimum, + .maximum, + .bit_not, + // @check + .clz, + .ctz, + .pop_count, + .byte_swap, + .bit_reverse, + => { + const un_node = data[inst_index].un_node; + const bin_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .builtin = .{ .param = 0 } }); + const param = try self.walkRef(file, parent_scope, un_node.operand, false); + + const param_index = self.exprs.items.len; + try self.exprs.append(self.arena, param.expr); + + self.exprs.items[bin_index] = .{ .builtin = .{ .name = @tagName(tags[inst_index]), .param = param_index } }; + + return DocData.WalkResult{ + .typeRef = param.typeRef orelse .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .builtinIndex = bin_index }, + }; + }, + + .float_to_int, + .int_to_float, + .int_to_ptr, + .int_to_enum, + .float_cast, + .int_cast, + .ptr_cast, + .truncate, + .align_cast, + .has_decl, + .has_field, + .div_exact, + .div_floor, + .div_trunc, + .mod, + .rem, + .mod_rem, + .shl_exact, + .shr_exact, + .bitcast, + .vector_type, + // @check + .bit_offset_of, + .offset_of, + .splat, + .reduce, + => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + + const binop_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .builtinBin = .{ .lhs = 0, .rhs = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.rhs, + false, + ); + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const rhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, rhs.expr); + self.exprs.items[binop_index] = .{ .builtinBin = .{ .name = @tagName(tags[inst_index]), .lhs = lhs_index, .rhs = rhs_index } }; + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .builtinBinIndex = binop_index }, + }; + }, + .error_union_type => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.rhs, + false, + ); + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .ErrorUnion = .{ + .lhs = lhs.expr, + .rhs = rhs.expr, + } }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .errorUnion = type_slot_index }, + }; + }, + .merge_error_sets => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.lhs, + false, + ); + var rhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + extra.data.rhs, + false, + ); + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .ErrorUnion = .{ + .lhs = lhs.expr, + .rhs = rhs.expr, + } }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .errorSets = type_slot_index }, + }; + }, + // .elem_type => { + // const un_node = data[inst_index].un_node; + + // var operand: DocData.WalkResult = try self.walkRef( + // file, + // parent_scope, + // un_node.operand, + // false, + // ); + + // return operand; + // }, + .ptr_type_simple => { + const ptr = data[inst_index].ptr_type_simple; + const elem_type_ref = try self.walkRef(file, parent_scope, ptr.elem_type, false); + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .Pointer = .{ .size = ptr.size, .child = elem_type_ref.expr, .is_mutable = ptr.is_mutable, .is_volatile = ptr.is_volatile, .is_allowzero = ptr.is_allowzero }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .ptr_type => { + const ptr = data[inst_index].ptr_type; + const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index); + var extra_index = extra.end; + + const type_slot_index = self.types.items.len; + const elem_type_ref = try self.walkRef( + file, + parent_scope, + extra.data.elem_type, + false, + ); + + // @check if `addrspace`, `bit_start` and `host_size` really need to be + // present in json + var sentinel: ?DocData.Expr = null; + if (ptr.flags.has_sentinel) { + const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + const ref_result = try self.walkRef(file, parent_scope, ref, false); + sentinel = ref_result.expr; + extra_index += 1; + } + + var @"align": ?DocData.Expr = null; + if (ptr.flags.has_align) { + const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + const ref_result = try self.walkRef(file, parent_scope, ref, false); + @"align" = ref_result.expr; + extra_index += 1; + } + var address_space: ?DocData.Expr = null; + if (ptr.flags.has_addrspace) { + const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + const ref_result = try self.walkRef(file, parent_scope, ref, false); + address_space = ref_result.expr; + extra_index += 1; + } + var bit_start: ?DocData.Expr = null; + if (ptr.flags.has_bit_range) { + const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + const ref_result = try self.walkRef(file, parent_scope, ref, false); + address_space = ref_result.expr; + extra_index += 1; + } + + var host_size: ?DocData.Expr = null; + if (ptr.flags.has_bit_range) { + const ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + const ref_result = try self.walkRef(file, parent_scope, ref, false); + host_size = ref_result.expr; + } + + try self.types.append(self.arena, .{ + .Pointer = .{ + .size = ptr.size, + .child = elem_type_ref.expr, + .has_align = ptr.flags.has_align, + .@"align" = @"align", + .has_addrspace = ptr.flags.has_addrspace, + .address_space = address_space, + .has_sentinel = ptr.flags.has_sentinel, + .sentinel = sentinel, + .is_mutable = ptr.flags.is_mutable, + .is_volatile = ptr.flags.is_volatile, + .has_bit_range = ptr.flags.has_bit_range, + .bit_start = bit_start, + .host_size = host_size, + }, + }); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .array_type => { + const pl_node = data[inst_index].pl_node; + const bin = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index).data; + const len = try self.walkRef(file, parent_scope, bin.lhs, false); + const child = try self.walkRef(file, parent_scope, bin.rhs, false); + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .Array = .{ + .len = len.expr, + .child = child.expr, + }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .array_type_sentinel => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.ArrayTypeSentinel, pl_node.payload_index); + const len = try self.walkRef(file, parent_scope, extra.data.len, false); + const sentinel = try self.walkRef(file, parent_scope, extra.data.sentinel, false); + const elem_type = try self.walkRef(file, parent_scope, extra.data.elem_type, false); + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .Array = .{ + .len = len.expr, + .child = elem_type.expr, + .sentinel = sentinel.expr, + }, + }); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .array_init => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index); + const operands = file.zir.refSlice(extra.end, extra.data.operands_len); + const array_data = try self.arena.alloc(usize, operands.len - 1); + + std.debug.assert(operands.len > 0); + var array_type = try self.walkRef(file, parent_scope, operands[0], false); + + for (operands[1..]) |op, idx| { + const wr = try self.walkRef(file, parent_scope, op, false); + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, wr.expr); + array_data[idx] = expr_index; + } + + return DocData.WalkResult{ + .typeRef = array_type.expr, + .expr = .{ .array = array_data }, + }; + }, + .array_init_anon => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index); + const operands = file.zir.refSlice(extra.end, extra.data.operands_len); + const array_data = try self.arena.alloc(usize, operands.len); + + for (operands) |op, idx| { + const wr = try self.walkRef(file, parent_scope, op, false); + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, wr.expr); + array_data[idx] = expr_index; + } + + return DocData.WalkResult{ + .typeRef = null, + .expr = .{ .array = array_data }, + }; + }, + .array_init_ref => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index); + const operands = file.zir.refSlice(extra.end, extra.data.operands_len); + const array_data = try self.arena.alloc(usize, operands.len - 1); + + std.debug.assert(operands.len > 0); + var array_type = try self.walkRef(file, parent_scope, operands[0], false); + + for (operands[1..]) |op, idx| { + const wr = try self.walkRef(file, parent_scope, op, false); + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, wr.expr); + array_data[idx] = expr_index; + } + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .Pointer = .{ + .size = .One, + .child = array_type.expr, + }, + }); + + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .array = array_data }); + + return DocData.WalkResult{ + .typeRef = .{ .type = type_slot_index }, + .expr = .{ .@"&" = expr_index }, + }; + }, + .array_init_anon_ref => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index); + const operands = file.zir.refSlice(extra.end, extra.data.operands_len); + const array_data = try self.arena.alloc(usize, operands.len); + + for (operands) |op, idx| { + const wr = try self.walkRef(file, parent_scope, op, false); + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, wr.expr); + array_data[idx] = expr_index; + } + + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .array = array_data }); + + return DocData.WalkResult{ + .typeRef = null, + .expr = .{ .@"&" = expr_index }, + }; + }, + .float => { + const float = data[inst_index].float; + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_float_type) }, + .expr = .{ .float = float }, + }; + }, + // @check: In frontend I'm handling float128 with `.toFixed(2)` + .float128 => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Float128, pl_node.payload_index); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_float_type) }, + .expr = .{ .float128 = extra.data.get() }, + }; + }, + .negate => { + const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + un_node.operand, + need_type, + ); + switch (operand.expr) { + .int => |*int| int.negated = true, + else => { + printWithContext( + file, + inst_index, + "TODO: support negation for more types", + .{}, + ); + }, + } + return operand; + }, + .size_of => { + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + false, + ); + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .sizeOf = operand_index }, + }; + }, + .bit_size_of => { + // not working correctly with `align()` + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + need_type, + ); + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + return DocData.WalkResult{ + .typeRef = operand.typeRef, + .expr = .{ .bitSizeOf = operand_index }, + }; + }, + .enum_to_int => { + // not working correctly with `align()` + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + false, + ); + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .enumToInt = operand_index }, + }; + }, + .switch_block => { + // WIP + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.SwitchBlock, pl_node.payload_index); + const cond_index = self.exprs.items.len; + _ = try self.walkRef(file, parent_scope, extra.data.operand, false); + + const ast_index = self.ast_nodes.items.len; + const type_index = self.types.items.len - 1; + + // const ast_line = self.ast_nodes.items[ast_index - 1]; + + // const sep = "=" ** 200; + // log.debug("{s}", .{sep}); + // log.debug("SWITCH BLOCK", .{}); + // log.debug("extra = {any}", .{extra}); + // log.debug("outer_decl = {any}", .{self.types.items[type_index]}); + // log.debug("ast_lines = {}", .{ast_line}); + // log.debug("{s}", .{sep}); + + const switch_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .switchOp = .{ .cond_index = cond_index, .file_name = file.sub_file_path, .ast = ast_index, .outer_decl = type_index } }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .switchIndex = switch_index }, + }; + }, + .switch_cond => { + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + need_type, + ); + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + // const ast_index = self.ast_nodes.items.len; + // const sep = "=" ** 200; + // log.debug("{s}", .{sep}); + // log.debug("SWITCH COND", .{}); + // log.debug("ast index = {}", .{ast_index}); + // log.debug("ast previous = {}", .{self.ast_nodes.items[ast_index - 1]}); + // log.debug("{s}", .{sep}); + + return DocData.WalkResult{ + .typeRef = operand.typeRef, + .expr = .{ .typeOf = operand_index }, + }; + }, + + .typeof => { + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + need_type, + ); + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + return DocData.WalkResult{ + .typeRef = operand.typeRef, + .expr = .{ .typeOf = operand_index }, + }; + }, + .typeof_builtin => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index); + const body = file.zir.extra[extra.end..][extra.data.body_len - 1]; + + var operand: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + data[body].@"break".operand, + false, + ); + + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + return DocData.WalkResult{ + .typeRef = operand.typeRef, + .expr = .{ .typeOf = operand_index }, + }; + }, + .type_info => { + // @check + const un_node = data[inst_index].un_node; + const operand = try self.walkRef( + file, + parent_scope, + un_node.operand, + need_type, + ); + + const operand_index = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + return DocData.WalkResult{ + .typeRef = operand.typeRef, + .expr = .{ .typeOf = operand_index }, + }; + }, + .as_node => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index); + const dest_type_walk = try self.walkRef( + file, + parent_scope, + extra.data.dest_type, + false, + ); + + const operand = try self.walkRef( + file, + parent_scope, + extra.data.operand, + false, + ); + + const operand_idx = self.exprs.items.len; + try self.exprs.append(self.arena, operand.expr); + + const dest_type_idx = self.exprs.items.len; + try self.exprs.append(self.arena, dest_type_walk.expr); + + // TODO: there's something wrong with how both `as` and `WalkrResult` + // try to store type information. + return DocData.WalkResult{ + .typeRef = dest_type_walk.expr, + .expr = .{ + .as = .{ + .typeRefArg = dest_type_idx, + .exprArg = operand_idx, + }, + }, + }; + }, + .optional_type => { + const un_node = data[inst_index].un_node; + const operand: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + un_node.operand, + false, + ); + + const operand_idx = self.types.items.len; + try self.types.append(self.arena, .{ + .Optional = .{ .name = "?TODO", .child = operand.expr }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = operand_idx }, + }; + }, + .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 => { + // TODO: field type uses Zir.Inst.FieldType, it just happens to have the + // same layout as Zir.Inst.Field :^) + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index); + + var path: std.ArrayListUnmanaged(DocData.Expr) = .{}; + var lhs = @enumToInt(extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs + + try path.append(self.arena, .{ + .string = file.zir.nullTerminatedString(extra.data.field_name_start), + }); + // Put inside path the starting index of each decl name that + // we encounter as we navigate through all the field_vals + while (tags[lhs] == .field_val or + tags[lhs] == .field_call_bind or + tags[lhs] == .field_ptr or + tags[lhs] == .field_type) + { + const lhs_extra = file.zir.extraData( + Zir.Inst.Field, + data[lhs].pl_node.payload_index, + ); + + try path.append(self.arena, .{ + .string = file.zir.nullTerminatedString(lhs_extra.data.field_name_start), + }); + lhs = @enumToInt(lhs_extra.data.lhs) - Ref.typed_value_map.len; // underflow = need to handle Refs + } + + // TODO: double check that we really don't need type info here + const wr = try self.walkInstruction(file, parent_scope, lhs, false); + try path.append(self.arena, wr.expr); + + // This way the data in `path` has the same ordering that the ref + // path has in the text: most general component first. + std.mem.reverse(DocData.Expr, path.items); + + // Righ now, every element of `path` is a string except its first + // element (at index 0). We're now going to attempt to resolve each + // string. If one or more components in this path are not yet fully + // analyzed, the path will only be solved partially, but we expect + // to eventually solve it fully(or give up in case of a + // comptimeExpr). This means that: + // - (1) Paths can be not fully analyzed temporarily, so any code + // that requires to know where a ref path leads to, neeeds to + // implement support for lazyness (see self.pending_ref_paths) + // - (2) Paths can sometimes never resolve fully. This means that + // any value that depends on that will have to become a + // comptimeExpr. + try self.tryResolveRefPath(file, lhs, path.items); + return DocData.WalkResult{ .expr = .{ .refPath = path.items } }; + }, + .int_type => { + const int_type = data[inst_index].int_type; + const sign = if (int_type.signedness == .unsigned) "u" else "i"; + const bits = int_type.bit_count; + const name = try std.fmt.allocPrint(self.arena, "{s}{}", .{ sign, bits }); + + try self.types.append(self.arena, .{ + .Int = .{ .name = name }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = self.types.items.len - 1 }, + }; + }, + .block => { + const res = DocData.WalkResult{ .expr = .{ + .comptimeExpr = self.comptime_exprs.items.len, + } }; + try self.comptime_exprs.append(self.arena, .{ + .code = "if(banana) 1 else 0", + }); + return res; + }, + .block_inline => { + return self.walkRef( + file, + parent_scope, + getBlockInlineBreak(file.zir, inst_index), + need_type, + ); + }, + .struct_init => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.StructInit, pl_node.payload_index); + const field_vals = try self.arena.alloc( + DocData.Expr.FieldVal, + extra.data.fields_len, + ); + + var type_ref: DocData.Expr = undefined; + var idx = extra.end; + for (field_vals) |*fv| { + const init_extra = file.zir.extraData(Zir.Inst.StructInit.Item, idx); + defer idx = init_extra.end; + + const field_name = blk: { + const field_inst_index = init_extra.data.field_type; + if (tags[field_inst_index] != .field_type) unreachable; + const field_pl_node = data[field_inst_index].pl_node; + const field_extra = file.zir.extraData( + Zir.Inst.FieldType, + field_pl_node.payload_index, + ); + + // On first iteration use field info to find out the struct type + if (idx == extra.end) { + const wr = try self.walkRef( + file, + parent_scope, + field_extra.data.container_type, + false, + ); + type_ref = wr.expr; + } + break :blk file.zir.nullTerminatedString(field_extra.data.name_start); + }; + const value = try self.walkRef( + file, + parent_scope, + init_extra.data.init, + need_type, + ); + fv.* = .{ .name = field_name, .val = value }; + } + + return DocData.WalkResult{ + .typeRef = type_ref, + .expr = .{ .@"struct" = field_vals }, + }; + }, + .struct_init_empty => { + const un_node = data[inst_index].un_node; + var operand: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + un_node.operand, + false, + ); + + _ = operand; + + // WIP + + printWithContext( + file, + inst_index, + "TODO: implement `{s}` for walkInstruction\n\n", + .{@tagName(tags[inst_index])}, + ); + return self.cteTodo(@tagName(tags[inst_index])); + }, + .error_set_decl => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.ErrorSetDecl, pl_node.payload_index); + const fields = try self.arena.alloc( + DocData.Type.Field, + extra.data.fields_len, + ); + var idx = extra.end; + for (fields) |*f| { + const name = file.zir.nullTerminatedString(file.zir.extra[idx]); + idx += 1; + + const docs = file.zir.nullTerminatedString(file.zir.extra[idx]); + idx += 1; + + f.* = .{ + .name = name, + .docs = docs, + }; + } + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .ErrorSet = .{ + .name = "todo errset", + .fields = fields, + }, + }); + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .param_anytype, .param_anytype_comptime => { + // @check if .param_anytype_comptime can be here + // Analysis of anytype function params happens in `.func`. + // This switch case handles the case where an expression depends + // on an anytype field. E.g.: `fn foo(bar: anytype) @TypeOf(bar)`. + // This means that we're looking at a generic expression. + const str_tok = data[inst_index].str_tok; + const name = str_tok.get(file.zir); + const cte_slot_index = self.comptime_exprs.items.len; + try self.comptime_exprs.append(self.arena, .{ + .code = name, + }); + return DocData.WalkResult{ .expr = .{ .comptimeExpr = cte_slot_index } }; + }, + .param, .param_comptime => { + // See .param_anytype for more information. + const pl_tok = data[inst_index].pl_tok; + const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index); + const name = file.zir.nullTerminatedString(extra.data.name); + + const cte_slot_index = self.comptime_exprs.items.len; + try self.comptime_exprs.append(self.arena, .{ + .code = name, + }); + return DocData.WalkResult{ .expr = .{ .comptimeExpr = cte_slot_index } }; + }, + .call => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index); + + const callee = try self.walkRef(file, parent_scope, extra.data.callee, need_type); + + const args_len = extra.data.flags.args_len; + var args = try self.arena.alloc(DocData.Expr, args_len); + const arg_refs = file.zir.refSlice(extra.end, args_len); + for (arg_refs) |ref, idx| { + // TODO: consider toggling need_type to true if we ever want + // to show discrepancies between the types of provided + // arguments and the types declared in the function + // signature for its parameters. + const wr = try self.walkRef(file, parent_scope, ref, false); + args[idx] = wr.expr; + } + + const cte_slot_index = self.comptime_exprs.items.len; + try self.comptime_exprs.append(self.arena, .{ + .code = "func call", + }); + + const call_slot_index = self.calls.items.len; + try self.calls.append(self.arena, .{ + .func = callee.expr, + .args = args, + .ret = .{ .comptimeExpr = cte_slot_index }, + }); + + return DocData.WalkResult{ + .typeRef = if (callee.typeRef) |tr| switch (tr) { + .type => |func_type_idx| self.types.items[func_type_idx].Fn.ret, + else => null, + } else null, + .expr = .{ .call = call_slot_index }, + }; + }, + .func, .func_inferred => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + const result = self.analyzeFunction( + file, + parent_scope, + inst_index, + self_ast_node_index, + type_slot_index, + ); + + return result; + }, + .func_fancy => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + const result = self.analyzeFancyFunction( + file, + parent_scope, + inst_index, + self_ast_node_index, + type_slot_index, + ); + + return result; + }, + .extended => { + const extended = data[inst_index].extended; + switch (extended.opcode) { + else => { + printWithContext( + file, + inst_index, + "TODO: implement `walkInstruction.extended` for {s}", + .{@tagName(extended.opcode)}, + ); + return self.cteTodo(@tagName(extended.opcode)); + }, + .typeof_peer => { + // Zir says it's a NodeMultiOp but in this case it's TypeOfPeer + const extra = file.zir.extraData(Zir.Inst.TypeOfPeer, extended.operand); + const args = file.zir.refSlice(extra.end, extended.small); + const array_data = try self.arena.alloc(usize, args.len); + + var array_type: ?DocData.Expr = null; + for (args) |arg, idx| { + const wr = try self.walkRef(file, parent_scope, arg, idx == 0); + if (idx == 0) { + array_type = wr.typeRef; + } + + const expr_index = self.exprs.items.len; + try self.exprs.append(self.arena, wr.expr); + array_data[idx] = expr_index; + } + + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ + .Array = .{ + .len = .{ + .int = .{ + .value = args.len, + .negated = false, + }, + }, + .child = .{ .type = 0 }, + }, + }); + const result = DocData.WalkResult{ + .typeRef = .{ .type = type_slot_index }, + .expr = .{ .typeOf_peer = array_data }, + }; + + return result; + }, + .opaque_decl => { + const small = @bitCast(Zir.Inst.OpaqueDecl.Small, extended.small); + var extra_index: usize = extended.operand; + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, file.zir.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + _ = src_node; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + _ = decls_len; + + const decls_bits = file.zir.extra[extra_index]; + _ = decls_bits; + + // const sep = "=" ** 200; + // log.debug("{s}", .{sep}); + // log.debug("small = {any}", .{small}); + // log.debug("src_node = {}", .{src_node}); + // log.debug("decls_len = {}", .{decls_len}); + // log.debug("decls_bit = {}", .{decls_bits}); + // log.debug("{s}", .{sep}); + const type_slot_index = self.types.items.len - 1; + try self.types.append(self.arena, .{ .Opaque = .{ .name = "TODO" } }); + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.anyopaque_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .variable => { + const small = @bitCast(Zir.Inst.ExtendedVar.Small, extended.small); + var extra_index: usize = extended.operand; + if (small.has_lib_name) extra_index += 1; + if (small.has_align) extra_index += 1; + + const value: DocData.WalkResult = if (small.has_init) .{ + .expr = .{ .void = .{} }, + } else .{ + .expr = .{ .void = .{} }, + }; + + return value; + }, + .union_decl => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + var scope: Scope = .{ + .parent = parent_scope, + .enclosing_type = type_slot_index, + }; + + const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, file.zir.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + _ = src_node; + + const tag_type: ?Ref = if (small.has_tag_type) blk: { + const tag_type = file.zir.extra[extra_index]; + extra_index += 1; + break :blk @intToEnum(Ref, tag_type); + } else null; + _ = tag_type; + + const body_len = if (small.has_body_len) blk: { + const body_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + _ = fields_len; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + + const decls_first_index = self.decls.items.len; + // Decl name lookahead for reserving slots in `scope` (and `decls`). + // Done to make sure that all decl refs can be resolved correctly, + // even if we haven't fully analyzed the decl yet. + { + var it = file.zir.declIterator(@intCast(u32, inst_index)); + try self.decls.resize(self.arena, decls_first_index + it.decls_len); + for (self.decls.items[decls_first_index..]) |*slot| { + slot._analyzed = false; + } + var decls_slot_index = decls_first_index; + while (it.next()) |d| : (decls_slot_index += 1) { + const decl_name_index = file.zir.extra[d.sub_index + 5]; + try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index); + } + } + + extra_index = try self.walkDecls( + file, + &scope, + decls_first_index, + decls_len, + &decl_indexes, + &priv_decl_indexes, + extra_index, + ); + + extra_index += body_len; + + var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( + self.arena, + fields_len, + ); + var field_name_indexes = try std.ArrayListUnmanaged(usize).initCapacity( + self.arena, + fields_len, + ); + try self.collectUnionFieldInfo( + file, + &scope, + fields_len, + &field_type_refs, + &field_name_indexes, + extra_index, + ); + + self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; + + self.types.items[type_slot_index] = .{ + .Union = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .fields = field_type_refs.items, .ast = self_ast_node_index }, + }; + + if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { + for (paths.items) |resume_info| { + try self.tryResolveRefPath( + resume_info.file, + inst_index, + resume_info.ref_path, + ); + } + + _ = self.ref_paths_pending_on_types.remove(type_slot_index); + // TODO: we should deallocate the arraylist that holds all the + // decl paths. not doing it now since it's arena-allocated + // anyway, but maybe we should put it elsewhere. + } + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .enum_decl => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + var scope: Scope = .{ + .parent = parent_scope, + .enclosing_type = type_slot_index, + }; + + const small = @bitCast(Zir.Inst.EnumDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, file.zir.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + _ = src_node; + + const tag_type: ?Ref = if (small.has_tag_type) blk: { + const tag_type = file.zir.extra[extra_index]; + extra_index += 1; + break :blk @intToEnum(Ref, tag_type); + } else null; + _ = tag_type; + + const body_len = if (small.has_body_len) blk: { + const body_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk body_len; + } else 0; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + _ = fields_len; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + + const decls_first_index = self.decls.items.len; + // Decl name lookahead for reserving slots in `scope` (and `decls`). + // Done to make sure that all decl refs can be resolved correctly, + // even if we haven't fully analyzed the decl yet. + { + var it = file.zir.declIterator(@intCast(u32, inst_index)); + try self.decls.resize(self.arena, decls_first_index + it.decls_len); + for (self.decls.items[decls_first_index..]) |*slot| { + slot._analyzed = false; + } + var decls_slot_index = decls_first_index; + while (it.next()) |d| : (decls_slot_index += 1) { + const decl_name_index = file.zir.extra[d.sub_index + 5]; + try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index); + } + } + + extra_index = try self.walkDecls( + file, + &scope, + decls_first_index, + decls_len, + &decl_indexes, + &priv_decl_indexes, + extra_index, + ); + + // const body = file.zir.extra[extra_index..][0..body_len]; + extra_index += body_len; + + var field_name_indexes: std.ArrayListUnmanaged(usize) = .{}; + { + var bit_bag_idx = extra_index; + var cur_bit_bag: u32 = undefined; + extra_index += std.math.divCeil(usize, fields_len, 32) catch unreachable; + + var idx: usize = 0; + while (idx < fields_len) : (idx += 1) { + if (idx % 32 == 0) { + cur_bit_bag = file.zir.extra[bit_bag_idx]; + bit_bag_idx += 1; + } + + const has_value = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + const field_name_index = file.zir.extra[extra_index]; + extra_index += 1; + + const doc_comment_index = file.zir.extra[extra_index]; + extra_index += 1; + + const value_ref: ?Ref = if (has_value) blk: { + const value_ref = file.zir.extra[extra_index]; + extra_index += 1; + break :blk @intToEnum(Ref, value_ref); + } else null; + _ = value_ref; + + const field_name = file.zir.nullTerminatedString(field_name_index); + + try field_name_indexes.append(self.arena, self.ast_nodes.items.len); + const doc_comment: ?[]const u8 = if (doc_comment_index != 0) + file.zir.nullTerminatedString(doc_comment_index) + else + null; + try self.ast_nodes.append(self.arena, .{ + .name = field_name, + .docs = doc_comment, + }); + } + } + + self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; + + self.types.items[type_slot_index] = .{ + .Enum = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .ast = self_ast_node_index }, + }; + if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { + for (paths.items) |resume_info| { + try self.tryResolveRefPath( + resume_info.file, + inst_index, + resume_info.ref_path, + ); + } + + _ = self.ref_paths_pending_on_types.remove(type_slot_index); + // TODO: we should deallocate the arraylist that holds all the + // decl paths. not doing it now since it's arena-allocated + // anyway, but maybe we should put it elsewhere. + } + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .struct_decl => { + const type_slot_index = self.types.items.len; + try self.types.append(self.arena, .{ .Unanalyzed = .{} }); + + var scope: Scope = .{ + .parent = parent_scope, + .enclosing_type = type_slot_index, + }; + + const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); + var extra_index: usize = extended.operand; + + const src_node: ?i32 = if (small.has_src_node) blk: { + const src_node = @bitCast(i32, file.zir.extra[extra_index]); + extra_index += 1; + break :blk src_node; + } else null; + _ = src_node; + + const fields_len = if (small.has_fields_len) blk: { + const fields_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk fields_len; + } else 0; + _ = fields_len; + + const decls_len = if (small.has_decls_len) blk: { + const decls_len = file.zir.extra[extra_index]; + extra_index += 1; + break :blk decls_len; + } else 0; + + var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; + + const decls_first_index = self.decls.items.len; + // Decl name lookahead for reserving slots in `scope` (and `decls`). + // Done to make sure that all decl refs can be resolved correctly, + // even if we haven't fully analyzed the decl yet. + { + var it = file.zir.declIterator(@intCast(u32, inst_index)); + try self.decls.resize(self.arena, decls_first_index + it.decls_len); + for (self.decls.items[decls_first_index..]) |*slot| { + slot._analyzed = false; + } + var decls_slot_index = decls_first_index; + while (it.next()) |d| : (decls_slot_index += 1) { + const decl_name_index = file.zir.extra[d.sub_index + 5]; + try scope.insertDeclRef(self.arena, decl_name_index, decls_slot_index); + } + } + + extra_index = try self.walkDecls( + file, + &scope, + decls_first_index, + decls_len, + &decl_indexes, + &priv_decl_indexes, + extra_index, + ); + + var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{}; + var field_name_indexes: std.ArrayListUnmanaged(usize) = .{}; + try self.collectStructFieldInfo( + file, + &scope, + fields_len, + &field_type_refs, + &field_name_indexes, + extra_index, + ); + + self.ast_nodes.items[self_ast_node_index].fields = field_name_indexes.items; + + self.types.items[type_slot_index] = .{ + .Struct = .{ .name = "todo_name", .src = self_ast_node_index, .privDecls = priv_decl_indexes.items, .pubDecls = decl_indexes.items, .fields = field_type_refs.items, .line_number = self.ast_nodes.items[self_ast_node_index].line, .outer_decl = type_slot_index - 1, .ast = self_ast_node_index }, + }; + if (self.ref_paths_pending_on_types.get(type_slot_index)) |paths| { + for (paths.items) |resume_info| { + try self.tryResolveRefPath( + resume_info.file, + inst_index, + resume_info.ref_path, + ); + } + + _ = self.ref_paths_pending_on_types.remove(type_slot_index); + // TODO: we should deallocate the arraylist that holds all the + // decl paths. not doing it now since it's arena-allocated + // anyway, but maybe we should put it elsewhere. + } + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; + }, + .this => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .this = parent_scope.enclosing_type }, + }; + }, + .error_to_int, + .int_to_error, + => { + const extra = file.zir.extraData(Zir.Inst.UnNode, extended.operand).data; + const bin_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .builtin = .{ .param = 0 } }); + const param = try self.walkRef(file, parent_scope, extra.operand, false); + + const param_index = self.exprs.items.len; + try self.exprs.append(self.arena, param.expr); + + self.exprs.items[bin_index] = .{ .builtin = .{ .name = @tagName(tags[inst_index]), .param = param_index } }; + + return DocData.WalkResult{ + .typeRef = param.typeRef orelse .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .builtinIndex = bin_index }, + }; + }, + } + }, + } +} + +/// Called by `walkInstruction` when encountering a container type. +/// Iterates over all decl definitions in its body and it also analyzes each +/// decl's body recursively by calling into `walkInstruction`. +/// +/// Does not append to `self.decls` directly because `walkInstruction` +/// is expected to look-ahead scan all decls and reserve `body_len` +/// slots in `self.decls`, which are then filled out by this function. +fn walkDecls( + self: *Autodoc, + file: *File, + scope: *Scope, + decls_first_index: usize, + decls_len: u32, + decl_indexes: *std.ArrayListUnmanaged(usize), + priv_decl_indexes: *std.ArrayListUnmanaged(usize), + extra_start: usize, +) AutodocErrors!usize { + const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; + var extra_index = extra_start + bit_bags_count; + var bit_bag_index: usize = extra_start; + var cur_bit_bag: u32 = undefined; + var decl_i: u32 = 0; + + while (decl_i < decls_len) : (decl_i += 1) { + const decls_slot_index = decls_first_index + decl_i; + + if (decl_i % 8 == 0) { + cur_bit_bag = file.zir.extra[bit_bag_index]; + bit_bag_index += 1; + } + const is_pub = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const is_exported = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + // const sub_index = extra_index; + + // const hash_u32s = file.zir.extra[extra_index..][0..4]; + extra_index += 4; + const line = file.zir.extra[extra_index]; + extra_index += 1; + const decl_name_index = file.zir.extra[extra_index]; + extra_index += 1; + const value_index = file.zir.extra[extra_index]; + extra_index += 1; + const doc_comment_index = file.zir.extra[extra_index]; + extra_index += 1; + + const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { + const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + extra_index += 1; + break :inst inst; + }; + _ = align_inst; + + const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { + const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + extra_index += 1; + break :inst inst; + }; + _ = section_inst; + + const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { + const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + extra_index += 1; + break :inst inst; + }; + _ = addrspace_inst; + + // const pub_str = if (is_pub) "pub " else ""; + // const hash_bytes = @bitCast([16]u8, hash_u32s.*); + + var is_test = false; // we discover if it's a test by lookin at its name + const name: []const u8 = blk: { + if (decl_name_index == 0) { + break :blk if (is_exported) "usingnamespace" else "comptime"; + } else if (decl_name_index == 1) { + is_test = true; + break :blk "test"; + } else if (decl_name_index == 2) { + is_test = true; + // TODO: remove temporary hack + break :blk "test"; + // // it is a decltest + // const decl_being_tested = scope.resolveDeclName(doc_comment_index); + // const ast_node_index = idx: { + // const idx = self.ast_nodes.items.len; + // const file_source = file.getSource(self.module.gpa) catch unreachable; // TODO fix this + // const source_of_decltest_function = srcloc: { + // const func_index = getBlockInlineBreak(file.zir, value_index); + // // a decltest is always a function + // const tag = file.zir.instructions.items(.tag)[Zir.refToIndex(func_index).?]; + // std.debug.assert(tag == .func_extended); + + // const pl_node = file.zir.instructions.items(.data)[Zir.refToIndex(func_index).?].pl_node; + // const extra = file.zir.extraData(Zir.Inst.ExtendedFunc, pl_node.payload_index); + // const bits = @bitCast(Zir.Inst.ExtendedFunc.Bits, extra.data.bits); + + // var extra_index_for_this_func: usize = extra.end; + // if (bits.has_lib_name) extra_index_for_this_func += 1; + // if (bits.has_cc) extra_index_for_this_func += 1; + // if (bits.has_align) extra_index_for_this_func += 1; + + // const ret_ty_body = file.zir.extra[extra_index_for_this_func..][0..extra.data.ret_body_len]; + // extra_index_for_this_func += ret_ty_body.len; + + // const body = file.zir.extra[extra_index_for_this_func..][0..extra.data.body_len]; + // extra_index_for_this_func += body.len; + + // var src_locs: Zir.Inst.Func.SrcLocs = undefined; + // if (body.len != 0) { + // src_locs = file.zir.extraData(Zir.Inst.Func.SrcLocs, extra_index_for_this_func).data; + // } else { + // src_locs = .{ + // .lbrace_line = line, + // .rbrace_line = line, + // .columns = 0, // TODO get columns when body.len == 0 + // }; + // } + // break :srcloc src_locs; + // }; + // const source_slice = slice: { + // var start_byte_offset: u32 = 0; + // var end_byte_offset: u32 = 0; + // const rbrace_col = @truncate(u16, source_of_decltest_function.columns >> 16); + // var lines: u32 = 0; + // for (file_source.bytes) |b, i| { + // if (b == '\n') { + // lines += 1; + // } + // if (lines == source_of_decltest_function.lbrace_line) { + // start_byte_offset = @intCast(u32, i); + // } + // if (lines == source_of_decltest_function.rbrace_line) { + // end_byte_offset = @intCast(u32, i) + rbrace_col; + // break; + // } + // } + // break :slice file_source.bytes[start_byte_offset..end_byte_offset]; + // }; + // try self.ast_nodes.append(self.arena, .{ + // .file = 0, + // .line = line, + // .col = 0, + // .name = try self.arena.dupe(u8, source_slice), + // }); + // break :idx idx; + // }; + // self.decls.items[decl_being_tested].decltest = ast_node_index; + // self.decls.items[decls_slot_index] = .{ + // ._analyzed = true, + // .name = "test", + // .isTest = true, + // .src = ast_node_index, + // .value = .{ .expr = .{ .type = 0 } }, + // .kind = "const", + // }; + // continue; + } else { + const raw_decl_name = file.zir.nullTerminatedString(decl_name_index); + if (raw_decl_name.len == 0) { + is_test = true; + break :blk file.zir.nullTerminatedString(decl_name_index + 1); + } else { + break :blk raw_decl_name; + } + } + }; + + const doc_comment: ?[]const u8 = if (doc_comment_index != 0) + file.zir.nullTerminatedString(doc_comment_index) + else + null; + + // astnode + const ast_node_index = idx: { + const idx = self.ast_nodes.items.len; + try self.ast_nodes.append(self.arena, .{ + .file = 0, + .line = line, + .col = 0, + .docs = doc_comment, + .fields = null, // walkInstruction will fill `fields` if necessary + }); + break :idx idx; + }; + + const walk_result = if (is_test) // TODO: decide if tests should show up at all + DocData.WalkResult{ .expr = .{ .void = .{} } } + else + try self.walkInstruction(file, scope, value_index, true); + + if (is_pub) { + try decl_indexes.append(self.arena, decls_slot_index); + } else { + try priv_decl_indexes.append(self.arena, decls_slot_index); + } + + // // decl.typeRef == decl.val...typeRef + // const decl_type_ref: DocData.TypeRef = switch (walk_result) { + // .int => |i| i.typeRef, + // .void => .{ .type = @enumToInt(Ref.void_type) }, + // .@"undefined", .@"null" => |v| v, + // .@"unreachable" => .{ .type = @enumToInt(Ref.noreturn_type) }, + // .@"struct" => |s| s.typeRef, + // .bool => .{ .type = @enumToInt(Ref.bool_type) }, + // .type => .{ .type = @enumToInt(Ref.type_type) }, + // // this last case is special becauese it's not pointing + // // at the type of the value, but rather at the value itself + // // the js better be aware ot this! + // .declRef => |d| .{ .declRef = d }, + // }; + + self.decls.items[decls_slot_index] = .{ + ._analyzed = true, + .name = name, + .isTest = is_test, + .src = ast_node_index, + //.typeRef = decl_type_ref, + .value = walk_result, + .kind = "const", // find where this information can be found + }; + + // Unblock any pending decl path that was waiting for this decl. + if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| { + for (paths.items) |resume_info| { + try self.tryResolveRefPath( + resume_info.file, + value_index, + resume_info.ref_path, + ); + } + + _ = self.ref_paths_pending_on_decls.remove(decls_slot_index); + // TODO: we should deallocate the arraylist that holds all the + // ref paths. not doing it now since it's arena-allocated + // anyway, but maybe we should put it elsewhere. + } + } + + return extra_index; +} + +/// An unresolved path has a non-string WalkResult at its beginnig, while every +/// other element is a string WalkResult. Resolving means iteratively map each +/// string to a Decl / Type / Call / etc. +/// +/// If we encounter an unanalyzed decl during the process, we append the +/// unsolved sub-path to `self.ref_paths_pending_on_decls` and bail out. +/// Same happens when a decl holds a type definition that hasn't been fully +/// analyzed yet (except that we append to `self.ref_paths_pending_on_types`. +/// +/// When walkDecls / walkInstruction finishes analyzing a decl / type, it will +/// then check if there's any pending ref path blocked on it and, if any, it +/// will progress their resolution by calling tryResolveRefPath again. +/// +/// Ref paths can also depend on other ref paths. See +/// `self.pending_ref_paths` for more info. +/// +/// A ref path that has a component that resolves into a comptimeExpr will +/// give up its resolution process entirely, leaving the remaining components +/// as strings. +fn tryResolveRefPath( + self: *Autodoc, + /// File from which the decl path originates. + file: *File, + inst_index: usize, // used only for panicWithContext + path: []DocData.Expr, +) AutodocErrors!void { + var i: usize = 0; + outer: while (i < path.len - 1) : (i += 1) { + const parent = path[i]; + const child_string = path[i + 1].string; // we expect to find a string union case + + var resolved_parent = parent; + var j: usize = 0; + while (j < 10_000) : (j += 1) { + switch (resolved_parent) { + else => break, + .this => |t| resolved_parent = .{ .type = t }, + .declRef => |decl_index| { + const decl = self.decls.items[decl_index]; + if (decl._analyzed) { + resolved_parent = decl.value.expr; + continue; + } + + // This decl path is pending completion + { + const res = try self.pending_ref_paths.getOrPut( + self.arena, + &path[path.len - 1], + ); + if (!res.found_existing) res.value_ptr.* = .{}; + } + + const res = try self.ref_paths_pending_on_decls.getOrPut( + self.arena, + decl_index, + ); + if (!res.found_existing) res.value_ptr.* = .{}; + try res.value_ptr.*.append(self.arena, .{ + .file = file, + .ref_path = path[i..path.len], + }); + + // We return instead doing `break :outer` to prevent the + // code after the :outer while loop to run, as it assumes + // that the path will have been fully analyzed (or we + // have given up because of a comptimeExpr). + return; + }, + .refPath => |rp| { + if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| { + try waiter_list.append(self.arena, .{ + .file = file, + .ref_path = path[i..path.len], + }); + + // This decl path is pending completion + { + const res = try self.pending_ref_paths.getOrPut( + self.arena, + &path[path.len - 1], + ); + if (!res.found_existing) res.value_ptr.* = .{}; + } + + return; + } + + // If the last element is a string or a CTE, then we give up, + // otherwise we resovle the parent to it and loop again. + // NOTE: we assume that if we find a string, it's because of + // a CTE component somewhere in the path. We know that the path + // is not pending futher evaluation because we just checked! + const last = rp[rp.len - 1]; + switch (last) { + .comptimeExpr, .string => break :outer, + else => { + resolved_parent = last; + continue; + }, + } + }, + } + } else { + panicWithContext( + file, + inst_index, + "exhausted eval quota for `{}`in tryResolveDecl\n", + .{resolved_parent}, + ); + } + + switch (resolved_parent) { + else => { + // NOTE: indirect references to types / decls should be handled + // in the switch above this one! + printWithContext( + file, + inst_index, + "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}", + .{ @tagName(resolved_parent), resolved_parent }, + ); + path[i + 1] = (try self.cteTodo("match failure")).expr; + continue :outer; + }, + .comptimeExpr, .call, .typeOf => { + // Since we hit a cte, we leave the remaining strings unresolved + // and completely give up on resolving this decl path. + //decl_path.hasCte = true; + break :outer; + }, + .type => |t_index| switch (self.types.items[t_index]) { + else => { + panicWithContext( + file, + inst_index, + "TODO: handle `{s}` in tryResolveDeclPath.type\nInfo: {}", + .{ @tagName(self.types.items[t_index]), resolved_parent }, + ); + }, + .Unanalyzed => { + // This decl path is pending completion + { + const res = try self.pending_ref_paths.getOrPut( + self.arena, + &path[path.len - 1], + ); + if (!res.found_existing) res.value_ptr.* = .{}; + } + + const res = try self.ref_paths_pending_on_types.getOrPut( + self.arena, + t_index, + ); + if (!res.found_existing) res.value_ptr.* = .{}; + try res.value_ptr.*.append(self.arena, .{ + .file = file, + .ref_path = path[i..path.len], + }); + + return; + }, + .Enum => |t_enum| { + for (t_enum.pubDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + for (t_enum.privDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + + for (self.ast_nodes.items[t_enum.src].fields.?) |ast_node, idx| { + const name = self.ast_nodes.items[ast_node].name.?; + if (std.mem.eql(u8, name, child_string)) { + // TODO: should we really create an artificial + // decl for this type? Probably not. + + path[i + 1] = .{ + .fieldRef = .{ + .type = t_index, + .index = idx, + }, + }; + continue :outer; + } + } + + // if we got here, our search failed + printWithContext( + file, + inst_index, + "failed to match `{s}` in enum", + .{child_string}, + ); + + path[i + 1] = (try self.cteTodo("match failure")).expr; + continue :outer; + }, + .Union => |t_union| { + for (t_union.pubDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + for (t_union.privDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + + for (self.ast_nodes.items[t_union.src].fields.?) |ast_node, idx| { + const name = self.ast_nodes.items[ast_node].name.?; + if (std.mem.eql(u8, name, child_string)) { + // TODO: should we really create an artificial + // decl for this type? Probably not. + + path[i + 1] = .{ + .fieldRef = .{ + .type = t_index, + .index = idx, + }, + }; + continue :outer; + } + } + + // if we got here, our search failed + printWithContext( + file, + inst_index, + "failed to match `{s}` in union", + .{child_string}, + ); + path[i + 1] = (try self.cteTodo("match failure")).expr; + continue :outer; + }, + + .Struct => |t_struct| { + for (t_struct.pubDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + for (t_struct.privDecls) |d| { + // TODO: this could be improved a lot + // by having our own string table! + const decl = self.decls.items[d]; + if (std.mem.eql(u8, decl.name, child_string)) { + path[i + 1] = .{ .declRef = d }; + continue :outer; + } + } + + for (self.ast_nodes.items[t_struct.src].fields.?) |ast_node, idx| { + const name = self.ast_nodes.items[ast_node].name.?; + if (std.mem.eql(u8, name, child_string)) { + // TODO: should we really create an artificial + // decl for this type? Probably not. + + path[i + 1] = .{ + .fieldRef = .{ + .type = t_index, + .index = idx, + }, + }; + continue :outer; + } + } + + // if we got here, our search failed + // printWithContext( + // file, + // inst_index, + // "failed to match `{s}` in struct", + // .{child_string}, + // ); + // path[i + 1] = (try self.cteTodo("match failure")).expr; + // + // that's working + path[i + 1] = (try self.cteTodo(child_string)).expr; + continue :outer; + }, + }, + } + } + + if (self.pending_ref_paths.get(&path[path.len - 1])) |waiter_list| { + // It's important to de-register oureslves as pending before + // attempting to resolve any other decl. + _ = self.pending_ref_paths.remove(&path[path.len - 1]); + + for (waiter_list.items) |resume_info| { + try self.tryResolveRefPath(resume_info.file, inst_index, resume_info.ref_path); + } + // TODO: this is where we should free waiter_list, but its in the arena + // that said, we might want to store it elsewhere and reclaim memory asap + } +} +fn analyzeFancyFunction( + self: *Autodoc, + file: *File, + scope: *Scope, + inst_index: usize, + self_ast_node_index: usize, + type_slot_index: usize, +) AutodocErrors!DocData.WalkResult { + const tags = file.zir.instructions.items(.tag); + const data = file.zir.instructions.items(.data); + const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index)); + + try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len); + var param_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( + self.arena, + fn_info.total_params_len, + ); + var param_ast_indexes = try std.ArrayListUnmanaged(usize).initCapacity( + self.arena, + fn_info.total_params_len, + ); + + // TODO: handle scope rules for fn parameters + for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| { + switch (tags[param_index]) { + else => { + panicWithContext( + file, + param_index, + "TODO: handle `{s}` in walkInstruction.func\n", + .{@tagName(tags[param_index])}, + ); + }, + .param_anytype, .param_anytype_comptime => { + // TODO: where are the doc comments? + const str_tok = data[param_index].str_tok; + + const name = str_tok.get(file.zir); + + param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len); + self.ast_nodes.appendAssumeCapacity(.{ + .name = name, + .docs = "", + .@"comptime" = tags[param_index] == .param_anytype_comptime, + }); + + param_type_refs.appendAssumeCapacity( + DocData.Expr{ .@"anytype" = .{} }, + ); + }, + .param, .param_comptime => { + const pl_tok = data[param_index].pl_tok; + const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index); + const doc_comment = if (extra.data.doc_comment != 0) + file.zir.nullTerminatedString(extra.data.doc_comment) + else + ""; + const name = file.zir.nullTerminatedString(extra.data.name); + + param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len); + try self.ast_nodes.append(self.arena, .{ + .name = name, + .docs = doc_comment, + .@"comptime" = tags[param_index] == .param_comptime, + }); + + const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1]; + const break_operand = data[break_index].@"break".operand; + const param_type_ref = try self.walkRef(file, scope, break_operand, false); + + param_type_refs.appendAssumeCapacity(param_type_ref.expr); + }, + } + } + + self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items; + + const inst_data = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); + + var extra_index: usize = extra.end; + + var lib_name: []const u8 = ""; + if (extra.data.bits.has_lib_name) { + lib_name = file.zir.nullTerminatedString(file.zir.extra[extra_index]); + extra_index += 1; + } + + var align_index: ?usize = null; + if (extra.data.bits.has_align_ref) { + const align_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + align_index = self.exprs.items.len; + _ = try self.walkRef(file, scope, align_ref, false); + extra_index += 1; + } else if (extra.data.bits.has_align_body) { + const align_body_len = file.zir.extra[extra_index]; + extra_index += 1; + const align_body = file.zir.extra[extra_index .. extra_index + align_body_len]; + _ = align_body; + // TODO: analyze the block (or bail with a comptimeExpr) + extra_index += align_body_len; + } else { + // default alignment + } + + var addrspace_index: ?usize = null; + if (extra.data.bits.has_addrspace_ref) { + const addrspace_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + addrspace_index = self.exprs.items.len; + _ = try self.walkRef(file, scope, addrspace_ref, false); + extra_index += 1; + } else if (extra.data.bits.has_addrspace_body) { + const addrspace_body_len = file.zir.extra[extra_index]; + extra_index += 1; + const addrspace_body = file.zir.extra[extra_index .. extra_index + addrspace_body_len]; + _ = addrspace_body; + // TODO: analyze the block (or bail with a comptimeExpr) + extra_index += addrspace_body_len; + } else { + // default alignment + } + + var section_index: ?usize = null; + if (extra.data.bits.has_section_ref) { + const section_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + section_index = self.exprs.items.len; + _ = try self.walkRef(file, scope, section_ref, false); + extra_index += 1; + } else if (extra.data.bits.has_section_body) { + const section_body_len = file.zir.extra[extra_index]; + extra_index += 1; + const section_body = file.zir.extra[extra_index .. extra_index + section_body_len]; + _ = section_body; + // TODO: analyze the block (or bail with a comptimeExpr) + extra_index += section_body_len; + } else { + // default alignment + } + + var cc_index: ?usize = null; + if (extra.data.bits.has_cc_ref) { + const cc_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + cc_index = self.exprs.items.len; + _ = try self.walkRef(file, scope, cc_ref, false); + extra_index += 1; + } else if (extra.data.bits.has_cc_body) { + const cc_body_len = file.zir.extra[extra_index]; + extra_index += 1; + const cc_body = file.zir.extra[extra_index .. extra_index + cc_body_len]; + _ = cc_body; + // TODO: analyze the block (or bail with a comptimeExpr) + extra_index += cc_body_len; + } else { + // auto calling convention + } + + // ret + const ret_type_ref: DocData.Expr = switch (fn_info.ret_ty_body.len) { + 0 => switch (fn_info.ret_ty_ref) { + .none => DocData.Expr{ .void = .{} }, + else => blk: { + const ref = fn_info.ret_ty_ref; + const wr = try self.walkRef(file, scope, ref, false); + break :blk wr.expr; + }, + }, + else => blk: { + const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1]; + const break_operand = data[last_instr_index].@"break".operand; + const wr = try self.walkRef(file, scope, break_operand, false); + break :blk wr.expr; + }, + }; + + // TODO: a complete version of this will probably need a scope + // in order to evaluate correctly closures around funcion + // parameters etc. + const generic_ret: ?DocData.Expr = switch (ret_type_ref) { + .type => |t| blk: { + if (fn_info.body.len == 0) break :blk null; + if (t == @enumToInt(Ref.type_type)) { + break :blk try self.getGenericReturnType( + file, + scope, + fn_info.body[fn_info.body.len - 1], + ); + } else { + break :blk null; + } + }, + else => null, + }; + + self.types.items[type_slot_index] = .{ + .Fn = .{ + .name = "todo_name func", + .src = self_ast_node_index, + .params = param_type_refs.items, + .ret = ret_type_ref, + .generic_ret = generic_ret, + .is_extern = extra.data.bits.is_extern, + .has_cc = cc_index != null, + .has_align = align_index != null, + .has_lib_name = extra.data.bits.has_lib_name, + .lib_name = lib_name, + .is_inferred_error = extra.data.bits.is_inferred_error, + .cc = cc_index, + .@"align" = align_index, + }, + }; + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; +} +fn analyzeFunction( + self: *Autodoc, + file: *File, + scope: *Scope, + inst_index: usize, + self_ast_node_index: usize, + type_slot_index: usize, +) AutodocErrors!DocData.WalkResult { + const tags = file.zir.instructions.items(.tag); + const data = file.zir.instructions.items(.data); + const fn_info = file.zir.getFnInfo(@intCast(u32, inst_index)); + + try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len); + var param_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( + self.arena, + fn_info.total_params_len, + ); + var param_ast_indexes = try std.ArrayListUnmanaged(usize).initCapacity( + self.arena, + fn_info.total_params_len, + ); + + // TODO: handle scope rules for fn parameters + for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| { + switch (tags[param_index]) { + else => { + panicWithContext( + file, + param_index, + "TODO: handle `{s}` in walkInstruction.func\n", + .{@tagName(tags[param_index])}, + ); + }, + .param_anytype, .param_anytype_comptime => { + // TODO: where are the doc comments? + const str_tok = data[param_index].str_tok; + + const name = str_tok.get(file.zir); + + param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len); + self.ast_nodes.appendAssumeCapacity(.{ + .name = name, + .docs = "", + .@"comptime" = tags[param_index] == .param_anytype_comptime, + }); + + param_type_refs.appendAssumeCapacity( + DocData.Expr{ .@"anytype" = .{} }, + ); + }, + .param, .param_comptime => { + const pl_tok = data[param_index].pl_tok; + const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index); + const doc_comment = if (extra.data.doc_comment != 0) + file.zir.nullTerminatedString(extra.data.doc_comment) + else + ""; + const name = file.zir.nullTerminatedString(extra.data.name); + + param_ast_indexes.appendAssumeCapacity(self.ast_nodes.items.len); + try self.ast_nodes.append(self.arena, .{ + .name = name, + .docs = doc_comment, + .@"comptime" = tags[param_index] == .param_comptime, + }); + + const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1]; + const break_operand = data[break_index].@"break".operand; + const param_type_ref = try self.walkRef(file, scope, break_operand, false); + + param_type_refs.appendAssumeCapacity(param_type_ref.expr); + }, + } + } + + // ret + const ret_type_ref: DocData.Expr = switch (fn_info.ret_ty_body.len) { + 0 => switch (fn_info.ret_ty_ref) { + .none => DocData.Expr{ .void = .{} }, + else => blk: { + const ref = fn_info.ret_ty_ref; + const wr = try self.walkRef(file, scope, ref, false); + break :blk wr.expr; + }, + }, + else => blk: { + //const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1]; + //const break_operand = data[last_instr_index].@"break".operand; + //const wr = try self.walkRef(file, scope, break_operand, false); + const wr = try self.walkRef(file, scope, fn_info.ret_ty_ref, false); + break :blk wr.expr; + }, + }; + + // TODO: a complete version of this will probably need a scope + // in order to evaluate correctly closures around funcion + // parameters etc. + const generic_ret: ?DocData.Expr = switch (ret_type_ref) { + .type => |t| blk: { + if (fn_info.body.len == 0) break :blk null; + if (t == @enumToInt(Ref.type_type)) { + break :blk try self.getGenericReturnType( + file, + scope, + fn_info.body[fn_info.body.len - 1], + ); + } else { + break :blk null; + } + }, + else => null, + }; + + self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items; + self.types.items[type_slot_index] = .{ + .Fn = .{ + .name = "todo_name func", + .src = self_ast_node_index, + .params = param_type_refs.items, + .ret = ret_type_ref, + .generic_ret = generic_ret, + }, + }; + + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.type_type) }, + .expr = .{ .type = type_slot_index }, + }; +} + +fn getGenericReturnType( + self: *Autodoc, + file: *File, + scope: *Scope, + body_end: usize, +) !DocData.Expr { + const wr = try self.walkInstruction(file, scope, body_end, false); + return wr.expr; +} + +fn collectUnionFieldInfo( + self: *Autodoc, + file: *File, + scope: *Scope, + fields_len: usize, + field_type_refs: *std.ArrayListUnmanaged(DocData.Expr), + field_name_indexes: *std.ArrayListUnmanaged(usize), + ei: usize, +) !void { + if (fields_len == 0) return; + var extra_index = ei; + + const bits_per_field = 4; + const fields_per_u32 = 32 / bits_per_field; + const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; + var bit_bag_index: usize = extra_index; + extra_index += bit_bags_count; + + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % fields_per_u32 == 0) { + cur_bit_bag = file.zir.extra[bit_bag_index]; + bit_bag_index += 1; + } + const has_type = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_tag = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const unused = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + _ = unused; + + const field_name = file.zir.nullTerminatedString(file.zir.extra[extra_index]); + extra_index += 1; + const doc_comment_index = file.zir.extra[extra_index]; + extra_index += 1; + const field_type = if (has_type) + @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]) + else + .void_type; + if (has_type) extra_index += 1; + + if (has_align) extra_index += 1; + if (has_tag) extra_index += 1; + + // type + { + const walk_result = try self.walkRef(file, scope, field_type, false); + try field_type_refs.append(self.arena, walk_result.expr); + } + + // ast node + { + try field_name_indexes.append(self.arena, self.ast_nodes.items.len); + const doc_comment: ?[]const u8 = if (doc_comment_index != 0) + file.zir.nullTerminatedString(doc_comment_index) + else + null; + try self.ast_nodes.append(self.arena, .{ + .name = field_name, + .docs = doc_comment, + }); + } + } +} + +fn collectStructFieldInfo( + self: *Autodoc, + file: *File, + scope: *Scope, + fields_len: usize, + field_type_refs: *std.ArrayListUnmanaged(DocData.Expr), + field_name_indexes: *std.ArrayListUnmanaged(usize), + ei: usize, +) !void { + if (fields_len == 0) return; + var extra_index = ei; + + const bits_per_field = 4; + const fields_per_u32 = 32 / bits_per_field; + const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; + + const Field = struct { + field_name: u32, + doc_comment_index: u32, + type_body_len: u32 = 0, + align_body_len: u32 = 0, + init_body_len: u32 = 0, + type_ref: Zir.Inst.Ref = .none, + }; + const fields = try self.arena.alloc(Field, fields_len); + + var bit_bag_index: usize = extra_index; + extra_index += bit_bags_count; + + var cur_bit_bag: u32 = undefined; + var field_i: u32 = 0; + while (field_i < fields_len) : (field_i += 1) { + if (field_i % fields_per_u32 == 0) { + cur_bit_bag = file.zir.extra[bit_bag_index]; + bit_bag_index += 1; + } + const has_align = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_default = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + // const is_comptime = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + const has_type_body = @truncate(u1, cur_bit_bag) != 0; + cur_bit_bag >>= 1; + + const field_name = file.zir.extra[extra_index]; + extra_index += 1; + const doc_comment_index = file.zir.extra[extra_index]; + extra_index += 1; + + fields[field_i] = .{ + .field_name = field_name, + .doc_comment_index = doc_comment_index, + }; + + if (has_type_body) { + fields[field_i].type_body_len = file.zir.extra[extra_index]; + } else { + fields[field_i].type_ref = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); + } + extra_index += 1; + + if (has_align) { + fields[field_i].align_body_len = file.zir.extra[extra_index]; + extra_index += 1; + } + if (has_default) { + fields[field_i].init_body_len = file.zir.extra[extra_index]; + extra_index += 1; + } + } + + const data = file.zir.instructions.items(.data); + + for (fields) |field| { + const type_expr = expr: { + if (field.type_ref != .none) { + const walk_result = try self.walkRef(file, scope, field.type_ref, false); + break :expr walk_result.expr; + } + + std.debug.assert(field.type_body_len != 0); + const body = file.zir.extra[extra_index..][0..field.type_body_len]; + extra_index += body.len; + + const break_inst = body[body.len - 1]; + const operand = data[break_inst].@"break".operand; + const walk_result = try self.walkRef(file, scope, operand, false); + break :expr walk_result.expr; + }; + + extra_index += field.align_body_len; + extra_index += field.init_body_len; + + try field_type_refs.append(self.arena, type_expr); + + // ast node + { + try field_name_indexes.append(self.arena, self.ast_nodes.items.len); + const doc_comment: ?[]const u8 = if (field.doc_comment_index != 0) + file.zir.nullTerminatedString(field.doc_comment_index) + else + null; + try self.ast_nodes.append(self.arena, .{ + .name = file.zir.nullTerminatedString(field.field_name), + .docs = doc_comment, + }); + } + } +} + +/// A Zir Ref can either refer to common types and values, or to a Zir index. +/// WalkRef resolves common cases and delegates to `walkInstruction` otherwise. +fn walkRef( + self: *Autodoc, + file: *File, + parent_scope: *Scope, + ref: Ref, + need_type: bool, // true when the caller needs also a typeRef for the return value +) AutodocErrors!DocData.WalkResult { + const enum_value = @enumToInt(ref); + if (enum_value <= @enumToInt(Ref.anyerror_void_error_union_type)) { + // We can just return a type that indexes into `types` with the + // enum value because in the beginning we pre-filled `types` with + // the types that are listed in `Ref`. + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(std.builtin.TypeId.Type) }, + .expr = .{ .type = enum_value }, + }; + } else if (enum_value < Ref.typed_value_map.len) { + switch (ref) { + else => { + std.debug.panic("TODO: handle {s} in `walkRef`\n", .{ + @tagName(ref), + }); + }, + .undef => { + return DocData.WalkResult{ .expr = .@"undefined" }; + }, + .zero => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = 0 } }, + }; + }, + .one => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = 1 } }, + }; + }, + + .void_value => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.void_type) }, + .expr = .{ .void = .{} }, + }; + }, + .unreachable_value => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.noreturn_type) }, + .expr = .{ .@"unreachable" = .{} }, + }; + }, + .null_value => { + return DocData.WalkResult{ .expr = .@"null" }; + }, + .bool_true => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.bool_type) }, + .expr = .{ .bool = true }, + }; + }, + .bool_false => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.bool_type) }, + .expr = .{ .bool = false }, + }; + }, + .empty_struct => { + return DocData.WalkResult{ .expr = .{ .@"struct" = &.{} } }; + }, + .zero_usize => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.usize_type) }, + .expr = .{ .int = .{ .value = 0 } }, + }; + }, + .one_usize => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.usize_type) }, + .expr = .{ .int = .{ .value = 1 } }, + }; + }, + // TODO: dunno what to do with those + .calling_convention_type => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.calling_convention_type) }, + // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = 1 } }, + }; + }, + .calling_convention_c => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.calling_convention_c) }, + // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = 1 } }, + }; + }, + .calling_convention_inline => { + return DocData.WalkResult{ + .typeRef = .{ .type = @enumToInt(Ref.calling_convention_inline) }, + // .typeRef = .{ .type = @enumToInt(Ref.comptime_int_type) }, + .expr = .{ .int = .{ .value = 1 } }, + }; + }, + // .generic_poison => { + // return DocData.WalkResult{ .int = .{ + // .type = @enumToInt(Ref.comptime_int_type), + // .value = 1, + // } }; + // }, + } + } else { + const zir_index = enum_value - Ref.typed_value_map.len; + return self.walkInstruction(file, parent_scope, zir_index, need_type); + } +} + +fn getBlockInlineBreak(zir: Zir, inst_index: usize) Zir.Inst.Ref { + const tags = zir.instructions.items(.tag); + const data = zir.instructions.items(.data); + const pl_node = data[inst_index].pl_node; + const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index); + const break_index = zir.extra[extra.end..][extra.data.body_len - 1]; + std.debug.assert(tags[break_index] == .break_inline); + return data[break_index].@"break".operand; +} + +fn printWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) void { + log.debug("Context [{s}] % {} \n " ++ fmt, .{ file.sub_file_path, inst } ++ args); +} + +fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) noreturn { + printWithContext(file, inst, fmt, args); + unreachable; +} + +fn cteTodo(self: *Autodoc, msg: []const u8) error{OutOfMemory}!DocData.WalkResult { + const cte_slot_index = self.comptime_exprs.items.len; + try self.comptime_exprs.append(self.arena, .{ + .code = msg, + }); + return DocData.WalkResult{ .expr = .{ .comptimeExpr = cte_slot_index } }; +} + +fn writeFileTableToJson(map: std.AutoArrayHashMapUnmanaged(*File, usize), jsw: anytype) !void { + try jsw.beginObject(); + var it = map.iterator(); + while (it.next()) |entry| { + try jsw.objectField(entry.key_ptr.*.sub_file_path); + try jsw.emitNumber(entry.value_ptr.*); + } + try jsw.endObject(); +} + +fn writePackageTableToJson( + map: std.AutoHashMapUnmanaged(*Package, DocData.DocPackage.TableEntry), + jsw: anytype, +) !void { + try jsw.beginObject(); + var it = map.valueIterator(); + while (it.next()) |entry| { + try jsw.objectField(entry.name); + try jsw.emitNumber(entry.value); + } + try jsw.endObject(); +} diff --git a/src/Compilation.zig b/src/Compilation.zig index 659fa5e972..5204910193 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -34,6 +34,7 @@ const ThreadPool = @import("ThreadPool.zig"); const WaitGroup = @import("WaitGroup.zig"); const libtsan = @import("libtsan.zig"); const Zir = @import("Zir.zig"); +const Autodoc = @import("Autodoc.zig"); const Color = @import("main.zig").Color; /// General-purpose allocator. Used for both temporary and long-term storage. @@ -2287,6 +2288,14 @@ pub fn update(comp: *Compilation) !void { return; } + if (comp.emit_docs) |doc_location| { + if (comp.bin_file.options.module) |module| { + var autodoc = Autodoc.init(module, doc_location); + defer autodoc.deinit(); + try autodoc.generateZirData(); + } + } + // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and // -femit-asm to handle, in the case of C objects. comp.emitOthers(); @@ -5160,8 +5169,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); const emit_llvm_bc_path = try stage1LocPath(arena, comp.emit_llvm_bc, directory); - const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); - const emit_docs_path = try stage1LocPath(arena, comp.emit_docs, directory); const stage1_pkg = try createStage1Pkg(arena, "root", mod.main_pkg, null); const test_filter = comp.test_filter orelse ""[0..0]; const test_name_prefix = comp.test_name_prefix orelse ""[0..0]; @@ -5182,10 +5189,6 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node .emit_llvm_ir_len = emit_llvm_ir_path.len, .emit_bitcode_ptr = emit_llvm_bc_path.ptr, .emit_bitcode_len = emit_llvm_bc_path.len, - .emit_analysis_json_ptr = emit_analysis_path.ptr, - .emit_analysis_json_len = emit_analysis_path.len, - .emit_docs_ptr = emit_docs_path.ptr, - .emit_docs_len = emit_docs_path.len, .builtin_zig_path_ptr = builtin_zig_path.ptr, .builtin_zig_path_len = builtin_zig_path.len, .test_filter_ptr = test_filter.ptr, diff --git a/src/stage1.zig b/src/stage1.zig index 005dc312ba..fd6f53c096 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -96,10 +96,6 @@ pub const Module = extern struct { emit_llvm_ir_len: usize, emit_bitcode_ptr: [*]const u8, emit_bitcode_len: usize, - emit_analysis_json_ptr: [*]const u8, - emit_analysis_json_len: usize, - emit_docs_ptr: [*]const u8, - emit_docs_len: usize, builtin_zig_path_ptr: [*]const u8, builtin_zig_path_len: usize, test_filter_ptr: [*]const u8, diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp index ba5df49c59..4028c3872d 100644 --- a/src/stage1/all_types.hpp +++ b/src/stage1/all_types.hpp @@ -2141,8 +2141,6 @@ struct CodeGen { Buf asm_file_output_path; Buf llvm_ir_file_output_path; Buf bitcode_file_output_path; - Buf analysis_json_output_path; - Buf docs_output_path; Buf *builtin_zig_path; diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 81e5208e91..8b88446295 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -16,7 +16,6 @@ #include "util.hpp" #include "zig_llvm.h" #include "stage2.h" -#include "dump_analysis.hpp" #include "softfloat.hpp" #include "zigendian.h" @@ -10546,57 +10545,6 @@ void codegen_build_object(CodeGen *g) { gen_root_source(g); - if (buf_len(&g->analysis_json_output_path) != 0) { - const char *analysis_json_filename = buf_ptr(&g->analysis_json_output_path); - FILE *f = fopen(analysis_json_filename, "wb"); - if (f == nullptr) { - fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); - exit(1); - } - zig_print_analysis_dump(g, f, " ", "\n"); - if (fclose(f) != 0) { - fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); - exit(1); - } - } - if (buf_len(&g->docs_output_path) != 0) { - Error err; - Buf *doc_dir_path = &g->docs_output_path; - if ((err = os_make_path(doc_dir_path))) { - fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); - exit(1); - } - Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "docs" OS_SEP "index.html", - buf_ptr(g->zig_lib_dir)); - Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); - Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "docs" OS_SEP "main.js", - buf_ptr(g->zig_lib_dir)); - Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); - - if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { - fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path), - buf_ptr(index_html_dest_path), err_str(err)); - exit(1); - } - if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) { - fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path), - buf_ptr(main_js_dest_path), err_str(err)); - exit(1); - } - const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path))); - FILE *f = fopen(data_js_filename, "wb"); - if (f == nullptr) { - fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno)); - exit(1); - } - fprintf(f, "zigAnalysis="); - zig_print_analysis_dump(g, f, "", ""); - fprintf(f, ";"); - if (fclose(f) != 0) { - fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno)); - exit(1); - } - } codegen_add_time_event(g, "Code Generation"); { diff --git a/src/stage1/dump_analysis.cpp b/src/stage1/dump_analysis.cpp deleted file mode 100644 index 15cd7c2874..0000000000 --- a/src/stage1/dump_analysis.cpp +++ /dev/null @@ -1,1431 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "dump_analysis.hpp" -#include "analyze.hpp" -#include "ir.hpp" -#include "codegen.hpp" -#include "os.hpp" - -enum JsonWriterState { - JsonWriterStateInvalid, - JsonWriterStateValue, - JsonWriterStateArrayStart, - JsonWriterStateArray, - JsonWriterStateObjectStart, - JsonWriterStateObject, -}; - -#define JSON_MAX_DEPTH 10 - -struct JsonWriter { - size_t state_index; - FILE *f; - const char *one_indent; - const char *nl; - JsonWriterState state[JSON_MAX_DEPTH]; -}; - -static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { - jw->state_index = 1; - jw->f = f; - jw->one_indent = one_indent; - jw->nl = nl; - jw->state[0] = JsonWriterStateInvalid; - jw->state[1] = JsonWriterStateValue; -} - -static void jw_nl_indent(JsonWriter *jw) { - assert(jw->state_index >= 1); - fprintf(jw->f, "%s", jw->nl); - for (size_t i = 0; i < jw->state_index - 1; i += 1) { - fprintf(jw->f, "%s", jw->one_indent); - } -} - -static void jw_push_state(JsonWriter *jw, JsonWriterState state) { - jw->state_index += 1; - assert(jw->state_index < JSON_MAX_DEPTH); - jw->state[jw->state_index] = state; -} - -static void jw_pop_state(JsonWriter *jw) { - assert(jw->state_index != 0); - jw->state_index -= 1; -} - -static void jw_begin_array(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "["); - jw->state[jw->state_index] = JsonWriterStateArrayStart; -} - -static void jw_begin_object(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "{"); - jw->state[jw->state_index] = JsonWriterStateObjectStart; -} - -static void jw_array_elem(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateObjectStart: - case JsonWriterStateObject: - zig_unreachable(); - case JsonWriterStateArray: - fprintf(jw->f, ","); - ZIG_FALLTHROUGH; - case JsonWriterStateArrayStart: - jw->state[jw->state_index] = JsonWriterStateArray; - jw_push_state(jw, JsonWriterStateValue); - jw_nl_indent(jw); - return; - } - zig_unreachable(); -} - -static void jw_write_escaped_string(JsonWriter *jw, const char *s) { - fprintf(jw->f, "\""); - for (;; s += 1) { - switch (*s) { - case 0: - fprintf(jw->f, "\""); - return; - case '"': - fprintf(jw->f, "\\\""); - continue; - case '\t': - fprintf(jw->f, "\\t"); - continue; - case '\r': - fprintf(jw->f, "\\r"); - continue; - case '\n': - fprintf(jw->f, "\\n"); - continue; - case '\b': - fprintf(jw->f, "\\b"); - continue; - case '\f': - fprintf(jw->f, "\\f"); - continue; - case '\\': - fprintf(jw->f, "\\\\"); - continue; - default: - fprintf(jw->f, "%c", *s); - continue; - } - } -} - -static void jw_object_field(JsonWriter *jw, const char *name) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateArray: - case JsonWriterStateArrayStart: - zig_unreachable(); - case JsonWriterStateObject: - fprintf(jw->f, ","); - ZIG_FALLTHROUGH; - case JsonWriterStateObjectStart: - jw->state[jw->state_index] = JsonWriterStateObject; - jw_push_state(jw, JsonWriterStateValue); - jw_nl_indent(jw); - jw_write_escaped_string(jw, name); - fprintf(jw->f, ": "); - return; - } - zig_unreachable(); -} - -static void jw_end_array(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateObjectStart: - case JsonWriterStateObject: - zig_unreachable(); - case JsonWriterStateArrayStart: - fprintf(jw->f, "]"); - jw_pop_state(jw); - return; - case JsonWriterStateArray: - jw_nl_indent(jw); - jw_pop_state(jw); - fprintf(jw->f, "]"); - return; - } - zig_unreachable(); -} - - -static void jw_end_object(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - zig_unreachable(); - case JsonWriterStateValue: - zig_unreachable(); - case JsonWriterStateArray: - zig_unreachable(); - case JsonWriterStateArrayStart: - zig_unreachable(); - case JsonWriterStateObjectStart: - fprintf(jw->f, "}"); - jw_pop_state(jw); - return; - case JsonWriterStateObject: - jw_nl_indent(jw); - jw_pop_state(jw); - fprintf(jw->f, "}"); - return; - } - zig_unreachable(); -} - -static void jw_null(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "null"); - jw_pop_state(jw); -} - -static void jw_bool(JsonWriter *jw, bool x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - if (x) { - fprintf(jw->f, "true"); - } else { - fprintf(jw->f, "false"); - } - jw_pop_state(jw); -} - -static void jw_int(JsonWriter *jw, int64_t x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - if (x > 4503599627370496 || x < -4503599627370496) { - fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); - } else { - fprintf(jw->f, "%" ZIG_PRI_i64, x); - } - jw_pop_state(jw); -} - -static void jw_bigint(JsonWriter *jw, const BigInt *x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - Buf *str = buf_alloc(); - bigint_append_buf(str, x, 10); - - if (bigint_fits_in_bits(x, 52, true)) { - fprintf(jw->f, "%s", buf_ptr(str)); - } else { - fprintf(jw->f, "\"%s\"", buf_ptr(str)); - } - jw_pop_state(jw); - - buf_destroy(str); -} - -static void jw_string(JsonWriter *jw, const char *s) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - jw_write_escaped_string(jw, s); - jw_pop_state(jw); -} - - -static void tree_print(FILE *f, ZigType *ty, size_t indent); - -static int compare_type_abi_sizes_desc(const void *a, const void *b) { - uint64_t size_a = (*(ZigType * const*)(a))->abi_size; - uint64_t size_b = (*(ZigType * const*)(b))->abi_size; - if (size_a > size_b) - return -1; - if (size_a < size_b) - return 1; - return 0; -} - -static void start_child(FILE *f, size_t indent) { - fprintf(f, "\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void start_peer(FILE *f, size_t indent) { - fprintf(f, ",\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { - ZigList children = {}; - uint64_t sum_from_fields = 0; - for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - TypeStructField *field = struct_type->data.structure.fields[i]; - children.append(field->type_entry); - sum_from_fields += field->type_entry->abi_size; - } - qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); - - start_peer(f, indent); - fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); - - start_peer(f, indent); - fprintf(f, "\"fields\": ["); - - for (size_t i = 0; i < children.length; i += 1) { - if (i == 0) { - start_child(f, indent + 1); - } else { - start_peer(f, indent + 1); - } - fprintf(f, "{"); - - ZigType *child_type = children.at(i); - tree_print(f, child_type, indent + 2); - - start_child(f, indent + 1); - fprintf(f, "}"); - } - - start_child(f, indent); - fprintf(f, "]"); -} - -static void tree_print(FILE *f, ZigType *ty, size_t indent) { - start_child(f, indent); - fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); - - start_peer(f, indent); - fprintf(f, "\"sizef\": \""); - zig_pretty_print_bytes(f, ty->abi_size); - fprintf(f, "\""); - - start_peer(f, indent); - fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); - - switch (ty->id) { - case ZigTypeIdFnFrame: - return tree_print_struct(f, ty->data.frame.locals_struct, indent); - case ZigTypeIdStruct: - return tree_print_struct(f, ty, indent); - default: - start_child(f, indent); - return; - } -} - -void zig_print_stack_report(CodeGen *g, FILE *f) { - if (g->largest_frame_fn == nullptr) { - fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); - return; - } - fprintf(f, "{"); - tree_print(f, g->largest_frame_fn->frame_type, 1); - - start_child(f, 0); - fprintf(f, "}\n"); -} - -struct AnalDumpCtx { - CodeGen *g; - JsonWriter jw; - - ZigList type_list; - HashMap type_map; - - ZigList pkg_list; - HashMap pkg_map; - - ZigList file_list; - HashMap file_map; - - ZigList decl_list; - HashMap decl_map; - - ZigList fn_list; - HashMap fn_map; - HashMap fn_decl_map; - - ZigList node_list; - HashMap node_map; - - ZigList err_list; - HashMap err_map; -}; - -static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); -static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value); - -static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { - Error err; - if (value->type != ty) { - return; - } - if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { - codegen_report_errors_and_exit(ctx->g); - } - if (value->special == ConstValSpecialUndef) { - return; - } - if (value->special == ConstValSpecialRuntime) { - return; - } - switch (ty->id) { - case ZigTypeIdMetaType: { - ZigType *val_ty = value->data.x_type; - (void)anal_dump_get_type_id(ctx, val_ty); - return; - } - default: - return; - } - zig_unreachable(); -} - -static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { - uint32_t type_id = ctx->type_list.length; - auto existing_entry = ctx->type_map.put_unique(ty, type_id); - if (existing_entry == nullptr) { - ctx->type_list.append(ty); - } else { - type_id = existing_entry->value; - } - return type_id; -} - -static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { - assert(pkg != nullptr); - uint32_t pkg_id = ctx->pkg_list.length; - auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); - if (existing_entry == nullptr) { - ctx->pkg_list.append(pkg); - } else { - pkg_id = existing_entry->value; - } - return pkg_id; -} - -static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { - uint32_t file_id = ctx->file_list.length; - auto existing_entry = ctx->file_map.put_unique(file, file_id); - if (existing_entry == nullptr) { - ctx->file_list.append(file); - } else { - file_id = existing_entry->value; - } - return file_id; -} - -static uint32_t anal_dump_get_node_id(AnalDumpCtx *ctx, AstNode *node) { - uint32_t node_id = ctx->node_list.length; - auto existing_entry = ctx->node_map.put_unique(node, node_id); - if (existing_entry == nullptr) { - ctx->node_list.append(node); - } else { - node_id = existing_entry->value; - } - return node_id; -} - -static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { - uint32_t fn_id = ctx->fn_list.length; - auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); - if (existing_entry == nullptr) { - ctx->fn_list.append(fn); - - // poke the fn - (void)anal_dump_get_type_id(ctx, fn->type_entry); - (void)anal_dump_get_node_id(ctx, fn->proto_node); - } else { - fn_id = existing_entry->value; - } - return fn_id; -} - -static uint32_t anal_dump_get_err_id(AnalDumpCtx *ctx, ErrorTableEntry *err) { - uint32_t err_id = ctx->err_list.length; - auto existing_entry = ctx->err_map.put_unique(err, err_id); - if (existing_entry == nullptr) { - ctx->err_list.append(err); - } else { - err_id = existing_entry->value; - } - return err_id; -} - -static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { - uint32_t decl_id = ctx->decl_list.length; - auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); - if (existing_entry == nullptr) { - ctx->decl_list.append(tld); - - if (tld->import != nullptr) { - (void)anal_dump_get_type_id(ctx, tld->import); - } - - // poke the types - switch (tld->id) { - case TldIdVar: { - TldVar *tld_var = reinterpret_cast(tld); - ZigVar *var = tld_var->var; - - if (var != nullptr) { - (void)anal_dump_get_type_id(ctx, var->var_type); - - if (var->const_value != nullptr) { - anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); - } - } - break; - } - case TldIdFn: { - TldFn *tld_fn = reinterpret_cast(tld); - ZigFn *fn = tld_fn->fn_entry; - - if (fn != nullptr) { - (void)anal_dump_get_type_id(ctx, fn->type_entry); - ctx->fn_decl_map.put_unique(fn, decl_id); - } - break; - } - default: - break; - } - - } else { - decl_id = existing_entry->value; - } - return decl_id; -} - -static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { - uint32_t type_id = anal_dump_get_type_id(ctx, ty); - jw_int(&ctx->jw, type_id); -} - -static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { - uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); - jw_int(&ctx->jw, pkg_id); -} - -static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { - uint32_t file_id = anal_dump_get_file_id(ctx, file); - jw_int(&ctx->jw, file_id); -} - -static void anal_dump_node_ref(AnalDumpCtx *ctx, AstNode *node) { - uint32_t node_id = anal_dump_get_node_id(ctx, node); - jw_int(&ctx->jw, node_id); -} - -static void anal_dump_fn_ref(AnalDumpCtx *ctx, ZigFn *fn) { - uint32_t fn_id = anal_dump_get_fn_id(ctx, fn); - jw_int(&ctx->jw, fn_id); -} - -static void anal_dump_err_ref(AnalDumpCtx *ctx, ErrorTableEntry *err) { - uint32_t err_id = anal_dump_get_err_id(ctx, err); - jw_int(&ctx->jw, err_id); -} - -static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { - uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); - jw_int(&ctx->jw, decl_id); -} - -static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { - JsonWriter *jw = &ctx->jw; - - Buf full_path_buf = BUF_INIT; - os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); - Buf *resolve_paths[] = { &full_path_buf, }; - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(resolve_paths, 1); - - auto import_entry = ctx->g->import_table.maybe_get(resolved_path); - if (!import_entry) { - return; - } - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&pkg->pkg_path)); - - jw_object_field(jw, "file"); - anal_dump_file_ref(ctx, resolved_path); - - jw_object_field(jw, "main"); - anal_dump_type_ref(ctx, import_entry->value); - - jw_object_field(jw, "table"); - jw_begin_object(jw); - auto it = pkg->package_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - ZigPackage *child_pkg = entry->value; - if (child_pkg != nullptr) { - jw_object_field(jw, buf_ptr(entry->key)); - anal_dump_pkg_ref(ctx, child_pkg); - } - } - jw_end_object(jw); - - jw_end_object(jw); -} - -static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { - JsonWriter *jw = &ctx->jw; - - bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; - if (make_obj) { - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "import"); - anal_dump_type_ref(ctx, tld->import); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, tld->source_node); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(tld->name)); - } - - switch (tld->id) { - case TldIdVar: { - TldVar *tld_var = reinterpret_cast(tld); - ZigVar *var = tld_var->var; - - if (var != nullptr) { - jw_object_field(jw, "kind"); - if (var->src_is_const) { - jw_string(jw, "const"); - } else { - jw_string(jw, "var"); - } - - if (var->is_thread_local) { - jw_object_field(jw, "threadlocal"); - jw_bool(jw, true); - } - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, var->var_type); - - if (var->const_value != nullptr) { - jw_object_field(jw, "value"); - anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); - } - } - break; - } - case TldIdFn: { - TldFn *tld_fn = reinterpret_cast(tld); - ZigFn *fn = tld_fn->fn_entry; - - if (fn != nullptr) { - jw_object_field(jw, "kind"); - jw_string(jw, "const"); - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, fn->type_entry); - - jw_object_field(jw, "value"); - anal_dump_fn_ref(ctx, fn); - } - break; - } - default: - break; - } - - if (make_obj) { - jw_end_object(jw); - } -} - -static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { - JsonWriter *jw = &ctx->jw; - jw_string(jw, buf_ptr(file)); -} - -static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { - Error err; - - if (value->type != ty) { - jw_null(&ctx->jw); - return; - } - if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { - codegen_report_errors_and_exit(ctx->g); - } - if (value->special == ConstValSpecialUndef) { - jw_string(&ctx->jw, "undefined"); - return; - } - if (value->special == ConstValSpecialRuntime) { - jw_null(&ctx->jw); - return; - } - switch (ty->id) { - case ZigTypeIdMetaType: { - ZigType *val_ty = value->data.x_type; - anal_dump_type_ref(ctx, val_ty); - return; - } - case ZigTypeIdFn: { - if (value->data.x_ptr.special == ConstPtrSpecialFunction) { - ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; - anal_dump_fn_ref(ctx, val_fn); - } else { - jw_null(&ctx->jw); - } - return; - } - case ZigTypeIdOptional: { - if(optional_value_is_null(value)){ - jw_string(&ctx->jw, "null"); - } else { - jw_null(&ctx->jw); - } - return; - } - case ZigTypeIdBool: { - jw_string(&ctx->jw, value->data.x_bool ? "true" : "false"); - return; - } - case ZigTypeIdInt: { - jw_bigint(&ctx->jw, &value->data.x_bigint); - return; - } - default: - jw_null(&ctx->jw); - return; - } - zig_unreachable(); -} - -static void anal_dump_pointer_attrs(AnalDumpCtx *ctx, ZigType *ty) { - JsonWriter *jw = &ctx->jw; - if (ty->data.pointer.explicit_alignment != 0) { - jw_object_field(jw, "align"); - jw_int(jw, ty->data.pointer.explicit_alignment); - } - if (ty->data.pointer.is_const) { - jw_object_field(jw, "const"); - jw_bool(jw, true); - } - if (ty->data.pointer.is_volatile) { - jw_object_field(jw, "volatile"); - jw_bool(jw, true); - } - if (ty->data.pointer.allow_zero) { - jw_object_field(jw, "allowZero"); - jw_bool(jw, true); - } - if (ty->data.pointer.host_int_bytes != 0) { - jw_object_field(jw, "hostIntBytes"); - jw_int(jw, ty->data.pointer.host_int_bytes); - - jw_object_field(jw, "bitOffsetInHost"); - jw_int(jw, ty->data.pointer.bit_offset_in_host); - } - - jw_object_field(jw, "elem"); - anal_dump_type_ref(ctx, ty->data.pointer.child_type); -} - -static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { - JsonWriter *jw = &ctx->jw; - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "kind"); - jw_int(jw, type_id_index(ty)); - - switch (ty->id) { - case ZigTypeIdMetaType: - case ZigTypeIdBool: - case ZigTypeIdEnumLiteral: - break; - case ZigTypeIdStruct: { - if (ty->data.structure.special == StructSpecialSlice) { - jw_object_field(jw, "len"); - jw_int(jw, 2); - anal_dump_pointer_attrs(ctx, ty->data.structure.fields[slice_ptr_index]->type_entry); - break; - } - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.structure.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.structure.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.structure.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.structure.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - jw_array_elem(jw); - anal_dump_type_ref(ctx, ty->data.structure.fields[i]->type_entry); - } - jw_end_array(jw); - } - - if (ty->data.structure.root_struct != nullptr) { - Buf *path_buf = ty->data.structure.root_struct->path; - - jw_object_field(jw, "file"); - anal_dump_file_ref(ctx, path_buf); - } - break; - } - case ZigTypeIdUnion: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.unionation.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.unionation.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.unionation.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.unionation.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { - jw_array_elem(jw); - anal_dump_type_ref(ctx, ty->data.unionation.fields[i].type_entry); - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdEnum: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.enumeration.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.enumeration.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.enumeration.src_field_count; i += 1) { - jw_array_elem(jw); - jw_bigint(jw, &ty->data.enumeration.fields[i].value); - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdFloat: { - jw_object_field(jw, "bits"); - jw_int(jw, ty->data.floating.bit_count); - break; - } - case ZigTypeIdInt: { - if (ty->data.integral.is_signed) { - jw_object_field(jw, "i"); - } else { - jw_object_field(jw, "u"); - } - jw_int(jw, ty->data.integral.bit_count); - break; - } - case ZigTypeIdFn: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "generic"); - jw_bool(jw, ty->data.fn.is_generic); - - if (ty->data.fn.fn_type_id.return_type != nullptr) { - jw_object_field(jw, "ret"); - anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.return_type); - } - - if (ty->data.fn.fn_type_id.param_count != 0) { - jw_object_field(jw, "args"); - jw_begin_array(jw); - for (size_t i = 0; i < ty->data.fn.fn_type_id.param_count; i += 1) { - jw_array_elem(jw); - if (ty->data.fn.fn_type_id.param_info[i].type != nullptr) { - anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.param_info[i].type); - } else { - jw_null(jw); - } - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdOptional: { - jw_object_field(jw, "child"); - anal_dump_type_ref(ctx, ty->data.maybe.child_type); - break; - } - case ZigTypeIdPointer: { - switch (ty->data.pointer.ptr_len) { - case PtrLenSingle: - break; - case PtrLenUnknown: - jw_object_field(jw, "len"); - jw_int(jw, 1); - break; - case PtrLenC: - jw_object_field(jw, "len"); - jw_int(jw, 3); - break; - } - anal_dump_pointer_attrs(ctx, ty); - break; - } - case ZigTypeIdErrorSet: { - if (type_is_global_error_set(ty)) { - break; - } - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - if (ty->data.error_set.infer_fn != nullptr) { - jw_object_field(jw, "fn"); - anal_dump_fn_ref(ctx, ty->data.error_set.infer_fn); - } - jw_object_field(jw, "errors"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ty->data.error_set.err_count; i += 1) { - jw_array_elem(jw); - ErrorTableEntry *err = ty->data.error_set.errors[i]; - anal_dump_err_ref(ctx, err); - } - jw_end_array(jw); - break; - } - case ZigTypeIdErrorUnion: { - jw_object_field(jw, "err"); - anal_dump_type_ref(ctx, ty->data.error_union.err_set_type); - - jw_object_field(jw, "payload"); - anal_dump_type_ref(ctx, ty->data.error_union.payload_type); - - break; - } - case ZigTypeIdArray: { - jw_object_field(jw, "len"); - jw_int(jw, ty->data.array.len); - - jw_object_field(jw, "elem"); - anal_dump_type_ref(ctx, ty->data.array.child_type); - break; - } - case ZigTypeIdVector: { - jw_object_field(jw, "len"); - jw_int(jw, ty->data.vector.len); - - jw_object_field(jw, "elem"); - anal_dump_type_ref(ctx, ty->data.vector.elem_type); - break; - } - case ZigTypeIdAnyFrame: { - if (ty->data.any_frame.result_type != nullptr) { - jw_object_field(jw, "result"); - anal_dump_type_ref(ctx, ty->data.any_frame.result_type); - } - break; - } - case ZigTypeIdFnFrame: { - jw_object_field(jw, "fnName"); - jw_string(jw, buf_ptr(&ty->data.frame.fn->symbol_name)); - - jw_object_field(jw, "fn"); - anal_dump_fn_ref(ctx, ty->data.frame.fn); - break; - } - case ZigTypeIdInvalid: - zig_unreachable(); - default: - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - break; - } - jw_end_object(jw); -} - -static Buf *collect_doc_comments(RootStruct *root_struct, TokenIndex first_token) { - if (first_token == 0) - return nullptr; - - TokenId *token_ids = root_struct->token_ids; - TokenLoc *token_locs = root_struct->token_locs; - Buf *str = buf_alloc(); - const char *source = buf_ptr(root_struct->source_code); - TokenIndex doc_token = first_token; - for (;token_ids[doc_token] == TokenIdDocComment; doc_token += 1) { - // chops off '///' but leaves '\n' - uint32_t start_pos = token_locs[doc_token].offset; - uint32_t token_len = 0; - while (source[start_pos + token_len] != '\n' && - source[start_pos + token_len] != 0) - { - token_len += 1; - } - buf_append_mem(str, source + start_pos + 3, token_len - 3); - } - return str; -} - -static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "file"); - RootStruct *root_struct = node->owner->data.structure.root_struct; - anal_dump_file_ref(ctx, root_struct->path); - - jw_object_field(jw, "line"); - jw_int(jw, root_struct->token_locs[node->main_token].line); - - jw_object_field(jw, "col"); - jw_int(jw, root_struct->token_locs[node->main_token].column); - - const Buf *doc_comments_buf = nullptr; - const Buf *name_buf = nullptr; - const ZigList *field_nodes = nullptr; - bool is_var_args = false; - bool is_noalias = false; - bool is_comptime = false; - - switch (node->type) { - case NodeTypeParamDecl: - doc_comments_buf = collect_doc_comments(root_struct, node->data.param_decl.doc_comments); - name_buf = node->data.param_decl.name; - is_var_args = node->data.param_decl.is_var_args; - is_noalias = node->data.param_decl.is_noalias; - is_comptime = node->data.param_decl.is_comptime; - break; - case NodeTypeFnProto: - doc_comments_buf = collect_doc_comments(root_struct, node->data.fn_proto.doc_comments); - field_nodes = &node->data.fn_proto.params; - is_var_args = node->data.fn_proto.is_var_args; - break; - case NodeTypeVariableDeclaration: - doc_comments_buf = collect_doc_comments(root_struct, node->data.variable_declaration.doc_comments); - break; - case NodeTypeErrorSetField: - doc_comments_buf = collect_doc_comments(root_struct, node->data.err_set_field.doc_comments); - break; - case NodeTypeStructField: - doc_comments_buf = collect_doc_comments(root_struct, node->data.struct_field.doc_comments); - name_buf = node->data.struct_field.name; - break; - case NodeTypeContainerDecl: - field_nodes = &node->data.container_decl.fields; - doc_comments_buf = collect_doc_comments(root_struct, node->data.container_decl.doc_comments); - break; - default: - break; - } - - if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { - jw_object_field(jw, "docs"); - jw_string(jw, buf_ptr(doc_comments_buf)); - } - - if (name_buf != nullptr) { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(name_buf)); - } - - if (field_nodes != nullptr) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - for (size_t i = 0; i < field_nodes->length; i += 1) { - jw_array_elem(jw); - anal_dump_node_ref(ctx, field_nodes->at(i)); - } - jw_end_array(jw); - } - - if (is_var_args) { - jw_object_field(jw, "varArgs"); - jw_bool(jw, true); - } - - if (is_comptime) { - jw_object_field(jw, "comptime"); - jw_bool(jw, true); - } - - if (is_noalias) { - jw_object_field(jw, "noalias"); - jw_bool(jw, true); - } - - jw_end_object(jw); -} - -static void anal_dump_err(AnalDumpCtx *ctx, const ErrorTableEntry *err) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, err->decl_node); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&err->name)); - - jw_end_object(jw); -} - -static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, fn->proto_node); - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, fn->type_entry); - - auto entry = ctx->fn_decl_map.maybe_get(fn); - if (entry != nullptr) { - jw_object_field(jw, "decl"); - jw_int(jw, entry->value); - } - - jw_end_object(jw); -} - -void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { - AnalDumpCtx ctx = {}; - ctx.g = g; - JsonWriter *jw = &ctx.jw; - jw_init(jw, f, one_indent, nl); - ctx.type_map.init(16); - ctx.pkg_map.init(16); - ctx.file_map.init(16); - ctx.decl_map.init(16); - ctx.node_map.init(16); - ctx.fn_map.init(16); - ctx.fn_decl_map.init(16); - ctx.err_map.init(16); - - jw_begin_object(jw); - - jw_object_field(jw, "typeKinds"); - jw_begin_array(jw); - for (size_t i = 0; i < type_id_len(); i += 1) { - jw_array_elem(jw); - jw_string(jw, type_id_name(type_id_at_index(i))); - } - jw_end_array(jw); - - jw_object_field(jw, "params"); - jw_begin_object(jw); - { - jw_object_field(jw, "zigVersion"); - jw_string(jw, stage2_version_string()); - - jw_object_field(jw, "builds"); - jw_begin_array(jw); - jw_array_elem(jw); - jw_begin_object(jw); - jw_object_field(jw, "target"); - Buf triple_buf = BUF_INIT; - target_triple_zig(&triple_buf, g->zig_target); - jw_string(jw, buf_ptr(&triple_buf)); - jw_end_object(jw); - jw_end_array(jw); - - jw_object_field(jw, "rootName"); - jw_string(jw, buf_ptr(g->root_out_name)); - } - jw_end_object(jw); - - jw_object_field(jw, "rootPkg"); - anal_dump_pkg_ref(&ctx, g->main_pkg); - - // FIXME: Remove this ugly workaround. - // Right now the code in docs/main.js relies on the root of the main package being itself. - g->main_pkg->package_table.put(buf_create_from_str("root"), g->main_pkg); - - // Poke the functions - for (size_t i = 0; i < g->fn_defs.length; i += 1) { - ZigFn *fn = g->fn_defs.at(i); - (void)anal_dump_get_fn_id(&ctx, fn); - } - - jw_object_field(jw, "calls"); - jw_begin_array(jw); - { - ZigList var_stack = {}; - - auto it = g->memoized_fn_eval_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - var_stack.resize(0); - ZigFn *fn = nullptr; - - Scope *scope = entry->key; - while (scope != nullptr) { - if (scope->id == ScopeIdVarDecl) { - ZigVar *var = reinterpret_cast(scope)->var; - var_stack.append(var); - } else if (scope->id == ScopeIdFnDef) { - fn = reinterpret_cast(scope)->fn_entry; - break; - } - scope = scope->parent; - } - ZigValue *result = entry->value; - - assert(fn != nullptr); - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "fn"); - anal_dump_fn_ref(&ctx, fn); - - jw_object_field(jw, "result"); - { - jw_begin_object(jw); - - jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, result->type); - - jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, result->type, result); - - jw_end_object(jw); - } - - if (var_stack.length != 0) { - jw_object_field(jw, "args"); - jw_begin_array(jw); - - while (var_stack.length != 0) { - ZigVar *var = var_stack.pop(); - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, var->var_type); - - jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); - - jw_end_object(jw); - } - jw_end_array(jw); - } - - jw_end_object(jw); - } - - var_stack.deinit(); - } - jw_end_array(jw); - - jw_object_field(jw, "packages"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { - anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); - } - jw_end_array(jw); - - jw_object_field(jw, "types"); - jw_begin_array(jw); - - for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { - ZigType *ty = ctx.type_list.at(i); - anal_dump_type(&ctx, ty); - } - jw_end_array(jw); - - jw_object_field(jw, "decls"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { - Tld *decl = ctx.decl_list.at(i); - anal_dump_decl(&ctx, decl); - } - jw_end_array(jw); - - jw_object_field(jw, "fns"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { - ZigFn *fn = ctx.fn_list.at(i); - jw_array_elem(jw); - anal_dump_fn(&ctx, fn); - } - jw_end_array(jw); - - jw_object_field(jw, "errors"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { - const ErrorTableEntry *err = ctx.err_list.at(i); - jw_array_elem(jw); - anal_dump_err(&ctx, err); - } - jw_end_array(jw); - - jw_object_field(jw, "astNodes"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.node_list.length; i += 1) { - const AstNode *node = ctx.node_list.at(i); - jw_array_elem(jw); - anal_dump_node(&ctx, node); - } - jw_end_array(jw); - - jw_object_field(jw, "files"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { - Buf *file = ctx.file_list.at(i); - jw_array_elem(jw); - anal_dump_file(&ctx, file); - } - jw_end_array(jw); - - jw_end_object(jw); -} diff --git a/src/stage1/dump_analysis.hpp b/src/stage1/dump_analysis.hpp deleted file mode 100644 index 6d1c644ea2..0000000000 --- a/src/stage1/dump_analysis.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_DUMP_ANALYSIS_HPP -#define ZIG_DUMP_ANALYSIS_HPP - -#include "all_types.hpp" -#include - -void zig_print_stack_report(CodeGen *g, FILE *f); -void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl); - -#endif diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp index 856bb61dcb..860d4ba4b1 100644 --- a/src/stage1/stage1.cpp +++ b/src/stage1/stage1.cpp @@ -74,8 +74,6 @@ void zig_stage1_build_object(struct ZigStage1 *stage1) { buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len); buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len); buf_init_from_mem(&g->bitcode_file_output_path, stage1->emit_bitcode_ptr, stage1->emit_bitcode_len); - buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len); - buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len); if (stage1->builtin_zig_path_len != 0) { g->builtin_zig_path = buf_create_from_mem(stage1->builtin_zig_path_ptr, stage1->builtin_zig_path_len); diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h index 2b2e5a85b0..26b36db879 100644 --- a/src/stage1/stage1.h +++ b/src/stage1/stage1.h @@ -161,12 +161,6 @@ struct ZigStage1 { const char *emit_bitcode_ptr; size_t emit_bitcode_len; - const char *emit_analysis_json_ptr; - size_t emit_analysis_json_len; - - const char *emit_docs_ptr; - size_t emit_docs_len; - const char *builtin_zig_path_ptr; size_t builtin_zig_path_len; diff --git a/tools/merge_anal_dumps.zig b/tools/merge_anal_dumps.zig deleted file mode 100644 index 7c77e76a02..0000000000 --- a/tools/merge_anal_dumps.zig +++ /dev/null @@ -1,454 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("std"); -const json = std.json; -const mem = std.mem; -const fieldIndex = std.meta.fieldIndex; -const TypeId = builtin.TypeId; - -pub fn main() anyerror!void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer arena.deinit(); - - const allocator = arena.allocator(); - - const args = try std.process.argsAlloc(allocator); - - var parser: json.Parser = undefined; - var dump = Dump.init(allocator); - for (args[1..]) |arg| { - parser = json.Parser.init(allocator, false); - const json_text = try std.fs.cwd().readFileAlloc(allocator, arg, std.math.maxInt(usize)); - const tree = try parser.parse(json_text); - try dump.mergeJson(tree.root); - } - - const stdout = try std.io.getStdOut(); - try dump.render(stdout.writer()); -} - -/// AST source node -const Node = struct { - file: usize, - line: usize, - col: usize, - fields: []usize, - - fn hash(n: Node) u64 { - var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, n.file); - std.hash.autoHash(&hasher, n.line); - std.hash.autoHash(&hasher, n.col); - return hasher.final(); - } - - fn eql(a: Node, b: Node) bool { - return a.file == b.file and - a.line == b.line and - a.col == b.col; - } -}; - -const Error = struct { - src: usize, - name: []const u8, - - fn hash(n: Error) u64 { - var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, n.src); - return hasher.final(); - } - - fn eql(a: Error, b: Error) bool { - return a.src == b.src; - } -}; - -const simple_types = [_][]const u8{ - "Type", - "Void", - "Bool", - "NoReturn", - "ComptimeFloat", - "ComptimeInt", - "Undefined", - "Null", - "AnyFrame", - "EnumLiteral", -}; - -const Type = union(builtin.TypeId) { - Type, - Void, - Bool, - NoReturn, - ComptimeFloat, - ComptimeInt, - Undefined, - Null, - AnyFrame, - EnumLiteral, - - Int: Int, - Float: usize, // bits - - Vector: Array, - Optional: usize, // payload type index - Pointer: Pointer, - Array: Array, - - Struct, // TODO - ErrorUnion, // TODO - ErrorSet, // TODO - Enum, // TODO - Union, // TODO - Fn, // TODO - BoundFn, // TODO - Opaque, // TODO - Frame, // TODO - - const Int = struct { - bits: usize, - signed: bool, - }; - - const Pointer = struct { - elem: usize, - alignment: usize, - is_const: bool, - is_volatile: bool, - allow_zero: bool, - host_int_bytes: usize, - bit_offset_in_host: usize, - }; - - const Array = struct { - elem: usize, - len: usize, - }; - - fn hash(t: Type) u64 { - var hasher = std.hash.Wyhash.init(0); - std.hash.autoHash(&hasher, t); - return hasher.final(); - } - - fn eql(a: Type, b: Type) bool { - return std.meta.eql(a, b); - } -}; - -const Dump = struct { - zig_id: ?[]const u8 = null, - zig_version: ?[]const u8 = null, - root_name: ?[]const u8 = null, - targets: std.ArrayList([]const u8), - - file_list: std.ArrayList([]const u8), - file_map: FileMap, - - node_list: std.ArrayList(Node), - node_map: NodeMap, - - error_list: std.ArrayList(Error), - error_map: ErrorMap, - - type_list: std.ArrayList(Type), - type_map: TypeMap, - - const FileMap = std.StringHashMap(usize); - const NodeMap = std.HashMap(Node, usize, Node.hash, Node.eql, 80); - const ErrorMap = std.HashMap(Error, usize, Error.hash, Error.eql, 80); - const TypeMap = std.HashMap(Type, usize, Type.hash, Type.eql, 80); - - fn init(allocator: mem.Allocator) Dump { - return Dump{ - .targets = std.ArrayList([]const u8).init(allocator), - .file_list = std.ArrayList([]const u8).init(allocator), - .file_map = FileMap.init(allocator), - .node_list = std.ArrayList(Node).init(allocator), - .node_map = NodeMap.init(allocator), - .error_list = std.ArrayList(Error).init(allocator), - .error_map = ErrorMap.init(allocator), - .type_list = std.ArrayList(Type).init(allocator), - .type_map = TypeMap.init(allocator), - }; - } - - fn mergeJson(self: *Dump, root: json.Value) !void { - const params = &root.Object.get("params").?.value.Object; - const zig_id = params.get("zigId").?.value.String; - const zig_version = params.get("zigVersion").?.value.String; - const root_name = params.get("rootName").?.value.String; - try mergeSameStrings(&self.zig_id, zig_id); - try mergeSameStrings(&self.zig_version, zig_version); - try mergeSameStrings(&self.root_name, root_name); - - for (params.get("builds").?.value.Array.items) |json_build| { - const target = json_build.Object.get("target").?.value.String; - try self.targets.append(target); - } - - // Merge files. If the string matches, it's the same file. - const other_files = root.Object.get("files").?.value.Array.items; - var other_file_to_mine = std.AutoHashMap(usize, usize).init(self.a()); - for (other_files) |other_file, i| { - const gop = try self.file_map.getOrPut(other_file.String); - if (!gop.found_existing) { - gop.kv.value = self.file_list.items.len; - try self.file_list.append(other_file.String); - } - try other_file_to_mine.putNoClobber(i, gop.kv.value); - } - - // Merge AST nodes. If the file id, line, and column all match, it's the same AST node. - const other_ast_nodes = root.Object.get("astNodes").?.value.Array.items; - var other_ast_node_to_mine = std.AutoHashMap(usize, usize).init(self.a()); - for (other_ast_nodes) |other_ast_node_json, i| { - const other_file_id = jsonObjInt(other_ast_node_json, "file"); - const other_node = Node{ - .line = jsonObjInt(other_ast_node_json, "line"), - .col = jsonObjInt(other_ast_node_json, "col"), - .file = other_file_to_mine.getValue(other_file_id).?, - .fields = ([*]usize)(undefined)[0..0], - }; - const gop = try self.node_map.getOrPut(other_node); - if (!gop.found_existing) { - gop.kv.value = self.node_list.items.len; - try self.node_list.append(other_node); - } - try other_ast_node_to_mine.putNoClobber(i, gop.kv.value); - } - // convert fields lists - for (other_ast_nodes) |other_ast_node_json, i| { - const my_node_index = other_ast_node_to_mine.get(i).?.value; - const my_node = &self.node_list.items[my_node_index]; - if (other_ast_node_json.Object.get("fields")) |fields_json_kv| { - const other_fields = fields_json_kv.value.Array.items; - my_node.fields = try self.a().alloc(usize, other_fields.len); - for (other_fields) |other_field_index, field_i| { - const other_index = @intCast(usize, other_field_index.Integer); - my_node.fields[field_i] = other_ast_node_to_mine.get(other_index).?.value; - } - } - } - - // Merge errors. If the AST Node matches, it's the same error value. - const other_errors = root.Object.get("errors").?.value.Array.items; - var other_error_to_mine = std.AutoHashMap(usize, usize).init(self.a()); - for (other_errors) |other_error_json, i| { - const other_src_id = jsonObjInt(other_error_json, "src"); - const other_error = Error{ - .src = other_ast_node_to_mine.getValue(other_src_id).?, - .name = other_error_json.Object.get("name").?.value.String, - }; - const gop = try self.error_map.getOrPut(other_error); - if (!gop.found_existing) { - gop.kv.value = self.error_list.items.len; - try self.error_list.append(other_error); - } - try other_error_to_mine.putNoClobber(i, gop.kv.value); - } - - // Merge types. Now it starts to get advanced. - // First we identify all the simple types and merge those. - // Example: void, type, noreturn - // We can also do integers and floats. - const other_types = root.Object.get("types").?.value.Array.items; - var other_types_to_mine = std.AutoHashMap(usize, usize).init(self.a()); - for (other_types) |other_type_json, i| { - const type_kind = jsonObjInt(other_type_json, "kind"); - switch (type_kind) { - fieldIndex(TypeId, "Int").? => { - var signed: bool = undefined; - var bits: usize = undefined; - if (other_type_json.Object.get("i")) |kv| { - signed = true; - bits = @intCast(usize, kv.value.Integer); - } else if (other_type_json.Object.get("u")) |kv| { - signed = false; - bits = @intCast(usize, kv.value.Integer); - } else { - unreachable; - } - const other_type = Type{ - .Int = Type.Int{ - .bits = bits, - .signed = signed, - }, - }; - try self.mergeOtherType(other_type, i, &other_types_to_mine); - }, - fieldIndex(TypeId, "Float").? => { - const other_type = Type{ - .Float = jsonObjInt(other_type_json, "bits"), - }; - try self.mergeOtherType(other_type, i, &other_types_to_mine); - }, - else => {}, - } - - inline for (simple_types) |simple_type_name| { - if (type_kind == std.meta.fieldIndex(builtin.TypeId, simple_type_name).?) { - const other_type = @unionInit(Type, simple_type_name, {}); - try self.mergeOtherType(other_type, i, &other_types_to_mine); - } - } - } - } - - fn mergeOtherType( - self: *Dump, - other_type: Type, - other_type_index: usize, - other_types_to_mine: *std.AutoHashMap(usize, usize), - ) !void { - const gop = try self.type_map.getOrPut(other_type); - if (!gop.found_existing) { - gop.kv.value = self.type_list.items.len; - try self.type_list.append(other_type); - } - try other_types_to_mine.putNoClobber(other_type_index, gop.kv.value); - } - - fn render(self: *Dump, stream: anytype) !void { - var jw = json.WriteStream(@TypeOf(stream).Child, 10).init(stream); - try jw.beginObject(); - - try jw.objectField("typeKinds"); - try jw.beginArray(); - inline for (@typeInfo(builtin.TypeId).Enum.fields) |field| { - try jw.arrayElem(); - try jw.emitString(field.name); - } - try jw.endArray(); - - try jw.objectField("params"); - try jw.beginObject(); - - try jw.objectField("zigId"); - try jw.emitString(self.zig_id.?); - - try jw.objectField("zigVersion"); - try jw.emitString(self.zig_version.?); - - try jw.objectField("rootName"); - try jw.emitString(self.root_name.?); - - try jw.objectField("builds"); - try jw.beginArray(); - for (self.targets.items) |target| { - try jw.arrayElem(); - try jw.beginObject(); - try jw.objectField("target"); - try jw.emitString(target); - try jw.endObject(); - } - try jw.endArray(); - - try jw.endObject(); - - try jw.objectField("types"); - try jw.beginArray(); - for (self.type_list.items) |t| { - try jw.arrayElem(); - try jw.beginObject(); - - try jw.objectField("kind"); - try jw.emitNumber(@enumToInt(builtin.TypeId(t))); - - switch (t) { - .Int => |int| { - if (int.signed) { - try jw.objectField("i"); - } else { - try jw.objectField("u"); - } - try jw.emitNumber(int.bits); - }, - .Float => |bits| { - try jw.objectField("bits"); - try jw.emitNumber(bits); - }, - - else => {}, - } - - try jw.endObject(); - } - try jw.endArray(); - - try jw.objectField("errors"); - try jw.beginArray(); - for (self.error_list.items) |zig_error| { - try jw.arrayElem(); - try jw.beginObject(); - - try jw.objectField("src"); - try jw.emitNumber(zig_error.src); - - try jw.objectField("name"); - try jw.emitString(zig_error.name); - - try jw.endObject(); - } - try jw.endArray(); - - try jw.objectField("astNodes"); - try jw.beginArray(); - for (self.node_list.items) |node| { - try jw.arrayElem(); - try jw.beginObject(); - - try jw.objectField("file"); - try jw.emitNumber(node.file); - - try jw.objectField("line"); - try jw.emitNumber(node.line); - - try jw.objectField("col"); - try jw.emitNumber(node.col); - - if (node.fields.len != 0) { - try jw.objectField("fields"); - try jw.beginArray(); - - for (node.fields) |field_node_index| { - try jw.arrayElem(); - try jw.emitNumber(field_node_index); - } - try jw.endArray(); - } - - try jw.endObject(); - } - try jw.endArray(); - - try jw.objectField("files"); - try jw.beginArray(); - for (self.file_list.items) |file| { - try jw.arrayElem(); - try jw.emitString(file); - } - try jw.endArray(); - - try jw.endObject(); - } - - fn a(self: Dump) mem.Allocator { - return self.targets.allocator; - } - - fn mergeSameStrings(opt_dest: *?[]const u8, src: []const u8) !void { - if (opt_dest.*) |dest| { - if (!mem.eql(u8, dest, src)) - return error.MismatchedDumps; - } else { - opt_dest.* = src; - } - } -}; - -fn jsonObjInt(json_val: json.Value, field: []const u8) usize { - const uncasted = json_val.Object.get(field).?.value.Integer; - return @intCast(usize, uncasted); -}