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 = {
25862586 fnsList,
25872587 varsList,
25882588 valsList,
2589 testsList
2589 testsList,
2590 unsList
25902591 ) {
25912592 for (let i = 0; i < decls.length; i += 1) {
25922593 let decl = getDecl(decls[i]);
......@@ -2644,6 +2645,10 @@ const NAV_MODES = {
26442645 valsList.push(decl);
26452646 }
26462647 }
2648
2649 if (decl.is_uns) {
2650 unsList.push(decl);
2651 }
26472652 }
26482653 }
26492654
......@@ -2669,6 +2674,8 @@ const NAV_MODES = {
26692674
26702675 let testsList = [];
26712676
2677 let unsList = [];
2678
26722679 categorizeDecls(
26732680 container.pubDecls,
26742681 typesList,
......@@ -2677,7 +2684,8 @@ const NAV_MODES = {
26772684 fnsList,
26782685 varsList,
26792686 valsList,
2680 testsList
2687 testsList,
2688 unsList
26812689 );
26822690 if (curNav.showPrivDecls)
26832691 categorizeDecls(
......@@ -2688,9 +2696,40 @@ const NAV_MODES = {
26882696 fnsList,
26892697 varsList,
26902698 valsList,
2691 testsList
2699 testsList,
2700 unsList
26922701 );
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
26942733 typesList.sort(byNameProperty);
26952734 namespacesList.sort(byNameProperty);
26962735 errSetsList.sort(byNameProperty);
......@@ -3090,7 +3129,7 @@ const NAV_MODES = {
30903129 function findSubDecl(parentTypeOrDecl, childName) {
30913130 let parentType = parentTypeOrDecl;
30923131 {
3093 // Generic functions / resorlving decls
3132 // Generic functions / resolving decls
30943133 if ("value" in parentType) {
30953134 const rv = resolveValue(parentType.value);
30963135 if ("type" in rv.expr) {
......@@ -3116,20 +3155,35 @@ const NAV_MODES = {
31163155 }
31173156 }
31183157
3119 if (!parentType.pubDecls) return null;
3120 for (let i = 0; i < parentType.pubDecls.length; i += 1) {
3121 let declIndex = parentType.pubDecls[i];
3122 let childDecl = getDecl(declIndex);
3123 if (childDecl.name === childName) {
3124 return childDecl;
3158 if (parentType.pubDecls) {
3159 for (let i = 0; i < parentType.pubDecls.length; i += 1) {
3160 let declIndex = parentType.pubDecls[i];
3161 let childDecl = getDecl(declIndex);
3162 if (childDecl.name === childName) {
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 }
31253171 }
31263172 }
3127 if (!parentType.privDecls) return null;
3128 for (let i = 0; i < parentType.privDecls.length; i += 1) {
3129 let declIndex = parentType.privDecls[i];
3130 let childDecl = getDecl(declIndex);
3131 if (childDecl.name === childName) {
3132 return childDecl;
3173
3174 if (parentType.privDecls) {
3175 for (let i = 0; i < parentType.privDecls.length; i += 1) {
3176 let declIndex = parentType.privDecls[i];
3177 let childDecl = getDecl(declIndex);
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 }
31333187 }
31343188 }
31353189 return null;
......@@ -3908,6 +3962,7 @@ const NAV_MODES = {
39083962 src: decl[2],
39093963 value: decl[3],
39103964 decltest: decl[4],
3965 is_uns: decl[5],
39113966 };
39123967 }
39133968
src/Autodoc.zig+558-398
......@@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged(
3737 std.ArrayListUnmanaged(RefPathResumeInfo),
3838) = .{},
3939ref_paths_pending_on_decls: std.AutoHashMapUnmanaged(
40 usize,
40 *Scope.DeclStatus,
4141 std.ArrayListUnmanaged(RefPathResumeInfo),
4242) = .{},
4343ref_paths_pending_on_types: std.AutoHashMapUnmanaged(
......@@ -344,28 +344,48 @@ fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File {
344344}
345345
346346/// 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.
348349const Scope = struct {
349350 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
351356 enclosing_type: usize, // index into `types`
352357
353 /// Assumes all decls in present scope and upper scopes have already
354 /// been either fully resolved or at least reserved.
355 pub fn resolveDeclName(self: Scope, string_table_idx: u32) usize {
358 pub const DeclStatus = union(enum) {
359 Analyzed: usize, // index into `decls`
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 {
356368 var cur: ?*const Scope = &self;
357369 return while (cur) |s| : (cur = s.parent) {
358 break s.map.get(string_table_idx) orelse continue;
359 } else unreachable;
370 break s.map.getPtr(string_table_idx) orelse continue;
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 };
360380 }
361381
362382 pub fn insertDeclRef(
363383 self: *Scope,
364384 arena: std.mem.Allocator,
365 decl_name_index: u32, // decl name
366 decls_slot_index: usize,
385 decl_name_index: u32, // index into the current file's string table
386 decl_status: DeclStatus,
367387 ) !void {
368 try self.map.put(arena, decl_name_index, decls_slot_index);
388 try self.map.put(arena, decl_name_index, decl_status);
369389 }
370390};
371391
......@@ -479,7 +499,7 @@ const DocData = struct {
479499 value: WalkResult,
480500 // The index in astNodes of the `test declname { }` node
481501 decltest: ?usize = null,
482 _analyzed: bool, // omitted in json data
502 is_uns: bool = false, // usingnamespace
483503
484504 pub fn jsonStringify(
485505 self: Decl,
......@@ -676,7 +696,8 @@ const DocData = struct {
676696 @"&": usize, // index in `exprs`
677697 type: usize, // index in `types`
678698 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`
680701 builtinField: enum { len, ptr },
681702 fieldRef: FieldRef,
682703 refPath: []Expr,
......@@ -775,7 +796,11 @@ const DocData = struct {
775796 var jsw = std.json.writeStream(w, 15);
776797 if (opts.whitespace) |ws| jsw.whitespace = ws;
777798 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 }
779804 switch (self) {
780805 .int => {
781806 if (self.int.negated) try w.writeAll("-");
......@@ -784,11 +809,16 @@ const DocData = struct {
784809 .builtinField => {
785810 try jsw.emitString(@tagName(self.builtinField));
786811 },
812 .declRef => {
813 try jsw.emitNumber(self.declRef.Analyzed);
814 },
787815 else => {
788816 inline for (comptime std.meta.fields(Expr)) |case| {
789817 // TODO: this is super ugly, fix once `inline else` is a thing
790818 if (comptime std.mem.eql(u8, case.name, "builtinField"))
791819 continue;
820 if (comptime std.mem.eql(u8, case.name, "declRef"))
821 continue;
792822 if (@field(Expr, case.name) == active_tag) {
793823 try std.json.stringify(@field(self, case.name), opts, w);
794824 jsw.state_index -= 1;
......@@ -1133,7 +1163,7 @@ fn walkInstruction(
11331163 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } };
11341164
11351165 return DocData.WalkResult{
1136 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,
1166 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
11371167 .expr = .{ .sliceIndex = slice_index },
11381168 };
11391169 },
......@@ -1175,7 +1205,7 @@ fn walkInstruction(
11751205 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } };
11761206
11771207 return DocData.WalkResult{
1178 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,
1208 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
11791209 .expr = .{ .sliceIndex = slice_index },
11801210 };
11811211 },
......@@ -1226,7 +1256,7 @@ fn walkInstruction(
12261256 self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } };
12271257
12281258 return DocData.WalkResult{
1229 .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef,
1259 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
12301260 .expr = .{ .sliceIndex = slice_index },
12311261 };
12321262 },
......@@ -1993,12 +2023,9 @@ fn walkInstruction(
19932023 },
19942024 .decl_val, .decl_ref => {
19952025 const str_tok = data[inst_index].str_tok;
1996 const decls_slot_index = parent_scope.resolveDeclName(str_tok.start);
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.
2026 const decl_status = parent_scope.resolveDeclName(str_tok.start, file, inst_index);
20002027 return DocData.WalkResult{
2001 .expr = .{ .declRef = decls_slot_index },
2028 .expr = .{ .declRef = decl_status },
20022029 };
20032030 },
20042031 .field_val, .field_call_bind, .field_ptr, .field_type => {
......@@ -2430,49 +2457,16 @@ fn walkInstruction(
24302457 else
24312458 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
24392460 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
24402461 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
24412462
2442 const decls_first_index = self.decls.items.len;
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(
2463 extra_index = try self.analyzeAllDecls(
24682464 file,
24692465 &scope,
2466 inst_index,
24702467 src_info,
2471 decls_first_index,
2472 decls_len,
24732468 &decl_indexes,
24742469 &priv_decl_indexes,
2475 extra_index,
24762470 );
24772471
24782472 self.types.items[type_slot_index] = .{
......@@ -2549,13 +2543,14 @@ fn walkInstruction(
25492543 else
25502544 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: {
25532549 const tag_type = file.zir.extra[extra_index];
25542550 extra_index += 1;
25552551 const tag_ref = @intToEnum(Ref, tag_type);
2556 const wr = try self.walkRef(file, parent_scope, parent_src, tag_ref, false);
2557 break :blk wr.expr;
2558 } else null;
2552 break :blk tag_ref;
2553 } else .none;
25592554
25602555 const body_len = if (small.has_body_len) blk: {
25612556 const body_len = file.zir.extra[extra_index];
......@@ -2569,51 +2564,28 @@ fn walkInstruction(
25692564 break :blk fields_len;
25702565 } 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
25782567 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25792568 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
25802569
2581 const decls_first_index = self.decls.items.len;
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(
2570 extra_index = try self.analyzeAllDecls(
26072571 file,
26082572 &scope,
2573 inst_index,
26092574 src_info,
2610 decls_first_index,
2611 decls_len,
26122575 &decl_indexes,
26132576 &priv_decl_indexes,
2614 extra_index,
26152577 );
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
26172589 extra_index += body_len;
26182590
26192591 var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity(
......@@ -2643,7 +2615,7 @@ fn walkInstruction(
26432615 .privDecls = priv_decl_indexes.items,
26442616 .pubDecls = decl_indexes.items,
26452617 .fields = field_type_refs.items,
2646 .tag = tag_type,
2618 .tag = tag_type.expr,
26472619 .auto_enum = small.auto_enum_tag,
26482620 },
26492621 };
......@@ -2711,49 +2683,16 @@ fn walkInstruction(
27112683 break :blk fields_len;
27122684 } 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
27202686 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
27212687 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
27222688
2723 const decls_first_index = self.decls.items.len;
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(
2689 extra_index = try self.analyzeAllDecls(
27492690 file,
27502691 &scope,
2692 inst_index,
27512693 src_info,
2752 decls_first_index,
2753 decls_len,
27542694 &decl_indexes,
27552695 &priv_decl_indexes,
2756 extra_index,
27572696 );
27582697
27592698 // const body = file.zir.extra[extra_index..][0..body_len];
......@@ -2862,12 +2801,6 @@ fn walkInstruction(
28622801 break :blk fields_len;
28632802 } 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
28712804 // TODO: Expose explicit backing integer types in some way.
28722805 if (small.has_backing_int) {
28732806 const backing_int_body_len = file.zir.extra[extra_index];
......@@ -2882,40 +2815,13 @@ fn walkInstruction(
28822815 var decl_indexes: std.ArrayListUnmanaged(usize) = .{};
28832816 var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{};
28842817
2885 const decls_first_index = self.decls.items.len;
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(
2818 extra_index = try self.analyzeAllDecls(
29112819 file,
29122820 &scope,
2821 inst_index,
29132822 src_info,
2914 decls_first_index,
2915 decls_len,
29162823 &decl_indexes,
29172824 &priv_decl_indexes,
2918 extra_index,
29192825 );
29202826
29212827 var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{};
......@@ -3096,189 +3002,286 @@ fn walkInstruction(
30963002/// Does not append to `self.decls` directly because `walkInstruction`
30973003/// is expected to look-ahead scan all decls and reserve `body_len`
30983004/// slots in `self.decls`, which are then filled out by this function.
3099fn walkDecls(
3005fn analyzeAllDecls(
31003006 self: *Autodoc,
31013007 file: *File,
31023008 scope: *Scope,
3009 parent_inst_index: usize,
31033010 parent_src: SrcLocInfo,
3104 decls_first_index: usize,
3105 decls_len: usize,
31063011 decl_indexes: *std.ArrayListUnmanaged(usize),
31073012 priv_decl_indexes: *std.ArrayListUnmanaged(usize),
3108 extra_start: usize,
31093013) AutodocErrors!usize {
3110 const data = file.zir.instructions.items(.data);
3111 const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable;
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;
3014 const first_decl_indexes_slot = decl_indexes.items.len;
3015 const original_it = file.zir.declIterator(@intCast(u32, parent_inst_index));
31163016
3117 // NOTE: we're not outputting every ZIR decl as a Autodoc decl.
3118 // tests, comptime blocks and usingnamespace are skipped.
3119 // this is why we `need good_decls_i`.
3120 var good_decls_i: usize = 0;
3121 while (decl_i < decls_len) : (decl_i += 1) {
3122 const decls_slot_index = decls_first_index + good_decls_i;
3017 // First loop to discover decl names
3018 {
3019 var it = original_it;
3020 while (it.next()) |d| {
3021 const decl_name_index = file.zir.extra[d.sub_index + 5];
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) {
3125 cur_bit_bag = file.zir.extra[bit_bag_index];
3126 bit_bag_index += 1;
3029 try scope.insertDeclRef(self.arena, decl_name_index, .Pending);
31273030 }
3128 const is_pub = @truncate(u1, cur_bit_bag) != 0;
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;
3031 }
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];
3141 extra_index += 4;
3061 // Third loop to analyze all remaining decls
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];
3144 extra_index += 1;
3145 const decl_name_index = file.zir.extra[extra_index];
3146 extra_index += 1;
3147 const value_index = file.zir.extra[extra_index];
3148 extra_index += 1;
3149 const doc_comment_index = file.zir.extra[extra_index];
3150 extra_index += 1;
3072 try self.analyzeDecl(
3073 file,
3074 scope,
3075 parent_src,
3076 decl_indexes,
3077 priv_decl_indexes,
3078 d,
3079 );
3080 }
31513081
3152 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
3153 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3154 extra_index += 1;
3155 break :inst inst;
3156 };
3157 _ = align_inst;
3082 return it.extra_index;
3083}
31583084
3159 const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
3160 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3161 extra_index += 1;
3162 break :inst inst;
3163 };
3164 _ = section_inst;
3085// Asserts the given decl is public
3086fn analyzeDecl(
3087 self: *Autodoc,
3088 file: *File,
3089 scope: *Scope,
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: {
3167 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3168 extra_index += 1;
3169 break :inst inst;
3170 };
3171 _ = addrspace_inst;
3101 var extra_index = d.sub_index;
3102 // const hash_u32s = file.zir.extra[extra_index..][0..4];
31723103
3173 // This is known to work because decl values are always block_inlines
3174 const value_pl_node = data[value_index].pl_node;
3175 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3104 extra_index += 4;
3105 // const line = file.zir.extra[extra_index];
31763106
3177 const name: []const u8 = switch (decl_name_index) {
3178 0, 1 => continue, // comptime or usingnamespace decl
3179 2 => {
3180 // decl test
3181 const decl_being_tested = scope.resolveDeclName(doc_comment_index);
3182 const func_index = getBlockInlineBreak(file.zir, value_index).?;
3107 extra_index += 1;
3108 const decl_name_index = file.zir.extra[extra_index];
31833109
3184 const pl_node = data[Zir.refToIndex(func_index).?].pl_node;
3185 const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src);
3186 const tree = try file.getTree(self.module.gpa);
3187 const test_source_code = tree.getNodeSource(fn_src.src_node);
3110 extra_index += 1;
3111 const value_index = file.zir.extra[extra_index];
31883112
3189 const ast_node_index = self.ast_nodes.items.len;
3190 try self.ast_nodes.append(self.arena, .{
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 };
3113 extra_index += 1;
3114 const doc_comment_index = file.zir.extra[extra_index];
32073115
3208 // If we got here, it means that this decl is not a test, usingnamespace
3209 // or a comptime block decl.
3210 good_decls_i += 1;
3116 extra_index += 1;
3117 const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: {
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)
3213 file.zir.nullTerminatedString(doc_comment_index)
3214 else
3215 null;
3124 const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
3125 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3126 extra_index += 1;
3127 break :inst inst;
3128 };
3129 _ = section_inst;
32163130
3217 // astnode
3218 const ast_node_index = idx: {
3219 const idx = self.ast_nodes.items.len;
3220 try self.ast_nodes.append(self.arena, .{
3221 .file = self.files.getIndex(file).?,
3222 .line = decl_src.line,
3223 .col = 0,
3224 .docs = doc_comment,
3225 .fields = null, // walkInstruction will fill `fields` if necessary
3226 });
3227 break :idx idx;
3228 };
3131 const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: {
3132 const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]);
3133 extra_index += 1;
3134 break :inst inst;
3135 };
3136 _ = addrspace_inst;
3137
3138 // This is known to work because decl values are always block_inlines
3139 const value_pl_node = data[value_index].pl_node;
3140 const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
3141
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) {
3233 try decl_indexes.append(self.arena, decls_slot_index);
3234 } else {
3235 try priv_decl_indexes.append(self.arena, decls_slot_index);
3236 }
3193 const walk_result = try self.walkInstruction(file, scope, decl_src, value_index, true);
32373194
3238 // // decl.typeRef == decl.val...typeRef
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 };
3195 const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const";
32633196
3264 // Unblock any pending decl path that was waiting for this decl.
3265 if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| {
3266 for (paths.items) |resume_info| {
3267 try self.tryResolveRefPath(
3268 resume_info.file,
3269 value_index,
3270 resume_info.ref_path,
3271 );
3272 }
3197 const decls_slot_index = self.decls.items.len;
3198 try self.decls.append(self.arena, .{
3199 .name = name,
3200 .src = ast_node_index,
3201 .value = walk_result,
3202 .kind = kind,
3203 });
3204
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);
3275 // TODO: we should deallocate the arraylist that holds all the
3276 // ref paths. not doing it now since it's arena-allocated
3277 // anyway, but maybe we should put it elsewhere.
3211 const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, 0);
3212 std.debug.assert(decl_status_ptr.* == .Pending);
3213 decl_status_ptr.* = .{ .Analyzed = decls_slot_index };
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 );
32783223 }
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.
32793229 }
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 }
32823285}
32833286
32843287/// An unresolved path has a non-string WalkResult at its beginnig, while every
......@@ -3290,7 +3293,7 @@ fn walkDecls(
32903293/// Same happens when a decl holds a type definition that hasn't been fully
32913294/// analyzed yet (except that we append to `self.ref_paths_pending_on_types`.
32923295///
3293/// When walkDecls / walkInstruction finishes analyzing a decl / type, it will
3296/// When analyzeAllDecls / walkInstruction finishes analyzing a decl / type, it will
32943297/// then check if there's any pending ref path blocked on it and, if any, it
32953298/// will progress their resolution by calling tryResolveRefPath again.
32963299///
......@@ -3318,37 +3321,51 @@ fn tryResolveRefPath(
33183321 switch (resolved_parent) {
33193322 else => break,
33203323 .this => |t| resolved_parent = .{ .type = t },
3321 .declRef => |decl_index| {
3324 .declIndex => |decl_index| {
33223325 const decl = self.decls.items[decl_index];
3323 if (decl._analyzed) {
3324 resolved_parent = decl.value.expr;
3325 continue;
3326 }
3327
3328 // This decl path is pending completion
3329 {
3330 const res = try self.pending_ref_paths.getOrPut(
3331 self.arena,
3332 &path[path.len - 1],
3333 );
3334 if (!res.found_existing) res.value_ptr.* = .{};
3335 }
3326 resolved_parent = decl.value.expr;
3327 continue;
3328 },
3329 .declRef => |decl_status_ptr| {
3330 // NOTE: must be kep in sync with `findNameInUnsDecls`
3331 switch (decl_status_ptr.*) {
3332 // The use of unreachable here is conservative.
3333 // It might be that it truly should be up to us to
3334 // request the analys of this decl, but it's not clear
3335 // at the moment of writing.
3336 .NotRequested => unreachable,
3337 .Analyzed => |decl_index| {
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(
3338 self.arena,
3339 decl_index,
3340 );
3341 if (!res.found_existing) res.value_ptr.* = .{};
3342 try res.value_ptr.*.append(self.arena, .{
3343 .file = file,
3344 .ref_path = path[i..path.len],
3345 });
3352 const res = try self.ref_paths_pending_on_decls.getOrPut(
3353 self.arena,
3354 decl_status_ptr,
3355 );
3356 if (!res.found_existing) res.value_ptr.* = .{};
3357 try res.value_ptr.*.append(self.arena, .{
3358 .file = file,
3359 .ref_path = path[i..path.len],
3360 });
33463361
3347 // We return instead doing `break :outer` to prevent the
3348 // code after the :outer while loop to run, as it assumes
3349 // that the path will have been fully analyzed (or we
3350 // have given up because of a comptimeExpr).
3351 return;
3362 // We return instead doing `break :outer` to prevent the
3363 // code after the :outer while loop to run, as it assumes
3364 // that the path will have been fully analyzed (or we
3365 // have given up because of a comptimeExpr).
3366 return;
3367 },
3368 }
33523369 },
33533370 .refPath => |rp| {
33543371 if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| {
......@@ -3388,7 +3405,7 @@ fn tryResolveRefPath(
33883405 panicWithContext(
33893406 file,
33903407 inst_index,
3391 "exhausted eval quota for `{}`in tryResolveDecl\n",
3408 "exhausted eval quota for `{}`in tryResolveRefPath\n",
33923409 .{resolved_parent},
33933410 );
33943411 }
......@@ -3461,26 +3478,39 @@ fn tryResolveRefPath(
34613478 );
34623479 }
34633480 },
3464 .Enum => |t_enum| {
3465 for (t_enum.pubDecls) |d| {
3466 // TODO: this could be improved a lot
3467 // by having our own string table!
3468 const decl = self.decls.items[d];
3469 if (std.mem.eql(u8, decl.name, child_string)) {
3470 path[i + 1] = .{ .declRef = d };
3481 // TODO: the following searches could probably
3482 // be performed more efficiently on the corresponding
3483 // scope
3484 .Enum => |t_enum| { // foo.bar.baz
3485 // Look into locally-defined pub decls
3486 for (t_enum.pubDecls) |idx| {
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 };
34713491 continue :outer;
34723492 }
34733493 }
3474 for (t_enum.privDecls) |d| {
3475 // TODO: this could be improved a lot
3476 // by having our own string table!
3477 const decl = self.decls.items[d];
3478 if (std.mem.eql(u8, decl.name, child_string)) {
3479 path[i + 1] = .{ .declRef = d };
3494
3495 // Look into locally-defined priv decls
3496 for (t_enum.privDecls) |idx| {
3497 const d = self.decls.items[idx];
3498 if (d.is_uns) continue;
3499 if (std.mem.eql(u8, d.name, child_string)) {
3500 path[i + 1] = .{ .declIndex = idx };
34803501 continue :outer;
34813502 }
34823503 }
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
34843514 for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| {
34853515 const name = self.ast_nodes.items[ast_node].name.?;
34863516 if (std.mem.eql(u8, name, child_string)) {
......@@ -3509,25 +3539,35 @@ fn tryResolveRefPath(
35093539 continue :outer;
35103540 },
35113541 .Union => |t_union| {
3512 for (t_union.pubDecls) |d| {
3513 // TODO: this could be improved a lot
3514 // by having our own string table!
3515 const decl = self.decls.items[d];
3516 if (std.mem.eql(u8, decl.name, child_string)) {
3517 path[i + 1] = .{ .declRef = d };
3542 // Look into locally-defined pub decls
3543 for (t_union.pubDecls) |idx| {
3544 const d = self.decls.items[idx];
3545 if (d.is_uns) continue;
3546 if (std.mem.eql(u8, d.name, child_string)) {
3547 path[i + 1] = .{ .declIndex = idx };
35183548 continue :outer;
35193549 }
35203550 }
3521 for (t_union.privDecls) |d| {
3522 // TODO: this could be improved a lot
3523 // by having our own string table!
3524 const decl = self.decls.items[d];
3525 if (std.mem.eql(u8, decl.name, child_string)) {
3526 path[i + 1] = .{ .declRef = d };
3551
3552 // Look into locally-defined priv decls
3553 for (t_union.privDecls) |idx| {
3554 const d = self.decls.items[idx];
3555 if (d.is_uns) continue;
3556 if (std.mem.eql(u8, d.name, child_string)) {
3557 path[i + 1] = .{ .declIndex = idx };
35273558 continue :outer;
35283559 }
35293560 }
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
35313571 for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| {
35323572 const name = self.ast_nodes.items[ast_node].name.?;
35333573 if (std.mem.eql(u8, name, child_string)) {
......@@ -3556,25 +3596,35 @@ fn tryResolveRefPath(
35563596 },
35573597
35583598 .Struct => |t_struct| {
3559 for (t_struct.pubDecls) |d| {
3560 // TODO: this could be improved a lot
3561 // by having our own string table!
3562 const decl = self.decls.items[d];
3563 if (std.mem.eql(u8, decl.name, child_string)) {
3564 path[i + 1] = .{ .declRef = d };
3599 // Look into locally-defined pub decls
3600 for (t_struct.pubDecls) |idx| {
3601 const d = self.decls.items[idx];
3602 if (d.is_uns) continue;
3603 if (std.mem.eql(u8, d.name, child_string)) {
3604 path[i + 1] = .{ .declIndex = idx };
35653605 continue :outer;
35663606 }
35673607 }
3568 for (t_struct.privDecls) |d| {
3569 // TODO: this could be improved a lot
3570 // by having our own string table!
3571 const decl = self.decls.items[d];
3572 if (std.mem.eql(u8, decl.name, child_string)) {
3573 path[i + 1] = .{ .declRef = d };
3608
3609 // Look into locally-defined priv decls
3610 for (t_struct.privDecls) |idx| {
3611 const d = self.decls.items[idx];
3612 if (d.is_uns) continue;
3613 if (std.mem.eql(u8, d.name, child_string)) {
3614 path[i + 1] = .{ .declIndex = idx };
35743615 continue :outer;
35753616 }
35763617 }
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
35783628 for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| {
35793629 const name = self.ast_nodes.items[ast_node].name.?;
35803630 if (std.mem.eql(u8, name, child_string)) {
......@@ -3605,25 +3655,37 @@ fn tryResolveRefPath(
36053655 continue :outer;
36063656 },
36073657 .Opaque => |t_opaque| {
3608 for (t_opaque.pubDecls) |d| {
3609 // TODO: this could be improved a lot
3610 // by having our own string table!
3611 const decl = self.decls.items[d];
3612 if (std.mem.eql(u8, decl.name, child_string)) {
3613 path[i + 1] = .{ .declRef = d };
3658 // Look into locally-defined pub decls
3659 for (t_opaque.pubDecls) |idx| {
3660 const d = self.decls.items[idx];
3661 if (d.is_uns) continue;
3662 if (std.mem.eql(u8, d.name, child_string)) {
3663 path[i + 1] = .{ .declIndex = idx };
36143664 continue :outer;
36153665 }
36163666 }
3617 for (t_opaque.privDecls) |d| {
3618 // TODO: this could be improved a lot
3619 // by having our own string table!
3620 const decl = self.decls.items[d];
3621 if (std.mem.eql(u8, decl.name, child_string)) {
3622 path[i + 1] = .{ .declRef = d };
3667
3668 // Look into locally-defined priv decls
3669 for (t_opaque.privDecls) |idx| {
3670 const d = self.decls.items[idx];
3671 if (d.is_uns) continue;
3672 if (std.mem.eql(u8, d.name, child_string)) {
3673 path[i + 1] = .{ .declIndex = idx };
36233674 continue :outer;
36243675 }
36253676 }
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
36273689 // if we got here, our search failed
36283690 printWithContext(
36293691 file,
......@@ -3670,6 +3732,104 @@ fn tryResolveRefPath(
36703732 // that said, we might want to store it elsewhere and reclaim memory asap
36713733 }
36723734}
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
36733833fn analyzeFancyFunction(
36743834 self: *Autodoc,
36753835 file: *File,
src/Compilation.zig+1-1
......@@ -2052,7 +2052,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
20522052 return;
20532053 }
20542054
2055 if (!build_options.only_c) {
2055 if (!build_options.only_c and !build_options.omit_pkg_fetching_code) {
20562056 if (comp.emit_docs) |doc_location| {
20572057 if (comp.bin_file.options.module) |module| {
20582058 var autodoc = Autodoc.init(module, doc_location);
src/Zir.zig+2
......@@ -3667,6 +3667,7 @@ pub const DeclIterator = struct {
36673667 pub const Item = struct {
36683668 name: [:0]const u8,
36693669 sub_index: u32,
3670 flags: u4,
36703671 };
36713672
36723673 pub fn next(it: *DeclIterator) ?Item {
......@@ -3691,6 +3692,7 @@ pub const DeclIterator = struct {
36913692 return Item{
36923693 .sub_index = sub_index,
36933694 .name = name,
3695 .flags = flags,
36943696 };
36953697 }
36963698};