| ... | @@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged( | ... | @@ -37,7 +37,7 @@ pending_ref_paths: std.AutoHashMapUnmanaged( |
| 37 | std.ArrayListUnmanaged(RefPathResumeInfo), | 37 | std.ArrayListUnmanaged(RefPathResumeInfo), |
| 38 | ) = .{}, | 38 | ) = .{}, |
| 39 | ref_paths_pending_on_decls: std.AutoHashMapUnmanaged( | 39 | ref_paths_pending_on_decls: std.AutoHashMapUnmanaged( |
| 40 | usize, | 40 | *Scope.DeclStatus, |
| 41 | std.ArrayListUnmanaged(RefPathResumeInfo), | 41 | std.ArrayListUnmanaged(RefPathResumeInfo), |
| 42 | ) = .{}, | 42 | ) = .{}, |
| 43 | ref_paths_pending_on_types: std.AutoHashMapUnmanaged( | 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,28 +344,48 @@ fn createFromPath(base_dir: std.fs.Dir, path: []const u8) !std.fs.File { |
| 344 | } | 344 | } |
| 345 | | 345 | |
| 346 | /// Represents a chain of scopes, used to resolve decl references to the | 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 | const Scope = struct { | 349 | const Scope = struct { |
| 349 | parent: ?*Scope, | 350 | parent: ?*Scope, |
| 350 | map: std.AutoHashMapUnmanaged(u32, usize) = .{}, // index into `decls` | 351 | map: std.AutoHashMapUnmanaged( |
| | 352 | u32, // index into the current file's string table (decl name) |
| | 353 | DeclStatus, |
| | 354 | ) = .{}, |
| | 355 | |
| 351 | enclosing_type: usize, // index into `types` | 356 | enclosing_type: usize, // index into `types` |
| 352 | | 357 | |
| 353 | /// Assumes all decls in present scope and upper scopes have already | 358 | pub const DeclStatus = union(enum) { |
| 354 | /// been either fully resolved or at least reserved. | 359 | Analyzed: usize, // index into `decls` |
| 355 | pub fn resolveDeclName(self: Scope, string_table_idx: u32) usize { | 360 | Pending, |
| | 361 | NotRequested: u32, // instr_index |
| | 362 | |
| | 363 | }; |
| | 364 | |
| | 365 | /// Returns a pointer so that the caller has a chance to modify the value |
| | 366 | /// in case they decide to start analyzing a previously not requested decl. |
| | 367 | pub fn resolveDeclName(self: Scope, string_table_idx: u32, file: *File, inst_index: usize) *DeclStatus { |
| 356 | var cur: ?*const Scope = &self; | 368 | var cur: ?*const Scope = &self; |
| 357 | return while (cur) |s| : (cur = s.parent) { | 369 | return while (cur) |s| : (cur = s.parent) { |
| 358 | break s.map.get(string_table_idx) orelse continue; | 370 | break s.map.getPtr(string_table_idx) orelse continue; |
| 359 | } else unreachable; | 371 | } else { |
| | 372 | printWithContext( |
| | 373 | file, |
| | 374 | inst_index, |
| | 375 | "Could not find `{s}`\n\n", |
| | 376 | .{file.zir.nullTerminatedString(string_table_idx)}, |
| | 377 | ); |
| | 378 | unreachable; |
| | 379 | }; |
| 360 | } | 380 | } |
| 361 | | 381 | |
| 362 | pub fn insertDeclRef( | 382 | pub fn insertDeclRef( |
| 363 | self: *Scope, | 383 | self: *Scope, |
| 364 | arena: std.mem.Allocator, | 384 | arena: std.mem.Allocator, |
| 365 | decl_name_index: u32, // decl name | 385 | decl_name_index: u32, // index into the current file's string table |
| 366 | decls_slot_index: usize, | 386 | decl_status: DeclStatus, |
| 367 | ) !void { | 387 | ) !void { |
| 368 | try self.map.put(arena, decl_name_index, decls_slot_index); | 388 | try self.map.put(arena, decl_name_index, decl_status); |
| 369 | } | 389 | } |
| 370 | }; | 390 | }; |
| 371 | | 391 | |
| ... | @@ -479,7 +499,7 @@ const DocData = struct { | ... | @@ -479,7 +499,7 @@ const DocData = struct { |
| 479 | value: WalkResult, | 499 | value: WalkResult, |
| 480 | // The index in astNodes of the `test declname { }` node | 500 | // The index in astNodes of the `test declname { }` node |
| 481 | decltest: ?usize = null, | 501 | decltest: ?usize = null, |
| 482 | _analyzed: bool, // omitted in json data | 502 | is_uns: bool = false, // usingnamespace |
| 483 | | 503 | |
| 484 | pub fn jsonStringify( | 504 | pub fn jsonStringify( |
| 485 | self: Decl, | 505 | self: Decl, |
| ... | @@ -676,7 +696,8 @@ const DocData = struct { | ... | @@ -676,7 +696,8 @@ const DocData = struct { |
| 676 | @"&": usize, // index in `exprs` | 696 | @"&": usize, // index in `exprs` |
| 677 | type: usize, // index in `types` | 697 | type: usize, // index in `types` |
| 678 | this: usize, // index in `types` | 698 | this: usize, // index in `types` |
| 679 | declRef: usize, // index in `decls` | 699 | declRef: *Scope.DeclStatus, |
| | 700 | declIndex: usize, // index into `decls`, alternative repr for `declRef` |
| 680 | builtinField: enum { len, ptr }, | 701 | builtinField: enum { len, ptr }, |
| 681 | fieldRef: FieldRef, | 702 | fieldRef: FieldRef, |
| 682 | refPath: []Expr, | 703 | refPath: []Expr, |
| ... | @@ -775,7 +796,11 @@ const DocData = struct { | ... | @@ -775,7 +796,11 @@ const DocData = struct { |
| 775 | var jsw = std.json.writeStream(w, 15); | 796 | var jsw = std.json.writeStream(w, 15); |
| 776 | if (opts.whitespace) |ws| jsw.whitespace = ws; | 797 | if (opts.whitespace) |ws| jsw.whitespace = ws; |
| 777 | try jsw.beginObject(); | 798 | try jsw.beginObject(); |
| 778 | try jsw.objectField(@tagName(active_tag)); | 799 | if (active_tag == .declIndex) { |
| | 800 | try jsw.objectField("declRef"); |
| | 801 | } else { |
| | 802 | try jsw.objectField(@tagName(active_tag)); |
| | 803 | } |
| 779 | switch (self) { | 804 | switch (self) { |
| 780 | .int => { | 805 | .int => { |
| 781 | if (self.int.negated) try w.writeAll("-"); | 806 | if (self.int.negated) try w.writeAll("-"); |
| ... | @@ -784,11 +809,16 @@ const DocData = struct { | ... | @@ -784,11 +809,16 @@ const DocData = struct { |
| 784 | .builtinField => { | 809 | .builtinField => { |
| 785 | try jsw.emitString(@tagName(self.builtinField)); | 810 | try jsw.emitString(@tagName(self.builtinField)); |
| 786 | }, | 811 | }, |
| | 812 | .declRef => { |
| | 813 | try jsw.emitNumber(self.declRef.Analyzed); |
| | 814 | }, |
| 787 | else => { | 815 | else => { |
| 788 | inline for (comptime std.meta.fields(Expr)) |case| { | 816 | inline for (comptime std.meta.fields(Expr)) |case| { |
| 789 | // TODO: this is super ugly, fix once `inline else` is a thing | 817 | // TODO: this is super ugly, fix once `inline else` is a thing |
| 790 | if (comptime std.mem.eql(u8, case.name, "builtinField")) | 818 | if (comptime std.mem.eql(u8, case.name, "builtinField")) |
| 791 | continue; | 819 | continue; |
| | 820 | if (comptime std.mem.eql(u8, case.name, "declRef")) |
| | 821 | continue; |
| 792 | if (@field(Expr, case.name) == active_tag) { | 822 | if (@field(Expr, case.name) == active_tag) { |
| 793 | try std.json.stringify(@field(self, case.name), opts, w); | 823 | try std.json.stringify(@field(self, case.name), opts, w); |
| 794 | jsw.state_index -= 1; | 824 | jsw.state_index -= 1; |
| ... | @@ -1133,7 +1163,7 @@ fn walkInstruction( | ... | @@ -1133,7 +1163,7 @@ fn walkInstruction( |
| 1133 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } }; | 1163 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index } }; |
| 1134 | | 1164 | |
| 1135 | return DocData.WalkResult{ | 1165 | return DocData.WalkResult{ |
| 1136 | .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, | 1166 | .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef, |
| 1137 | .expr = .{ .sliceIndex = slice_index }, | 1167 | .expr = .{ .sliceIndex = slice_index }, |
| 1138 | }; | 1168 | }; |
| 1139 | }, | 1169 | }, |
| ... | @@ -1175,7 +1205,7 @@ fn walkInstruction( | ... | @@ -1175,7 +1205,7 @@ fn walkInstruction( |
| 1175 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } }; | 1205 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index } }; |
| 1176 | | 1206 | |
| 1177 | return DocData.WalkResult{ | 1207 | return DocData.WalkResult{ |
| 1178 | .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, | 1208 | .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef, |
| 1179 | .expr = .{ .sliceIndex = slice_index }, | 1209 | .expr = .{ .sliceIndex = slice_index }, |
| 1180 | }; | 1210 | }; |
| 1181 | }, | 1211 | }, |
| ... | @@ -1226,7 +1256,7 @@ fn walkInstruction( | ... | @@ -1226,7 +1256,7 @@ fn walkInstruction( |
| 1226 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } }; | 1256 | self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = end_index, .sentinel = sentinel_index } }; |
| 1227 | | 1257 | |
| 1228 | return DocData.WalkResult{ | 1258 | return DocData.WalkResult{ |
| 1229 | .typeRef = self.decls.items[lhs.expr.declRef].value.typeRef, | 1259 | .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef, |
| 1230 | .expr = .{ .sliceIndex = slice_index }, | 1260 | .expr = .{ .sliceIndex = slice_index }, |
| 1231 | }; | 1261 | }; |
| 1232 | }, | 1262 | }, |
| ... | @@ -1993,12 +2023,9 @@ fn walkInstruction( | ... | @@ -1993,12 +2023,9 @@ fn walkInstruction( |
| 1993 | }, | 2023 | }, |
| 1994 | .decl_val, .decl_ref => { | 2024 | .decl_val, .decl_ref => { |
| 1995 | const str_tok = data[inst_index].str_tok; | 2025 | const str_tok = data[inst_index].str_tok; |
| 1996 | const decls_slot_index = parent_scope.resolveDeclName(str_tok.start); | 2026 | const decl_status = parent_scope.resolveDeclName(str_tok.start, file, inst_index); |
| 1997 | // While it would make sense to grab the original decl's typeRef info, | | |
| 1998 | // that decl might not have been analyzed yet! The frontend will have | | |
| 1999 | // to navigate through all declRefs to find the underlying type. | | |
| 2000 | return DocData.WalkResult{ | 2027 | return DocData.WalkResult{ |
| 2001 | .expr = .{ .declRef = decls_slot_index }, | 2028 | .expr = .{ .declRef = decl_status }, |
| 2002 | }; | 2029 | }; |
| 2003 | }, | 2030 | }, |
| 2004 | .field_val, .field_call_bind, .field_ptr, .field_type => { | 2031 | .field_val, .field_call_bind, .field_ptr, .field_type => { |
| ... | @@ -2430,49 +2457,16 @@ fn walkInstruction( | ... | @@ -2430,49 +2457,16 @@ fn walkInstruction( |
| 2430 | else | 2457 | else |
| 2431 | parent_src; | 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 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2460 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2440 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2461 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2441 | | 2462 | |
| 2442 | const decls_first_index = self.decls.items.len; | 2463 | extra_index = try self.analyzeAllDecls( |
| 2443 | // Decl name lookahead for reserving slots in `scope` (and `decls`). | | |
| 2444 | // Done to make sure that all decl refs can be resolved correctly, | | |
| 2445 | // even if we haven't fully analyzed the decl yet. | | |
| 2446 | { | | |
| 2447 | var it = file.zir.declIterator(@intCast(u32, inst_index)); | | |
| 2448 | while (it.next()) |d| { | | |
| 2449 | const decl_name_index = file.zir.extra[d.sub_index + 5]; | | |
| 2450 | switch (decl_name_index) { | | |
| 2451 | 0, 1, 2 => continue, | | |
| 2452 | else => if (file.zir.string_bytes[decl_name_index] == 0) { | | |
| 2453 | continue; | | |
| 2454 | }, | | |
| 2455 | } | | |
| 2456 | | | |
| 2457 | const decl_slot_index = self.decls.items.len; | | |
| 2458 | try self.decls.append(self.arena, undefined); | | |
| 2459 | self.decls.items[decl_slot_index]._analyzed = false; | | |
| 2460 | | | |
| 2461 | // TODO: inspect usingnamespace decls and unpack their contents! | | |
| 2462 | | | |
| 2463 | try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index); | | |
| 2464 | } | | |
| 2465 | } | | |
| 2466 | | | |
| 2467 | extra_index = try self.walkDecls( | | |
| 2468 | file, | 2464 | file, |
| 2469 | &scope, | 2465 | &scope, |
| | 2466 | inst_index, |
| 2470 | src_info, | 2467 | src_info, |
| 2471 | decls_first_index, | | |
| 2472 | decls_len, | | |
| 2473 | &decl_indexes, | 2468 | &decl_indexes, |
| 2474 | &priv_decl_indexes, | 2469 | &priv_decl_indexes, |
| 2475 | extra_index, | | |
| 2476 | ); | 2470 | ); |
| 2477 | | 2471 | |
| 2478 | self.types.items[type_slot_index] = .{ | 2472 | self.types.items[type_slot_index] = .{ |
| ... | @@ -2549,13 +2543,14 @@ fn walkInstruction( | ... | @@ -2549,13 +2543,14 @@ fn walkInstruction( |
| 2549 | else | 2543 | else |
| 2550 | parent_src; | 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 | const tag_type = file.zir.extra[extra_index]; | 2549 | const tag_type = file.zir.extra[extra_index]; |
| 2554 | extra_index += 1; | 2550 | extra_index += 1; |
| 2555 | const tag_ref = @intToEnum(Ref, tag_type); | 2551 | const tag_ref = @intToEnum(Ref, tag_type); |
| 2556 | const wr = try self.walkRef(file, parent_scope, parent_src, tag_ref, false); | 2552 | break :blk tag_ref; |
| 2557 | break :blk wr.expr; | 2553 | } else .none; |
| 2558 | } else null; | | |
| 2559 | | 2554 | |
| 2560 | const body_len = if (small.has_body_len) blk: { | 2555 | const body_len = if (small.has_body_len) blk: { |
| 2561 | const body_len = file.zir.extra[extra_index]; | 2556 | const body_len = file.zir.extra[extra_index]; |
| ... | @@ -2569,51 +2564,28 @@ fn walkInstruction( | ... | @@ -2569,51 +2564,28 @@ fn walkInstruction( |
| 2569 | break :blk fields_len; | 2564 | break :blk fields_len; |
| 2570 | } else 0; | 2565 | } else 0; |
| 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 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2567 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2579 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2568 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2580 | | 2569 | |
| 2581 | const decls_first_index = self.decls.items.len; | 2570 | extra_index = try self.analyzeAllDecls( |
| 2582 | // Decl name lookahead for reserving slots in `scope` (and `decls`). | | |
| 2583 | // Done to make sure that all decl refs can be resolved correctly, | | |
| 2584 | // even if we haven't fully analyzed the decl yet. | | |
| 2585 | { | | |
| 2586 | var it = file.zir.declIterator(@intCast(u32, inst_index)); | | |
| 2587 | while (it.next()) |d| { | | |
| 2588 | const decl_name_index = file.zir.extra[d.sub_index + 5]; | | |
| 2589 | switch (decl_name_index) { | | |
| 2590 | 0, 1, 2 => continue, | | |
| 2591 | else => if (file.zir.string_bytes[decl_name_index] == 0) { | | |
| 2592 | continue; | | |
| 2593 | }, | | |
| 2594 | } | | |
| 2595 | | | |
| 2596 | const decl_slot_index = self.decls.items.len; | | |
| 2597 | try self.decls.append(self.arena, undefined); | | |
| 2598 | self.decls.items[decl_slot_index]._analyzed = false; | | |
| 2599 | | | |
| 2600 | // TODO: inspect usingnamespace decls and unpack their contents! | | |
| 2601 | | | |
| 2602 | try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index); | | |
| 2603 | } | | |
| 2604 | } | | |
| 2605 | | | |
| 2606 | extra_index = try self.walkDecls( | | |
| 2607 | file, | 2571 | file, |
| 2608 | &scope, | 2572 | &scope, |
| | 2573 | inst_index, |
| 2609 | src_info, | 2574 | src_info, |
| 2610 | decls_first_index, | | |
| 2611 | decls_len, | | |
| 2612 | &decl_indexes, | 2575 | &decl_indexes, |
| 2613 | &priv_decl_indexes, | 2576 | &priv_decl_indexes, |
| 2614 | extra_index, | | |
| 2615 | ); | 2577 | ); |
| 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 | extra_index += body_len; | 2589 | extra_index += body_len; |
| 2618 | | 2590 | |
| 2619 | var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( | 2591 | var field_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity( |
| ... | @@ -2643,7 +2615,7 @@ fn walkInstruction( | ... | @@ -2643,7 +2615,7 @@ fn walkInstruction( |
| 2643 | .privDecls = priv_decl_indexes.items, | 2615 | .privDecls = priv_decl_indexes.items, |
| 2644 | .pubDecls = decl_indexes.items, | 2616 | .pubDecls = decl_indexes.items, |
| 2645 | .fields = field_type_refs.items, | 2617 | .fields = field_type_refs.items, |
| 2646 | .tag = tag_type, | 2618 | .tag = tag_type.expr, |
| 2647 | .auto_enum = small.auto_enum_tag, | 2619 | .auto_enum = small.auto_enum_tag, |
| 2648 | }, | 2620 | }, |
| 2649 | }; | 2621 | }; |
| ... | @@ -2711,49 +2683,16 @@ fn walkInstruction( | ... | @@ -2711,49 +2683,16 @@ fn walkInstruction( |
| 2711 | break :blk fields_len; | 2683 | break :blk fields_len; |
| 2712 | } else 0; | 2684 | } else 0; |
| 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 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2686 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2721 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2687 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2722 | | 2688 | |
| 2723 | const decls_first_index = self.decls.items.len; | 2689 | extra_index = try self.analyzeAllDecls( |
| 2724 | // Decl name lookahead for reserving slots in `scope` (and `decls`). | | |
| 2725 | // Done to make sure that all decl refs can be resolved correctly, | | |
| 2726 | // even if we haven't fully analyzed the decl yet. | | |
| 2727 | { | | |
| 2728 | var it = file.zir.declIterator(@intCast(u32, inst_index)); | | |
| 2729 | while (it.next()) |d| { | | |
| 2730 | const decl_name_index = file.zir.extra[d.sub_index + 5]; | | |
| 2731 | switch (decl_name_index) { | | |
| 2732 | 0, 1, 2 => continue, | | |
| 2733 | else => if (file.zir.string_bytes[decl_name_index] == 0) { | | |
| 2734 | continue; | | |
| 2735 | }, | | |
| 2736 | } | | |
| 2737 | | | |
| 2738 | const decl_slot_index = self.decls.items.len; | | |
| 2739 | try self.decls.append(self.arena, undefined); | | |
| 2740 | self.decls.items[decl_slot_index]._analyzed = false; | | |
| 2741 | | | |
| 2742 | // TODO: inspect usingnamespace decls and unpack their contents! | | |
| 2743 | | | |
| 2744 | try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index); | | |
| 2745 | } | | |
| 2746 | } | | |
| 2747 | | | |
| 2748 | extra_index = try self.walkDecls( | | |
| 2749 | file, | 2690 | file, |
| 2750 | &scope, | 2691 | &scope, |
| | 2692 | inst_index, |
| 2751 | src_info, | 2693 | src_info, |
| 2752 | decls_first_index, | | |
| 2753 | decls_len, | | |
| 2754 | &decl_indexes, | 2694 | &decl_indexes, |
| 2755 | &priv_decl_indexes, | 2695 | &priv_decl_indexes, |
| 2756 | extra_index, | | |
| 2757 | ); | 2696 | ); |
| 2758 | | 2697 | |
| 2759 | // const body = file.zir.extra[extra_index..][0..body_len]; | 2698 | // const body = file.zir.extra[extra_index..][0..body_len]; |
| ... | @@ -2862,12 +2801,6 @@ fn walkInstruction( | ... | @@ -2862,12 +2801,6 @@ fn walkInstruction( |
| 2862 | break :blk fields_len; | 2801 | break :blk fields_len; |
| 2863 | } else 0; | 2802 | } else 0; |
| 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 | // TODO: Expose explicit backing integer types in some way. | 2804 | // TODO: Expose explicit backing integer types in some way. |
| 2872 | if (small.has_backing_int) { | 2805 | if (small.has_backing_int) { |
| 2873 | const backing_int_body_len = file.zir.extra[extra_index]; | 2806 | const backing_int_body_len = file.zir.extra[extra_index]; |
| ... | @@ -2882,40 +2815,13 @@ fn walkInstruction( | ... | @@ -2882,40 +2815,13 @@ fn walkInstruction( |
| 2882 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2815 | var decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2883 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; | 2816 | var priv_decl_indexes: std.ArrayListUnmanaged(usize) = .{}; |
| 2884 | | 2817 | |
| 2885 | const decls_first_index = self.decls.items.len; | 2818 | extra_index = try self.analyzeAllDecls( |
| 2886 | // Decl name lookahead for reserving slots in `scope` (and `decls`). | | |
| 2887 | // Done to make sure that all decl refs can be resolved correctly, | | |
| 2888 | // even if we haven't fully analyzed the decl yet. | | |
| 2889 | { | | |
| 2890 | var it = file.zir.declIterator(@intCast(u32, inst_index)); | | |
| 2891 | while (it.next()) |d| { | | |
| 2892 | const decl_name_index = file.zir.extra[d.sub_index + 5]; | | |
| 2893 | switch (decl_name_index) { | | |
| 2894 | 0, 1, 2 => continue, | | |
| 2895 | else => if (file.zir.string_bytes[decl_name_index] == 0) { | | |
| 2896 | continue; | | |
| 2897 | }, | | |
| 2898 | } | | |
| 2899 | | | |
| 2900 | const decl_slot_index = self.decls.items.len; | | |
| 2901 | try self.decls.append(self.arena, undefined); | | |
| 2902 | self.decls.items[decl_slot_index]._analyzed = false; | | |
| 2903 | | | |
| 2904 | // TODO: inspect usingnamespace decls and unpack their contents! | | |
| 2905 | | | |
| 2906 | try scope.insertDeclRef(self.arena, decl_name_index, decl_slot_index); | | |
| 2907 | } | | |
| 2908 | } | | |
| 2909 | | | |
| 2910 | extra_index = try self.walkDecls( | | |
| 2911 | file, | 2819 | file, |
| 2912 | &scope, | 2820 | &scope, |
| | 2821 | inst_index, |
| 2913 | src_info, | 2822 | src_info, |
| 2914 | decls_first_index, | | |
| 2915 | decls_len, | | |
| 2916 | &decl_indexes, | 2823 | &decl_indexes, |
| 2917 | &priv_decl_indexes, | 2824 | &priv_decl_indexes, |
| 2918 | extra_index, | | |
| 2919 | ); | 2825 | ); |
| 2920 | | 2826 | |
| 2921 | var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{}; | 2827 | var field_type_refs: std.ArrayListUnmanaged(DocData.Expr) = .{}; |
| ... | @@ -3096,189 +3002,286 @@ fn walkInstruction( | ... | @@ -3096,189 +3002,286 @@ fn walkInstruction( |
| 3096 | /// Does not append to `self.decls` directly because `walkInstruction` | 3002 | /// Does not append to `self.decls` directly because `walkInstruction` |
| 3097 | /// is expected to look-ahead scan all decls and reserve `body_len` | 3003 | /// is expected to look-ahead scan all decls and reserve `body_len` |
| 3098 | /// slots in `self.decls`, which are then filled out by this function. | 3004 | /// slots in `self.decls`, which are then filled out by this function. |
| 3099 | fn walkDecls( | 3005 | fn analyzeAllDecls( |
| 3100 | self: *Autodoc, | 3006 | self: *Autodoc, |
| 3101 | file: *File, | 3007 | file: *File, |
| 3102 | scope: *Scope, | 3008 | scope: *Scope, |
| | 3009 | parent_inst_index: usize, |
| 3103 | parent_src: SrcLocInfo, | 3010 | parent_src: SrcLocInfo, |
| 3104 | decls_first_index: usize, | | |
| 3105 | decls_len: usize, | | |
| 3106 | decl_indexes: *std.ArrayListUnmanaged(usize), | 3011 | decl_indexes: *std.ArrayListUnmanaged(usize), |
| 3107 | priv_decl_indexes: *std.ArrayListUnmanaged(usize), | 3012 | priv_decl_indexes: *std.ArrayListUnmanaged(usize), |
| 3108 | extra_start: usize, | | |
| 3109 | ) AutodocErrors!usize { | 3013 | ) AutodocErrors!usize { |
| 3110 | const data = file.zir.instructions.items(.data); | 3014 | const first_decl_indexes_slot = decl_indexes.items.len; |
| 3111 | const bit_bags_count = std.math.divCeil(usize, decls_len, 8) catch unreachable; | 3015 | const original_it = file.zir.declIterator(@intCast(u32, parent_inst_index)); |
| 3112 | var extra_index = extra_start + bit_bags_count; | | |
| 3113 | var bit_bag_index: usize = extra_start; | | |
| 3114 | var cur_bit_bag: u32 = undefined; | | |
| 3115 | var decl_i: u32 = 0; | | |
| 3116 | | 3016 | |
| 3117 | // NOTE: we're not outputting every ZIR decl as a Autodoc decl. | 3017 | // First loop to discover decl names |
| 3118 | // tests, comptime blocks and usingnamespace are skipped. | 3018 | { |
| 3119 | // this is why we `need good_decls_i`. | 3019 | var it = original_it; |
| 3120 | var good_decls_i: usize = 0; | 3020 | while (it.next()) |d| { |
| 3121 | while (decl_i < decls_len) : (decl_i += 1) { | 3021 | const decl_name_index = file.zir.extra[d.sub_index + 5]; |
| 3122 | const decls_slot_index = decls_first_index + good_decls_i; | 3022 | switch (decl_name_index) { |
| | 3023 | 0, 1, 2 => continue, |
| | 3024 | else => if (file.zir.string_bytes[decl_name_index] == 0) { |
| | 3025 | continue; |
| | 3026 | }, |
| | 3027 | } |
| 3123 | | 3028 | |
| 3124 | if (decl_i % 8 == 0) { | 3029 | try scope.insertDeclRef(self.arena, decl_name_index, .Pending); |
| 3125 | cur_bit_bag = file.zir.extra[bit_bag_index]; | | |
| 3126 | bit_bag_index += 1; | | |
| 3127 | } | 3030 | } |
| 3128 | const is_pub = @truncate(u1, cur_bit_bag) != 0; | 3031 | } |
| 3129 | cur_bit_bag >>= 1; | | |
| 3130 | const is_exported = @truncate(u1, cur_bit_bag) != 0; | | |
| 3131 | _ = is_exported; | | |
| 3132 | cur_bit_bag >>= 1; | | |
| 3133 | const has_align = @truncate(u1, cur_bit_bag) != 0; | | |
| 3134 | cur_bit_bag >>= 1; | | |
| 3135 | const has_section_or_addrspace = @truncate(u1, cur_bit_bag) != 0; | | |
| 3136 | cur_bit_bag >>= 1; | | |
| 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]; | 3061 | // Third loop to analyze all remaining decls |
| 3141 | extra_index += 4; | 3062 | var it = original_it; |
| | 3063 | while (it.next()) |d| { |
| | 3064 | const decl_name_index = file.zir.extra[d.sub_index + 5]; |
| | 3065 | switch (decl_name_index) { |
| | 3066 | 0, 1, 2 => continue, // skip over usingnamespace decls |
| | 3067 | else => if (file.zir.string_bytes[decl_name_index] == 0) { |
| | 3068 | continue; |
| | 3069 | }, |
| | 3070 | } |
| 3142 | | 3071 | |
| 3143 | // const line = file.zir.extra[extra_index]; | 3072 | try self.analyzeDecl( |
| 3144 | extra_index += 1; | 3073 | file, |
| 3145 | const decl_name_index = file.zir.extra[extra_index]; | 3074 | scope, |
| 3146 | extra_index += 1; | 3075 | parent_src, |
| 3147 | const value_index = file.zir.extra[extra_index]; | 3076 | decl_indexes, |
| 3148 | extra_index += 1; | 3077 | priv_decl_indexes, |
| 3149 | const doc_comment_index = file.zir.extra[extra_index]; | 3078 | d, |
| 3150 | extra_index += 1; | 3079 | ); |
| | 3080 | } |
| 3151 | | 3081 | |
| 3152 | const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { | 3082 | return it.extra_index; |
| 3153 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); | 3083 | } |
| 3154 | extra_index += 1; | | |
| 3155 | break :inst inst; | | |
| 3156 | }; | | |
| 3157 | _ = align_inst; | | |
| 3158 | | 3084 | |
| 3159 | const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { | 3085 | // Asserts the given decl is public |
| 3160 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); | 3086 | fn analyzeDecl( |
| 3161 | extra_index += 1; | 3087 | self: *Autodoc, |
| 3162 | break :inst inst; | 3088 | file: *File, |
| 3163 | }; | 3089 | scope: *Scope, |
| 3164 | _ = section_inst; | 3090 | parent_src: SrcLocInfo, |
| | 3091 | decl_indexes: *std.ArrayListUnmanaged(usize), |
| | 3092 | priv_decl_indexes: *std.ArrayListUnmanaged(usize), |
| | 3093 | d: Zir.DeclIterator.Item, |
| | 3094 | ) AutodocErrors!void { |
| | 3095 | const data = file.zir.instructions.items(.data); |
| | 3096 | const is_pub = @truncate(u1, d.flags >> 0) != 0; |
| | 3097 | // const is_exported = @truncate(u1, d.flags >> 1) != 0; |
| | 3098 | const has_align = @truncate(u1, d.flags >> 2) != 0; |
| | 3099 | const has_section_or_addrspace = @truncate(u1, d.flags >> 3) != 0; |
| 3165 | | 3100 | |
| 3166 | const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { | 3101 | var extra_index = d.sub_index; |
| 3167 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); | 3102 | // const hash_u32s = file.zir.extra[extra_index..][0..4]; |
| 3168 | extra_index += 1; | | |
| 3169 | break :inst inst; | | |
| 3170 | }; | | |
| 3171 | _ = addrspace_inst; | | |
| 3172 | | 3103 | |
| 3173 | // This is known to work because decl values are always block_inlines | 3104 | extra_index += 4; |
| 3174 | const value_pl_node = data[value_index].pl_node; | 3105 | // const line = file.zir.extra[extra_index]; |
| 3175 | const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src); | | |
| 3176 | | 3106 | |
| 3177 | const name: []const u8 = switch (decl_name_index) { | 3107 | extra_index += 1; |
| 3178 | 0, 1 => continue, // comptime or usingnamespace decl | 3108 | const decl_name_index = file.zir.extra[extra_index]; |
| 3179 | 2 => { | | |
| 3180 | // decl test | | |
| 3181 | const decl_being_tested = scope.resolveDeclName(doc_comment_index); | | |
| 3182 | const func_index = getBlockInlineBreak(file.zir, value_index).?; | | |
| 3183 | | 3109 | |
| 3184 | const pl_node = data[Zir.refToIndex(func_index).?].pl_node; | 3110 | extra_index += 1; |
| 3185 | const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src); | 3111 | const value_index = file.zir.extra[extra_index]; |
| 3186 | const tree = try file.getTree(self.module.gpa); | | |
| 3187 | const test_source_code = tree.getNodeSource(fn_src.src_node); | | |
| 3188 | | 3112 | |
| 3189 | const ast_node_index = self.ast_nodes.items.len; | 3113 | extra_index += 1; |
| 3190 | try self.ast_nodes.append(self.arena, .{ | 3114 | const doc_comment_index = file.zir.extra[extra_index]; |
| 3191 | .file = 0, | | |
| 3192 | .line = 0, | | |
| 3193 | .col = 0, | | |
| 3194 | .code = test_source_code, | | |
| 3195 | }); | | |
| 3196 | self.decls.items[decl_being_tested].decltest = ast_node_index; | | |
| 3197 | continue; | | |
| 3198 | }, | | |
| 3199 | else => blk: { | | |
| 3200 | if (file.zir.string_bytes[decl_name_index] == 0) { | | |
| 3201 | // test decl | | |
| 3202 | continue; | | |
| 3203 | } | | |
| 3204 | break :blk file.zir.nullTerminatedString(decl_name_index); | | |
| 3205 | }, | | |
| 3206 | }; | | |
| 3207 | | 3115 | |
| 3208 | // If we got here, it means that this decl is not a test, usingnamespace | 3116 | extra_index += 1; |
| 3209 | // or a comptime block decl. | 3117 | const align_inst: Zir.Inst.Ref = if (!has_align) .none else inst: { |
| 3210 | good_decls_i += 1; | 3118 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); |
| | 3119 | extra_index += 1; |
| | 3120 | break :inst inst; |
| | 3121 | }; |
| | 3122 | _ = align_inst; |
| 3211 | | 3123 | |
| 3212 | const doc_comment: ?[]const u8 = if (doc_comment_index != 0) | 3124 | const section_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { |
| 3213 | file.zir.nullTerminatedString(doc_comment_index) | 3125 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); |
| 3214 | else | 3126 | extra_index += 1; |
| 3215 | null; | 3127 | break :inst inst; |
| | 3128 | }; |
| | 3129 | _ = section_inst; |
| 3216 | | 3130 | |
| 3217 | // astnode | 3131 | const addrspace_inst: Zir.Inst.Ref = if (!has_section_or_addrspace) .none else inst: { |
| 3218 | const ast_node_index = idx: { | 3132 | const inst = @intToEnum(Zir.Inst.Ref, file.zir.extra[extra_index]); |
| 3219 | const idx = self.ast_nodes.items.len; | 3133 | extra_index += 1; |
| 3220 | try self.ast_nodes.append(self.arena, .{ | 3134 | break :inst inst; |
| 3221 | .file = self.files.getIndex(file).?, | 3135 | }; |
| 3222 | .line = decl_src.line, | 3136 | _ = addrspace_inst; |
| 3223 | .col = 0, | 3137 | |
| 3224 | .docs = doc_comment, | 3138 | // This is known to work because decl values are always block_inlines |
| 3225 | .fields = null, // walkInstruction will fill `fields` if necessary | 3139 | const value_pl_node = data[value_index].pl_node; |
| 3226 | }); | 3140 | const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src); |
| 3227 | break :idx idx; | 3141 | |
| 3228 | }; | 3142 | const name: []const u8 = switch (decl_name_index) { |
| | 3143 | 0, 1 => unreachable, // comptime or usingnamespace decl |
| | 3144 | 2 => { |
| | 3145 | unreachable; |
| | 3146 | // decl test |
| | 3147 | // const decl_status = scope.resolveDeclName(doc_comment_index); |
| | 3148 | // const decl_being_tested = decl_status.Analyzed; |
| | 3149 | // const func_index = getBlockInlineBreak(file.zir, value_index).?; |
| | 3150 | |
| | 3151 | // const pl_node = data[Zir.refToIndex(func_index).?].pl_node; |
| | 3152 | // const fn_src = try self.srcLocInfo(file, pl_node.src_node, decl_src); |
| | 3153 | // const tree = try file.getTree(self.module.gpa); |
| | 3154 | // const test_source_code = tree.getNodeSource(fn_src.src_node); |
| | 3155 | |
| | 3156 | // const ast_node_index = self.ast_nodes.items.len; |
| | 3157 | // try self.ast_nodes.append(self.arena, .{ |
| | 3158 | // .file = 0, |
| | 3159 | // .line = 0, |
| | 3160 | // .col = 0, |
| | 3161 | // .code = test_source_code, |
| | 3162 | // }); |
| | 3163 | // self.decls.items[decl_being_tested].decltest = ast_node_index; |
| | 3164 | // continue; |
| | 3165 | }, |
| | 3166 | else => blk: { |
| | 3167 | if (file.zir.string_bytes[decl_name_index] == 0) { |
| | 3168 | // test decl |
| | 3169 | unreachable; |
| | 3170 | } |
| | 3171 | break :blk file.zir.nullTerminatedString(decl_name_index); |
| | 3172 | }, |
| | 3173 | }; |
| 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) { | 3193 | const walk_result = try self.walkInstruction(file, scope, decl_src, value_index, true); |
| 3233 | try decl_indexes.append(self.arena, decls_slot_index); | | |
| 3234 | } else { | | |
| 3235 | try priv_decl_indexes.append(self.arena, decls_slot_index); | | |
| 3236 | } | | |
| 3237 | | 3194 | |
| 3238 | // // decl.typeRef == decl.val...typeRef | 3195 | const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const"; |
| 3239 | // const decl_type_ref: DocData.TypeRef = switch (walk_result) { | | |
| 3240 | // .int => |i| i.typeRef, | | |
| 3241 | // .void => .{ .type = @enumToInt(Ref.void_type) }, | | |
| 3242 | // .@"undefined", .@"null" => |v| v, | | |
| 3243 | // .@"unreachable" => .{ .type = @enumToInt(Ref.noreturn_type) }, | | |
| 3244 | // .@"struct" => |s| s.typeRef, | | |
| 3245 | // .bool => .{ .type = @enumToInt(Ref.bool_type) }, | | |
| 3246 | // .type => .{ .type = @enumToInt(Ref.type_type) }, | | |
| 3247 | // // this last case is special becauese it's not pointing | | |
| 3248 | // // at the type of the value, but rather at the value itself | | |
| 3249 | // // the js better be aware ot this! | | |
| 3250 | // .declRef => |d| .{ .declRef = d }, | | |
| 3251 | // }; | | |
| 3252 | | | |
| 3253 | const kind: []const u8 = if (try self.declIsVar(file, value_pl_node.src_node, parent_src)) "var" else "const"; | | |
| 3254 | | | |
| 3255 | self.decls.items[decls_slot_index] = .{ | | |
| 3256 | ._analyzed = true, | | |
| 3257 | .name = name, | | |
| 3258 | .src = ast_node_index, | | |
| 3259 | //.typeRef = decl_type_ref, | | |
| 3260 | .value = walk_result, | | |
| 3261 | .kind = kind, | | |
| 3262 | }; | | |
| 3263 | | 3196 | |
| 3264 | // Unblock any pending decl path that was waiting for this decl. | 3197 | const decls_slot_index = self.decls.items.len; |
| 3265 | if (self.ref_paths_pending_on_decls.get(decls_slot_index)) |paths| { | 3198 | try self.decls.append(self.arena, .{ |
| 3266 | for (paths.items) |resume_info| { | 3199 | .name = name, |
| 3267 | try self.tryResolveRefPath( | 3200 | .src = ast_node_index, |
| 3268 | resume_info.file, | 3201 | .value = walk_result, |
| 3269 | value_index, | 3202 | .kind = kind, |
| 3270 | resume_info.ref_path, | 3203 | }); |
| 3271 | ); | 3204 | |
| 3272 | } | 3205 | if (is_pub) { |
| | 3206 | try decl_indexes.append(self.arena, decls_slot_index); |
| | 3207 | } else { |
| | 3208 | try priv_decl_indexes.append(self.arena, decls_slot_index); |
| | 3209 | } |
| 3273 | | 3210 | |
| 3274 | _ = self.ref_paths_pending_on_decls.remove(decls_slot_index); | 3211 | const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, 0); |
| 3275 | // TODO: we should deallocate the arraylist that holds all the | 3212 | std.debug.assert(decl_status_ptr.* == .Pending); |
| 3276 | // ref paths. not doing it now since it's arena-allocated | 3213 | decl_status_ptr.* = .{ .Analyzed = decls_slot_index }; |
| 3277 | // anyway, but maybe we should put it elsewhere. | 3214 | |
| | 3215 | // Unblock any pending decl path that was waiting for this decl. |
| | 3216 | if (self.ref_paths_pending_on_decls.get(decl_status_ptr)) |paths| { |
| | 3217 | for (paths.items) |resume_info| { |
| | 3218 | try self.tryResolveRefPath( |
| | 3219 | resume_info.file, |
| | 3220 | value_index, |
| | 3221 | resume_info.ref_path, |
| | 3222 | ); |
| 3278 | } | 3223 | } |
| | 3224 | |
| | 3225 | _ = self.ref_paths_pending_on_decls.remove(decl_status_ptr); |
| | 3226 | // TODO: we should deallocate the arraylist that holds all the |
| | 3227 | // ref paths. not doing it now since it's arena-allocated |
| | 3228 | // anyway, but maybe we should put it elsewhere. |
| 3279 | } | 3229 | } |
| | 3230 | } |
| 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 | /// An unresolved path has a non-string WalkResult at its beginnig, while every | 3287 | /// An unresolved path has a non-string WalkResult at its beginnig, while every |
| ... | @@ -3290,7 +3293,7 @@ fn walkDecls( | ... | @@ -3290,7 +3293,7 @@ fn walkDecls( |
| 3290 | /// Same happens when a decl holds a type definition that hasn't been fully | 3293 | /// Same happens when a decl holds a type definition that hasn't been fully |
| 3291 | /// analyzed yet (except that we append to `self.ref_paths_pending_on_types`. | 3294 | /// analyzed yet (except that we append to `self.ref_paths_pending_on_types`. |
| 3292 | /// | 3295 | /// |
| 3293 | /// When walkDecls / walkInstruction finishes analyzing a decl / type, it will | 3296 | /// When analyzeAllDecls / walkInstruction finishes analyzing a decl / type, it will |
| 3294 | /// then check if there's any pending ref path blocked on it and, if any, it | 3297 | /// then check if there's any pending ref path blocked on it and, if any, it |
| 3295 | /// will progress their resolution by calling tryResolveRefPath again. | 3298 | /// will progress their resolution by calling tryResolveRefPath again. |
| 3296 | /// | 3299 | /// |
| ... | @@ -3318,37 +3321,51 @@ fn tryResolveRefPath( | ... | @@ -3318,37 +3321,51 @@ fn tryResolveRefPath( |
| 3318 | switch (resolved_parent) { | 3321 | switch (resolved_parent) { |
| 3319 | else => break, | 3322 | else => break, |
| 3320 | .this => |t| resolved_parent = .{ .type = t }, | 3323 | .this => |t| resolved_parent = .{ .type = t }, |
| 3321 | .declRef => |decl_index| { | 3324 | .declIndex => |decl_index| { |
| 3322 | const decl = self.decls.items[decl_index]; | 3325 | const decl = self.decls.items[decl_index]; |
| 3323 | if (decl._analyzed) { | 3326 | resolved_parent = decl.value.expr; |
| 3324 | resolved_parent = decl.value.expr; | 3327 | continue; |
| 3325 | continue; | 3328 | }, |
| 3326 | } | 3329 | .declRef => |decl_status_ptr| { |
| 3327 | | 3330 | // NOTE: must be kep in sync with `findNameInUnsDecls` |
| 3328 | // This decl path is pending completion | 3331 | switch (decl_status_ptr.*) { |
| 3329 | { | 3332 | // The use of unreachable here is conservative. |
| 3330 | const res = try self.pending_ref_paths.getOrPut( | 3333 | // It might be that it truly should be up to us to |
| 3331 | self.arena, | 3334 | // request the analys of this decl, but it's not clear |
| 3332 | &path[path.len - 1], | 3335 | // at the moment of writing. |
| 3333 | ); | 3336 | .NotRequested => unreachable, |
| 3334 | if (!res.found_existing) res.value_ptr.* = .{}; | 3337 | .Analyzed => |decl_index| { |
| 3335 | } | 3338 | const decl = self.decls.items[decl_index]; |
| | 3339 | resolved_parent = decl.value.expr; |
| | 3340 | continue; |
| | 3341 | }, |
| | 3342 | .Pending => { |
| | 3343 | // This decl path is pending completion |
| | 3344 | { |
| | 3345 | const res = try self.pending_ref_paths.getOrPut( |
| | 3346 | self.arena, |
| | 3347 | &path[path.len - 1], |
| | 3348 | ); |
| | 3349 | if (!res.found_existing) res.value_ptr.* = .{}; |
| | 3350 | } |
| 3336 | | 3351 | |
| 3337 | const res = try self.ref_paths_pending_on_decls.getOrPut( | 3352 | const res = try self.ref_paths_pending_on_decls.getOrPut( |
| 3338 | self.arena, | 3353 | self.arena, |
| 3339 | decl_index, | 3354 | decl_status_ptr, |
| 3340 | ); | 3355 | ); |
| 3341 | if (!res.found_existing) res.value_ptr.* = .{}; | 3356 | if (!res.found_existing) res.value_ptr.* = .{}; |
| 3342 | try res.value_ptr.*.append(self.arena, .{ | 3357 | try res.value_ptr.*.append(self.arena, .{ |
| 3343 | .file = file, | 3358 | .file = file, |
| 3344 | .ref_path = path[i..path.len], | 3359 | .ref_path = path[i..path.len], |
| 3345 | }); | 3360 | }); |
| 3346 | | 3361 | |
| 3347 | // We return instead doing `break :outer` to prevent the | 3362 | // We return instead doing `break :outer` to prevent the |
| 3348 | // code after the :outer while loop to run, as it assumes | 3363 | // code after the :outer while loop to run, as it assumes |
| 3349 | // that the path will have been fully analyzed (or we | 3364 | // that the path will have been fully analyzed (or we |
| 3350 | // have given up because of a comptimeExpr). | 3365 | // have given up because of a comptimeExpr). |
| 3351 | return; | 3366 | return; |
| | 3367 | }, |
| | 3368 | } |
| 3352 | }, | 3369 | }, |
| 3353 | .refPath => |rp| { | 3370 | .refPath => |rp| { |
| 3354 | if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| { | 3371 | if (self.pending_ref_paths.getPtr(&rp[rp.len - 1])) |waiter_list| { |
| ... | @@ -3388,7 +3405,7 @@ fn tryResolveRefPath( | ... | @@ -3388,7 +3405,7 @@ fn tryResolveRefPath( |
| 3388 | panicWithContext( | 3405 | panicWithContext( |
| 3389 | file, | 3406 | file, |
| 3390 | inst_index, | 3407 | inst_index, |
| 3391 | "exhausted eval quota for `{}`in tryResolveDecl\n", | 3408 | "exhausted eval quota for `{}`in tryResolveRefPath\n", |
| 3392 | .{resolved_parent}, | 3409 | .{resolved_parent}, |
| 3393 | ); | 3410 | ); |
| 3394 | } | 3411 | } |
| ... | @@ -3461,26 +3478,39 @@ fn tryResolveRefPath( | ... | @@ -3461,26 +3478,39 @@ fn tryResolveRefPath( |
| 3461 | ); | 3478 | ); |
| 3462 | } | 3479 | } |
| 3463 | }, | 3480 | }, |
| 3464 | .Enum => |t_enum| { | 3481 | // TODO: the following searches could probably |
| 3465 | for (t_enum.pubDecls) |d| { | 3482 | // be performed more efficiently on the corresponding |
| 3466 | // TODO: this could be improved a lot | 3483 | // scope |
| 3467 | // by having our own string table! | 3484 | .Enum => |t_enum| { // foo.bar.baz |
| 3468 | const decl = self.decls.items[d]; | 3485 | // Look into locally-defined pub decls |
| 3469 | if (std.mem.eql(u8, decl.name, child_string)) { | 3486 | for (t_enum.pubDecls) |idx| { |
| 3470 | path[i + 1] = .{ .declRef = d }; | 3487 | const d = self.decls.items[idx]; |
| | 3488 | if (d.is_uns) continue; |
| | 3489 | if (std.mem.eql(u8, d.name, child_string)) { |
| | 3490 | path[i + 1] = .{ .declIndex = idx }; |
| 3471 | continue :outer; | 3491 | continue :outer; |
| 3472 | } | 3492 | } |
| 3473 | } | 3493 | } |
| 3474 | for (t_enum.privDecls) |d| { | 3494 | |
| 3475 | // TODO: this could be improved a lot | 3495 | // Look into locally-defined priv decls |
| 3476 | // by having our own string table! | 3496 | for (t_enum.privDecls) |idx| { |
| 3477 | const decl = self.decls.items[d]; | 3497 | const d = self.decls.items[idx]; |
| 3478 | if (std.mem.eql(u8, decl.name, child_string)) { | 3498 | if (d.is_uns) continue; |
| 3479 | path[i + 1] = .{ .declRef = d }; | 3499 | if (std.mem.eql(u8, d.name, child_string)) { |
| | 3500 | path[i + 1] = .{ .declIndex = idx }; |
| 3480 | continue :outer; | 3501 | continue :outer; |
| 3481 | } | 3502 | } |
| 3482 | } | 3503 | } |
| 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 | for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| { | 3514 | for (self.ast_nodes.items[t_enum.src].fields.?, 0..) |ast_node, idx| { |
| 3485 | const name = self.ast_nodes.items[ast_node].name.?; | 3515 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3486 | if (std.mem.eql(u8, name, child_string)) { | 3516 | if (std.mem.eql(u8, name, child_string)) { |
| ... | @@ -3509,25 +3539,35 @@ fn tryResolveRefPath( | ... | @@ -3509,25 +3539,35 @@ fn tryResolveRefPath( |
| 3509 | continue :outer; | 3539 | continue :outer; |
| 3510 | }, | 3540 | }, |
| 3511 | .Union => |t_union| { | 3541 | .Union => |t_union| { |
| 3512 | for (t_union.pubDecls) |d| { | 3542 | // Look into locally-defined pub decls |
| 3513 | // TODO: this could be improved a lot | 3543 | for (t_union.pubDecls) |idx| { |
| 3514 | // by having our own string table! | 3544 | const d = self.decls.items[idx]; |
| 3515 | const decl = self.decls.items[d]; | 3545 | if (d.is_uns) continue; |
| 3516 | if (std.mem.eql(u8, decl.name, child_string)) { | 3546 | if (std.mem.eql(u8, d.name, child_string)) { |
| 3517 | path[i + 1] = .{ .declRef = d }; | 3547 | path[i + 1] = .{ .declIndex = idx }; |
| 3518 | continue :outer; | 3548 | continue :outer; |
| 3519 | } | 3549 | } |
| 3520 | } | 3550 | } |
| 3521 | for (t_union.privDecls) |d| { | 3551 | |
| 3522 | // TODO: this could be improved a lot | 3552 | // Look into locally-defined priv decls |
| 3523 | // by having our own string table! | 3553 | for (t_union.privDecls) |idx| { |
| 3524 | const decl = self.decls.items[d]; | 3554 | const d = self.decls.items[idx]; |
| 3525 | if (std.mem.eql(u8, decl.name, child_string)) { | 3555 | if (d.is_uns) continue; |
| 3526 | path[i + 1] = .{ .declRef = d }; | 3556 | if (std.mem.eql(u8, d.name, child_string)) { |
| | 3557 | path[i + 1] = .{ .declIndex = idx }; |
| 3527 | continue :outer; | 3558 | continue :outer; |
| 3528 | } | 3559 | } |
| 3529 | } | 3560 | } |
| 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 | for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| { | 3571 | for (self.ast_nodes.items[t_union.src].fields.?, 0..) |ast_node, idx| { |
| 3532 | const name = self.ast_nodes.items[ast_node].name.?; | 3572 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3533 | if (std.mem.eql(u8, name, child_string)) { | 3573 | if (std.mem.eql(u8, name, child_string)) { |
| ... | @@ -3556,25 +3596,35 @@ fn tryResolveRefPath( | ... | @@ -3556,25 +3596,35 @@ fn tryResolveRefPath( |
| 3556 | }, | 3596 | }, |
| 3557 | | 3597 | |
| 3558 | .Struct => |t_struct| { | 3598 | .Struct => |t_struct| { |
| 3559 | for (t_struct.pubDecls) |d| { | 3599 | // Look into locally-defined pub decls |
| 3560 | // TODO: this could be improved a lot | 3600 | for (t_struct.pubDecls) |idx| { |
| 3561 | // by having our own string table! | 3601 | const d = self.decls.items[idx]; |
| 3562 | const decl = self.decls.items[d]; | 3602 | if (d.is_uns) continue; |
| 3563 | if (std.mem.eql(u8, decl.name, child_string)) { | 3603 | if (std.mem.eql(u8, d.name, child_string)) { |
| 3564 | path[i + 1] = .{ .declRef = d }; | 3604 | path[i + 1] = .{ .declIndex = idx }; |
| 3565 | continue :outer; | 3605 | continue :outer; |
| 3566 | } | 3606 | } |
| 3567 | } | 3607 | } |
| 3568 | for (t_struct.privDecls) |d| { | 3608 | |
| 3569 | // TODO: this could be improved a lot | 3609 | // Look into locally-defined priv decls |
| 3570 | // by having our own string table! | 3610 | for (t_struct.privDecls) |idx| { |
| 3571 | const decl = self.decls.items[d]; | 3611 | const d = self.decls.items[idx]; |
| 3572 | if (std.mem.eql(u8, decl.name, child_string)) { | 3612 | if (d.is_uns) continue; |
| 3573 | path[i + 1] = .{ .declRef = d }; | 3613 | if (std.mem.eql(u8, d.name, child_string)) { |
| | 3614 | path[i + 1] = .{ .declIndex = idx }; |
| 3574 | continue :outer; | 3615 | continue :outer; |
| 3575 | } | 3616 | } |
| 3576 | } | 3617 | } |
| 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 | for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| { | 3628 | for (self.ast_nodes.items[t_struct.src].fields.?, 0..) |ast_node, idx| { |
| 3579 | const name = self.ast_nodes.items[ast_node].name.?; | 3629 | const name = self.ast_nodes.items[ast_node].name.?; |
| 3580 | if (std.mem.eql(u8, name, child_string)) { | 3630 | if (std.mem.eql(u8, name, child_string)) { |
| ... | @@ -3605,25 +3655,37 @@ fn tryResolveRefPath( | ... | @@ -3605,25 +3655,37 @@ fn tryResolveRefPath( |
| 3605 | continue :outer; | 3655 | continue :outer; |
| 3606 | }, | 3656 | }, |
| 3607 | .Opaque => |t_opaque| { | 3657 | .Opaque => |t_opaque| { |
| 3608 | for (t_opaque.pubDecls) |d| { | 3658 | // Look into locally-defined pub decls |
| 3609 | // TODO: this could be improved a lot | 3659 | for (t_opaque.pubDecls) |idx| { |
| 3610 | // by having our own string table! | 3660 | const d = self.decls.items[idx]; |
| 3611 | const decl = self.decls.items[d]; | 3661 | if (d.is_uns) continue; |
| 3612 | if (std.mem.eql(u8, decl.name, child_string)) { | 3662 | if (std.mem.eql(u8, d.name, child_string)) { |
| 3613 | path[i + 1] = .{ .declRef = d }; | 3663 | path[i + 1] = .{ .declIndex = idx }; |
| 3614 | continue :outer; | 3664 | continue :outer; |
| 3615 | } | 3665 | } |
| 3616 | } | 3666 | } |
| 3617 | for (t_opaque.privDecls) |d| { | 3667 | |
| 3618 | // TODO: this could be improved a lot | 3668 | // Look into locally-defined priv decls |
| 3619 | // by having our own string table! | 3669 | for (t_opaque.privDecls) |idx| { |
| 3620 | const decl = self.decls.items[d]; | 3670 | const d = self.decls.items[idx]; |
| 3621 | if (std.mem.eql(u8, decl.name, child_string)) { | 3671 | if (d.is_uns) continue; |
| 3622 | path[i + 1] = .{ .declRef = d }; | 3672 | if (std.mem.eql(u8, d.name, child_string)) { |
| | 3673 | path[i + 1] = .{ .declIndex = idx }; |
| 3623 | continue :outer; | 3674 | continue :outer; |
| 3624 | } | 3675 | } |
| 3625 | } | 3676 | } |
| 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 | // if we got here, our search failed | 3689 | // if we got here, our search failed |
| 3628 | printWithContext( | 3690 | printWithContext( |
| 3629 | file, | 3691 | file, |
| ... | @@ -3670,6 +3732,104 @@ fn tryResolveRefPath( | ... | @@ -3670,6 +3732,104 @@ fn tryResolveRefPath( |
| 3670 | // that said, we might want to store it elsewhere and reclaim memory asap | 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 | fn analyzeFancyFunction( | 3833 | fn analyzeFancyFunction( |
| 3674 | self: *Autodoc, | 3834 | self: *Autodoc, |
| 3675 | file: *File, | 3835 | file: *File, |