From 71ab20eca95fd0c1e740f2100c858a3b936d83da Mon Sep 17 00:00:00 2001 From: Nils Lehnen <30603423+iderex@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:49:12 +0200 Subject: [PATCH] Refuse a span name declared in the enum and missing from the published set (#61) 0061 requires every span name to be declared in one place in the tree, and gives #67 as one of its two reasons: a set derivable by reading one file is a set a run can print rather than one somebody keeps a list of. `SpanName::all` is that set, and it is a second list beside the enum. The compiler refuses a variant missing an arm in `SpanName::as_str` and says nothing about one missing from `SpanName::all`, so the failure that list can have is silent: a span the core emits and the published set does not name, with nothing anywhere going red. That is the shape declaring the names in one place exists to prevent, arriving inside the place they are declared. It matters most exactly when 0061 says the six sub-intervals arrive. Each is placed with the issue that builds the subsystem emitting it, so six people add six variants at six different times, and every one of them is a chance for the enum and the set to come apart by one. The case reads the enum out of this file's own source, embedded at compile time because `std::fs` under `src/` is refused by the `no-filesystem-access` rule, and compares it against `SpanName::all`. It carries a floor as well: a reading that finds fewer variants than the enum has fails rather than passing quietly, because a parser that stopped matching finds nothing and reads exactly like a set with no difference in it. Watched failing on a variant added to the enum and not to the set, and watched staying green on the same variant added to both; PR #299 carries both runs. Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com> --- src/measurement/mod.rs | 93 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/measurement/mod.rs b/src/measurement/mod.rs index 0375db5..90d9b9e 100644 --- a/src/measurement/mod.rs +++ b/src/measurement/mod.rs @@ -52,6 +52,10 @@ use core::time::Duration; /// record; renaming one of the three below is a change to 0008, because those /// three are what a build is gated on and a rename detaches the gate from what it /// was gating. +/// +/// A variant added here and not added to [`SpanName::all`] is refused by the +/// suite rather than by the compiler, and [`SpanName::all`]'s own documentation +/// says why that difference matters. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[non_exhaustive] pub enum SpanName { @@ -71,6 +75,14 @@ impl SpanName { /// Here so that #67 can print the set rather than keep a copy of it, and so /// that the rule below is applied to the whole of it rather than to whichever /// members somebody remembered. + /// + /// IT IS A SECOND LIST BESIDE THE ENUM AND THE SUITE IS WHAT KEEPS THE TWO + /// TOGETHER. The compiler refuses a variant missing from [`SpanName::as_str`] + /// and has nothing to say about one missing from here, so the failure this + /// list can have is silence: a span the core emits and the published set does + /// not name, which is the exact shape 0061 declares the names in one place to + /// prevent. `every_variant_of_the_enum_is_in_the_declared_set` below reads + /// the enum out of this file's own source and refuses the difference. #[must_use] pub const fn all() -> &'static [Self] { &[ @@ -512,6 +524,87 @@ mod tests { } } + /// This file's own source, embedded when it is compiled. + /// + /// A run-time reading is not available and is not wanted. The + /// `no-filesystem-access` rule in `.github/invariants/rules` refuses the + /// language's own file-opening vocabulary anywhere under `src/`, and this + /// sentence is written around that rule rather than naming what it matches, + /// which is the rule biting a comment rather than a crossing and is the same + /// departure `crate::diagnostics` already records for its neighbour. What is + /// compared here is two declarations in one file that the compiler already + /// holds, so nothing has to be opened at all. + const THIS_FILE: &str = include_str!("mod.rs"); + + /// The variants written in the enum, read out of the declaration itself. + /// + /// It reads the block rather than the whole file so that the words in the + /// prose around it cannot become members, and it stops at the first line that + /// closes the block at column zero. + fn variants_the_enum_declares() -> Vec { + let opens = THIS_FILE + .find("pub enum SpanName {") + .expect("this file declares the enum it is about"); + let block = &THIS_FILE[opens..]; + let closes = block + .find( + " +}", + ) + .expect("the declaration this file reads is closed"); + + block[..closes] + .lines() + .filter_map(|line| { + let named = line.strip_prefix(" ")?.strip_suffix(',')?; + let starts_a_name = named + .chars() + .next() + .is_some_and(|character| character.is_ascii_uppercase()); + let is_one_word = named + .chars() + .all(|character| character.is_ascii_alphanumeric()); + (starts_a_name && is_one_word).then(|| named.to_owned()) + }) + .collect() + } + + /// 0061's rule that every name is declared in one place, held against the one + /// way this file can come apart. + /// + /// The compiler refuses a variant missing an arm in [`SpanName::as_str`] and + /// says nothing about one missing from [`SpanName::all`], so the failure is + /// silent: the set #67 publishes is short by a name the core emits, and + /// nothing anywhere goes red. That matters most exactly when 0061 says the + /// six sub-intervals arrive, one at a time, from six different issues. + #[test] + fn every_variant_of_the_enum_is_in_the_declared_set() { + let declared: Vec = variants_the_enum_declares(); + assert!( + declared.len() >= 3, + "the reading found {declared:?}, which is fewer than the enum carries, so this case is proving nothing" + ); + + let listed: Vec = SpanName::all() + .iter() + .map(|name| format!("{name:?}")) + .collect(); + + let missing: Vec<&String> = declared + .iter() + .filter(|variant| !listed.contains(variant)) + .collect(); + assert!( + missing.is_empty(), + "declared in the enum and absent from SpanName::all: {missing:?}", + ); + assert_eq!( + declared.len(), + listed.len(), + "the enum declares {declared:?} and the set names {listed:?}", + ); + } + #[test] fn every_declared_name_obeys_the_spelling_rule() { for name in SpanName::all() {