diff --git a/crates/mind-body/src/control.rs b/crates/mind-body/src/control.rs index 77c4a5b..0792c44 100644 --- a/crates/mind-body/src/control.rs +++ b/crates/mind-body/src/control.rs @@ -131,6 +131,41 @@ pub enum Effective { /// `read_effective` makes one; `open_and_launch` takes it by value and refuses /// one read for another instance, so one reading opens at most one run, of the /// instance it was read for. +/// +/// Outside this crate the type system leaves no way to make one: the fields are +/// private, there is no public constructor, and the derives give no path (no +/// `Default`, `Clone`, `Deserialize` or `From`). Each way is tried here and +/// refused: +/// +/// ```compile_fail,E0451 +/// use mind_body::control::Grant; +/// // a struct literal +/// let _ = Grant { instance: eureka_pipeline::Slug("eureka".into()), cadence: std::time::Duration::ZERO, run_cap_usd: rust_decimal::Decimal::ONE }; +/// ``` +/// +/// ```compile_fail,E0277 +/// // not Default +/// let _: mind_body::control::Grant = Default::default(); +/// ``` +/// +/// ```compile_fail,E0599 +/// // not Clone, so a reading cannot be copied into a second run +/// fn copy(grant: &mind_body::control::Grant) -> mind_body::control::Grant { +/// grant.clone() +/// } +/// ``` +/// +/// ```compile_fail,E0277 +/// // not Deserialize +/// fn read() {} +/// read::(); +/// ``` +/// +/// ```compile_fail,E0277 +/// // not From its parts +/// use mind_body::control::Grant; +/// let _ = Grant::from((eureka_pipeline::Slug("eureka".into()), std::time::Duration::ZERO, rust_decimal::Decimal::ONE)); +/// ``` #[derive(Debug, PartialEq, Eq)] pub struct Grant { instance: Slug, @@ -253,12 +288,6 @@ pub fn load_state(source: &impl ControlSource) -> Result { /// ``` /// /// ```compile_fail -/// use mind_body::control::Grant; -/// // a Grant cannot be written out -/// let _ = Grant { instance: eureka_pipeline::Slug("eureka".into()), cadence: std::time::Duration::ZERO, run_cap_usd: rust_decimal::Decimal::ONE }; -/// ``` -/// -/// ```compile_fail /// // and the test constructor is not in the library build /// let _ = mind_body::control::Grant::for_test; /// ``` diff --git a/crates/mind-body/src/launch.rs b/crates/mind-body/src/launch.rs index ae504a8..cef1e0a 100644 --- a/crates/mind-body/src/launch.rs +++ b/crates/mind-body/src/launch.rs @@ -371,11 +371,10 @@ fn breaker_question(item: &PipelineRef, label: &Label, now: DateTime) -> Pi #[cfg(test)] mod tests { use std::cell::{Cell, RefCell}; - use std::collections::BTreeSet; use std::time::Duration as StdDuration; use eureka_pipeline::PipelineKind as K; - use huginn_mind::{PipelineAdmissionOutcome as Outcome, PipelineStatus}; + use huginn_mind::{PipelineAdmissionOutcome as Outcome, PipelineSelectionPage, PipelineStatus}; use rust_decimal::Decimal; use super::*; @@ -847,155 +846,206 @@ mod tests { assert_eq!((rig.runs(), rig.started()), (3, 0)); } - /// Every name that differs from `name` and is shaped to pass a compare weaker - /// than equality. The set is generated from the real name, not listed: every - /// single-character substitution (over ASCII and some wide characters), deletion - /// and insertion; every transposition; every permutation; rotations; every - /// prefix and suffix; case flips; whitespace and NUL padding at either end; - /// repetitions past any plausible length bound; and seeded random names of the - /// same length and of other lengths. A compare that agrees with equality on all - /// of them, and so passes the gate test, is equality for every name a caller - /// can plausibly build. - fn near_misses(name: &str) -> Vec { + /// The one source of every drawn case: splitmix64 from a fixed seed, so a + /// failure names a case that runs again. + struct Draw(u64); + + impl Draw { + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9); + z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb); + z ^ (z >> 31) + } + + fn below(&mut self, bound: usize) -> usize { + (self.next() % bound as u64) as usize + } + } + + const LENGTH_EDGES: [usize; 13] = [1, 2, 3, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64]; + const ALPHABETS: [&str; 6] = [ + "abcdefghijklmnopqrstuvwxyz", + "abcdefghijklmnopqrstuvwxyz0123456789", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + "ab-_", + "a-b_c-d_e0", + "abcdefghijklmnopqrstuvwxyz-_", + ]; + + /// A valid slug of `length` characters, over an alphabet the draw picks, with + /// both separators (`-`, `_`) mixed in at random positions. + fn slug_of(draw: &mut Draw, length: usize) -> String { + loop { + let alphabet: Vec = ALPHABETS[draw.below(ALPHABETS.len())].chars().collect(); + let name: String = (0..length) + .map(|_| match draw.below(6) { + 0 => '-', + 1 => '_', + _ => alphabet[draw.below(alphabet.len())], + }) + .collect(); + if Slug(name.clone()).validate_slug().is_ok() { + return name; + } + } + } + + /// Valid slugs that differ from `name`: edits of it at every position (a + /// drawn character, the other separator, the other case), its prefixes, the + /// same name with every separator swapped, and independent draws. + fn distinct_valid(name: &str, draw: &mut Draw) -> Vec { let chars: Vec = name.chars().collect(); - let wide = ['é', 'ÿ', '\u{ffff}', '😀']; - let alphabet: Vec = (0u8..128).map(char::from).chain(wide).collect(); - let text = |parts: &[char]| parts.iter().collect::(); - let mut out: BTreeSet = BTreeSet::new(); - for i in 0..chars.len() { - for &c in &alphabet { - let mut changed = chars.clone(); - changed[i] = c; - out.insert(text(&changed)); - } - let mut deleted = chars.clone(); - deleted.remove(i); - out.insert(text(&deleted)); - for j in i + 1..chars.len() { - let mut swapped = chars.clone(); - swapped.swap(i, j); - out.insert(text(&swapped)); - } - out.insert(text(&chars[..i])); - out.insert(text(&chars[i + 1..])); - out.insert(text(&[&chars[i..], &chars[..i]].concat())); - let mut flipped = chars.clone(); - flipped[i] = if chars[i].is_uppercase() { chars[i].to_ascii_lowercase() } else { chars[i].to_ascii_uppercase() }; - out.insert(text(&flipped)); - } - for i in 0..=chars.len() { - for &c in &alphabet { - let mut grown = chars.clone(); - grown.insert(i, c); - out.insert(text(&grown)); - } - } - for pad in ['\0', ' ', '\n', '\t', '\u{a0}'] { - for count in 1..=4 { - out.insert(format!("{name}{}", pad.to_string().repeat(count))); - out.insert(format!("{}{name}", pad.to_string().repeat(count))); - } - for width in [32, 64, 255] { - out.insert(format!("{name}{}", pad.to_string().repeat(width - chars.len()))); - } - } - for copies in [2, 3, 8, 40, 400, 4000] { - out.insert(name.repeat(copies)); - } - out.insert(name.to_uppercase()); - out.insert(name.to_lowercase()); - // Every permutation (Heap's algorithm). - let mut order = chars.clone(); - let mut counters = vec![0usize; order.len()]; - out.insert(text(&order)); - let mut at = 0; - while at < order.len() { - if counters[at] < at { - order.swap(if at % 2 == 0 { 0 } else { counters[at] }, at); - out.insert(text(&order)); - counters[at] += 1; - at = 0; - } else { - counters[at] = 0; - at += 1; - } - } - // Seeded random names: same length over the name's own alphabet, over all of - // ASCII, and of any length up to 300 over ASCII. - let mut state = 0x9e37_79b9_7f4a_7c15_u64; - let mut next = move || { - state ^= state << 13; - state ^= state >> 7; - state ^= state << 17; - state + let pool: Vec = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_".chars().collect(); + let swapped = |c: char| match c { + '-' => '_', + '_' => '-', + other => other, }; - for _ in 0..4096 { - out.insert((0..chars.len()).map(|_| chars[(next() % chars.len() as u64) as usize]).collect()); - out.insert((0..chars.len()).map(|_| char::from((next() % 128) as u8)).collect()); + let mut out = Vec::new(); + for i in 0..chars.len() { + let mut edited = chars.clone(); + edited[i] = swapped(chars[i]); + out.push(edited.iter().collect::()); + let mut edited = chars.clone(); + edited[i] = if chars[i].is_ascii_uppercase() { chars[i].to_ascii_lowercase() } else { chars[i].to_ascii_uppercase() }; + out.push(edited.iter().collect::()); + for _ in 0..3 { + let mut edited = chars.clone(); + edited[i] = pool[draw.below(pool.len())]; + out.push(edited.iter().collect::()); + } + out.push(chars[..i].iter().collect::()); } - for _ in 0..1024 { - let length = (next() % 301) as usize; - out.insert((0..length).map(|_| char::from((next() % 128) as u8)).collect()); + out.push(chars.iter().map(|&c| swapped(c)).collect::()); + out.push(name.to_uppercase()); + out.push(name.to_lowercase()); + if chars.len() < 64 { + out.push(format!("{name}-")); + out.push(format!("{name}_")); + out.push(format!("{name}0")); } - out.remove(name); - out.into_iter().collect() + for _ in 0..8 { + let length = 1 + draw.below(64); + out.push(slug_of(draw, length)); + } + out.retain(|other| other != name && Slug(other.clone()).validate_slug().is_ok()); + out + } + + /// A mind that knows only its instance. Any read or write is an error that + /// says so and is counted, so a gate that lets a launch through is seen by + /// the error it gets back, and a gate that refuses is seen to have touched + /// nothing. + struct Named { + instance: Slug, + reads: Cell, + } + + impl Named { + fn new(instance: &str) -> Self { + Self { instance: Slug(instance.into()), reads: Cell::new(0) } + } + } + + impl MindPort for Named { + fn instance(&self) -> &Slug { + &self.instance + } + + fn query(&self, _: &Selection) -> Result { + self.reads.set(self.reads.get() + 1); + Err(anyhow!("reached the mind")) + } + + fn admit(&self, _: &str, _: &str, _: Vec) -> Result { + self.reads.set(self.reads.get() + 1); + Err(anyhow!("reached the mind")) + } + } + + /// One launch by `turn` on a mind named `mind`, holding a Grant read for `grant`. + fn gated(mind: &Named, grant: &str, turn: RunTurn, claims: Vec) -> Result { + let (launcher, clock) = (Recording::default(), Fixed(Cell::new(at(0)))); + let ports = Ports { mind, launcher: &launcher, clock: &clock, host: "yggdrasil-host" }; + let grant = Grant::for_test(Slug(grant.into()), StdDuration::from_secs(60), five()); + let outcome = open_and_launch(&ports, LaunchRequest { turn, claims, agent: "agent-x".into(), grant }); + assert!(launcher.started.borrow().is_empty()); + outcome } #[test] - fn a_grant_read_for_any_other_instance_opens_nothing_and_starts_nothing() { - let rig = Rig::new(); - rig.mind.committed(vec![spec("x")]); - let forged = near_misses(INSTANCE); - // The generator itself: wide enough to carry each family, and the mind's own name is not in it. - assert!(forged.len() > 100_000, "{}", forged.len()); - for member in [format!("{}k", &INSTANCE[..INSTANCE.len() - 1]), INSTANCE.to_uppercase(), format!("{INSTANCE}\0"), INSTANCE.chars().rev().collect::()] { - assert!(forged.contains(&member), "{member:?}"); + fn a_grant_passes_for_exactly_the_minds_own_instance_and_for_no_other_valid_slug() { + let mut draw = Draw(0x6a09_e667_f3bc_c908); + let mut names: Vec = Vec::new(); + for i in 0..780 { + names.push(slug_of(&mut draw, LENGTH_EDGES[i % LENGTH_EDGES.len()])); } - assert!(!forged.iter().any(|name| name == INSTANCE)); - for name in &forged { + for _ in 0..220 { + let length = 1 + draw.below(64); + names.push(slug_of(&mut draw, length)); + } + let mut distinct = 0; + for name in &names { for turn in [RunTurn::SelfRun, RunTurn::PersonaTurn] { let claims = if turn == RunTurn::SelfRun { vec![spec_ref("x")] } else { vec![] }; - let ports = Ports { mind: &rig.mind, launcher: &rig.launcher, clock: &rig.clock, host: "yggdrasil-host" }; - let grant = Grant::for_test(Slug(name.clone()), StdDuration::from_secs(60), five()); - let outcome = open_and_launch(&ports, LaunchRequest { turn, claims, agent: "agent-x".into(), grant }).unwrap(); - assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "{name:?} {turn:?}"); + let mind = Named::new(name); + let through = gated(&mind, name, turn, claims.clone()).unwrap_err(); + assert_eq!(format!("{through:#}"), "reached the mind", "{name:?} {turn:?}: the mind's own Grant passes the gate"); + for other in distinct_valid(name, &mut draw) { + let mind = Named::new(name); + let outcome = gated(&mind, &other, turn, claims.clone()).unwrap(); + assert_eq!(outcome, LaunchOutcome::Refused(Declined::GrantForOtherInstance), "mind {name:?}, grant {other:?}, {turn:?}"); + assert_eq!(mind.reads.get(), 0, "a refused launch reads nothing"); + distinct += 1; + } } } - assert_eq!((rig.runs(), rig.started()), (0, 0)); - assert!(matches!(rig.launch(RunTurn::SelfRun, &[spec_ref("x")], five()), LaunchOutcome::Launched { .. }), "the mind's own Grant launches"); + assert!(distinct > 100_000, "{distinct}"); + // The families the property exists for are in the draw: long names, and both separators. + assert!(names.iter().any(|n| n.len() > 32) && names.iter().any(|n| n.contains('-')) && names.iter().any(|n| n.contains('_'))); } #[test] - fn a_persona_turn_with_any_claims_at_all_is_refused_and_opens_nothing() { - let rig = Rig::new(); - rig.mind.committed(vec![spec("x"), spec("blocked")]); - rig.mind.committed(vec![question_in("fork", &spec_ref("blocked"))]); + fn a_persona_turn_with_any_claims_at_all_is_refused_and_touches_nothing() { let kinds = PipelineKind::ALL; - let refused = |claims: &[PipelineRef]| { - let outcome = rig.launch(RunTurn::PersonaTurn, claims, five()); - assert_eq!(outcome, LaunchOutcome::Refused(Declined::PersonaRunTakesNoClaims), "{} claims", claims.len()); - }; - // Claim sets of every size from one to well past the number of kinds, built four ways: - // one queued spec repeated (duplicates), queued and blocked specs alternating, every kind - // in turn over one id, and every kind in turn over distinct ids. - for size in 1..=300 { - refused(&(0..size).map(|_| spec_ref("x")).collect::>()); - refused(&(0..size).map(|i| spec_ref(if i % 2 == 0 { "x" } else { "blocked" })).collect::>()); - refused(&(0..size).map(|i| reference(kinds[i % kinds.len()], "no-such-document")).collect::>()); - refused(&(0..size).map(|i| reference(kinds[i % kinds.len()], &format!("no-such-document-{i}"))).collect::>()); - } - // And sets far past any bound a refusal could plausibly be written with. - for size in [1_000, 10_000, 100_000] { - refused(&(0..size).map(|i| reference(kinds[i % kinds.len()], &format!("no-such-document-{i}"))).collect::>()); - } - // And every kind alone, repeated, whether or not the document exists. - for kind in kinds { + let mut draw = Draw(0xbb67_ae85_84ca_a73b); + let mut sets: Vec> = Vec::new(); + // Every rotation of the kinds, so no kind or position is the one that starts, at every small size. + for start in 0..kinds.len() { for size in 1..=40 { - refused(&(0..size).map(|_| reference(*kind, "no-such-document")).collect::>()); + sets.push((0..size).map(|i| reference(kinds[(start + i) % kinds.len()], &format!("doc-{i}"))).collect()); + sets.push((0..size).map(|i| reference(kinds[(start + i) % kinds.len()], "doc")).collect()); } } - assert_eq!((rig.runs(), rig.started()), (0, 0)); - assert!(matches!(rig.launch(RunTurn::PersonaTurn, &[], five()), LaunchOutcome::Launched { .. })); + // Every kind alone, repeated. + for kind in kinds { + for size in 1..=40 { + sets.push((0..size).map(|_| reference(*kind, "doc")).collect()); + } + } + // Drawn: every position a kind and an id from a pool of any size (one id repeated, to all distinct), + // at sizes from one to well past any bound a refusal could be written with. + let sizes = [1, 2, 3, 4, 5, 12, 13, 14, 26, 27, 64, 65, 100, 255, 256, 300]; + for round in 0..3_000 { + let size = if round < 600 { sizes[round % sizes.len()] } else { 1 + draw.below(300) }; + let pool = 1 + draw.below(size); + sets.push((0..size).map(|_| reference(kinds[draw.below(kinds.len())], &format!("doc-{}", draw.below(pool)))).collect()); + } + for size in [1_000, 10_000, 100_000] { + sets.push((0..size).map(|i| reference(kinds[draw.below(kinds.len())], &format!("doc-{i}"))).collect()); + } + for claims in sets { + let name = slug_of(&mut draw, LENGTH_EDGES[draw.below(LENGTH_EDGES.len())]); + let mind = Named::new(&name); + let outcome = gated(&mind, &name, RunTurn::PersonaTurn, claims.clone()).unwrap(); + assert_eq!(outcome, LaunchOutcome::Refused(Declined::PersonaRunTakesNoClaims), "{} claims, first {:?}", claims.len(), claims.first()); + assert_eq!(mind.reads.get(), 0); + } + // Without claims the same launch goes on to the mind. + let mind = Named::new("yggdrasil"); + assert_eq!(format!("{:#}", gated(&mind, "yggdrasil", RunTurn::PersonaTurn, vec![]).unwrap_err()), "reached the mind"); } #[test] diff --git a/crates/mind-body/src/lib.rs b/crates/mind-body/src/lib.rs index 8cf4565..598c6f0 100644 --- a/crates/mind-body/src/lib.rs +++ b/crates/mind-body/src/lib.rs @@ -11,8 +11,11 @@ // The control root is a constant, so the library reads no part of the process // environment (clippy.toml lists the readers); `forbid` keeps an `allow` from -// putting one back. +// putting one back. `forbid(unsafe_code)` closes the readers the list cannot +// name: a foreign `getenv` or any other FFI call needs `unsafe`. The rest of the +// Grant rule is ruling `eureka-body:ruling:mind-launch-forge-scanner-bound`. #![forbid(clippy::disallowed_methods)] +#![forbid(unsafe_code)] pub mod cli; pub mod control; diff --git a/crates/mind-body/tests/public_surface.rs b/crates/mind-body/tests/public_surface.rs index 464cba6..9f2e0a2 100644 --- a/crates/mind-body/tests/public_surface.rs +++ b/crates/mind-body/tests/public_surface.rs @@ -1,27 +1,29 @@ -//! The library's public surface, pinned as a whole. +//! The library's public surface, pinned as a tripwire. //! -//! The brake's rule is that no caller can name a root, a source or a way to make -//! a `Grant`. A test that names the routes it knows (`with_root`, `TEST_ROOT`) -//! passes on every route it does not name. This one reads the library's source -//! the way a consumer sees it and compares everything a consumer could reach to -//! `public_surface.txt`: every `pub` item with its signature, every `pub` field, -//! the derives and the header of every impl, every string literal that looks like -//! a path, and every `static` keyword (a process-global is the one channel -//! through which an existing public function's body could carry a root). A new -//! `pub static`, a new `pub fn` under any name, a `Clone` or `Default` derived -//! onto `Grant`, a root read from a file other than the constant: each is a line -//! this file does not have, and the test fails. +//! This reads the library's source the way a consumer sees it and compares +//! every `pub` item with its signature, every `pub` field, the derives and the +//! header of every impl, to `public_surface.txt`. A new `pub fn` under any name, +//! a `pub static`, a `Clone` or `Default` derived onto `Grant`, a new way to +//! reach the brake from outside: each is a line the snapshot does not have, and +//! the test fails. A deliberate change is a change to `public_surface.txt` in the +//! same commit, where a reviewer sees it; the failure prints the full current +//! surface to copy from. //! -//! Items under `#[cfg(test)]` are not the library and are skipped; `cfg(not(test))` -//! and the `unix`/`windows` splits are the library and are read. Any other `cfg`, a `#[path]`, an item macro, a -//! `macro_rules!`, an `extern` block or an exported symbol attribute would hide -//! an item from this reader, so each fails the test outright. A seam that is -//! `pub(crate)` and not `cfg(test)` is outside this reader's concern and inside -//! clippy's: it is dead code in the library build, and `-D warnings` refuses it. +//! What it sees: items reached through `mod` declarations and module files, +//! `cfg(test)` skipped (a test-only item is not the library), the `unix` and +//! `windows` splits read on both sides. An item macro, `macro_rules!`, `extern` +//! block, `#[path]`, an exported-symbol attribute or any other `cfg` fails the +//! test outright, because each would hide an item from this reader. //! -//! A deliberate change to the surface is a change to `public_surface.txt` in the -//! same commit, which is where a reviewer sees it. The failure prints the full -//! current surface to copy from. +//! What it cannot see, by design: items nested in function or `const _` bodies +//! (an `impl Grant` inside a const block), and what a function body does. It +//! proves nothing about how the control root is derived, and it does not defend +//! the crate against edits to its own source. That bound is ruling +//! `eureka-body:ruling:mind-launch-forge-scanner-bound`: the Grant is held by the +//! type system (private fields, no constructor, the `compile_fail` examples on +//! `Grant`), unsafe code is forbidden in the library, and changes to +//! `control.rs` and `launch.rs` are defended by review and by the root-owned +//! brake check at the unit. Do not extend this file into a walker of bodies. use std::collections::BTreeSet; use std::fs; @@ -72,40 +74,6 @@ fn text(node: impl ToTokens) -> String { without_docs(node.to_token_stream()).to_string() } -/// The string literals in `stream` that look like a path. -fn path_literals(stream: TokenStream, out: &mut BTreeSet) { - for token in stream { - match token { - TokenTree::Group(group) => path_literals(group.stream(), out), - TokenTree::Literal(literal) => { - let rendered = literal.to_string(); - if rendered.starts_with('"') && rendered.contains('/') { - out.insert(format!("path-literal {rendered}")); - } - } - _ => {} - } - } -} - -/// How many `static` items or declarations `stream` holds (a `'static` lifetime is not one). -/// A process-global is the one way an existing public function's body could carry a -/// root from one call to another, so every one in the library is in the snapshot. -fn static_keywords(stream: TokenStream) -> usize { - let mut count = 0; - let mut after_tick = false; - for token in stream { - let is_tick = matches!(&token, TokenTree::Punct(p) if p.as_char() == '\''); - match token { - TokenTree::Group(group) => count += static_keywords(group.stream()), - TokenTree::Ident(name) if name == "static" && !after_tick => count += 1, - _ => {} - } - after_tick = is_tick; - } - count -} - fn attributes(item: &Item) -> &[Attribute] { match item { Item::Const(i) => &i.attrs, @@ -157,32 +125,11 @@ fn derives(attrs: &[Attribute]) -> String { attrs.iter().filter(|a| a.path().is_ident("derive") || a.path().is_ident("repr")).map(text).collect::>().join(" ") } -fn label(item: &Item) -> String { - match item { - Item::Fn(i) => i.sig.ident.to_string(), - Item::Const(i) => i.ident.to_string(), - Item::Static(i) => i.ident.to_string(), - Item::Struct(i) => i.ident.to_string(), - Item::Enum(i) => i.ident.to_string(), - Item::Trait(i) => i.ident.to_string(), - Item::Type(i) => i.ident.to_string(), - _ => "an item".to_string(), - } -} - fn walk(items: &[Item], path: &str, dir: &Path, out: &mut BTreeSet) { for item in items { if !in_library(attributes(item), path) { continue; } - if !matches!(item, Item::Impl(_) | Item::Mod(_)) { - let stream = without_docs(item.to_token_stream()); - path_literals(stream.clone(), out); - let statics = static_keywords(stream); - if statics > 0 { - out.insert(format!("{path}: {statics} static keyword(s) in {}", label(item))); - } - } match item { Item::Fn(f) if is_pub(&f.vis) => { out.insert(format!("{path}: {}", text(&f.sig))); @@ -220,7 +167,6 @@ fn walk(items: &[Item], path: &str, dir: &Path, out: &mut BTreeSet) { None => format!("impl{} {}", text(&i.generics), text(&i.self_ty)), }; out.insert(format!("{path}: {header}")); - path_literals(without_docs(i.generics.to_token_stream()), out); for member in &i.items { let attrs = match member { ImplItem::Const(m) => &m.attrs, @@ -232,12 +178,6 @@ fn walk(items: &[Item], path: &str, dir: &Path, out: &mut BTreeSet) { if !in_library(attrs, path) { continue; } - let stream = without_docs(member.to_token_stream()); - path_literals(stream.clone(), out); - let statics = static_keywords(stream); - if statics > 0 { - out.insert(format!("{path}: {statics} static keyword(s) in a member of impl {}", text(&i.self_ty))); - } if i.trait_.is_none() { match member { ImplItem::Fn(f) if is_pub(&f.vis) => { @@ -308,7 +248,7 @@ fn the_librarys_public_surface_is_the_committed_one() { fn the_reader_sees_what_it_claims_to() { // The snapshot must hold the brake's own names, or the reader is blind to them. let actual = surface(); - for needle in ["mind_body::control: fn read_effective", "mind_body::control: struct Grant", "path-literal \"/etc/gamecult/minds\""] { + for needle in ["mind_body::control: fn read_effective", "mind_body::control: struct Grant"] { assert!(actual.iter().any(|line| line.contains(needle)), "{needle} is not in the surface"); } // And nothing under cfg(test) leaked in. diff --git a/crates/mind-body/tests/public_surface.txt b/crates/mind-body/tests/public_surface.txt index cc15f2f..6032314 100644 --- a/crates/mind-body/tests/public_surface.txt +++ b/crates/mind-body/tests/public_surface.txt @@ -69,4 +69,3 @@ mind_body::queue: impl MindPort for HuginnMind < '_ > mind_body::queue: impl< 'a > HuginnMind < 'a > mind_body::queue: pub trait MindPort { fn instance (& self) -> & Slug ; fn query (& self , selection : & Selection) -> Result < PipelineSelectionPage > ; fn admit (& self , agent : & str , session : & str , documents : Vec < PipelineDocument >) -> Result < PipelineAdmissionOutcome > ; } mind_body::queue: struct HuginnMind< 'a > pub-fields[] -path-literal "/etc/gamecult/minds"