a73x

cb3e0276

test: refAllDecls(Recursive) in every test-loop module root

a73x   2026-08-14 07:49

Commit message
test: refAllDecls(Recursive) in every test-loop module root

Pub decls only — private unreferenced decls stay dark; this narrows the
silent-module-loss hazard, not retires it. quic/quic_server/quic_client
use the plain variant (cImport namespace); wasm_core excluded (not in
the native test loop, a block there would never run).

engine.zig downgraded from recursive to plain: its recursive walk pulls
in ghostty-vt's whole namespace and hits pre-existing comptime errors
unrelated to mux's own code (lib/types.zig's exhaustive switch on
`type`, lib/union.zig's CValue codegen) — not a mux-owned regression.

Step-1 finding worth recording: the plan expected the probe
(`pub const dead_probe: NoSuchType = undefined;` added to paint.zig with
no test block) to PASS under `zig build test`, demonstrating decls
escaping analysis unreferenced. It did not — `zig build test` already
FAILED on the bare probe, because paint.zig is both a dedicated test
root and an actively-used dependency of four other test roots
(mux_main, client, webhub, webhub_main), and this Zig version (0.15.2)
resolves a pub decl's declared type eagerly once its container/namespace
is touched, even without a call site. Confirmed in isolation with two
minimal repros outside the build graph: a top-level `pub const` with a
nonexistent type, and a `pub fn` with a broken, uncalled body — both
fail under plain `zig test` with no refAllDecls anywhere. The narrower,
real gap refAllDeclsRecursive still closes: a pub decl in a module that
is imported but never actually referenced by name from any importer
(the import exists but nothing calls into it), which stays genuinely
dark without this block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

src/client.zig
Old New
@@ -2502,3 +2502,10 @@ test "openFailure: a message too long for the buffer clips, and still fails" {
2502 // Clipped is still a failure. The exit must not ride on formatting. 2502 // Clipped is still a failure. The exit must not ride on formatting.
2503 try std.testing.expectEqual(@as(u8, 1), f.exit); 2503 try std.testing.expectEqual(@as(u8, 1), f.exit);
2504 } 2504 }
2505
2506 // Forces semantic analysis of every pub decl under `zig build test`, so an
2507 // unreferenced decl must at least compile (the silent-module-loss hazard,
2508 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
2509 test {
2510 std.testing.refAllDeclsRecursive(@This());
2511 }
src/cmd.zig
Old New
@@ -151,3 +151,10 @@ test "a stray D after a completed command resets phase but never absorbs its pay
151 try std.testing.expectEqual(@as(u32, 0), t.start_row); 151 try std.testing.expectEqual(@as(u32, 0), t.start_row);
152 try std.testing.expectEqual(@as(u32, 2), t.end_row); 152 try std.testing.expectEqual(@as(u32, 2), t.end_row);
153 } 153 }
154
155 // Forces semantic analysis of every pub decl under `zig build test`, so an
156 // unreferenced decl must at least compile (the silent-module-loss hazard,
157 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
158 test {
159 std.testing.refAllDeclsRecursive(@This());
160 }
src/delta.zig
Old New
@@ -231,3 +231,10 @@ test "DeltaTracker: a resize behind the tracker's back resyncs instead of over-r
231 else => return error.ExpectedDiscontinuity, 231 else => return error.ExpectedDiscontinuity,
232 } 232 }
233 } 233 }
234
235 // Forces semantic analysis of every pub decl under `zig build test`, so an
236 // unreferenced decl must at least compile (the silent-module-loss hazard,
237 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
238 test {
239 std.testing.refAllDeclsRecursive(@This());
240 }
src/engine.zig
Old New
@@ -691,3 +691,11 @@ test "Engine: non-133 OSC and the ignored 133 subcommands emit no events" {
691 e.feed("\x1b]133;B\x07\x1b]133;P\x07\x1b]133;L\x07"); // B/P/L: not ours 691 e.feed("\x1b]133;B\x07\x1b]133;P\x07\x1b]133;L\x07"); // B/P/L: not ours
692 try std.testing.expectEqual(@as(usize, 0), e.markEvents().len); 692 try std.testing.expectEqual(@as(usize, 0), e.markEvents().len);
693 } 693 }
694
695 // Plain, not recursive: this module reaches ghostty-vt's namespace, and a
696 // recursive walk hits unrelated comptime errors in that external dependency
697 // (lib/types.zig's exhaustive switch on `type`, lib/union.zig's CValue
698 // codegen) that have nothing to do with mux's own code.
699 test {
700 std.testing.refAllDecls(@This());
701 }
src/handoff.zig
Old New
@@ -601,3 +601,10 @@ test "cache: refuses a permissive file, a missing one, and `endpoint none`" {
601 } 601 }
602 try std.testing.expectError(CacheError.CacheMalformed, readCache(path)); 602 try std.testing.expectError(CacheError.CacheMalformed, readCache(path));
603 } 603 }
604
605 // Forces semantic analysis of every pub decl under `zig build test`, so an
606 // unreferenced decl must at least compile (the silent-module-loss hazard,
607 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
608 test {
609 std.testing.refAllDeclsRecursive(@This());
610 }
src/keymap.zig
Old New
@@ -325,3 +325,10 @@ test "keymap: bracketed paste wraps" {
325 try pasteInto(&out, alloc, "two\nlines"); 325 try pasteInto(&out, alloc, "two\nlines");
326 try std.testing.expectEqualStrings("\x1b[200~two\nlines\x1b[201~", out.items); 326 try std.testing.expectEqualStrings("\x1b[200~two\nlines\x1b[201~", out.items);
327 } 327 }
328
329 // Forces semantic analysis of every pub decl under `zig build test`, so an
330 // unreferenced decl must at least compile (the silent-module-loss hazard,
331 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
332 test {
333 std.testing.refAllDeclsRecursive(@This());
334 }
src/main.zig
Old New
@@ -1314,3 +1314,10 @@ test "stopCmd: a socket path with nothing on it is exit 0, not a failure" {
1314 const sock = try std.fmt.bufPrint(&buf, "{s}/absent.sock", .{tmp.path()}); 1314 const sock = try std.fmt.bufPrint(&buf, "{s}/absent.sock", .{tmp.path()});
1315 try std.testing.expectEqual(@as(u8, 0), try stopCmd(std.testing.allocator, sock)); 1315 try std.testing.expectEqual(@as(u8, 0), try stopCmd(std.testing.allocator, sock));
1316 } 1316 }
1317
1318 // Forces semantic analysis of every pub decl under `zig build test`, so an
1319 // unreferenced decl must at least compile (the silent-module-loss hazard,
1320 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
1321 test {
1322 std.testing.refAllDeclsRecursive(@This());
1323 }
src/mux_main.zig
Old New
@@ -381,3 +381,10 @@ test "parseArgs: --version wins wherever it appears" {
381 try std.testing.expect(parse(&.{ "mux", "--version" }) == .version); 381 try std.testing.expect(parse(&.{ "mux", "--version" }) == .version);
382 try std.testing.expect(parse(&.{ "mux", "--sock", "/x", "--version" }) == .version); 382 try std.testing.expect(parse(&.{ "mux", "--sock", "/x", "--version" }) == .version);
383 } 383 }
384
385 // Forces semantic analysis of every pub decl under `zig build test`, so an
386 // unreferenced decl must at least compile (the silent-module-loss hazard,
387 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
388 test {
389 std.testing.refAllDeclsRecursive(@This());
390 }
src/muxa.zig
Old New
@@ -1572,3 +1572,10 @@ fn awaitDeadline(o: Opts, conn: *const Conn) i64 {
1572 if (o.timeout_ms == 0) return std.math.maxInt(i64); 1572 if (o.timeout_ms == 0) return std.math.maxInt(i64);
1573 return std.time.milliTimestamp() + o.timeout_ms + conn.graceMs(); 1573 return std.time.milliTimestamp() + o.timeout_ms + conn.graceMs();
1574 } 1574 }
1575
1576 // Forces semantic analysis of every pub decl under `zig build test`, so an
1577 // unreferenced decl must at least compile (the silent-module-loss hazard,
1578 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
1579 test {
1580 std.testing.refAllDeclsRecursive(@This());
1581 }
src/paint.zig
Old New
@@ -293,3 +293,10 @@ test "paintDeltaClipped brackets the whole paint in one synchronized update" {
293 // Committing first would show one frame with the cursor still hidden. 293 // Committing first would show one frame with the cursor still hidden.
294 try std.testing.expect(std.mem.endsWith(u8, out[0..n], "\x1b[?25h\x1b[?2026l")); 294 try std.testing.expect(std.mem.endsWith(u8, out[0..n], "\x1b[?25h\x1b[?2026l"));
295 } 295 }
296
297 // Forces semantic analysis of every pub decl under `zig build test`, so an
298 // unreferenced decl must at least compile (the silent-module-loss hazard,
299 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
300 test {
301 std.testing.refAllDeclsRecursive(@This());
302 }
src/predict.zig
Old New
@@ -1343,3 +1343,10 @@ test "PlainGrid reads a cell out of a dump, and blanks where the dump stops" {
1343 // Outside the grid is a different answer: nothing to compare against. 1343 // Outside the grid is a different answer: nothing to compare against.
1344 try std.testing.expectEqual(@as(?u8, null), g.cellChar(0, 80)); 1344 try std.testing.expectEqual(@as(?u8, null), g.cellChar(0, 80));
1345 } 1345 }
1346
1347 // Forces semantic analysis of every pub decl under `zig build test`, so an
1348 // unreferenced decl must at least compile (the silent-module-loss hazard,
1349 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
1350 test {
1351 std.testing.refAllDeclsRecursive(@This());
1352 }
src/protocol.zig
Old New
@@ -1095,3 +1095,10 @@ test "encodeStatusReply pins the 14-byte prefix layout" {
1095 0b11, // mode: icanon + echo 1095 0b11, // mode: icanon + echo
1096 }, buf[0..14]); 1096 }, buf[0..14]);
1097 } 1097 }
1098
1099 // Forces semantic analysis of every pub decl under `zig build test`, so an
1100 // unreferenced decl must at least compile (the silent-module-loss hazard,
1101 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
1102 test {
1103 std.testing.refAllDeclsRecursive(@This());
1104 }
src/proxy.zig
Old New
@@ -373,3 +373,10 @@ test "pump reports a missing daemon socket instead of hanging" {
373 try pump(std.posix.STDIN_FILENO, std.posix.STDOUT_FILENO, sock_path), 373 try pump(std.posix.STDIN_FILENO, std.posix.STDOUT_FILENO, sock_path),
374 ); 374 );
375 } 375 }
376
377 // Forces semantic analysis of every pub decl under `zig build test`, so an
378 // unreferenced decl must at least compile (the silent-module-loss hazard,
379 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
380 test {
381 std.testing.refAllDeclsRecursive(@This());
382 }
src/pty.zig
Old New
@@ -472,3 +472,10 @@ test "Pty: fgPgid tracks the foreground job" {
472 } 472 }
473 try std.testing.expectEqual(pty.child, try pty.fgPgid()); 473 try std.testing.expectEqual(pty.child, try pty.fgPgid());
474 } 474 }
475
476 // Forces semantic analysis of every pub decl under `zig build test`, so an
477 // unreferenced decl must at least compile (the silent-module-loss hazard,
478 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
479 test {
480 std.testing.refAllDeclsRecursive(@This());
481 }
src/quic.zig
Old New
@@ -669,3 +669,9 @@ pub fn pathFrom(
669 .user_data = null, 669 .user_data = null,
670 }; 670 };
671 } 671 }
672
673 // Plain, not recursive: this module reaches the QUIC stack's @cImport, and
674 // a recursive walk would force-analyze the entire wolfSSL/ngtcp2 namespace.
675 test {
676 std.testing.refAllDecls(@This());
677 }
src/quic_client.zig
Old New
@@ -499,3 +499,9 @@ test "Client.consume: takes from the front and keeps the rest" {
499 cl.consume(99); 499 cl.consume(99);
500 try std.testing.expectEqualStrings("", cl.inbound()); 500 try std.testing.expectEqualStrings("", cl.inbound());
501 } 501 }
502
503 // Plain, not recursive: this module reaches the QUIC stack's @cImport, and
504 // a recursive walk would force-analyze the entire wolfSSL/ngtcp2 namespace.
505 test {
506 std.testing.refAllDecls(@This());
507 }
src/quic_server.zig
Old New
@@ -1970,3 +1970,9 @@ test "Listener: closing a connection from inside a receive callback is deferred"
1970 // closeConn is the owner asking, so it gets no callback back. 1970 // closeConn is the owner asking, so it gets no callback back.
1971 try std.testing.expectEqual(@as(usize, 0), owner.closed); 1971 try std.testing.expectEqual(@as(usize, 0), owner.closed);
1972 } 1972 }
1973
1974 // Plain, not recursive: this module reaches the QUIC stack's @cImport, and
1975 // a recursive walk would force-analyze the entire wolfSSL/ngtcp2 namespace.
1976 test {
1977 std.testing.refAllDecls(@This());
1978 }
src/replica.zig
Old New
@@ -297,3 +297,10 @@ test "scrollStart: pages count up from the live viewport top, saturating at row
297 try std.testing.expectEqual(@as(u32, 52), r.scrollStart(2, 24)); 297 try std.testing.expectEqual(@as(u32, 52), r.scrollStart(2, 24));
298 try std.testing.expectEqual(@as(u32, 0), r.scrollStart(5, 24)); // 100 -| 120 298 try std.testing.expectEqual(@as(u32, 0), r.scrollStart(5, 24)); // 100 -| 120
299 } 299 }
300
301 // Forces semantic analysis of every pub decl under `zig build test`, so an
302 // unreferenced decl must at least compile (the silent-module-loss hazard,
303 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
304 test {
305 std.testing.refAllDeclsRecursive(@This());
306 }
src/server.zig
Old New
@@ -5697,3 +5697,10 @@ test "Server: without shell integration a foreground job's end is caught by the
5697 // this mechanism has to avoid. 5697 // this mechanism has to avoid.
5698 try std.testing.expectEqual(@as(?u8, null), rep.state.exit_code); 5698 try std.testing.expectEqual(@as(?u8, null), rep.state.exit_code);
5699 } 5699 }
5700
5701 // Forces semantic analysis of every pub decl under `zig build test`, so an
5702 // unreferenced decl must at least compile (the silent-module-loss hazard,
5703 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
5704 test {
5705 std.testing.refAllDeclsRecursive(@This());
5706 }
src/shellint.zig
Old New
@@ -510,3 +510,10 @@ test "prepare zsh: the shim directory is 0700 and the rc file 0600" {
510 const fst = try f.stat(); 510 const fst = try f.stat();
511 try std.testing.expectEqual(@as(u32, 0o600), @as(u32, @intCast(fst.mode & 0o777))); 511 try std.testing.expectEqual(@as(u32, 0o600), @as(u32, @intCast(fst.mode & 0o777)));
512 } 512 }
513
514 // Forces semantic analysis of every pub decl under `zig build test`, so an
515 // unreferenced decl must at least compile (the silent-module-loss hazard,
516 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
517 test {
518 std.testing.refAllDeclsRecursive(@This());
519 }
src/sockpath.zig
Old New
@@ -137,3 +137,10 @@ test "PathId: names the file it was taken from, not the path, and not a successo
137 defer successor.deinit(); 137 defer successor.deinit();
138 try std.testing.expect(!id.stillAt(sock_path)); 138 try std.testing.expect(!id.stillAt(sock_path));
139 } 139 }
140
141 // Forces semantic analysis of every pub decl under `zig build test`, so an
142 // unreferenced decl must at least compile (the silent-module-loss hazard,
143 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
144 test {
145 std.testing.refAllDeclsRecursive(@This());
146 }
src/spawn.zig
Old New
@@ -621,3 +621,10 @@ test "findInPath: empty PATH segments are skipped, never read as cwd" {
621 try findInPath(alloc, "", "muxd"), 621 try findInPath(alloc, "", "muxd"),
622 ); 622 );
623 } 623 }
624
625 // Forces semantic analysis of every pub decl under `zig build test`, so an
626 // unreferenced decl must at least compile (the silent-module-loss hazard,
627 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
628 test {
629 std.testing.refAllDeclsRecursive(@This());
630 }
src/testtmp.zig
Old New
@@ -95,3 +95,10 @@ test "TmpDir: a path short enough to bind a socket in" {
95 // harmless, or every test using this would have to choose between an 95 // harmless, or every test using this would have to choose between an
96 // early cleanup and a defer — and closing a closed directory aborts. 96 // early cleanup and a defer — and closing a closed directory aborts.
97 } 97 }
98
99 // Forces semantic analysis of every pub decl under `zig build test`, so an
100 // unreferenced decl must at least compile (the silent-module-loss hazard,
101 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
102 test {
103 std.testing.refAllDeclsRecursive(@This());
104 }
src/webhub.zig
Old New
@@ -917,3 +917,10 @@ test "frame messages: exact framing in, everything else named" {
917 const future = try parseFrameMessage(&[_]u8{ 0x00, 0x40, 0, 0, 0, 0 }); 917 const future = try parseFrameMessage(&[_]u8{ 0x00, 0x40, 0, 0, 0, 0 });
918 try std.testing.expectEqual(@as(u8, 0x40), @intFromEnum(future.t)); 918 try std.testing.expectEqual(@as(u8, 0x40), @intFromEnum(future.t));
919 } 919 }
920
921 // Forces semantic analysis of every pub decl under `zig build test`, so an
922 // unreferenced decl must at least compile (the silent-module-loss hazard,
923 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
924 test {
925 std.testing.refAllDeclsRecursive(@This());
926 }
src/webhub_main.zig
Old New
@@ -305,3 +305,10 @@ test "version short-circuits everything else on the line" {
305 const r = try parseArgs(alloc, &[_][:0]const u8{ "muxweb", "h", "--version", "--bogus" }, null); 305 const r = try parseArgs(alloc, &[_][:0]const u8{ "muxweb", "h", "--version", "--bogus" }, null);
306 try std.testing.expect(r == .version); 306 try std.testing.expect(r == .version);
307 } 307 }
308
309 // Forces semantic analysis of every pub decl under `zig build test`, so an
310 // unreferenced decl must at least compile (the silent-module-loss hazard,
311 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
312 test {
313 std.testing.refAllDeclsRecursive(@This());
314 }
src/xdg.zig
Old New
@@ -267,3 +267,10 @@ test "writeNewKey: creates 0600 with 32 bytes, refuses to overwrite" {
267 try std.testing.expectEqual(@as(usize, 32), try f2.preadAll(&second, 0)); 267 try std.testing.expectEqual(@as(usize, 32), try f2.preadAll(&second, 0));
268 try std.testing.expectEqualSlices(u8, &first, &second); 268 try std.testing.expectEqualSlices(u8, &first, &second);
269 } 269 }
270
271 // Forces semantic analysis of every pub decl under `zig build test`, so an
272 // unreferenced decl must at least compile (the silent-module-loss hazard,
273 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
274 test {
275 std.testing.refAllDeclsRecursive(@This());
276 }
test/delaypipe.zig
Old New
@@ -154,3 +154,10 @@ test "DELAY_MS is honoured when set, and never fatal when it is nonsense" {
154 try std.testing.expectEqual(default_delay_ms, parseDelay(null)); 154 try std.testing.expectEqual(default_delay_ms, parseDelay(null));
155 try std.testing.expectEqual(default_delay_ms, delayFromEnv()); 155 try std.testing.expectEqual(default_delay_ms, delayFromEnv());
156 } 156 }
157
158 // Forces semantic analysis of every pub decl under `zig build test`, so an
159 // unreferenced decl must at least compile (the silent-module-loss hazard,
160 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
161 test {
162 std.testing.refAllDeclsRecursive(@This());
163 }
test/ptyclient.zig
Old New
@@ -435,3 +435,10 @@ test "parseLine: verbs, spaces in payloads, comments" {
435 // A bad deadline must not strand the needle: parse before allocating. 435 // A bad deadline must not strand the needle: parse before allocating.
436 try std.testing.expectError(error.BadVerb, parseLine(alloc, "expect two words later")); 436 try std.testing.expectError(error.BadVerb, parseLine(alloc, "expect two words later"));
437 } 437 }
438
439 // Forces semantic analysis of every pub decl under `zig build test`, so an
440 // unreferenced decl must at least compile (the silent-module-loss hazard,
441 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
442 test {
443 std.testing.refAllDeclsRecursive(@This());
444 }
test/rawmode.zig
Old New
@@ -179,3 +179,10 @@ test "pump: EOF ends it as cleanly as the quit byte" {
179 std.posix.close(to_child[1]); // EOF, with no quit byte at all 179 std.posix.close(to_child[1]); // EOF, with no quit byte at all
180 th.join(); // returning at all is the assertion 180 th.join(); // returning at all is the assertion
181 } 181 }
182
183 // Forces semantic analysis of every pub decl under `zig build test`, so an
184 // unreferenced decl must at least compile (the silent-module-loss hazard,
185 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
186 test {
187 std.testing.refAllDeclsRecursive(@This());
188 }
test/render.zig
Old New
@@ -141,3 +141,10 @@ test "render: styled state survives replay identically to a direct feed" {
141 defer alloc.free(vb); 141 defer alloc.free(vb);
142 try std.testing.expectEqualStrings(vb, va); 142 try std.testing.expectEqualStrings(vb, va);
143 } 143 }
144
145 // Forces semantic analysis of every pub decl under `zig build test`, so an
146 // unreferenced decl must at least compile (the silent-module-loss hazard,
147 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
148 test {
149 std.testing.refAllDeclsRecursive(@This());
150 }
test/script.zig
Old New
@@ -72,3 +72,10 @@ test "decodeEscapes: named, hex, literal backslash" {
72 // parseInt would take the sign and decode this as 0x01. 72 // parseInt would take the sign and decode this as 0x01.
73 try std.testing.expectError(error.BadEscape, decodeEscapes(alloc, "\\x+1")); 73 try std.testing.expectError(error.BadEscape, decodeEscapes(alloc, "\\x+1"));
74 } 74 }
75
76 // Forces semantic analysis of every pub decl under `zig build test`, so an
77 // unreferenced decl must at least compile (the silent-module-loss hazard,
78 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
79 test {
80 std.testing.refAllDeclsRecursive(@This());
81 }
test/wsclient.zig
Old New
@@ -665,3 +665,10 @@ test "the dump this exits with is the daemon's own dump format" {
665 defer alloc.free(b); 665 defer alloc.free(b);
666 try std.testing.expectEqualStrings(a, b); 666 try std.testing.expectEqualStrings(a, b);
667 } 667 }
668
669 // Forces semantic analysis of every pub decl under `zig build test`, so an
670 // unreferenced decl must at least compile (the silent-module-loss hazard,
671 // decisions.md). Pub decls only: std.meta.declarations sees nothing private.
672 test {
673 std.testing.refAllDeclsRecursive(@This());
674 }