authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-09-04 22:45:57+02:00
committergravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2022-09-04 22:45:57+02:00
logdbd60e3d296f1c195c4b4d56ca55ecb30444d407
treebd35bec73ca68e38ed18eea3f57c1c2fdc5974d3
parent9ce841a0f04be0aed70d69abad7e4aacca754e3c

autodoc: add support for doc tests


3 files changed, 84 insertions(+), 111 deletions(-)

lib/docs/index.html+5-1
...@@ -711,7 +711,11 @@...@@ -711,7 +711,11 @@
711 <h2>Examples</h2>711 <h2>Examples</h2>
712 <ul id="listFnExamples" class="examples"></ul>712 <ul id="listFnExamples" class="examples"></ul>
713 </div>713 </div>
714 <div id="sectTests" class="hidden">714 <div id="sectDocTests" class="hidden">
715 <h2>DocTests</h2>
716 <pre id="docTestsCode"></pre>
717 </div>
718 <div id="sectTests" class="hidden">
715 <h2>Tests</h2>719 <h2>Tests</h2>
716 <div class="table-container">720 <div class="table-container">
717 <table>721 <table>
lib/docs/main.js+15
...@@ -13,6 +13,8 @@ var zigAnalysis;...@@ -13,6 +13,8 @@ var zigAnalysis;
13 const domListTypes = document.getElementById("listTypes");13 const domListTypes = document.getElementById("listTypes");
14 const domSectTests = document.getElementById("sectTests");14 const domSectTests = document.getElementById("sectTests");
15 const domListTests = document.getElementById("listTests");15 const domListTests = document.getElementById("listTests");
16 const domSectDocTests = document.getElementById("sectDocTests");
17 const domDocTestsCode = document.getElementById("docTestsCode");
16 const domSectNamespaces = document.getElementById("sectNamespaces");18 const domSectNamespaces = document.getElementById("sectNamespaces");
17 const domListNamespaces = document.getElementById("listNamespaces");19 const domListNamespaces = document.getElementById("listNamespaces");
18 const domSectErrSets = document.getElementById("sectErrSets");20 const domSectErrSets = document.getElementById("sectErrSets");
...@@ -384,6 +386,7 @@ var zigAnalysis;...@@ -384,6 +386,7 @@ var zigAnalysis;
384 domSectPkgs.classList.add("hidden");386 domSectPkgs.classList.add("hidden");
385 domSectTypes.classList.add("hidden");387 domSectTypes.classList.add("hidden");
386 domSectTests.classList.add("hidden");388 domSectTests.classList.add("hidden");
389 domSectDocTests.classList.add("hidden");
387 domSectNamespaces.classList.add("hidden");390 domSectNamespaces.classList.add("hidden");
388 domSectErrSets.classList.add("hidden");391 domSectErrSets.classList.add("hidden");
389 domSectFns.classList.add("hidden");392 domSectFns.classList.add("hidden");
...@@ -452,6 +455,10 @@ var zigAnalysis;...@@ -452,6 +455,10 @@ var zigAnalysis;
452 let lastIsDecl = isDecl(last);455 let lastIsDecl = isDecl(last);
453 let lastIsType = isType(last);456 let lastIsType = isType(last);
454 let lastIsContainerType = isContainerType(last);457 let lastIsContainerType = isContainerType(last);
458
459 if (lastIsDecl){
460 renderDocTest(last);
461 }
455462
456 if (lastIsContainerType) {463 if (lastIsContainerType) {
457 return renderContainer(last);464 return renderContainer(last);
...@@ -477,6 +484,14 @@ var zigAnalysis;...@@ -477,6 +484,14 @@ var zigAnalysis;
477484
478 return renderValue(last);485 return renderValue(last);
479 }486 }
487
488 }
489
490 function renderDocTest(decl) {
491 if (!("decltest" in decl)) return;
492 const astNode = zigAnalysis.astNodes[decl.decltest];
493 domSectDocTests.classList.remove("hidden");
494 domDocTestsCode.innerHTML = astNode.code;
480 }495 }
481496
482 function renderUnknownDecl(decl) {497 function renderUnknownDecl(decl) {
src/Autodoc.zig+64-110
...@@ -58,7 +58,7 @@ const RefPathResumeInfo = struct {...@@ -58,7 +58,7 @@ const RefPathResumeInfo = struct {
58const SrcLocInfo = struct {58const SrcLocInfo = struct {
59 bytes: u32 = 0,59 bytes: u32 = 0,
60 line: usize = 0,60 line: usize = 0,
61 src_node: i32 = 0,61 src_node: u32 = 0,
62};62};
6363
64var arena_allocator: std.heap.ArenaAllocator = undefined;64var arena_allocator: std.heap.ArenaAllocator = undefined;
...@@ -481,6 +481,7 @@ const DocData = struct {...@@ -481,6 +481,7 @@ const DocData = struct {
481 line: usize = 0,481 line: usize = 0,
482 col: usize = 0,482 col: usize = 0,
483 name: ?[]const u8 = null,483 name: ?[]const u8 = null,
484 code: ?[]const u8 = null,
484 docs: ?[]const u8 = null,485 docs: ?[]const u8 = null,
485 fields: ?[]usize = null, // index into astNodes486 fields: ?[]usize = null, // index into astNodes
486 @"comptime": bool = false,487 @"comptime": bool = false,
...@@ -2441,7 +2442,10 @@ fn walkInstruction(...@@ -2441,7 +2442,10 @@ fn walkInstruction(
2441 break :blk src_node;2442 break :blk src_node;
2442 } else null;2443 } else null;
24432444
2444 const src_info = try self.srcLocInfo(file, src_node, parent_src);2445 const src_info = if (src_node) |sn|
2446 try self.srcLocInfo(file, sn, parent_src)
2447 else
2448 parent_src;
2445 _ = src_info;2449 _ = src_info;
24462450
2447 const decls_len = if (small.has_decls_len) blk: {2451 const decls_len = if (small.has_decls_len) blk: {
...@@ -2500,7 +2504,10 @@ fn walkInstruction(...@@ -2500,7 +2504,10 @@ fn walkInstruction(
2500 break :blk src_node;2504 break :blk src_node;
2501 } else null;2505 } else null;
25022506
2503 const src_info = try self.srcLocInfo(file, src_node, parent_src);2507 const src_info = if (src_node) |sn|
2508 try self.srcLocInfo(file, sn, parent_src)
2509 else
2510 parent_src;
25042511
2505 const tag_type: ?Ref = if (small.has_tag_type) blk: {2512 const tag_type: ?Ref = if (small.has_tag_type) blk: {
2506 const tag_type = file.zir.extra[extra_index];2513 const tag_type = file.zir.extra[extra_index];
...@@ -2623,7 +2630,10 @@ fn walkInstruction(...@@ -2623,7 +2630,10 @@ fn walkInstruction(
2623 break :blk src_node;2630 break :blk src_node;
2624 } else null;2631 } else null;
26252632
2626 const src_info = try self.srcLocInfo(file, src_node, parent_src);2633 const src_info = if (src_node) |sn|
2634 try self.srcLocInfo(file, sn, parent_src)
2635 else
2636 parent_src;
26272637
2628 const tag_type: ?Ref = if (small.has_tag_type) blk: {2638 const tag_type: ?Ref = if (small.has_tag_type) blk: {
2629 const tag_type = file.zir.extra[extra_index];2639 const tag_type = file.zir.extra[extra_index];
...@@ -2770,7 +2780,10 @@ fn walkInstruction(...@@ -2770,7 +2780,10 @@ fn walkInstruction(
2770 break :blk src_node;2780 break :blk src_node;
2771 } else null;2781 } else null;
27722782
2773 const src_info = try self.srcLocInfo(file, src_node, parent_src);2783 const src_info = if (src_node) |sn|
2784 try self.srcLocInfo(file, sn, parent_src)
2785 else
2786 parent_src;
27742787
2775 const fields_len = if (small.has_fields_len) blk: {2788 const fields_len = if (small.has_fields_len) blk: {
2776 const fields_len = file.zir.extra[extra_index];2789 const fields_len = file.zir.extra[extra_index];
...@@ -2911,6 +2924,7 @@ fn walkDecls(...@@ -2911,6 +2924,7 @@ fn walkDecls(
2911 priv_decl_indexes: *std.ArrayListUnmanaged(usize),2924 priv_decl_indexes: *std.ArrayListUnmanaged(usize),
2912 extra_start: usize,2925 extra_start: usize,
2913) AutodocErrors!usize {2926) AutodocErrors!usize {
2927 const data = file.zir.instructions.items(.data);
2914 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;2928 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
2915 var extra_index = extra_start + bit_bags_count;2929 var extra_index = extra_start + bit_bags_count;
2916 var bit_bag_index: usize = extra_start;2930 var bit_bag_index: usize = extra_start;
...@@ -2938,7 +2952,7 @@ fn walkDecls(...@@ -2938,7 +2952,7 @@ fn walkDecls(
2938 // const hash_u32s = file.zir.extra[extra_index..][0..4];2952 // const hash_u32s = file.zir.extra[extra_index..][0..4];
2939 extra_index += 4;2953 extra_index += 4;
29402954
2941 //const line = file.zir.extra[extra_index];2955 // const line = file.zir.extra[extra_index];
2942 extra_index += 1;2956 extra_index += 1;
2943 const decl_name_index = file.zir.extra[extra_index];2957 const decl_name_index = file.zir.extra[extra_index];
2944 extra_index += 1;2958 extra_index += 1;
...@@ -2968,10 +2982,11 @@ fn walkDecls(...@@ -2968,10 +2982,11 @@ fn walkDecls(
2968 };2982 };
2969 _ = addrspace_inst;2983 _ = addrspace_inst;
29702984
2971 // const pub_str = if (is_pub) "pub " else "";2985 // This is known to work because decl values are always block_inlines
2972 // const hash_bytes = @bitCast([16]u8, hash_u32s.*);2986 const value_pl_node = data[value_index].pl_node;
2987 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
29732988
2974 var is_test = false; // we discover if it's a test by lookin at its name2989 var is_test = false; // we discover if it's a test by looking at its name
2975 const name: []const u8 = blk: {2990 const name: []const u8 = blk: {
2976 if (decl_name_index == 0) {2991 if (decl_name_index == 0) {
2977 break :blk if (is_exported) "usingnamespace" else "comptime";2992 break :blk if (is_exported) "usingnamespace" else "comptime";
...@@ -2979,84 +2994,32 @@ fn walkDecls(...@@ -2979,84 +2994,32 @@ fn walkDecls(
2979 is_test = true;2994 is_test = true;
2980 break :blk "test";2995 break :blk "test";
2981 } else if (decl_name_index == 2) {2996 } else if (decl_name_index == 2) {
2982 is_test = true;2997 // it is a decltest
2983 // TODO: remove temporary hack2998 const decl_being_tested = scope.resolveDeclName(doc_comment_index);
2984 break :blk "test";2999 const func_index = getBlockInlineBreak(file.zir, value_index);
2985 // // it is a decltest3000
2986 // const decl_being_tested = scope.resolveDeclName(doc_comment_index);3001 const pl_node = data[Zir.refToIndex(func_index).?].pl_node;
2987 // const ast_node_index = idx: {3002 const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);
2988 // const idx = self.ast_nodes.items.len;3003 const tree = try file.getTree(self.module.gpa);
2989 // const file_source = file.getSource(self.module.gpa) catch unreachable; // TODO fix this3004 const test_source_code = tree.getNodeSource(fn_src.src_node);
2990 // const source_of_decltest_function = srcloc: {3005
2991 // const func_index = getBlockInlineBreak(file.zir, value_index);3006 const ast_node_index = self.ast_nodes.items.len;
2992 // // a decltest is always a function3007 try self.ast_nodes.append(self.arena, .{
2993 // const tag = file.zir.instructions.items(.tag)[Zir.refToIndex(func_index).?];3008 .file = 0,
2994 // std.debug.assert(tag == .func_extended);3009 .line = 0,
29953010 .col = 0,
2996 // const pl_node = file.zir.instructions.items(.data)[Zir.refToIndex(func_index).?].pl_node;3011 .code = test_source_code,
2997 // const extra = file.zir.extraData(Zir.Inst.ExtendedFunc, pl_node.payload_index);3012 });
2998 // const bits = @bitCast(Zir.Inst.ExtendedFunc.Bits, extra.data.bits);3013 self.decls.items[decl_being_tested].decltest = ast_node_index;
29993014 self.decls.items[decls_slot_index] = .{
3000 // var extra_index_for_this_func: usize = extra.end;3015 ._analyzed = true,
3001 // if (bits.has_lib_name) extra_index_for_this_func += 1;3016 .name = "test",
3002 // if (bits.has_cc) extra_index_for_this_func += 1;3017 .isTest = true,
3003 // if (bits.has_align) extra_index_for_this_func += 1;3018 .src = ast_node_index,
30043019 .value = .{ .expr = .{ .type = 0 } },
3005 // const ret_ty_body = file.zir.extra[extra_index_for_this_func..][0..extra.data.ret_body_len];3020 .kind = "const",
3006 // extra_index_for_this_func += ret_ty_body.len;3021 };
30073022 continue;
3008 // const body = file.zir.extra[extra_index_for_this_func..][0..extra.data.body_len];
3009 // extra_index_for_this_func += body.len;
3010
3011 // var src_locs: Zir.Inst.Func.SrcLocs = undefined;
3012 // if (body.len != 0) {
3013 // src_locs = file.zir.extraData(Zir.Inst.Func.SrcLocs, extra_index_for_this_func).data;
3014 // } else {
3015 // src_locs = .{
3016 // .lbrace_line = line,
3017 // .rbrace_line = line,
3018 // .columns = 0, // TODO get columns when body.len == 0
3019 // };
3020 // }
3021 // break :srcloc src_locs;
3022 // };
3023 // const source_slice = slice: {
3024 // var start_byte_offset: u32 = 0;
3025 // var end_byte_offset: u32 = 0;
3026 // const rbrace_col = @truncate(u16, source_of_decltest_function.columns >> 16);
3027 // var lines: u32 = 0;
3028 // for (file_source.bytes) |b, i| {
3029 // if (b == '\n') {
3030 // lines += 1;
3031 // }
3032 // if (lines == source_of_decltest_function.lbrace_line) {
3033 // start_byte_offset = @intCast(u32, i);
3034 // }
3035 // if (lines == source_of_decltest_function.rbrace_line) {
3036 // end_byte_offset = @intCast(u32, i) + rbrace_col;
3037 // break;
3038 // }
3039 // }
3040 // break :slice file_source.bytes[start_byte_offset..end_byte_offset];
3041 // };
3042 // try self.ast_nodes.append(self.arena, .{
3043 // .file = 0,
3044 // .line = line,
3045 // .col = 0,
3046 // .name = try self.arena.dupe(u8, source_slice),
3047 // });
3048 // break :idx idx;
3049 // };
3050 // self.decls.items[decl_being_tested].decltest = ast_node_index;
3051 // self.decls.items[decls_slot_index] = .{
3052 // ._analyzed = true,
3053 // .name = "test",
3054 // .isTest = true,
3055 // .src = ast_node_index,
3056 // .value = .{ .expr = .{ .type = 0 } },
3057 // .kind = "const",
3058 // };
3059 // continue;
3060 } else {3023 } else {
3061 const raw_decl_name = file.zir.nullTerminatedString(decl_name_index);3024 const raw_decl_name = file.zir.nullTerminatedString(decl_name_index);
3062 if (raw_decl_name.len == 0) {3025 if (raw_decl_name.len == 0) {
...@@ -3073,11 +3036,6 @@ fn walkDecls(...@@ -3073,11 +3036,6 @@ fn walkDecls(
3073 else3036 else
3074 null;3037 null;
30753038
3076 // This is known to work because decl values are always block_inlines
3077 const data = file.zir.instructions.items(.data);
3078 const value_pl_node = data[value_index].pl_node;
3079 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3080
3081 // astnode3039 // astnode
3082 const ast_node_index = idx: {3040 const ast_node_index = idx: {
3083 const idx = self.ast_nodes.items.len;3041 const idx = self.ast_nodes.items.len;
...@@ -4215,24 +4173,20 @@ fn writePackageTableToJson(...@@ -4215,24 +4173,20 @@ fn writePackageTableToJson(
4215fn srcLocInfo(4173fn srcLocInfo(
4216 self: Autodoc,4174 self: Autodoc,
4217 file: *File,4175 file: *File,
4218 src_node: ?i32,4176 src_node: i32,
4219 parent_src: SrcLocInfo,4177 parent_src: SrcLocInfo,
4220) !SrcLocInfo {4178) !SrcLocInfo {
4221 if (src_node) |unwrapped_src_node| {4179 const sn = @intCast(u32, @intCast(i32, parent_src.src_node) + src_node);
4222 const sn = parent_src.src_node + unwrapped_src_node;4180 const tree = try file.getTree(self.module.gpa);
4223 const tree = try file.getTree(self.module.gpa);4181 const node_idx = @bitCast(Ast.Node.Index, sn);
4224 const node_idx = @bitCast(Ast.Node.Index, sn);4182 const tokens = tree.nodes.items(.main_token);
4225 const tokens = tree.nodes.items(.main_token);4183
42264184 const tok_idx = tokens[node_idx];
4227 const tok_idx = tokens[node_idx];4185 const start = tree.tokens.items(.start)[tok_idx];
4228 const start = tree.tokens.items(.start)[tok_idx];4186 const loc = tree.tokenLocation(parent_src.bytes, tok_idx);
4229 const loc = tree.tokenLocation(parent_src.bytes, tok_idx);4187 return SrcLocInfo{
4230 return SrcLocInfo{4188 .line = parent_src.line + loc.line,
4231 .line = parent_src.line + loc.line,4189 .bytes = start,
4232 .bytes = start,4190 .src_node = sn,
4233 .src_node = sn,4191 };
4234 };
4235 } else {
4236 return parent_src;
4237 }
4238}4192}