Skip to content

Type-check expectation arguments when precept is disabled - #57

Merged
carlsverre merged 1 commit into
mainfrom
typecheck-disabled-expectations
Jul 14, 2026
Merged

Type-check expectation arguments when precept is disabled#57
carlsverre merged 1 commit into
mainfrom
typecheck-disabled-expectations

Conversation

@carlsverre

Copy link
Copy Markdown
Contributor

Problem

When the enabled feature is off, every macro in macros_stubs.rs expands to {}, discarding its condition and details expressions entirely. That has two consequences:

  1. Arguments aren't type-checked in the disabled build. A typo or type error inside an expect_*! / observe! block only surfaces when the crate is compiled with enabled. This defeats the "same source compiles identically in every configuration" guarantee that makes observe! blocks safe to leave inline in the system under test.

  2. Temporaries that exist only to feed an expectation become unused. In a downstream crate built with -D warnings, a perfectly ordinary pattern warns (and fails CI):

    observe!(|| {
        let sum: u64 = inputs.iter().sum();          // only used by the assertion
        expect_always!(sum == expected, "conserved", { "sum": sum });
    });

    With enabled off, expect_always! vanishes and sum is now unused_variables / unused_assignments.

Fix

Rewrite each disabled stub to reference its arguments inside a dead if false { … } block:

macro_rules! define_and_emit_entry {
    ($expectation:expr, $property:expr, $condition:expr $(, $($details:tt)+)?) => {
        if false {
            let _ = $expectation;
            let _ = &$property;
            let _ = $condition;
            $( let _ = $crate::deps::serde_json::json!($($details)+); )?
        }
    };
}

if false { … } is exactly the right tool here:

  • Zero runtime overhead — the branch is provably unreachable and eliminated before codegen, so the arguments are never evaluated at runtime. The crate's "0 runtime overhead when disabled" promise is preserved.
  • Still fully type- and borrow-checked — dead code is still checked, so a type error in an expectation now fails to compile in every configuration.
  • Marks captures as usedlet _ = $condition / json!($details) reference every temporary, silencing both unused_variables and the liveness assigned…never read lints.

Argument evaluation mirrors the enabled path in macros.rs (by value, same moves/borrows), so anything that compiles with enabled also compiles without it.

Only macros_stubs.rs changes; the enabled path (macros.rs) is untouched.

Verification

  • cargo test passes with and without enabled.
  • cargo clippy --all-targets -- -D warnings clean with and without enabled (modulo a pre-existing fault.rs assertions_on_constants lint unrelated to this change).
  • A type error inside a disabled expect_always! still fails to compile (E0277).
  • A downstream crate whose observe! block has an assertion-only temporary no longer warns under -D warnings, in both feature configs.

🤖 Generated with Claude Code

When the `enabled` feature is off, the stubs in `macros_stubs.rs` expanded to
`{}`, discarding their condition and details expressions entirely. Two problems
followed:

1. The arguments were never type-checked in the disabled build, so a typo or
   type error in an `expect_*!` / `observe!` block only surfaced when the crate
   was compiled with `enabled` — defeating the "same source compiles identically
   in every configuration" guarantee that makes `observe!` blocks safe to leave
   inline.

2. Any temporary that existed solely to feed an expectation became an unused
   variable, tripping `unused_variables` / `unused_assignments` in downstream
   crates built with `-D warnings`.

Rewrite each stub to reference its arguments inside a dead `if false { … }`
block: fully type- and borrow-checked (dead code is still checked), every
captured expression is marked used, and the branch is eliminated before codegen
— preserving the crate's zero-runtime-overhead promise (arguments are never
evaluated at runtime). Argument evaluation mirrors the enabled path in macros.rs
(by value, same moves/borrows).

Verified: `cargo test` and `cargo clippy --all-targets -- -D warnings` pass with
and without `enabled`; a type error inside a disabled `expect_always!` still
fails to compile; and a downstream crate with an `observe!`-only temporary no
longer warns under `-D warnings`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@carlsverre
carlsverre merged commit ebbdf97 into main Jul 14, 2026
3 checks passed
@carlsverre
carlsverre deleted the typecheck-disabled-expectations branch July 14, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant