ww: test sources are *_test.ww only (Go contract)

Go compiles only _test.go files as tests; discovery now keys on the
_test.ww suffix alone. The line-leading-@test compatibility allowance
(noncanonical filenames admitted as test sources) is removed from both
driver stages and the coordinator. An @test declaration outside a
*_test.ww file is rejected loudly ("@test declaration outside
*_test.ww", wording byte-identical cs/ww) instead of silently running
under compose or silently dropping in a non-T build (#6). Tree audit
found zero real carriers; the two allowance fixtures flip canonical
(dep_test.ww, widget_test.ww). New pins: direnum attest-noncanon
reject row (both-stage stderr parity) and the coordinator
noncanonical_attest_rejected package row.
This commit is contained in:
2026-08-08 20:38:02 +09:00
parent 0cf9643be8
commit 659e859f34
10 changed files with 109 additions and 47 deletions

View File

@@ -352,10 +352,10 @@ fn locateimport(dirs: *u8, name: *u8, namelen: u64,
return nil;
};
// Temporary compatibility for the repository's pre-underscore test sources:
// a noncanonical filename is test-only only when its body contains a real
// line-leading @test declaration. This is the wwstage twin of
// cmd/ww/main.c:file_has_line_test.
// Go's contract: only *_test.ww is a test source. A line-leading @test
// declaration anywhere else would be silently dropped by a non-T build
// (#6, the Hare model), so directory enumeration rejects it loudly.
// This is the wwstage twin of cmd/ww/main.c:file_has_line_test.
fn dirfileattest(dirpath: *u8, name: *u8) bool = {
let path: *u8 = joinpath(dirpath, name);
let fd: i32 = os.open(pathstr(path), os.flag.RDONLY, 0i32);
@@ -393,20 +393,20 @@ fn dirfileattest(dirpath: *u8, name: *u8) bool = {
return false;
};
// Filter for production enumeration: keep `*.ww` minus canonical
// `*_test.ww` and the explicit compatibility sources above. Returns
// true to keep.
fn dirfilekeep(dirpath: *u8, name: *u8, nlen: u64) bool = {
// Classify a directory entry for production enumeration: 1 keep,
// 0 skip (non-source or *_test.ww), -1 @test outside *_test.ww
// (caller diagnoses and fails).
fn dirfileclass(dirpath: *u8, name: *u8, nlen: u64) i32 = {
// nlen<=3 guard kept: a bare ".ww" (len 3) is rejected here but
// would pass strings.hassuffix(".ww"); preserves cstage parity.
if (nlen <= 3u64) { return false; };
if (nlen <= 3u64) { return 0; };
let s: str;
s.ptr = name;
s.len = nlen: i32;
if (!strings.hassuffix(s, ".ww")) { return false; };
if (strings.hassuffix(s, "_test.ww")
|| dirfileattest(dirpath, name)) { return false; };
return true;
if (!strings.hassuffix(s, ".ww")) { return 0; };
if (strings.hassuffix(s, "_test.ww")) { return 0; };
if (dirfileattest(dirpath, name)) { return -1; };
return 1;
};
// Byte-wise memcmp returning < 0, 0, > 0. Rule-10 byte-id requires
@@ -429,10 +429,11 @@ fn bytecmp(a: *u8, alen: u64, b: *u8, blen: u64) i32 = {
return 0;
};
// enumeratedir — list production *.ww paths of `dirpath` (less canonical
// and compatibility test sources), byte-sort. Returns
// one exact pointer array of NUL-terminated full paths. This is the sole
// directory-membership discovery path; the owning seppkg retains the list.
// enumeratedir — list production *.ww paths of `dirpath` (less *_test.ww
// test sources), byte-sort. A line-leading @test in any other source is
// diagnosed here and returns -2. Returns one exact pointer array of
// NUL-terminated full paths. This is the sole directory-membership
// discovery path; the owning seppkg retains the list.
fn enumeratedir(dirpath: *u8) (**u8, i32) = {
let fd: i32 = os.open(pathstr(dirpath), os.flag.RDONLY, 0i32);
if (fd < 0) { return nil: **u8, -1; };
@@ -456,7 +457,15 @@ fn enumeratedir(dirpath: *u8) (**u8, i32) = {
let reclen: u64 = blo + (bhi * 256u64);
let nm: *u8 = buf.ptr + off + 19u64;
let nl: u64 = cstrlen(nm);
if (dirfilekeep(dirpath, nm, nl)) {
let cls: i32 = dirfileclass(dirpath, nm, nl);
if (cls < 0) {
cerr("ww: ");
cerr(pathstr(joinpath(dirpath, nm)));
cerr(": @test declaration outside *_test.ww\n");
os.close(fd);
return nil: **u8, -2;
};
if (cls > 0) {
if (n >= cap) {
let ncap: i32 = cap * 2;
let nn: []*u8 = alloc([], ncap: u64)!;
@@ -1000,7 +1009,10 @@ fn seploadpkg(g: *sepgraph, pi: i32, searchpath: *u8) i32 = {
sources, nsources = enumeratedir(g.pkg[pi].entry);
g.pkg[pi].sources = sources;
g.pkg[pi].nsources = nsources;
if (g.pkg[pi].nsources < 0) {
if (g.pkg[pi].nsources == -2) {
// diagnosed in enumeratedir
rc = -1;
} else { if (g.pkg[pi].nsources < 0) {
cerr("ww: cannot read directory ");
cerr(pathstr(g.pkg[pi].entry));
cerr("\n");
@@ -1010,7 +1022,7 @@ fn seploadpkg(g: *sepgraph, pi: i32, searchpath: *u8) i32 = {
cerr(pathstr(g.pkg[pi].entry));
cerr(": directory contains no WW package sources\n");
rc = -1;
}; };
}; }; };
let i: i32 = 0;
for (i < g.pkg[pi].nsources) {
if (rc == 0) {