Commit dbd60e3d29

Loris Cro <kappaloris@gmail.com>
2022-09-04 22:45:57
autodoc: add support for doc tests
1 parent 9ce841a
Changed files (3)
lib/docs/index.html
@@ -711,7 +711,11 @@
               <h2>Examples</h2>
               <ul id="listFnExamples" class="examples"></ul>
             </div>
-             <div id="sectTests" class="hidden">
+            <div id="sectDocTests" class="hidden">
+              <h2>DocTests</h2>
+              <pre id="docTestsCode"></pre>
+            </div>
+            <div id="sectTests" class="hidden">
               <h2>Tests</h2>
               <div class="table-container">
                 <table>
lib/docs/main.js
@@ -13,6 +13,8 @@ var zigAnalysis;
   const domListTypes = document.getElementById("listTypes");
   const domSectTests = document.getElementById("sectTests");
   const domListTests = document.getElementById("listTests");
+  const domSectDocTests = document.getElementById("sectDocTests");
+  const domDocTestsCode = document.getElementById("docTestsCode");
   const domSectNamespaces = document.getElementById("sectNamespaces");
   const domListNamespaces = document.getElementById("listNamespaces");
   const domSectErrSets = document.getElementById("sectErrSets");
@@ -384,6 +386,7 @@ var zigAnalysis;
     domSectPkgs.classList.add("hidden");
     domSectTypes.classList.add("hidden");
     domSectTests.classList.add("hidden");
+    domSectDocTests.classList.add("hidden");
     domSectNamespaces.classList.add("hidden");
     domSectErrSets.classList.add("hidden");
     domSectFns.classList.add("hidden");
@@ -452,6 +455,10 @@ var zigAnalysis;
     let lastIsDecl = isDecl(last);
     let lastIsType = isType(last);
     let lastIsContainerType = isContainerType(last);
+      
+    if (lastIsDecl){
+       renderDocTest(last);  
+    }
 
     if (lastIsContainerType) {
       return renderContainer(last);
@@ -477,6 +484,14 @@ var zigAnalysis;
 
       return renderValue(last);
     }
