authorgravatar for kappaloris@gmail.comLoris Cro <kappaloris@gmail.com> 2023-04-12 03:14:02+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-12 01:14:02+00:00
log602029bb2fb78048e46136784e717b57b8de8f2c
treee207df733980856281f8a0720265526a1dffd84e
parent52d552f11827a524c5f490bef5d6c4a2b07cddf8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Autodoc usingnamespace (#15216)

* autodoc: init support for usingnamespace decls * autodoc: don't build autodoc when building zig2.c * autodoc: usingnamespace decls support in frontend (#15203) * autodoc: init support for usingnamespace decls * autodoc: usingnamespace decls support in frontend --------- Co-authored-by: Krzysztof Wolicki <46651553+der-teufel-programming@users.noreply.github.com>

4 files changed, 632 insertions(+), 415 deletions(-)

lib/docs/main.js+71-16
...@@ -2586,7 +2586,8 @@ const NAV_MODES = {...@@ -2586,7 +2586,8 @@ const NAV_MODES = {
2586 fnsList,2586 fnsList,
2587 varsList,2587 varsList,
2588 valsList,2588 valsList,
2589 testsList2589 testsList,
2590 unsList
2590 ) {2591 ) {
2591 for (let i = 0; i < decls.length; i += 1) {2592 for (let i = 0; i < decls.length; i += 1) {
2592 let decl = getDecl(decls[i]);2593 let decl = getDecl(decls[i]);
...@@ -2644,6 +2645,10 @@ const NAV_MODES = {...@@ -2644,6 +2645,10 @@ const NAV_MODES = {
2644 valsList.push(decl);2645 valsList.push(decl);
2645 }2646 }
2646 }2647 }
2648
2649 if (decl.is_uns) {
2650 unsList.push(decl);
2651 }
2647 }2652 }
2648 }2653 }
26492654
...@@ -2669,6 +2674,8 @@ const NAV_MODES = {...@@ -2669,6 +2674,8 @@ const NAV_MODES = {
26692674
2670 let testsList = [];2675 let testsList = [];
26712676
2677 let unsList = [];
2678
2672 categorizeDecls(2679 categorizeDecls(
2673 container.pubDecls,2680 container.pubDecls,
2674 typesList,2681 typesList,
...@@ -2677,7 +2684,8 @@ const NAV_MODES = {...@@ -2677,7 +2684,8 @@ const NAV_MODES = {
2677 fnsList,2684 fnsList,
2678 varsList,2685 varsList,
2679 valsList,2686 valsList,
2680 testsList2687 testsList,
2688 unsList
2681 );2689 );
2682 if (curNav.showPrivDecls)2690 if (curNav.showPrivDecls)
2683 categorizeDecls(2691 categorizeDecls(
...@@ -2688,9 +2696,40 @@ const NAV_MODES = {...@@ -2688,9 +2696,40 @@ const NAV_MODES = {
2688 fnsList,2696 fnsList,
2689 varsList,2697 varsList,
2690 valsList,2698 valsList,
2691 testsList2699 testsList,
2700 unsList
2692 );2701 );
26932702
2703 while (unsList.length > 0) {
2704 let uns = unsList.shift();
2705 let declValue = resolveValue(uns.value);
2706 if (!("type" in declValue.expr)) continue;
2707 let uns_container = getType(declValue.expr.type);
2708 categorizeDecls(
2709 uns_container.pubDecls,
2710 typesList,
2711 namespacesList,
2712 errSetsList,
2713 fnsList,
2714 varsList,
2715 valsList,
2716 testsList,
2717 unsList
2718 );
2719 if (curNav.showPrivDecls)
2720 categorizeDecls(
2721 uns_container.privDecls,
2722 typesList,
2723 namespacesList,
2724 errSetsList,
2725 fnsList,
2726 varsList,
2727 valsList,
2728 testsList,
2729 unsList
2730 );
2731 }
2732
2694 typesList.sort(byNameProperty);2733 typesList.sort(byNameProperty);
2695 namespacesList.sort(byNameProperty);2734 namespacesList.sort(byNameProperty);
2696 errSetsList.sort(byNameProperty);2735 errSetsList.sort(byNameProperty);
...@@ -3090,7 +3129,7 @@ const NAV_MODES = {...@@ -3090,7 +3129,7 @@ const NAV_MODES = {
3090 function findSubDecl(parentTypeOrDecl, childName) {3129 function findSubDecl(parentTypeOrDecl, childName) {
3091 let parentType = parentTypeOrDecl;3130 let parentType = parentTypeOrDecl;
3092 {3131 {
3093 // Generic functions / resorlving decls3132 // Generic functions / resolving decls
3094 if ("value" in parentType) {3133 if ("value" in parentType) {
3095 const rv = resolveValue(parentType.value);3134 const rv = resolveValue(parentType.value);
3096 if ("type" in rv.expr) {3135 if ("type" in rv.expr) {
...@@ -3116,20 +3155,35 @@ const NAV_MODES = {...@@ -3116,20 +3155,35 @@ const NAV_MODES = {
3116 }3155 }
3117 }3156 }
31183157
3119 if (!parentType.pubDecls) return null;3158 if (parentType.pubDecls) {
3120 for (let i = 0; i < parentType.pubDecls.length; i += 1) {3159 for (let i = 0; i < parentType.pubDecls.length; i += 1) {
3121 let declIndex = parentType.pubDecls[i];3160 let declIndex = parentType.pubDecls[i];
3122 let childDecl = getDecl(declIndex);3161 let childDecl = getDecl(declIndex);
3123 if (childDecl.name === childName) {3162 if (childDecl.name === childName) {
3124 return childDecl;3163 return childDecl;
3164 } else if (childDecl.is_uns) {
3165 let declValue = resolveValue(childDecl.value);
3166 if (!("type" in declValue.expr)) continue;
3167 let uns_container = getType(declValue.expr.type);
3168 let uns_res = findSubDecl(uns_container, childName);
3169 if (uns_res !== null) return uns_res;
3170 }
3125 }3171 }
3126 }3172 }
3127 if (!parentType.privDecls) return null;3173
3128 for (let i = 0; i < parentType.privDecls.length; i += 1) {3174 if (parentType.privDecls) {
3129 let declIndex = parentType.privDecls[i];3175 for (let i = 0; i < parentType.privDecls.length; i += 1) {
3130 let childDecl = getDecl(declIndex);3176 let declIndex = parentType.privDecls[i];
3131 if (childDecl.name === childName) {3177 let childDecl = getDecl(declIndex);
3132 return childDecl;3178 if (childDecl.name === childName) {
3179 return childDecl;
3180 } else if (childDecl.is_uns) {
3181 let declValue = resolveValue(childDecl.value);
3182 if (!("type" in declValue.expr)) continue;
3183 let uns_container = getType(declValue.expr.type);
3184 let uns_res = findSubDecl(uns_container, childName);
3185 if (uns_res !== null) return uns_res;
3186 }
3133 }3187 }
3134 }3188 }
3135 return null;3189 return null;
...@@ -3908,6 +3962,7 @@ const NAV_MODES = {...@@ -3908,6 +3962,7 @@ const NAV_MODES = {
3908 src: decl[2],3962 src: decl[2],
3909 value: decl[3],3963 value: decl[3],
3910 decltest: decl[4],3964 decltest: decl[4],
3965 is_uns: decl[5],
3911 };3966 };
3912 }3967 }
39133968
src/Autodoc.zig+558-398
...@@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged(...@@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged(
37 std.ArrayListUnmanaged(RefPathResumeInfo),37 std.ArrayListUnmanaged(RefPathResumeInfo),
38) = .{},38) = .{},
39ref_paths_pending_on_decls: std.AutoHashMapUnmanaged(39ref_paths_pending_on_decls: std.AutoHashMapUnmanaged(
40 usize,40 *Scope.DeclStatus,
41 std.ArrayListUnmanaged(RefPathResumeInfo),41 std.ArrayListUnmanaged(RefPathResumeInfo),
42) = .{},42) = .{},
43ref_paths_pending_on_types: std.AutoHashMapUnmanaged(43ref_paths_pending_on_types: std.AutoHashMapUnmanaged(
...@@ -344,28 +344,48 @@ fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File {...@@ -344,28 +344,48 @@ fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File {
344}344}
345345
346/// Represents a chain of scopes, used to resolve decl references to the346/// Represents a chain of scopes, used to resolve decl references to the
347/// corresponding entry in `self.decls`.347/// corresponding entry in `self.decls`. It also keeps track of whether
348/// a given decl has been analyzed or not.
348const Scope = struct {349const Scope = struct {
349 parent: ?*Scope,350 parent: ?*Scope,
350 map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls`351 map: std.AutoHashMapUnmanaged(
352 u32, // index into the current file's string table (decl name)
353 DeclStatus,
354 ) = .{},
355
351 enclosing_type: usize, // index into `types`356 enclosing_type: usize, // index into `types`
352357
353 /// Assumes all decls in present scope and upper scopes have already358 pub const DeclStatus = union(enum) {
354 /// been either fully resolved or at least reserved.359 Analyzed: usize, // index into `decls`
355 pub fn resolveDeclName(self: Scope, string_table_idx: u32) usize {360 Pending,
361 NotRequested: u32, // instr_index
362
363 };
364
365 /// Returns a pointer so that the caller has a chance to modify the value
366 /// in case they decide to start analyzing a previously not requested decl.
367 pub fn resolveDeclName(self: Scope, string_table_idx: u32, file: *File, inst_index: usize) *DeclStatus {
356 var cur: ?*const Scope = &self;368 var cur: ?*const Scope = &self;
357 return while (cur) |s| : (cur = s.parent) {369 return while (cur) |s| : (cur = s.parent) {
358 break s.map.get(string_table_idx) orelse continue;370 break s.map.getPtr(string_table_idx) orelse continue;
359 } else unreachable;371 } else {
372 printWithContext(
373 file,
374 inst_index,
375 "Could not find `{s}`\n\n",
376 .{file.zir.nullTerminatedString(string_table_idx)},
377 );
378 unreachable;
379 };
360 }380 }
361381
362 pub fn insertDeclRef(382 pub fn insertDeclRef(
363 self: *Scope,383 self: *Scope,
364 arena: std.mem.Allocator,384 arena: std.mem.Allocator,
365 decl_name_index: u32, // decl name385 decl_name_index: u32, // index into the current file's string table
366 decls_slot_index: usize,386 decl_status: DeclStatus,
367 ) !void {387 ) !void {
368 try self.map.put(arena, decl_name_index, decls_slot_index);388 try self.map.put(arena, decl_name_index, decl_status);
369 }389 }
370};390};
371391
...@@ -479,7 +499,7 @@ const DocData = struct {...@@ -479,7 +499,7 @@ const DocData = struct {
479 value: WalkResult,499 value: WalkResult,
480 // The index in astNodes of the `test declname { }` node500 // The index in astNodes of the `test declname { }` node
481 decltest: ?usize = null,501 decltest: ?usize = null,
482 _analyzed: bool, // omitted in json data502 is_uns: bool = false, // usingnamespace
483503
484 pub fn jsonStringify(504 pub fn jsonStringify(
485 self: Decl,505 self: Decl,
...@@ -676,7 +696,8 @@ const DocData = struct {...@@ -676,7 +696,8 @@ const DocData = struct {
676 @"&": usize, // index in `exprs`696 @"&": usize, // index in `exprs`
677 type: usize, // index in `types`697 type: usize, // index in `types`
678 this: usize, // index in `types`698 this: usize, // index in `types`
679 declRef: usize, // index in `decls`699 declRef: *Scope.DeclStatus,
700 declIndex: usize, // index into `decls`, alternative repr for `declRef`
680 builtinField: enum { len, ptr },701 builtinField: enum { len, ptr },
681 fieldRef: FieldRef,702 fieldRef: FieldRef,
682 refPath: []Expr,703 refPath: []Expr,
...@@ -775,7 +796,11 @@ const DocData = struct {...@@ -775,7 +796,11 @@ const DocData = struct {
775 var jsw = std.json.writeStream(w, 15);796 var jsw = std.json.writeStream(w, 15);
776 if (opts.whitespace) |ws| jsw.whitespace = ws;797 if (opts.whitespace) |ws| jsw.whitespace = ws;
777 try jsw.beginObject();798 try jsw.beginObject();
778 try jsw.objectField(@tagName(active_tag));799 if (active_tag == .declIndex) {
800 try jsw.objectField("declRef");
801 } else {
802 try jsw.objectField(@tagName(active_tag));
803 }
779 switch (self) {804 switch (self) {
780 .int => {805 .int => {
781 if (self.int.negated) try w.writeAll("-");806 if (self.int.negated) try w.writeAll("-");
...@@ -784,11 +809,16 @@ const DocData = struct {...@@ -784,11 +809,16 @@ const DocData = struct {
784 .builtinField => {809 .builtinField => {
785 try jsw.emitString(@tagName(self.builtinField));810 try jsw.emitString(@tagName(self.builtinField));
786 },811 },
812 .declRef => {
813 try jsw.emitNumber(self.declRef.Analyzed);
814 },
787 else => {815 else => {
788 inline for (comptime std.meta.fields(Expr)) |case| {816 inline for (comptime std.meta.fields(Expr)) |case| {
789 // TODO: this is super ugly, fix once `inline else` is a thing817 // TODO: this is super ugly, fix once `inline else` is a thing
790 if (comptime std.mem.eql(u8, case.name, "builtinField"))818 if (comptime std.mem.eql(u8, case.name, "builtinField"))
791 continue;819 continue;
820 if (comptime std.mem.eql(u8, case.name, "declRef"))
821 continue;
792 if (@field(Expr, case.name) == active_tag) {822 if (@field(Expr, case.name) == active_tag) {
793 try std.json.stringify(@field(self, case.name), opts, w);823 try std.json.stringify(@field(self, case.name), opts, w);
794 jsw.state_index -= 1;824 jsw.state_index -= 1;
...@@ -1133,7 +1163,7 @@ fn walkInstruction(...@@ -1133,7 +1163,7 @@ fn walkInstruction(
1133 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } };1163 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } };
11341164
1135 return DocData.WalkResult{1165 return DocData.WalkResult{
1136 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,1166 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
1137 .expr = .{ .sliceIndex = slice_index },1167 .expr = .{ .sliceIndex = slice_index },
1138 };1168 };
1139 },1169 },
...@@ -1175,7 +1205,7 @@ fn walkInstruction(...@@ -1175,7 +1205,7 @@ fn walkInstruction(
1175 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } };1205 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } };
11761206
1177 return DocData.WalkResult{1207 return DocData.WalkResult{
1178 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,1208 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
1179 .expr = .{ .sliceIndex = slice_index },1209 .expr = .{ .sliceIndex = slice_index },
1180 };1210 };
1181 },1211 },
...@@ -1226,7 +1256,7 @@ fn walkInstruction(...@@ -1226,7 +1256,7 @@ fn walkInstruction(
1226 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } };1256 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } };
12271257
1228 return DocData.WalkResult{1258 return DocData.WalkResult{
1229 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,1259 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
1230 .expr = .{ .sliceIndex = slice_index },1260 .expr = .{ .sliceIndex = slice_index },
1231 };1261 };
1232 },1262 },
...@@ -1993,12 +2023,9 @@ fn walkInstruction(...@@ -1993,12 +2023,9 @@ fn walkInstruction(
1993 },2023 },
1994 .decl_val, .decl_ref => {2024 .decl_val, .decl_ref => {
1995 const str_tok = data[inst_index].str_tok;2025 const str_tok = data[inst_index].str_tok;
1996 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);2026 const decl_status = parent_scope.resolveDeclName(str_tok.start, file, inst_index);
1997 // While it would make sense to grab the original decl's typeRef info,
1998 // that decl might not have been analyzed yet! The frontend will have
1999 // to navigate through all declRefs to find the underlying type.
2000 return DocData.WalkResult{2027 return DocData.WalkResult{
2001 .expr = .{ .declRef = decls_slot_index },2028 .expr = .{ .declRef = decl_status },
2002 };2029 };
2003 },2030 },
2004 .field_val, .field_call_bind, .field_ptr, .field_type => {2031 .field_val, .field_call_bind, .field_ptr, .field_type => {
...@@ -2430,49 +2457,16 @@ fn walkInstruction(...@@ -2430,49 +2457,16 @@ fn walkInstruction(
2430 else2457 else
2431 parent_src;2458 parent_src;
24322459
2433 const decls_len = if (small.has_decls_len) blk: {
2434 const decls_len = file.zir.extra[extra_index];
2435 extra_index += 1;
2436 break :blk decls_len;
2437 } else 0;
2438
2439 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};2460 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
2440 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};2461 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
24412462
2442 const decls_first_index = self.decls.items.len;2463 extra_index = try self.analyzeAllDecls(
2443 // Decl name lookahead for reserving slots in `scope` (and `decls`).
2444 // Done to make sure that all decl refs can be resolved correctly,
2445 // even if we haven't fully analyzed the decl yet.
2446 {
2447 var it = file.zir.declIterator(@intCast(u32, inst_index));
2448 while (it.next()) |d| {
2449 const decl_name_index = file.zir.extra[d.sub_index + 5];
2450 switch (decl_name_index) {
2451 0, 1, 2 => continue,
2452 else => if (file.zir.string_bytes[decl_name_index] == 0) {
2453 continue;
2454 },
2455 }
2456
2457 const decl_slot_index = self.decls.items.len;
2458 try self.decls.append(self.arena, undefined);
2459 self.decls.items[decl_slot_index]._analyzed = false;
2460
2461 // TODO: inspect usingnamespace decls and unpack their contents!
2462
2463 try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index);
2464 }
2465 }
2466
2467 extra_index = try self.walkDecls(
2468 file,2464 file,
2469 &scope,2465 &scope,
2466 inst_index,
2470 src_info,2467 src_info,
2471 decls_first_index,
2472 decls_len,
2473 &decl_indexes,2468 &decl_indexes,
2474 &priv_decl_indexes,2469 &priv_decl_indexes,
2475 extra_index,
2476 );2470 );
24772471
2478 self.types.items[type_slot_index] = .{2472 self.types.items[type_slot_index] = .{
...@@ -2549,13 +2543,14 @@ fn walkInstruction(...@@ -2549,13 +2543,14 @@ fn walkInstruction(
2549 else2543 else
2550 parent_src;2544 parent_src;
25512545
2552 const tag_type: ?DocData.Expr = if (small.has_tag_type) blk: {2546 // We delay analysis because union tags can refer to
2547 // decls defined inside the union itself.
2548 const tag_type_ref: Ref = if (small.has_tag_type) blk: {
2553 const tag_type = file.zir.extra[extra_index];2549 const tag_type = file.zir.extra[extra_index];
2554 extra_index += 1;2550 extra_index += 1;
2555 const tag_ref = @intToEnum(Ref, tag_type);2551 const tag_ref = @intToEnum(Ref, tag_type);
2556 const wr = try self.walkRef(file, parent_scope, parent_src, tag_ref, false);2552 break :blk tag_ref;
2557 break :blk wr.expr;2553 } else .none;
2558 } else null;
25592554
2560 const body_len = if (small.has_body_len) blk: {2555 const body_len = if (small.has_body_len) blk: {
2561 const body_len = file.zir.extra[extra_index];2556 const body_len = file.zir.extra[extra_index];
...@@ -2569,51 +2564,28 @@ fn walkInstruction(...@@ -2569,51 +2564,28 @@ fn walkInstruction(
2569 break :blk fields_len;2564 break :blk fields_len;
2570 } else 0;2565 } else 0;
25712566
2572 const decls_len = if (small.has_decls_len) blk: {
2573 const decls_len = file.zir.extra[extra_index];
2574 extra_index += 1;
2575 break :blk decls_len;
2576 } else 0;
2577
2578 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};2567 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
2579 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};2568 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25802569
2581 const decls_first_index = self.decls.items.len;2570 extra_index = try self.analyzeAllDecls(
2582 // Decl name lookahead for reserving slots in `scope` (and `decls`).
2583 // Done to make sure that all decl refs can be resolved correctly,
2584 // even if we haven't fully analyzed the decl yet.
2585 {
2586 var it = file.zir.declIterator(@intCast(u32, inst_index));
2587 while (it.next()) |d| {
2588 const decl_name_index = file.zir.extra[d.sub_index + 5];
2589 switch (decl_name_index) {
2590 0, 1, 2 => continue,
2591 else => if (file.zir.string_bytes[decl_name_index] == 0) {
2592 continue;
2593 },
2594 }
2595
2596 const decl_slot_index = self.decls.items.len;
2597 try self.decls.append(self.arena, undefined);
2598 self.decls.items[decl_slot_index]._analyzed = false;
2599
2600 // TODO: inspect usingnamespace decls and unpack their contents!
2601
2602 try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index);
2603 }
2604 }
2605
2606 extra_index = try self.walkDecls(
2607 file,2571 file,
2608 &scope,2572 &scope,
2573 inst_index,
2609 src_info,2574 src_info,
2610 decls_first_index,
2611 decls_len,
2612 &decl_indexes,2575 &decl_indexes,
2613 &priv_decl_indexes,2576 &priv_decl_indexes,
2614 extra_index,
2615 );2577 );
26162578
2579 // Analyze the tag once all decls have been analyzed
2580 const tag_type = try self.walkRef(
2581 file,
2582 &scope,
2583 parent_src,
2584 tag_type_ref,
2585 false,
2586 );
2587
2588 // Fields
2617 extra_index += body_len;2589 extra_index += body_len;
26182590
2619 var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity(2591 var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity(
...@@ -2643,7 +2615,7 @@ fn walkInstruction(...@@ -2643,7 +2615,7 @@ fn walkInstruction(
2643 .privDecls = priv_decl_indexes.items,2615 .privDecls = priv_decl_indexes.items,
2644 .pubDecls = decl_indexes.items,2616 .pubDecls = decl_indexes.items,
2645 .fields = field_type_refs.items,2617 .fields = field_type_refs.items,
2646 .tag = tag_type,2618 .tag = tag_type.expr,
2647 .auto_enum = small.auto_enum_tag,2619 .auto_enum = small.auto_enum_tag,
2648 },2620 },
2649 };2621 };
...@@ -2711,49 +2683,16 @@ fn walkInstruction(...@@ -2711,49 +2683,16 @@ fn walkInstruction(
2711 break :blk fields_len;2683 break :blk fields_len;
2712 } else 0;2684 } else 0;
27132685
2714 const decls_len = if (small.has_decls_len) blk: {
2715 const decls_len = file.zir.extra[extra_index];
2716 extra_index += 1;
2717 break :blk decls_len;
2718 } else 0;
2719
2720 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};2686 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
2721 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};2687 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
27222688
2723 const decls_first_index = self.decls.items.len;2689 extra_index = try self.analyzeAllDecls(
2724 // Decl name lookahead for reserving slots in `scope` (and `decls`).
2725 // Done to make sure that all decl refs can be resolved correctly,
2726 // even if we haven't fully analyzed the decl yet.
2727 {
2728 var it = file.zir.declIterator(@intCast(u32, inst_index));
2729 while (it.next()) |d| {
2730 const decl_name_index = file.zir.extra[d.sub_index + 5];
2731 switch (decl_name_index) {
2732 0, 1, 2 => continue,
2733 else => if (file.zir.string_bytes[decl_name_index] == 0) {
2734 continue;
2735 },
2736 }
2737
2738 const decl_slot_index = self.decls.items.len;
2739 try self.decls.append(self.arena, undefined);
2740 self.decls.items[decl_slot_index]._analyzed = false;
2741
2742 // TODO: inspect usingnamespace decls and unpack their contents!
2743
2744 try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index);
2745 }
2746 }
2747
2748 extra_index = try self.walkDecls(
2749 file,2690 file,
2750 &scope,2691 &scope,
2692 inst_index,
2751 src_info,2693 src_info,
2752 decls_first_index,
2753 decls_len,
2754 &decl_indexes,2694 &decl_indexes,
2755 &priv_decl_indexes,2695 &priv_decl_indexes,
2756 extra_index,
2757 );2696 );
27582697
2759 // const body = file.zir.extra[extra_index..][0..body_len];2698 // const body = file.zir.extra[extra_index..][0..body_len];
...@@ -2862,12 +2801,6 @@ fn walkInstruction(...@@ -2862,12 +2801,6 @@ fn walkInstruction(
2862 break :blk fields_len;2801 break :blk fields_len;
2863 } else 0;2802 } else 0;
28642803
2865 const decls_len = if (small.has_decls_len) blk: {
2866 const decls_len = file.zir.extra[extra_index];
2867 extra_index += 1;
2868 break :blk decls_len;
2869 } else 0;
2870
2871 // TODO: Expose explicit backing integer types in some way.2804 // TODO: Expose explicit backing integer types in some way.
2872 if (small.has_backing_int) {2805 if (small.has_backing_int) {
2873 const backing_int_body_len = file.zir.extra[extra_index];2806 const backing_int_body_len = file.zir.extra[extra_index];
...@@ -2882,40 +2815,13 @@ fn walkInstruction(...@@ -2882,40 +2815,13 @@ fn walkInstruction(
2882 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};2815 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
2883 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};2816 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
28842817
2885 const decls_first_index = self.decls.items.len;2818 extra_index = try self.analyzeAllDecls(
2886 // Decl name lookahead for reserving slots in `scope` (and `decls`).
2887 // Done to make sure that all decl refs can be resolved correctly,
2888 // even if we haven't fully analyzed the decl yet.
2889 {
2890 var it = file.zir.declIterator(@intCast(u32, inst_index));
2891 while (it.next()) |d| {
2892 const decl_name_index = file.zir.extra[d.sub_index + 5];
2893 switch (decl_name_index) {
2894 0, 1, 2 => continue,
2895 else => if (file.zir.string_bytes[decl_name_index] == 0) {
2896 continue;
2897 },
2898 }
2899
2900 const decl_slot_index = self.decls.items.len;
2901 try self.decls.append(self.arena, undefined);
2902 self.decls.items[decl_slot_index]._analyzed = false;
2903
2904 // TODO: inspect usingnamespace decls and unpack their contents!
2905
2906 try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index);
2907 }
2908 }
2909
2910 extra_index = try self.walkDecls(
2911 file,2819 file,
2912 &scope,2820 &scope,
2821 inst_index,
2913 src_info,2822 src_info,
2914 decls_first_index,
2915 decls_len,
2916 &decl_indexes,2823 &decl_indexes,
2917 &priv_decl_indexes,2824 &priv_decl_indexes,
2918 extra_index,
2919 );2825 );
29202826
2921 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};2827 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};
...@@ -3096,189 +3002,286 @@ fn walkInstruction(...@@ -3096,189 +3002,286 @@ fn walkInstruction(
3096/// Does not append to `self.decls` directly because `walkInstruction`3002/// Does not append to `self.decls` directly because `walkInstruction`
3097/// is expected to look-ahead scan all decls and reserve `body_len`3003/// is expected to look-ahead scan all decls and reserve `body_len`
3098/// slots in `self.decls`, which are then filled out by this function.3004/// slots in `self.decls`, which are then filled out by this function.
3099fn walkDecls(3005fn analyzeAllDecls(
3100 self: *Autodoc,3006 self: *Autodoc,
3101 file: *File,3007 file: *File,
3102 scope: *Scope,3008 scope: *Scope,
3009 parent_inst_index: usize,
3103 parent_src: SrcLocInfo,3010 parent_src: SrcLocInfo,
3104 decls_first_index: usize,
3105 decls_len: usize,
3106 decl_indexes: *std.ArrayListUnmanaged(usize),3011 decl_indexes: *std.ArrayListUnmanaged(usize),
3107 priv_decl_indexes: *std.ArrayListUnmanaged(usize),3012 priv_decl_indexes: *std.ArrayListUnmanaged(usize),
3108 extra_start: usize,
3109) AutodocErrors!usize {3013) AutodocErrors!usize {
3110 const data = file.zir.instructions.items(.data);3014 const first_decl_indexes_slot = decl_indexes.items.len;
3111 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;3015 const original_it = file.zir.declIterator(@intCast(u32, parent_inst_index));
3112 var extra_index = extra_start + bit_bags_count;
3113 var bit_bag_index: usize = extra_start;
3114 var cur_bit_bag: u32 = undefined;
3115 var decl_i: u32 = 0;
31163016
3117 // NOTE: we're not outputting every ZIR decl as a Autodoc decl.3017 // First loop to discover decl names
3118 // tests, comptime blocks and usingnamespace are skipped.3018 {
3119 // this is why we `need good_decls_i`.3019 var it = original_it;
3120 var good_decls_i: usize = 0;3020 while (it.next()) |d| {
3121 while (decl_i < decls_len) : (decl_i += 1) {3021 const decl_name_index = file.zir.extra[d.sub_index + 5];
3122 const decls_slot_index = decls_first_index + good_decls_i;3022 switch (decl_name_index) {
3023 0, 1, 2 => continue,
3024 else => if (file.zir.string_bytes[decl_name_index] == 0) {
3025 continue;
3026 },
3027 }
31233028
3124 if (decl_i % 8 == 0) {3029 try scope.insertDeclRef(self.arena, decl_name_index, .Pending);
3125 cur_bit_bag = file.zir.extra[bit_bag_index];
3126 bit_bag_index += 1;
3127 }3030 }
3128 const is_pub = @truncate(u1, cur_bit_bag) != 0;3031 }
3129 cur_bit_bag >>= 1;
3130 const is_exported = @truncate(u1, cur_bit_bag) != 0;
3131 _ = is_exported;
3132 cur_bit_bag >>= 1;
3133 const has_align = @truncate(u1, cur_bit_bag) != 0;
3134 cur_bit_bag >>= 1;
3135 const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0;
3136 cur_bit_bag >>= 1;
31373032
3138 // const sub_index = extra_index;3033 // Second loop to analyze `usingnamespace` decls
3034 {
3035 var it = original_it;
3036 var decl_indexes_slot = first_decl_indexes_slot;
3037 while (it.next()) |d| : (decl_indexes_slot += 1) {
3038 const decl_name_index = file.zir.extra[d.sub_index + 5];
3039 switch (decl_name_index) {
3040 0 => {
3041 const is_exported = @truncate(u1, d.flags >> 1);
3042 switch (is_exported) {
3043 0 => continue, // comptime decl
3044 1 => {
3045 try self.analyzeUsingnamespaceDecl(
3046 file,
3047 scope,
3048 parent_src,
3049 decl_indexes,
3050 priv_decl_indexes,
3051 d,
3052 );
3053 },
3054 }
3055 },
3056 else => continue,
3057 }
3058 }
3059 }
31393060
3140 // const hash_u32s = file.zir.extra[extra_index..][0..4];3061 // Third loop to analyze all remaining decls
3141 extra_index += 4;3062 var it = original_it;
3063 while (it.next()) |d| {
3064 const decl_name_index = file.zir.extra[d.sub_index + 5];
3065 switch (decl_name_index) {
3066 0, 1, 2 => continue, // skip over usingnamespace decls
3067 else => if (file.zir.string_bytes[decl_name_index] == 0) {
3068 continue;
3069 },
3070 }
31423071
3143 // const line = file.zir.extra[extra_index];3072 try self.analyzeDecl(
3144 extra_index += 1;3073 file,
3145 const decl_name_index = file.zir.extra[extra_index];3074 scope,
3146 extra_index += 1;3075 parent_src,
3147 const value_index = file.zir.extra[extra_index];3076 decl_indexes,
3148 extra_index += 1;3077 priv_decl_indexes,
3149 const doc_comment_index = file.zir.extra[extra_index];3078 d,
3150 extra_index += 1;3079 );
3080 }
31513081
3152 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {3082 return it.extra_index;
3153 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);3083}
3154 extra_index += 1;
3155 break :inst inst;
3156 };
3157 _ = align_inst;
31583084
3159 const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {3085// Asserts the given decl is public
3160 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);3086fn analyzeDecl(
3161 extra_index += 1;3087 self: *Autodoc,
3162 break :inst inst;3088 file: *File,
3163 };3089 scope: *Scope,
3164 _ = section_inst;3090 parent_src: SrcLocInfo,
3091 decl_indexes: *std.ArrayListUnmanaged(usize),
3092 priv_decl_indexes: *std.ArrayListUnmanaged(usize),
3093 d: Zir.DeclIterator.Item,
3094) AutodocErrors!void {
3095 const data = file.zir.instructions.items(.data);
3096 const is_pub = @truncate(u1, d.flags >> 0) != 0;
3097 // const is_exported = @truncate(u1, d.flags >> 1) != 0;
3098 const has_align = @truncate(u1, d.flags >> 2) != 0;
3099 const has_section_or_addrspace = @truncate(u1, d.flags >> 3) != 0;
31653100
3166 const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {3101 var extra_index = d.sub_index;
3167 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);3102 // const hash_u32s = file.zir.extra[extra_index..][0..4];
3168 extra_index += 1;
3169 break :inst inst;
3170 };
3171 _ = addrspace_inst;
31723103
3173 // This is known to work because decl values are always block_inlines3104 extra_index += 4;
3174 const value_pl_node = data[value_index].pl_node;3105 // const line = file.zir.extra[extra_index];
3175 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
31763106
3177 const name: []const u8 = switch (decl_name_index) {3107 extra_index += 1;
3178 0, 1 => continue, // comptime or usingnamespace decl3108 const decl_name_index = file.zir.extra[extra_index];
3179 2 => {
3180 // decl test
3181 const decl_being_tested = scope.resolveDeclName(doc_comment_index);
3182 const func_index = getBlockInlineBreak(file.zir, value_index).?;
31833109
3184 const pl_node = data[Zir.refToIndex(func_index).?].pl_node;3110 extra_index += 1;
3185 const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);3111 const value_index = file.zir.extra[extra_index];
3186 const tree = try file.getTree(self.module.gpa);
3187 const test_source_code = tree.getNodeSource(fn_src.src_node);
31883112
3189 const ast_node_index = self.ast_nodes.items.len;3113 extra_index += 1;
3190 try self.ast_nodes.append(self.arena, .{3114 const doc_comment_index = file.zir.extra[extra_index];
3191 .file = 0,
3192 .line = 0,
3193 .col = 0,
3194 .code = test_source_code,
3195 });
3196 self.decls.items[decl_being_tested].decltest = ast_node_index;
3197 continue;
3198 },
3199 else => blk: {
3200 if (file.zir.string_bytes[decl_name_index] == 0) {
3201 // test decl
3202 continue;
3203 }
3204 break :blk file.zir.nullTerminatedString(decl_name_index);
3205 },
3206 };
32073115
3208 // If we got here, it means that this decl is not a test, usingnamespace3116 extra_index += 1;
3209 // or a comptime block decl.3117 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
3210 good_decls_i += 1;3118 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3119 extra_index += 1;
3120 break :inst inst;
3121 };
3122 _ = align_inst;
32113123
3212 const doc_comment: ?[]const u8 = if (doc_comment_index != 0)3124 const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
3213 file.zir.nullTerminatedString(doc_comment_index)3125 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3214 else3126 extra_index += 1;
3215 null;3127 break :inst inst;
3128 };
3129 _ = section_inst;
32163130
3217 // astnode3131 const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
3218 const ast_node_index = idx: {3132 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3219 const idx = self.ast_nodes.items.len;3133 extra_index += 1;
3220 try self.ast_nodes.append(self.arena, .{3134 break :inst inst;
3221 .file = self.files.getIndex(file).?,3135 };
3222 .line = decl_src.line,3136 _ = addrspace_inst;
3223 .col = 0,3137
3224 .docs = doc_comment,3138 // This is known to work because decl values are always block_inlines
3225 .fields = null, // walkInstruction will fill `fields` if necessary3139 const value_pl_node = data[value_index].pl_node;
3226 });3140 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3227 break :idx idx;3141
3228 };3142 const name: []const u8 = switch (decl_name_index) {
3143 0, 1 => unreachable, // comptime or usingnamespace decl
3144 2 => {
3145 unreachable;
3146 // decl test
3147 // const decl_status = scope.resolveDeclName(doc_comment_index);
3148 // const decl_being_tested = decl_status.Analyzed;
3149 // const func_index = getBlockInlineBreak(file.zir, value_index).?;
3150
3151 // const pl_node = data[Zir.refToIndex(func_index).?].pl_node;
3152 // const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);
3153 // const tree = try file.getTree(self.module.gpa);
3154 // const test_source_code = tree.getNodeSource(fn_src.src_node);
3155
3156 // const ast_node_index = self.ast_nodes.items.len;
3157 // try self.ast_nodes.append(self.arena, .{
3158 // .file = 0,
3159 // .line = 0,
3160 // .col = 0,
3161 // .code = test_source_code,
3162 // });
3163 // self.decls.items[decl_being_tested].decltest = ast_node_index;
3164 // continue;
3165 },
3166 else => blk: {
3167 if (file.zir.string_bytes[decl_name_index] == 0) {
3168 // test decl
3169 unreachable;
3170 }
3171 break :blk file.zir.nullTerminatedString(decl_name_index);
3172 },
3173 };
32293174
3230 const walk_result = try self.walkInstruction(file, scope, decl_src, value_index, true);3175 const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
3176 file.zir.nullTerminatedString(doc_comment_index)
3177 else
3178 null;
3179
3180 // astnode
3181 const ast_node_index = idx: {
3182 const idx = self.ast_nodes.items.len;
3183 try self.ast_nodes.append(self.arena, .{
3184 .file = self.files.getIndex(file).?,
3185 .line = decl_src.line,
3186 .col = 0,
3187 .docs = doc_comment,
3188 .fields = null, // walkInstruction will fill `fields` if necessary
3189 });
3190 break :idx idx;
3191 };
32313192
3232 if (is_pub) {3193 const walk_result = try self.walkInstruction(file, scope, decl_src, value_index, true);
3233 try decl_indexes.append(self.arena, decls_slot_index);
3234 } else {
3235 try priv_decl_indexes.append(self.arena, decls_slot_index);
3236 }
32373194
3238 // // decl.typeRef == decl.val...typeRef3195 const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const";
3239 // const decl_type_ref: DocData.TypeRef = switch (walk_result) {
3240 // .int => |i| i.typeRef,
3241 // .void => .{ .type = @enumToInt(Ref.void_type) },
3242 // .@"undefined", .@"null" => |v| v,
3243 // .@"unreachable" => .{ .type = @enumToInt(Ref.noreturn_type) },
3244 // .@"struct" => |s| s.typeRef,
3245 // .bool => .{ .type = @enumToInt(Ref.bool_type) },
3246 // .type => .{ .type = @enumToInt(Ref.type_type) },
3247 // // this last case is special becauese it's not pointing
3248 // // at the type of the value, but rather at the value itself
3249 // // the js better be aware ot this!
3250 // .declRef => |d| .{ .declRef = d },
3251 // };
3252
3253 const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const";
3254
3255 self.decls.items[decls_slot_index] = .{
3256 ._analyzed = true,
3257 .name = name,
3258 .src = ast_node_index,
3259 //.typeRef = decl_type_ref,
3260 .value = walk_result,
3261 .kind = kind,
3262 };
32633196
3264 // Unblock any pending decl path that was waiting for this decl.3197 const decls_slot_index = self.decls.items.len;
3265 if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| {3198 try self.decls.append(self.arena, .{
3266 for (paths.items) |resume_info| {3199 .name = name,
3267 try self.tryResolveRefPath(3200 .src = ast_node_index,
3268 resume_info.file,3201 .value = walk_result,
3269 value_index,3202 .kind = kind,
3270 resume_info.ref_path,3203 });
3271 );3204
3272 }3205 if (is_pub) {
3206 try decl_indexes.append(self.arena, decls_slot_index);
3207 } else {
3208 try priv_decl_indexes.append(self.arena, decls_slot_index);
3209 }
32733210
3274 _ = self.ref_paths_pending_on_decls.remove(decls_slot_index);3211 const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, 0);
3275 // TODO: we should deallocate the arraylist that holds all the3212 std.debug.assert(decl_status_ptr.* == .Pending);
3276 // ref paths. not doing it now since it's arena-allocated3213 decl_status_ptr.* = .{ .Analyzed = decls_slot_index };
3277 // anyway, but maybe we should put it elsewhere.3214
3215 // Unblock any pending decl path that was waiting for this decl.
3216 if (self.ref_paths_pending_on_decls.get(decl_status_ptr)) |paths| {
3217 for (paths.items) |resume_info| {
3218 try self.tryResolveRefPath(
3219 resume_info.file,
3220 value_index,
3221 resume_info.ref_path,
3222 );
3278 }3223 }
3224
3225 _ = self.ref_paths_pending_on_decls.remove(decl_status_ptr);
3226 // TODO: we should deallocate the arraylist that holds all the
3227 // ref paths. not doing it now since it's arena-allocated
3228 // anyway, but maybe we should put it elsewhere.
3279 }3229 }
3230}
32803231
3281 return extra_index;3232fn analyzeUsingnamespaceDecl(
3233 self: *Autodoc,
3234 file: *File,
3235 scope: *Scope,
3236 parent_src: SrcLocInfo,
3237 decl_indexes: *std.ArrayListUnmanaged(usize),
3238 priv_decl_indexes: *std.ArrayListUnmanaged(usize),
3239 d: Zir.DeclIterator.Item,
3240) AutodocErrors!void {
3241 const data = file.zir.instructions.items(.data);
3242
3243 const is_pub = @truncate(u1, d.flags) != 0;
3244 const value_index = file.zir.extra[d.sub_index + 6];
3245 const doc_comment_index = file.zir.extra[d.sub_index + 7];
3246
3247 // This is known to work because decl values are always block_inlines
3248 const value_pl_node = data[value_index].pl_node;
3249 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3250
3251 const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
3252 file.zir.nullTerminatedString(doc_comment_index)
3253 else
3254 null;
3255
3256 // astnode
3257 const ast_node_index = idx: {
3258 const idx = self.ast_nodes.items.len;
3259 try self.ast_nodes.append(self.arena, .{
3260 .file = self.files.getIndex(file).?,
3261 .line = decl_src.line,
3262 .col = 0,
3263 .docs = doc_comment,
3264 .fields = null, // walkInstruction will fill `fields` if necessary
3265 });
3266 break :idx idx;
3267 };
3268
3269 const walk_result = try self.walkInstruction(file, scope, decl_src, value_index, true);
3270
3271 const decl_slot_index = self.decls.items.len;
3272 try self.decls.append(self.arena, .{
3273 .name = "",
3274 .kind = "",
3275 .src = ast_node_index,
3276 .value = walk_result,
3277 .is_uns = true,
3278 });
3279
3280 if (is_pub) {
3281 try decl_indexes.append(self.arena, decl_slot_index);
3282 } else {
3283 try priv_decl_indexes.append(self.arena, decl_slot_index);
3284 }
3282}3285}
32833286
3284/// An unresolved path has a non-string WalkResult at its beginnig, while every3287/// An unresolved path has a non-string WalkResult at its beginnig, while every
...@@ -3290,7 +3293,7 @@ fn walkDecls(...@@ -3290,7 +3293,7 @@ fn walkDecls(
3290/// Same happens when a decl holds a type definition that hasn't been fully3293/// Same happens when a decl holds a type definition that hasn't been fully
3291/// analyzed yet (except that we append to `self.ref_paths_pending_on_types`.3294/// analyzed yet (except that we append to `self.ref_paths_pending_on_types`.
3292///3295///
3293/// When walkDecls / walkInstruction finishes analyzing a decl / type, it will3296/// When analyzeAllDecls / walkInstruction finishes analyzing a decl / type, it will
3294/// then check if there's any pending ref path blocked on it and, if any, it3297/// then check if there's any pending ref path blocked on it and, if any, it
3295/// will progress their resolution by calling tryResolveRefPath again.3298/// will progress their resolution by calling tryResolveRefPath again.
3296///3299///
...@@ -3318,37 +3321,51 @@ fn tryResolveRefPath(...@@ -3318,37 +3321,51 @@ fn tryResolveRefPath(
3318 switch (resolved_parent) {3321 switch (resolved_parent) {
3319 else => break,3322 else => break,
3320 .this => |t| resolved_parent = .{ .type = t },3323 .this => |t| resolved_parent = .{ .type = t },
3321 .declRef => |decl_index| {3324 .declIndex => |decl_index| {
3322 const decl = self.decls.items[decl_index];3325 const decl = self.decls.items[decl_index];
3323 if (decl._analyzed) {3326 resolved_parent = decl.value.expr;
3324 resolved_parent = decl.value.expr;3327 continue;
3325 continue;3328 },
3326 }3329 .declRef => |decl_status_ptr| {
33273330 // NOTE: must be kep in sync with `findNameInUnsDecls`
3328 // This decl path is pending completion3331 switch (decl_status_ptr.*) {
3329 {3332 // The use of unreachable here is conservative.
3330 const res = try self.pending_ref_paths.getOrPut(3333 // It might be that it truly should be up to us to
3331 self.arena,3334 // request the analys of this decl, but it's not clear
3332 &path[path.len - 1],3335 // at the moment of writing.
3333 );3336 .NotRequested => unreachable,
3334 if (!res.found_existing) res.value_ptr.* = .{};3337 .Analyzed => |decl_index| {
3335 }3338 const decl = self.decls.items[decl_index];
3339 resolved_parent = decl.value.expr;
3340 continue;
3341 },
3342 .Pending => {
3343 // This decl path is pending completion
3344 {
3345 const res = try self.pending_ref_paths.getOrPut(
3346 self.arena,
3347 &path[path.len - 1],
3348 );
3349 if (!res.found_existing) res.value_ptr.* = .{};
3350 }
33363351
3337 const res = try self.ref_paths_pending_on_decls.getOrPut(3352 const res = try self.ref_paths_pending_on_decls.getOrPut(
3338 self.arena,3353 self.arena,
3339 decl_index,3354 decl_status_ptr,
3340 );3355 );
3341 if (!res.found_existing) res.value_ptr.* = .{};3356 if (!res.found_existing) res.value_ptr.* = .{};
3342 try res.value_ptr.*.append(self.arena, .{3357 try res.value_ptr.*.append(self.arena, .{
3343 .file = file,3358 .file = file,
3344 .ref_path = path[i..path.len],3359 .ref_path = path[i..path.len],
3345 });3360 });
33463361
3347 // We return instead doing `break :outer` to prevent the3362 // We return instead doing `break :outer` to prevent the
3348 // code after the :outer while loop to run, as it assumes3363 // code after the :outer while loop to run, as it assumes
3349 // that the path will have been fully analyzed (or we3364 // that the path will have been fully analyzed (or we
3350 // have given up because of a comptimeExpr).3365 // have given up because of a comptimeExpr).
3351 return;3366 return;
3367 },
3368 }
3352 },3369 },
3353 .refPath => |rp| {3370 .refPath => |rp| {
3354 if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| {3371 if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| {
...@@ -3388,7 +3405,7 @@ fn tryResolveRefPath(...@@ -3388,7 +3405,7 @@ fn tryResolveRefPath(
3388 panicWithContext(3405 panicWithContext(
3389 file,3406 file,
3390 inst_index,3407 inst_index,
3391 "exhausted eval quota for `{}`in tryResolveDecl\n",3408 "exhausted eval quota for `{}`in tryResolveRefPath\n",
3392 .{resolved_parent},3409 .{resolved_parent},
3393 );3410 );
3394 }3411 }
...@@ -3461,26 +3478,39 @@ fn tryResolveRefPath(...@@ -3461,26 +3478,39 @@ fn tryResolveRefPath(
3461 );3478 );
3462 }3479 }
3463 },3480 },
3464 .Enum => |t_enum| {3481 // TODO: the following searches could probably
3465 for (t_enum.pubDecls) |d| {3482 // be performed more efficiently on the corresponding
3466 // TODO: this could be improved a lot3483 // scope
3467 // by having our own string table!3484 .Enum => |t_enum| { // foo.bar.baz
3468 const decl = self.decls.items[d];3485 // Look into locally-defined pub decls
3469 if (std.mem.eql(u8, decl.name, child_string)) {3486 for (t_enum.pubDecls) |idx| {
3470 path[i + 1] = .{ .declRef = d };3487 const d = self.decls.items[idx];
3488 if (d.is_uns) continue;
3489 if (std.mem.eql(u8, d.name, child_string)) {
3490 path[i + 1] = .{ .declIndex = idx };
3471 continue :outer;3491 continue :outer;
3472 }3492 }
3473 }3493 }
3474 for (t_enum.privDecls) |d| {3494
3475 // TODO: this could be improved a lot3495 // Look into locally-defined priv decls
3476 // by having our own string table!3496 for (t_enum.privDecls) |idx| {
3477 const decl = self.decls.items[d];3497 const d = self.decls.items[idx];
3478 if (std.mem.eql(u8, decl.name, child_string)) {3498 if (d.is_uns) continue;
3479 path[i + 1] = .{ .declRef = d };3499 if (std.mem.eql(u8, d.name, child_string)) {
3500 path[i + 1] = .{ .declIndex = idx };
3480 continue :outer;3501 continue :outer;
3481 }3502 }
3482 }3503 }
34833504
3505 switch (try self.findNameInUnsDecls(file, path[i..path.len], resolved_parent, child_string)) {
3506 .Pending => return,
3507 .NotFound => {},
3508 .Found => |match| {
3509 path[i + 1] = match;
3510 continue :outer;
3511 },
3512 }
3513
3484 for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| {3514 for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| {
3485 const name = self.ast_nodes.items[ast_node].name.?;3515 const name = self.ast_nodes.items[ast_node].name.?;
3486 if (std.mem.eql(u8, name, child_string)) {3516 if (std.mem.eql(u8, name, child_string)) {
...@@ -3509,25 +3539,35 @@ fn tryResolveRefPath(...@@ -3509,25 +3539,35 @@ fn tryResolveRefPath(
3509 continue :outer;3539 continue :outer;
3510 },3540 },
3511 .Union => |t_union| {3541 .Union => |t_union| {
3512 for (t_union.pubDecls) |d| {3542 // Look into locally-defined pub decls
3513 // TODO: this could be improved a lot3543 for (t_union.pubDecls) |idx| {
3514 // by having our own string table!3544 const d = self.decls.items[idx];
3515 const decl = self.decls.items[d];3545 if (d.is_uns) continue;
3516 if (std.mem.eql(u8, decl.name, child_string)) {3546 if (std.mem.eql(u8, d.name, child_string)) {
3517 path[i + 1] = .{ .declRef = d };3547 path[i + 1] = .{ .declIndex = idx };
3518 continue :outer;3548 continue :outer;
3519 }3549 }
3520 }3550 }
3521 for (t_union.privDecls) |d| {3551
3522 // TODO: this could be improved a lot3552 // Look into locally-defined priv decls
3523 // by having our own string table!3553 for (t_union.privDecls) |idx| {
3524 const decl = self.decls.items[d];3554 const d = self.decls.items[idx];
3525 if (std.mem.eql(u8, decl.name, child_string)) {3555 if (d.is_uns) continue;
3526 path[i + 1] = .{ .declRef = d };3556 if (std.mem.eql(u8, d.name, child_string)) {
3557 path[i + 1] = .{ .declIndex = idx };
3527 continue :outer;3558 continue :outer;
3528 }3559 }
3529 }3560 }
35303561
3562 switch (try self.findNameInUnsDecls(file, path[i..path.len], resolved_parent, child_string)) {
3563 .Pending => return,
3564 .NotFound => {},
3565 .Found => |match| {
3566 path[i + 1] = match;
3567 continue :outer;
3568 },
3569 }
3570
3531 for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| {3571 for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| {
3532 const name = self.ast_nodes.items[ast_node].name.?;3572 const name = self.ast_nodes.items[ast_node].name.?;
3533 if (std.mem.eql(u8, name, child_string)) {3573 if (std.mem.eql(u8, name, child_string)) {
...@@ -3556,25 +3596,35 @@ fn tryResolveRefPath(...@@ -3556,25 +3596,35 @@ fn tryResolveRefPath(
3556 },3596 },
35573597
3558 .Struct => |t_struct| {3598 .Struct => |t_struct| {
3559 for (t_struct.pubDecls) |d| {3599 // Look into locally-defined pub decls
3560 // TODO: this could be improved a lot3600 for (t_struct.pubDecls) |idx| {
3561 // by having our own string table!3601 const d = self.decls.items[idx];
3562 const decl = self.decls.items[d];3602 if (d.is_uns) continue;
3563 if (std.mem.eql(u8, decl.name, child_string)) {3603 if (std.mem.eql(u8, d.name, child_string)) {
3564 path[i + 1] = .{ .declRef = d };3604 path[i + 1] = .{ .declIndex = idx };
3565 continue :outer;3605 continue :outer;
3566 }3606 }
3567 }3607 }
3568 for (t_struct.privDecls) |d| {3608
3569 // TODO: this could be improved a lot3609 // Look into locally-defined priv decls
3570 // by having our own string table!3610 for (t_struct.privDecls) |idx| {
3571 const decl = self.decls.items[d];3611 const d = self.decls.items[idx];
3572 if (std.mem.eql(u8, decl.name, child_string)) {3612 if (d.is_uns) continue;
3573 path[i + 1] = .{ .declRef = d };3613 if (std.mem.eql(u8, d.name, child_string)) {
3614 path[i + 1] = .{ .declIndex = idx };
3574 continue :outer;3615 continue :outer;
3575 }3616 }
3576 }3617 }
35773618
3619 switch (try self.findNameInUnsDecls(file, path[i..path.len], resolved_parent, child_string)) {
3620 .Pending => return,
3621 .NotFound => {},
3622 .Found => |match| {
3623 path[i + 1] = match;
3624 continue :outer;
3625 },
3626 }
3627
3578 for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| {3628 for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| {
3579 const name = self.ast_nodes.items[ast_node].name.?;3629 const name = self.ast_nodes.items[ast_node].name.?;
3580 if (std.mem.eql(u8, name, child_string)) {3630 if (std.mem.eql(u8, name, child_string)) {
...@@ -3605,25 +3655,37 @@ fn tryResolveRefPath(...@@ -3605,25 +3655,37 @@ fn tryResolveRefPath(
3605 continue :outer;3655 continue :outer;
3606 },3656 },
3607 .Opaque => |t_opaque| {3657 .Opaque => |t_opaque| {
3608 for (t_opaque.pubDecls) |d| {3658 // Look into locally-defined pub decls
3609 // TODO: this could be improved a lot3659 for (t_opaque.pubDecls) |idx| {
3610 // by having our own string table!3660 const d = self.decls.items[idx];
3611 const decl = self.decls.items[d];3661 if (d.is_uns) continue;
3612 if (std.mem.eql(u8, decl.name, child_string)) {3662 if (std.mem.eql(u8, d.name, child_string)) {
3613 path[i + 1] = .{ .declRef = d };3663 path[i + 1] = .{ .declIndex = idx };
3614 continue :outer;3664 continue :outer;
3615 }3665 }
3616 }3666 }
3617 for (t_opaque.privDecls) |d| {3667
3618 // TODO: this could be improved a lot3668 // Look into locally-defined priv decls
3619 // by having our own string table!3669 for (t_opaque.privDecls) |idx| {
3620 const decl = self.decls.items[d];3670 const d = self.decls.items[idx];
3621 if (std.mem.eql(u8, decl.name, child_string)) {3671 if (d.is_uns) continue;
3622 path[i + 1] = .{ .declRef = d };3672 if (std.mem.eql(u8, d.name, child_string)) {
3673 path[i + 1] = .{ .declIndex = idx };
3623 continue :outer;3674 continue :outer;
3624 }3675 }
3625 }3676 }
36263677
3678 // We delay looking into Uns decls since they could be
3679 // not fully analyzed yet.
3680 switch (try self.findNameInUnsDecls(file, path[i..path.len], resolved_parent, child_string)) {
3681 .Pending => return,
3682 .NotFound => {},
3683 .Found => |match| {
3684 path[i + 1] = match;
3685 continue :outer;
3686 },
3687 }
3688
3627 // if we got here, our search failed3689 // if we got here, our search failed
3628 printWithContext(3690 printWithContext(
3629 file,3691 file,
...@@ -3670,6 +3732,104 @@ fn tryResolveRefPath(...@@ -3670,6 +3732,104 @@ fn tryResolveRefPath(
3670 // that said, we might want to store it elsewhere and reclaim memory asap3732 // that said, we might want to store it elsewhere and reclaim memory asap
3671 }3733 }
3672}3734}
3735
3736const UnsSearchResult = union(enum) {
3737 Found: DocData.Expr,
3738 Pending,
3739 NotFound,
3740};
3741
3742fn findNameInUnsDecls(
3743 self: *Autodoc,
3744 file: *File,
3745 tail: []DocData.Expr,
3746 uns_expr: DocData.Expr,
3747 name: []const u8,
3748) !UnsSearchResult {
3749 var to_analyze = std.SegmentedList(DocData.Expr, 1){};
3750 // TODO: make this an appendAssumeCapacity
3751 try to_analyze.append(self.arena, uns_expr);
3752
3753 while (to_analyze.pop()) |cte| {
3754 var container_expression = cte;
3755 for (0..10_000) |_| {
3756 // TODO: handle other types of indirection, like @import
3757 const type_index = switch (container_expression) {
3758 .type => |t| t,
3759 .declRef => |decl_status_ptr| {
3760 switch (decl_status_ptr.*) {
3761 // The use of unreachable here is conservative.
3762 // It might be that it truly should be up to us to
3763 // request the analys of this decl, but it's not clear
3764 // at the moment of writing.
3765 .NotRequested => unreachable,
3766 .Analyzed => |decl_index| {
3767 const decl = self.decls.items[decl_index];
3768 container_expression = decl.value.expr;
3769 continue;
3770 },
3771 .Pending => {
3772 // This decl path is pending completion
3773 {
3774 const res = try self.pending_ref_paths.getOrPut(
3775 self.arena,
3776 &tail[tail.len - 1],
3777 );
3778 if (!res.found_existing) res.value_ptr.* = .{};
3779 }
3780
3781 const res = try self.ref_paths_pending_on_decls.getOrPut(
3782 self.arena,
3783 decl_status_ptr,
3784 );
3785 if (!res.found_existing) res.value_ptr.* = .{};
3786 try res.value_ptr.*.append(self.arena, .{
3787 .file = file,
3788 .ref_path = tail,
3789 });
3790
3791 // TODO: save some state that keeps track of our
3792 // progress because, as things stand, we
3793 // always re-start the search from scratch
3794 return .Pending;
3795 },
3796 }
3797 },
3798 else => {
3799 log.debug(
3800 "Handle `{s}` in findNameInUnsDecls (first switch)",
3801 .{@tagName(cte)},
3802 );
3803 return .{ .Found = .{ .comptimeExpr = 0 } };
3804 },
3805 };
3806
3807 const t = self.types.items[type_index];
3808 const decls = switch (t) {
3809 else => {
3810 log.debug(
3811 "Handle `{s}` in findNameInUnsDecls (second switch)",
3812 .{@tagName(cte)},
3813 );
3814 return .{ .Found = .{ .comptimeExpr = 0 } };
3815 },
3816 inline .Struct, .Union, .Opaque, .Enum => |c| c.pubDecls,
3817 };
3818
3819 for (decls) |idx| {
3820 const d = self.decls.items[idx];
3821 if (d.is_uns) {
3822 try to_analyze.append(self.arena, d.value.expr);
3823 } else if (std.mem.eql(u8, d.name, name)) {
3824 return .{ .Found = .{ .declIndex = idx } };
3825 }
3826 }
3827 }
3828 }
3829
3830 return .NotFound;
3831}
3832
3673fn analyzeFancyFunction(3833fn analyzeFancyFunction(
3674 self: *Autodoc,3834 self: *Autodoc,
3675 file: *File,3835 file: *File,
src/Compilation.zig+1-1
...@@ -2052,7 +2052,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void...@@ -2052,7 +2052,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
2052 return;2052 return;
2053 }2053 }
20542054
2055 if (!build_options.only_c) {2055 if (!build_options.only_c and !build_options.omit_pkg_fetching_code) {
2056 if (comp.emit_docs) |doc_location| {2056 if (comp.emit_docs) |doc_location| {
2057 if (comp.bin_file.options.module) |module| {2057 if (comp.bin_file.options.module) |module| {
2058 var autodoc = Autodoc.init(module, doc_location);2058 var autodoc = Autodoc.init(module, doc_location);
src/Zir.zig+2
...@@ -3667,6 +3667,7 @@ pub const DeclIterator = struct {...@@ -3667,6 +3667,7 @@ pub const DeclIterator = struct {
3667 pub const Item = struct {3667 pub const Item = struct {
3668 name: [:0]const u8,3668 name: [:0]const u8,
3669 sub_index: u32,3669 sub_index: u32,
3670 flags: u4,
3670 };3671 };
36713672
3672 pub fn next(it: *DeclIterator) ?Item {3673 pub fn next(it: *DeclIterator) ?Item {
...@@ -3691,6 +3692,7 @@ pub const DeclIterator = struct {...@@ -3691,6 +3692,7 @@ pub const DeclIterator = struct {
3691 return Item{3692 return Item{
3692 .sub_index = sub_index,3693 .sub_index = sub_index,
3693 .name = name,3694 .name = name,
3695 .flags = flags,
3694 };3696 };
3695 }3697 }
3696};3698};