Skip to content

SDK DX: anyhow interop, bare check!, macro error spans, suite-level tags #40

Description

@lann

From the adversarial review (2026-08-04). SDK/macro ergonomics found by writing probe suites; all verified against real compiler output. Ordered by expected M1 porting pain:

  1. ? does not work with anyhow::Error in case bodies: the blanket impl<E: std::error::Error> From<E> for Failure (crates/component-test-core/src/verdict.rs) excludes anyhow::Error (which deliberately doesn't implement Error), and the workaround type Failure isn't in the prelude the macro injects. Porting a conformance suite means fallible helpers everywhere; today every call site needs a hand-rolled map_err plus tribal knowledge of the import. Proposal: prelude extension trait (or_fail() on Result<T, E: Display>) and/or export Failure in the prelude; document the anyhow gap on failed().
  2. check!(cond) without a message dies with unexpected end of macro invocation — everyone arriving from assert! writes this first. Add a ($cond:expr $(,)?) arm using stringify!($cond) as detail, or a targeted compile_error!.
  3. Diagnostic-anchor spans: the decline-pair lint anchors at Span::call_site() (attribute line) instead of the first offending tagged case (spans are already collected); verdict-shape/type errors also land the primary caret on the attribute (arity is checked syntactically, types aren't) — in a 50-case file users read secondary notes to find the case. Anchor at the case fn (e.g. a typed local let __f: fn(&TestContext) -> _ = #path; spanned at the fn).
  4. No way to tag a whole suite: root-level #[cases(tags(...))] dies with cannot find attribute. Support #[suite(tags(...))] or emit a targeted "put it on an inner module" error.
  5. Non-leaf WIT-label violations point at the case fn, not the offending module ident; and NameError::BadChar says contains forbidden character 'A' without stating the grammar — the message isn't frozen, improve it.
  6. The two example suites model two diagnostic APIs: fixture-suite (the #[suite] DX showcase) uses raw ctx.diagnostic("...".into()) while sample uses the curated ctx.diag("..."). Unify on diag (goldens pin output, not source — no regeneration needed).

Related: #28.

Metadata

Metadata

Assignees

No one assigned

    Labels

    sdkGuest-side libraries for authoring suites

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions