| ... | ... | @@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged( |
| 37 | 37 | std.ArrayListUnmanaged(RefPathResumeInfo), |
| 38 | 38 | ) = .{}, |
| 39 | 39 | ref_paths_pending_on_decls: std.AutoHashMapUnmanaged( |
| 40 | | usize, |
| 40 | *Scope.DeclStatus, |
| 41 | 41 | std.ArrayListUnmanaged(RefPathResumeInfo), |
| 42 | 42 | ) = .{}, |
| 43 | 43 | ref_paths_pending_on_types: std.AutoHashMapUnmanaged( |
| ... | ... | @@ -344,28 +344,48 @@ fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File { |
| 344 | 344 | } |
| 345 | 345 | |
| 346 | 346 | /// 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. |
| 348 | 349 | const Scope = struct { |
| 349 | 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 | 356 | enclosing_type: usize, // index into `types` |
| 352 | 357 | |
| 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 { |
| 356 | 368 | var cur: ?*const Scope = &self; |
| 357 | 369 | 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 | }; |
| 360 | 380 | } |
| 361 | 381 | |
| 362 | 382 | pub fn insertDeclRef( |
| 363 | 383 | self: *Scope, |
| 364 | 384 | 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, |
| 367 | 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 | }; |
| 371 | 391 | |
| ... | ... | @@ -479,7 +499,7 @@ const DocData = struct { |
| 479 | 499 | value: WalkResult, |
| 480 | 500 | // The index in astNodes of the `test declname { }` node |
| 481 | 501 | decltest: ?usize = null, |
| 482 | | _analyzed: bool, // omitted in json data |
| 502 | is_uns: bool = false, // usingnamespace |
| 483 | 503 | |
| 484 | 504 | pub fn jsonStringify( |
| 485 | 505 | self: Decl, |
| ... | ... | @@ -676,7 +696,8 @@ const DocData = struct { |
| 676 | 696 | @"&": usize, // index in `exprs` |
| 677 | 697 | type: usize, // index in `types` |
| 678 | 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 | 701 | builtinField: enum { len, ptr }, |
| 681 | 702 | fieldRef: FieldRef, |
| 682 | 703 | refPath: []Expr, |
| ... | ... | @@ -775,7 +796,11 @@ const DocData = struct { |
| 775 | 796 | var jsw = std.json.writeStream(w, 15); |
| 776 | 797 | if (opts.whitespace) |ws| jsw.whitespace = ws; |
| 777 | 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 | 804 | switch (self) { |
| 780 | 805 | .int => { |
| 781 | 806 | if (self.int.negated) try w.writeAll("-"); |
| ... | ... | @@ -784,11 +809,16 @@ const DocData = struct { |
| 784 | 809 | .builtinField => { |
| 785 | 810 | try jsw.emitString(@tagName(self.builtinField)); |
| 786 | 811 | }, |
| 812 | .declRef => { |
| 813 | try jsw.emitNumber(self.declRef.Analyzed); |
| 814 | }, |
| 787 | 815 | else => { |
| 788 | 816 | inline for (comptime std.meta.fields(Expr)) |case| { |
| 789 | 817 | // TODO: this is super ugly, fix once `inline else` is a thing |
| 790 | 818 | if (comptime std.mem.eql(u8, case.name, "builtinField")) |
| 791 | 819 | continue; |
| 820 | if (comptime std.mem.eql(u8, case.name, "declRef")) |
| 821 | continue; |
| 792 | 822 | if (@field(Expr, case.name) == active_tag) { |
| 793 | 823 | try std.json.stringify(@field(self, case.name), opts, w); |
| 794 | 824 | jsw.state_index -= 1; |
| ... | ... | @@ -1133,7 +1163,7 @@ fn walkInstruction( |
| 1133 | 1163 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } }; |
| 1134 | 1164 | |
| 1135 | 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 | 1167 | .expr = .{ .sliceIndex = slice_index }, |
| 1138 | 1168 | }; |
| 1139 | 1169 | }, |
| ... | ... | @@ -1175,7 +1205,7 @@ fn walkInstruction( |
| 1175 | 1205 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } }; |
| 1176 | 1206 | |
| 1177 | 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 | 1209 | .expr = .{ .sliceIndex = slice_index }, |
| 1180 | 1210 | }; |
| 1181 | 1211 | }, |
| ... | ... | @@ -1226,7 +1256,7 @@ fn walkInstruction( |
| 1226 | 1256 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } }; |
| 1227 | 1257 | |
| 1228 | 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 | 1260 | .expr = .{ .sliceIndex = slice_index }, |
| 1231 | 1261 | }; |
| 1232 | 1262 | }, |
| ... | ... | @@ -1993,12 +2023,9 @@ fn walkInstruction( |
| 1993 | 2023 | }, |
| 1994 | 2024 | .decl_val, .decl_ref => { |
| 1995 | 2025 | 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); |
| 2000 | 2027 | return DocData.WalkResult{ |
| 2001 | | .expr = .{ .declRef = decls_slot_index }, |
| 2028 | .expr = .{ .declRef = decl_status }, |
| 2002 | 2029 | }; |
| 2003 | 2030 | }, |
| 2004 | 2031 | .field_val, .field_call_bind, .field_ptr, .field_type => { |
| ... | ... | @@ -2430,49 +2457,16 @@ fn walkInstruction( |
| 2430 | 2457 | else |
| 2431 | 2458 | parent_src; |
| 2432 | 2459 | |
| 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 | 2460 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2440 | 2461 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2441 | 2462 | |
| 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( |
| 2468 | 2464 | file, |
| 2469 | 2465 | &scope, |
| 2466 | inst_index, |
| 2470 | 2467 | src_info, |
| 2471 | | decls_first_index, |
| 2472 | | decls_len, |
| 2473 | 2468 | &decl_indexes, |
| 2474 | 2469 | &priv_decl_indexes, |
| 2475 | | extra_index, |
| 2476 | 2470 | ); |
| 2477 | 2471 | |
| 2478 | 2472 | self.types.items[type_slot_index] = .{ |
| ... | ... | @@ -2549,13 +2543,14 @@ fn walkInstruction( |
| 2549 | 2543 | else |
| 2550 | 2544 | parent_src; |
| 2551 | 2545 | |
| 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 | 2549 | const tag_type = file.zir.extra[extra_index]; |
| 2554 | 2550 | extra_index += 1; |
| 2555 | 2551 | 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; |
| 2559 | 2554 | |
| 2560 | 2555 | const body_len = if (small.has_body_len) blk: { |
| 2561 | 2556 | const body_len = file.zir.extra[extra_index]; |
| ... | ... | @@ -2569,51 +2564,28 @@ fn walkInstruction( |
| 2569 | 2564 | break :blk fields_len; |
| 2570 | 2565 | } else 0; |
| 2571 | 2566 | |
| 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 | 2567 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2579 | 2568 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2580 | 2569 | |
| 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( |
| 2607 | 2571 | file, |
| 2608 | 2572 | &scope, |
| 2573 | inst_index, |
| 2609 | 2574 | src_info, |
| 2610 | | decls_first_index, |
| 2611 | | decls_len, |
| 2612 | 2575 | &decl_indexes, |
| 2613 | 2576 | &priv_decl_indexes, |
| 2614 | | extra_index, |
| 2615 | 2577 | ); |
| 2616 | 2578 | |
| 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 | 2589 | extra_index += body_len; |
| 2618 | 2590 | |
| 2619 | 2591 | var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( |
| ... | ... | @@ -2643,7 +2615,7 @@ fn walkInstruction( |
| 2643 | 2615 | .privDecls = priv_decl_indexes.items, |
| 2644 | 2616 | .pubDecls = decl_indexes.items, |
| 2645 | 2617 | .fields = field_type_refs.items, |
| 2646 | | .tag = tag_type, |
| 2618 | .tag = tag_type.expr, |
| 2647 | 2619 | .auto_enum = small.auto_enum_tag, |
| 2648 | 2620 | }, |
| 2649 | 2621 | }; |
| ... | ... | @@ -2711,49 +2683,16 @@ fn walkInstruction( |
| 2711 | 2683 | break :blk fields_len; |
| 2712 | 2684 | } else 0; |
| 2713 | 2685 | |
| 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 | 2686 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2721 | 2687 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2722 | 2688 | |
| 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( |
| 2749 | 2690 | file, |
| 2750 | 2691 | &scope, |
| 2692 | inst_index, |
| 2751 | 2693 | src_info, |
| 2752 | | decls_first_index, |
| 2753 | | decls_len, |
| 2754 | 2694 | &decl_indexes, |
| 2755 | 2695 | &priv_decl_indexes, |
| 2756 | | extra_index, |
| 2757 | 2696 | ); |
| 2758 | 2697 | |
| 2759 | 2698 | // const body = file.zir.extra[extra_index..][0..body_len]; |
| ... | ... | @@ -2862,12 +2801,6 @@ fn walkInstruction( |
| 2862 | 2801 | break :blk fields_len; |
| 2863 | 2802 | } else 0; |
| 2864 | 2803 | |
| 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 | 2804 | // TODO: Expose explicit backing integer types in some way. |
| 2872 | 2805 | if (small.has_backing_int) { |
| 2873 | 2806 | const backing_int_body_len = file.zir.extra[extra_index]; |
| ... | ... | @@ -2882,40 +2815,13 @@ fn walkInstruction( |
| 2882 | 2815 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2883 | 2816 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2884 | 2817 | |
| 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( |
| 2911 | 2819 | file, |
| 2912 | 2820 | &scope, |
| 2821 | inst_index, |
| 2913 | 2822 | src_info, |
| 2914 | | decls_first_index, |
| 2915 | | decls_len, |
| 2916 | 2823 | &decl_indexes, |
| 2917 | 2824 | &priv_decl_indexes, |
| 2918 | | extra_index, |
| 2919 | 2825 | ); |
| 2920 | 2826 | |
| 2921 | 2827 | var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{}; |
| ... | ... | @@ -3096,189 +3002,286 @@ fn walkInstruction( |
| 3096 | 3002 | /// Does not append to `self.decls` directly because `walkInstruction` |
| 3097 | 3003 | /// is expected to look-ahead scan all decls and reserve `body_len` |
| 3098 | 3004 | /// slots in `self.decls`, which are then filled out by this function. |
| 3099 | | fn walkDecls( |
| 3005 | fn analyzeAllDecls( |
| 3100 | 3006 | self: *Autodoc, |
| 3101 | 3007 | file: *File, |
| 3102 | 3008 | scope: *Scope, |
| 3009 | parent_inst_index: usize, |
| 3103 | 3010 | parent_src: SrcLocInfo, |
| 3104 | | decls_first_index: usize, |
| 3105 | | decls_len: usize, |
| 3106 | 3011 | decl_indexes: *std.ArrayListUnmanaged(usize), |
| 3107 | 3012 | priv_decl_indexes: *std.ArrayListUnmanaged(usize), |
| 3108 | | extra_start: usize, |
| 3109 | 3013 | ) 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)); |
| 3116 | 3016 | |
| 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 | } |
| 3123 | 3028 | |
| 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); |
| 3127 | 3030 | } |
| 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 | } |
| 3137 | 3032 | |
| 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 | } |
| 3139 | 3060 | |
| 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 | } |
| 3142 | 3071 | |
| 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 | } |
| 3151 | 3081 | |
| 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 | } |
| 3158 | 3084 | |
| 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 |
| 3086 | fn 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; |
| 3165 | 3100 | |
| 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]; |
| 3172 | 3103 | |
| 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]; |
| 3176 | 3106 | |
| 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]; |
| 3183 | 3109 | |
| 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]; |
| 3188 | 3112 | |
| 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]; |
| 3207 | 3115 | |
| 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; |
| 3211 | 3123 | |
| 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; |
| 3216 | 3130 | |
| 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 | }; |
| 3229 | 3174 | |
| 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 | }; |
| 3231 | 3192 | |
| 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); |
| 3237 | 3194 | |
| 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"; |
| 3263 | 3196 | |
| 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 | } |
| 3273 | 3210 | |
| 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 | ); |
| 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 | } |
| 3280 | 3231 | |
| 3281 | | return extra_index; |
| 3232 | fn 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 | } |
| 3283 | 3286 | |
| 3284 | 3287 | /// An unresolved path has a non-string WalkResult at its beginnig, while every |
| ... | ... | @@ -3290,7 +3293,7 @@ fn walkDecls( |
| 3290 | 3293 | /// Same happens when a decl holds a type definition that hasn't been fully |
| 3291 | 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 will |
| 3296 | /// When analyzeAllDecls / walkInstruction finishes analyzing a decl / type, it will |
| 3294 | 3297 | /// then check if there's any pending ref path blocked on it and, if any, it |
| 3295 | 3298 | /// will progress their resolution by calling tryResolveRefPath again. |
| 3296 | 3299 | /// |
| ... | ... | @@ -3318,37 +3321,51 @@ fn tryResolveRefPath( |
| 3318 | 3321 | switch (resolved_parent) { |
| 3319 | 3322 | else => break, |
| 3320 | 3323 | .this => |t| resolved_parent = .{ .type = t }, |
| 3321 | | .declRef => |decl_index| { |
| 3324 | .declIndex => |decl_index| { |
| 3322 | 3325 | 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 | } |
| 3336 | 3351 | |
| 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 | }); |
| 3346 | 3361 | |
| 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 | } |
| 3352 | 3369 | }, |
| 3353 | 3370 | .refPath => |rp| { |
| 3354 | 3371 | if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| { |
| ... | ... | @@ -3388,7 +3405,7 @@ fn tryResolveRefPath( |
| 3388 | 3405 | panicWithContext( |
| 3389 | 3406 | file, |
| 3390 | 3407 | inst_index, |
| 3391 | | "exhausted eval quota for `{}`in tryResolveDecl\n", |
| 3408 | "exhausted eval quota for `{}`in tryResolveRefPath\n", |
| 3392 | 3409 | .{resolved_parent}, |
| 3393 | 3410 | ); |
| 3394 | 3411 | } |
| ... | ... | @@ -3461,26 +3478,39 @@ fn tryResolveRefPath( |
| 3461 | 3478 | ); |
| 3462 | 3479 | } |
| 3463 | 3480 | }, |
| 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 }; |
| 3471 | 3491 | continue :outer; |
| 3472 | 3492 | } |
| 3473 | 3493 | } |
| 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 }; |
| 3480 | 3501 | continue :outer; |
| 3481 | 3502 | } |
| 3482 | 3503 | } |
| 3483 | 3504 | |
| 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 | 3514 | for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| { |
| 3485 | 3515 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3486 | 3516 | if (std.mem.eql(u8, name, child_string)) { |
| ... | ... | @@ -3509,25 +3539,35 @@ fn tryResolveRefPath( |
| 3509 | 3539 | continue :outer; |
| 3510 | 3540 | }, |
| 3511 | 3541 | .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 }; |
| 3518 | 3548 | continue :outer; |
| 3519 | 3549 | } |
| 3520 | 3550 | } |
| 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 }; |
| 3527 | 3558 | continue :outer; |
| 3528 | 3559 | } |
| 3529 | 3560 | } |
| 3530 | 3561 | |
| 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 | 3571 | for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| { |
| 3532 | 3572 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3533 | 3573 | if (std.mem.eql(u8, name, child_string)) { |
| ... | ... | @@ -3556,25 +3596,35 @@ fn tryResolveRefPath( |
| 3556 | 3596 | }, |
| 3557 | 3597 | |
| 3558 | 3598 | .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 }; |
| 3565 | 3605 | continue :outer; |
| 3566 | 3606 | } |
| 3567 | 3607 | } |
| 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 }; |
| 3574 | 3615 | continue :outer; |
| 3575 | 3616 | } |
| 3576 | 3617 | } |
| 3577 | 3618 | |
| 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 | 3628 | for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| { |
| 3579 | 3629 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3580 | 3630 | if (std.mem.eql(u8, name, child_string)) { |
| ... | ... | @@ -3605,25 +3655,37 @@ fn tryResolveRefPath( |
| 3605 | 3655 | continue :outer; |
| 3606 | 3656 | }, |
| 3607 | 3657 | .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 }; |
| 3614 | 3664 | continue :outer; |
| 3615 | 3665 | } |
| 3616 | 3666 | } |
| 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 }; |
| 3623 | 3674 | continue :outer; |
| 3624 | 3675 | } |
| 3625 | 3676 | } |
| 3626 | 3677 | |
| 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 | 3689 | // if we got here, our search failed |
| 3628 | 3690 | printWithContext( |
| 3629 | 3691 | file, |
| ... | ... | @@ -3670,6 +3732,104 @@ fn tryResolveRefPath( |
| 3670 | 3732 | // that said, we might want to store it elsewhere and reclaim memory asap |
| 3671 | 3733 | } |
| 3672 | 3734 | } |
| 3735 | |
| 3736 | const UnsSearchResult = union(enum) { |
| 3737 | Found: DocData.Expr, |
| 3738 | Pending, |
| 3739 | NotFound, |
| 3740 | }; |
| 3741 | |
| 3742 | fn 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 | |
| 3673 | 3833 | fn analyzeFancyFunction( |
| 3674 | 3834 | self: *Autodoc, |
| 3675 | 3835 | file: *File, |