+    
+  }
+    
+  function renderDocTest(decl) {
+    if (!("decltest" in decl)) return;
+    const astNode = zigAnalysis.astNodes[decl.decltest];
+    domSectDocTests.classList.remove("hidden");
+    domDocTestsCode.innerHTML = astNode.code;
   }
 
   function renderUnknownDecl(decl) {
src/Autodoc.zig
@@ -58,7 +58,7 @@ const RefPathResumeInfo = struct {
 const SrcLocInfo = struct {
     bytes: u32 = 0,
     line: usize = 0,
-    src_node: i32 = 0,
+    src_node: u32 = 0,
 };
 
 var arena_allocator: std.heap.ArenaAllocator = undefined;
@@ -481,6 +481,7 @@ const DocData = struct {
         line: usize = 0,
         col: usize = 0,
         name: ?[]const u8 = null,
+        code: ?[]const u8 = null,
         docs: ?[]const u8 = null,
         fields: ?[]usize = null, // index into astNodes
         @"comptime": bool = false,
@@ -2441,7 +2442,10 @@ fn walkInstruction(
                         break :blk src_node;
                     } else null;
 
-                    const src_info = try self.srcLocInfo(file, src_node, parent_src);
+                    const src_info = if (src_node) |sn|
+                        try self.srcLocInfo(file, sn, parent_src)
+                    else
+                        parent_src;
                     _ = src_info;
 
                     const decls_len = if (small.has_decls_len) blk: {
@@ -2500,7 +2504,10 @@ fn walkInstruction(
                         break :blk src_node;
                     } else null;
 
-                    const src_info = try self.srcLocInfo(file, src_node, parent_src);
+                    const src_info = if (src_node) |sn|
+                        try self.srcLocInfo(file, sn, parent_src)
+                    else
+                        parent_src;
 
                     const tag_type: ?Ref = if (small.has_tag_type) blk: {
                         const tag_type = file.zir.extra[extra_index];
@@ -2623,7 +2630,10 @@ fn walkInstruction(
                         break :blk src_node;
                     } else null;
 
-                    const src_info = try self.srcLocInfo(file, src_node, parent_src);
+                    const src_info = if (src_node) |sn|
+                        try self.srcLocInfo(file, sn, parent_src)
+                    else
+                        parent_src;
 
                     const tag_type: ?Ref = if (small.has_tag_type) blk: {
                         const tag_type = file.zir.extra[extra_index];
@@ -2770,7 +2780,10 @@ fn walkInstruction(
                         break :blk src_node;
                     } else null;
 
-                    const src_info = try self.srcLocInfo(file, src_node, parent_src);
+                    const src_info = if (src_node) |sn|
+                        try self.srcLocInfo(file, sn, parent_src)
+                    else
+                        parent_src;
 
                     const fields_len = if (small.has_fields_len) blk: {
                         const fields_len = file.zir.extra[extra_index];
@@ -2911,6 +2924,7 @@ fn walkDecls(
     priv_decl_indexes: *std.ArrayListUnmanaged(usize),
     extra_start: usize,
 ) AutodocErrors!usize {
+    const data = file.zir.instructions.items(.data);
     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;
@@ -2938,7 +2952,7 @@ fn walkDecls(
         // const hash_u32s = file.zir.extra[extra_index..][0..4];
         extra_index += 4;
 
-        //const line = file.zir.extra[extra_index];
+        // const line = file.zir.extra[extra_index];
         extra_index += 1;
         const decl_name_index = file.zir.extra[extra_index];
         extra_index += 1;
@@ -2968,10 +2982,11 @@ fn walkDecls(
         };
         _ = addrspace_inst;
 
-        // const pub_str = if (is_pub) "pub " else "";
-        // const hash_bytes = @bitCast([16]u8, hash_u32s.*);
+        // This is known to work because decl values are always block_inlines
+        const value_pl_node = data[value_index].pl_node;
+        const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
 
-        var is_test = false; // we discover if it's a test by lookin at its name
+        var is_test = false; // we discover if it's a test by looking at its name
         const name: []const u8 = blk: {
             if (decl_name_index == 0) {
                 break :blk if (is_exported) "usingnamespace" else "comptime";
@@ -2979,84 +2994,32 @@ fn walkDecls(
                 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;
+                // it is a decltest
+                const decl_being_tested = scope.resolveDeclName(doc_comment_index);
+                const func_index = getBlockInlineBreak(file.zir, value_index);
+
+                const pl_node = data[Zir.refToIndex(func_index).?].pl_node;
+                const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);
+                const tree = try file.getTree(self.module.gpa);
+                const test_source_code = tree.getNodeSource(fn_src.src_node);
+
+                const ast_node_index = self.ast_nodes.items.len;
+                try self.ast_nodes.append(self.arena, .{
+                    .file = 0,
+                    .line = 0,
+                    .col = 0,
+                    .code = test_source_code,
+                });
+                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) {
@@ -3073,11 +3036,6 @@ fn walkDecls(
         else
             null;
 
-        // This is known to work because decl values are always block_inlines
-        const data = file.zir.instructions.items(.data);
-        const value_pl_node = data[value_index].pl_node;
-        const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
-
         // astnode
         const ast_node_index = idx: {
             const idx = self.ast_nodes.items.len;
@@ -4215,24 +4173,20 @@ fn writePackageTableToJson(
 fn srcLocInfo(
     self: Autodoc,
     file: *File,
-    src_node: ?i32,
+    src_node: i32,
     parent_src: SrcLocInfo,
 ) !SrcLocInfo {
-    if (src_node) |unwrapped_src_node| {
-        const sn = parent_src.src_node + unwrapped_src_node;
-        const tree = try file.getTree(self.module.gpa);
-        const node_idx = @bitCast(Ast.Node.Index, sn);
-        const tokens = tree.nodes.items(.main_token);
-
-        const tok_idx = tokens[node_idx];
-        const start = tree.tokens.items(.start)[tok_idx];
-        const loc = tree.tokenLocation(parent_src.bytes, tok_idx);
-        return SrcLocInfo{
-            .line = parent_src.line + loc.line,
-            .bytes = start,
-            .src_node = sn,
-        };
-    } else {
-        return parent_src;
-    }
+    const sn = @intCast(u32, @intCast(i32, parent_src.src_node) + src_node);
+    const tree = try file.getTree(self.module.gpa);
+    const node_idx = @bitCast(Ast.Node.Index, sn);
+    const tokens = tree.nodes.items(.main_token);
+
+    const tok_idx = tokens[node_idx];
+    const start = tree.tokens.items(.start)[tok_idx];
+    const loc = tree.tokenLocation(parent_src.bytes, tok_idx);
+    return SrcLocInfo{
+        .line = parent_src.line + loc.line,
+        .bytes = start,
+        .src_node = sn,
+    };
 }