Skip to main content

max / quasi

Make the population predicate checkable, not just runnable `--selftest` runs the predicate over 17 synthetic sources with known answers. Every case is a defect that actually happened: a `#[cfg(test)]` stripper that brace-matched from the next `{` ate four real goingson functions, a doc-comment mention of a vocabulary type was counted as a construction, and a tuple return was missed. This script is the authority for the number that decides whether the transition is weeks or months, so its implementation should be verifiable rather than trusted. It earned itself on the first run by forcing a decision I had left implicit: an alias is resolved for membership and reported as written, which is what makes the record's own histogram list "Described (Screen alias) 18" as its own row.
Author: Max Johnson <me@maxj.phd> · 2026-09-03 15:53 UTC
Signed with PGP, not checked
Commit: d1a999b5fd36cc2741ddac436a4fd60b1f4b6d9a
Parent: 75e0a12
1 file changed, +109 insertions, -0 deletions
@@ -20,6 +20,12 @@
20 20 python3 scripts/population.py # the count, per tree
21 21 python3 scripts/population.py --list # one `path:line name -> type` per line
22 22 python3 scripts/population.py --json # the same, machine-readable
23 + python3 scripts/population.py --selftest # the predicate against known answers
24 +
25 + Run `--selftest` before quoting a number from this. Every case in it is a defect
26 + that actually happened: a `#[cfg(test)]` stripper that brace-matched from the
27 + next `{` blanked four real functions in goingson, a doc-comment mention of a
28 + vocabulary type was counted as a construction, and a tuple return was missed.
23 29
24 30 Paths are read relative to `~/Code` by default; pass `--root` for a tree
25 31 somewhere else.
@@ -343,17 +349,120 @@
343 349 return found
344 350
345 351
352 + # The cases the implementation has been wrong on, as executable assertions.
353 + # Every one is a defect that actually happened: a naive `#[cfg(test)]` stripper
354 + # ate four real functions, a doc-comment mention was counted as a construction,
355 + # and a tuple return was missed. `--selftest` is how those stay fixed.
356 + SELFTEST = [
357 + # (source, expected [(fn, head)])
358 + # A plain shape.
359 + ("use quasi_router::Node;\nfn body(cx: &Cx) -> Node { Node::page(\"x\") }",
360 + [("body", "Node")]),
361 + # Wrappers are stripped outside-in; `Result`'s payload is its first argument.
362 + ("use quasi_router::{Node, Row};\n"
363 + "fn a(c: &C) -> Result<Vec<Node>, E> { todo!() }\n"
364 + "fn b(c: &C) -> Option<Row> { todo!() }",
365 + [("a", "Node"), ("b", "Row")]),
366 + # `Result<(), E>` names no vocabulary type and is not a shape.
367 + ("use quasi_router::Node;\nfn a(c: &C) -> Result<(), E> { todo!() }", []),
368 + # A doc comment naming a vocabulary type is not a construction. This is the
369 + # defect behind two of the five irreproducible counts.
370 + ("use quasi_router::Node;\n/// Returns a Node, eventually.\nfn a(c: &C) -> u32 { 0 }",
371 + []),
372 + # Nor is a string literal.
373 + ("use quasi_router::Node;\nfn a(c: &C) -> u32 { let s = \"fn x() -> Node\"; 0 }",
374 + []),
375 + # An inline `#[cfg(test)]` block is stripped ...
376 + ("use quasi_router::Node;\n"
377 + "fn real(c: &C) -> Node { todo!() }\n"
378 + "#[cfg(test)]\nmod tests {\n fn fake(c: &C) -> Node { todo!() }\n}\n",
379 + [("real", "Node")]),
380 + # ... but `#[cfg(test)] mod tests;` has no block, and brace-matching from
381 + # the next `{` would blank an arbitrary later region. Four real goingson
382 + # functions were lost to exactly this.
383 + ("use quasi_router::Node;\n"
384 + "#[cfg(test)]\nmod tests;\n"
385 + "fn survives(c: &C) -> Node { todo!() }\n",
386 + [("survives", "Node")]),
387 + # A signature with no body is a trait method or an extern, not a shape.
388 + ("use quasi_router::Node;\ntrait T { fn a(&self) -> Node; }", []),
389 + # A returned unit is not a shape.
390 + ("use quasi_router::Node;\nfn a(c: &C) {}", []),
391 + # A generic parameter list before the arguments must not break the parse.
392 + ("use quasi_router::Node;\nfn a<T: Into<String>>(t: T) -> Node { todo!() }",
393 + [("a", "Node")]),
394 + # A `where` clause sits between the return type and the body.
395 + ("use quasi_router::Node;\nfn a<T>(t: T) -> Node where T: Into<String> { todo!() }",
396 + [("a", "Node")]),
397 + # A tuple return: one site in the trees, and `shaped` names one type, but it
398 + # is a shape function and it counts.
399 + ("use quasi_router::Cells;\n"
400 + "fn a(c: &C) -> (Vec<&'static str>, Vec<Cells>) { todo!() }",
401 + [("a", "Cells")]),
402 + # A file-local type wearing a vocabulary name shadows it.
403 + ("use quasi_router::Node;\nstruct Row;\nfn a(c: &C) -> Row { Row }", []),
404 + # Reached by path rather than by import.
405 + ("fn a(c: &C) -> quasi_router::Node { todo!() }", [("a", "Node")]),
406 + # A vocabulary name that was never imported and is not path-qualified is
407 + # somebody else's type.
408 + ("fn a(c: &C) -> Node { todo!() }", []),
409 + # An alias is resolved for MEMBERSHIP and reported AS WRITTEN. Both halves
410 + # are deliberate: the shape counts, and `--list` says what the source says,
411 + # which is why the record's own return-type histogram lists "Described
412 + # (Screen alias) 18" as its own row rather than folding it into Screen.
413 + ("use quasi_router::Screen as Described;\nfn a(c: &C) -> Described { todo!() }",
414 + [("a", "Described")]),
415 + # `Response` is quasi's transport, not its description vocabulary. 566
416 + # further functions in these directories return one; admitting them roughly
417 + # doubles the count.
418 + ("use quasi_router::Response;\nfn a(c: &C) -> Response { todo!() }", []),
419 + ]
420 +
421 +
422 + def selftest() -> int:
423 + """Run the predicate over synthetic sources with known answers."""
424 + import tempfile
425 +
426 + failures = 0
427 + for n, (src, expected) in enumerate(SELFTEST, 1):
428 + with tempfile.TemporaryDirectory() as d:
429 + f = Path(d) / "case.rs"
430 + f.write_text(src, encoding="utf-8")
431 + got = [(h["fn"], head_type(h["returns"]) or "?") for h in shapes_in(f)]
432 + # head_type is re-applied for the label only; membership already
433 + # filtered. Compare as sets, since order is not the claim.
434 + want = {(a, b) for a, b in expected}
435 + have = {(a, b) for a, b in got}
436 + if have != want:
437 + failures += 1
438 + print(f"case {n} FAILED")
439 + print(f" source: {src!r}")
440 + print(f" expected: {sorted(want)}")
441 + print(f" got: {sorted(have)}")
442 + total = len(SELFTEST)
443 + if failures:
444 + print(f"\n{failures} of {total} cases failed.")
445 + return 1
446 + print(f"{total} of {total} cases pass.")
447 + return 0
448 +
449 +
346 450 def main() -> int:
347 451 ap = argparse.ArgumentParser(description=__doc__,
348 452 formatter_class=argparse.RawDescriptionHelpFormatter)
349 453 ap.add_argument("--root", default=str(Path.home() / "Code"))
350 454 ap.add_argument("--list", action="store_true")
351 455 ap.add_argument("--json", action="store_true")
456 + ap.add_argument("--selftest", action="store_true",
457 + help="run the predicate over synthetic sources with known answers")
352 458 ap.add_argument("--wide", action="store_true",
353 459 help="also count the deferred leaf suppliers (Rest, Candidate, "
354 460 "Accepted, RegionKind, Chrome)")
355 461 args = ap.parse_args()
356 462
463 + if args.selftest:
464 + return selftest()
465 +
357 466 if args.wide:
358 467 VOCABULARY.update(DEFERRED_LEAVES)
359 468