Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/errors/checks/verbose-cascade.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ The root cause is **present and repeated** — the same `HasField<Symbol!("name"

`cargo-cgp` collapses the whole cascade to a single block. First it **drops every intermediate provider-trait block** as derived noise and recovers each *checked* component's failure to a `root cause:` tree that descends all the way to the leaf. Then it **coalesces** the checked components that share that leaf: rather than one block per component, it emits one `[CGP-E001]` headline naming every affected consumer trait — `` the consumer traits `CanBaz`, `CanBar`, and `CanFoo` are not implemented for context `App` `` — with a caret at each check entry and a single `root cause: [CGP-E106] missing field \`name\`` tree beneath. So a six-block raw cascade over three checked components becomes one block, and cargo's re-count reports one error. A component rustc happened to surface provider-side (a lone `[CGP-E002]` block) is worded uniformly as a consumer in the merged headline, since a `check_components!` entry failing *is* the consumer trait failing.

Listing every affected consumer in one headline is possible because the tool holds the compilation's diagnostics until they have all arrived (there is no end-of-compilation hook), then groups them by a consumer-independent cause signature. The codes are defined in the [cargo-cgp error-code catalog](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/error-code.md).
Listing every affected consumer in one headline is possible because the tool holds the compilation's diagnostics until they have all arrived (there is no end-of-compilation hook), then groups together the ones that *share* a root cause. Grouping on a shared cause rather than on a whole-failure key is what also reaches the harder shape, where one omission is reached at several instantiations and so surfaces as several root causes: each check entry stops at the first unmet leaf on its own branch while a use-site call reaches them all, so no two of those cause sets are equal and grouping on equality left one mistake reported as several blocks. The codes are defined in the [cargo-cgp error-code catalog](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/error-code.md).

## Resolving it

Fix the single root cause and the entire cascade collapses at once — supply the `name` field, and all six blocks disappear together. The difficulty is isolation, not repair, so the [debugging guide](../../guides/debugging.md) prescribes two moves: **check one suspect component in isolation** in its own `check_components!` block, which strips the cascade down to that component's chain, or **bisect** a large table by commenting entries out until the failure vanishes. Both converge on the one broken link far faster than reading the cascade top to bottom.

## Notes for tooling

`cargo-cgp` reshapes this class in full, as [How cargo-cgp presents it](#how-cargo-cgp-presents-it) describes: it drops the intermediate provider-trait blocks, recovers each checked component's innermost concrete cause, and coalesces the components that share one leaf into a single headline that lists every affected consumer over one root-cause tree. The coalescing keys on a consumer-independent cause signature, so it generalizes to any set of checked components bottoming out on the same root cause, not just a field.
`cargo-cgp` reshapes this class in full, as [How cargo-cgp presents it](#how-cargo-cgp-presents-it) describes: it drops the intermediate provider-trait blocks, recovers each checked component's innermost concrete cause, and coalesces the components that share one leaf into a single headline that lists every affected consumer over one root-cause tree. The coalescing keys on the root causes themselves, consumer-independently and one cause at a time, so it generalizes to any set of checked components bottoming out on the same root causenot just a field, and not only components whose causes match exactly, since failures whose cause sets merely overlap are grouped too.

## Backing fixtures

Expand Down
9 changes: 8 additions & 1 deletion docs/guides/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,14 @@ A scratch module or throwaway test is the natural place for this — it compiles

## Inspect what the macro actually emitted

When a reproduction still puzzles you, stop guessing about the expansion and look at it. The wiring failure is always "the emitted impls do not resolve," and you cannot reason about why until you see the impls. In any project, `cargo expand` prints the macro-expanded source directly; in a project set up with the CGP test utilities, the `snapshot_*!` helpers in `cgp-macro-test-util` (`snapshot_cgp_impl!`, `snapshot_delegate_components!`, `snapshot_cgp_namespace!`, …) additionally emit the real generated code into the module *and* pretty-print it into an inline snapshot you can review and keep. Either way, reading the generated `where` clauses and the exact key types is often enough to spot the defect — a bound on the wrong type, a path with a segment too many, a parameter that should not be there.
When a reproduction still puzzles you, stop guessing about the expansion and look at it. The wiring failure is always "the emitted impls do not resolve," and you cannot reason about why until you see the impls. [`cargo cgp expand`](../reference/cargo-cgp.md#expanding-the-generated-code) is the way to see them: it prints the crate after macro expansion with CGP's type-level constructs resugared, so a key that the error rendered as a `Symbol<6, Chars<'h', …>>` spine reads `Symbol!("height")` here. Narrow it to the construct in question rather than reading a whole crate — naming a provider trait gives you every impl of it, which is exactly "what did this component generate":

```sh
cargo cgp expand --lib --item AreaCalculator # a trait: its definition and every impl of it
cargo cgp expand --lib --item contexts::MockApp # a type: its HasField impls and its wiring entries
```

Reading the generated `where` clauses and the exact key types is often enough to spot the defect — a bound on the wrong type, a path with a segment too many, a parameter that should not be there. Where `cargo cgp expand` is unavailable, plain `cargo expand` shows the same expansion without the resugaring; and in a project set up with the CGP test utilities, the `snapshot_*!` helpers in `cgp-macro-test-util` (`snapshot_cgp_impl!`, `snapshot_delegate_components!`, `snapshot_cgp_namespace!`, …) additionally emit the real generated code into the module *and* pretty-print it into an inline snapshot you can review and keep.

Comparing two expansions side by side localizes a difference precisely. When one form works and another does not, expand both and diff the output; the delta is the bug. This is how a `#[default_impl]` defect can be pinned: the namespace-registration impl it emitted carried a `where` clause that the equivalent body entry did not, and seeing the two expansions next to each other made the leaked clause obvious. Capturing the expansion as a snapshot doubles as a permanent regression test, so the effort is not throwaway.

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ This index is the catalog of constructs. When you add, remove, or rename a const

One document here describes a tool rather than a construct, and it is the exception to the per-construct rule above: it has no subdirectory and no Syntax/Expansion shape. It is registered here because a reader looking up how to *read* a CGP error belongs in the reference.

- [`cargo-cgp`](cargo-cgp.md) — CGP's first-class error toolchain: the cargo subcommand that rewrites CGP compiler errors into a readable, root-cause-first form, how to install and run it, and how its `[CGP-Exxx]` output maps to the [error catalog](../errors/README.md). Recommend it for building, checking, and debugging CGP code.
- [`cargo-cgp`](cargo-cgp.md) — CGP's first-class error toolchain: the cargo subcommand that rewrites CGP compiler errors into a readable, root-cause-first form, how to install and run it, how its `[CGP-Exxx]` output maps to the [error catalog](../errors/README.md), and its companion `expand` command, which shows the ordinary Rust a target's CGP macros generate with the type-level constructs resugared. Recommend it for building, checking, and debugging CGP code.

## Component definition macros — [macros/](macros/)

Expand Down
67 changes: 63 additions & 4 deletions docs/reference/cargo-cgp.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# cargo-cgp — the CGP error toolchain

`cargo-cgp` is CGP's first-class toolchain: a cargo subcommand that stands in for `cargo check` and rewrites CGP's compiler errors into a compact, root-cause-first form. It is the **recommended way to build and check CGP code and to read a CGP compile error**, and an agent working in this repository should reach for it before plain `cargo check` when diagnosing a wiring failure. This document is enough to install and use it without leaving `cgp`; the tool itself lives in a separate repository, [github.com/contextgeneric/cargo-cgp](https://github.com/contextgeneric/cargo-cgp), and its own docs are the exhaustive reference.
`cargo-cgp` is CGP's first-class toolchain: a cargo subcommand that stands in for `cargo check` and rewrites CGP's compiler errors into a compact, root-cause-first form, and that also expands a target's macros with CGP's type-level constructs made legible. It is the **recommended way to build and check CGP code and to read a CGP compile error**, and an agent working in this repository should reach for it before plain `cargo check` when diagnosing a wiring failure. This document is enough to install and use it without leaving `cgp`; the tool itself lives in a separate repository, [github.com/contextgeneric/cargo-cgp](https://github.com/contextgeneric/cargo-cgp), and its own docs are the exhaustive reference.

This is a *tooling* reference, not a CGP construct — it is the one document under `reference/` that describes an external tool rather than a macro, trait, or provider. The [error catalog](../errors/README.md) is its companion: the catalog documents each error *class* and how `cargo-cgp` presents it, while this document covers running the tool.
This is a *tooling* reference, not a CGP construct — it is the one document under `reference/` that describes an external tool rather than a macro, trait, or provider. The [error catalog](../errors/README.md) is its companion: the catalog documents each error *class* and how `cargo-cgp` presents it, while this document covers running the tool — both its `check` and its [`expand`](#expanding-the-generated-code) command.

## Why it exists

Expand Down Expand Up @@ -49,7 +49,7 @@ cargo cgp check # like `cargo check`, with CGP errors reshaped
cargo cgp check --workspace # every argument after `check` is forwarded to `cargo check`
```

`check` is the only command cargo-cgp adds to your build workflow — it re-compiles your code under the pinned nightly purely to produce readable diagnostics. There is no `cargo cgp build`, `run`, or `test`; run those with plain cargo (besides `check`, only `setup` and `update` exist, and they merely provision the tool). **cargo-cgp is optional, and its one advantage is readable errors during development.** CGP itself is an ordinary library that compiles on any stable Rust ≥ 1.89, so `cargo check`, `cargo build`, `cargo run`, and `cargo test` all work on a CGP project unchanged. Reach for `cargo cgp check` when you hit — or expect — a wiring error and want it readable; use plain `cargo check` when you do not; and always build, run, and test with ordinary cargo.
`check` is the command you reach for day to day; its companion [`expand`](#expanding-the-generated-code) shows the Rust your macros generate. Neither changes how you build: both re-compile your code under the pinned nightly purely to show you something, and there is no `cargo cgp build`, `run`, or `test`; run those with plain cargo (besides these two, only `setup` and `update` exist, and they merely provision the tool). **cargo-cgp is optional, and its advantage is readable errors — and readable expansions — during development.** CGP itself is an ordinary library that compiles on any stable Rust ≥ 1.89, so `cargo check`, `cargo build`, `cargo run`, and `cargo test` all work on a CGP project unchanged. Reach for `cargo cgp check` when you hit — or expect — a wiring error and want it readable; use plain `cargo check` when you do not; and always build, run, and test with ordinary cargo.

Read its output as ordinary rustc/cargo diagnostics with the recognized CGP errors rewritten. When the tool rewrites an error into a known class it stamps a short code in brackets and leads with the cause:

Expand All @@ -74,6 +74,65 @@ To use it as **Rust Analyzer's** on-save check backend so the reshaped errors ap

Never apply this to a user's Rust Analyzer configuration on your own initiative — present it as an option and edit their editor settings only when they explicitly ask.

## Expanding the generated code

`cargo cgp expand` prints the crate as the compiler sees it after macro expansion, which answers the question a CGP error cannot: *what did that macro actually generate?* Reach for it when a wiring failure stops making sense from the message alone — the failure is always "the emitted impls do not resolve," and you cannot reason about which impl is wrong until you read it — or simply to confirm what a construct produces before trusting your memory of it.

What makes it worth using over plain `cargo expand` is that CGP's type-level constructs come back **resugared**: a field tag reads `Symbol!("width")` rather than the raw `Symbol<5, Chars<'w', …>>` spine the compiler prints, a handler pipeline reads `Product![StepOne, StepTwo]`, and a namespace key reads `Path!(@app.GreeterComponent)`. Everything else is a full expansion — `#[derive(Debug)]` and `println!` appear in their generated form too — so what you get is the whole program with the CGP parts legible. Expanding the [area-calculation](../examples/area-calculation.md) example's `rectangle_area` function shows the shape:

```sh
cargo cgp expand --lib --item RectangleArea
```

```rust
pub trait RectangleArea {
fn rectangle_area(&self) -> f64;
}
impl<__Context__> RectangleArea for __Context__
where
Self: HasField<Symbol!("width"), Value = f64>
+ HasField<Symbol!("height"), Value = f64>,
{
fn rectangle_area(&self) -> f64 {
let width: f64 = self
.get_field(::core::marker::PhantomData::<Symbol!("width")>)
.clone();
let height: f64 = self
.get_field(::core::marker::PhantomData::<Symbol!("height")>)
.clone();
width * height
}
}
```

Reading that answers two questions at once: what the trait `#[cgp_fn]` generated looks like, and what the `#[implicit]` arguments turned into — a `HasField` bound per argument and a `get_field` call that clones the value.

**It expands exactly one target**, so a package with both a library and a binary needs `--lib` or `--bin NAME`; without one, cargo declines with *"extra arguments to `rustc` can only be passed to one target"*. Everything else is forwarded to `cargo rustc`, so `-p`, `--features`, and the rest work as usual, and the expansion goes to stdout so it pipes and redirects:

```sh
cargo cgp expand --lib # the whole library target
cargo cgp expand --bin server # one binary
cargo cgp expand --lib > expanded.rs # stdout, so redirect or pipe freely
```

**A whole crate's expansion is long, so `--item <path>` narrows it to one part.** The path names a module or item inside the crate being expanded, written as a `::`-separated path with an optional leading `crate::` (`self::` and a bare `::` work too), and what it selects depends on what it names:

```sh
cargo cgp expand --lib --item contexts # a module: its contents
cargo cgp expand --lib --item contexts::MockApp # a type: its declaration and every impl for it
cargo cgp expand --lib --item AreaCalculator # a trait: its definition and every impl of it
```

The **trait** form is the one to reach for on CGP code, because a component's generated items are almost all impls and an impl has no name of its own. Naming a component's *provider* trait (`--item AreaCalculator`) gives you the provider trait definition, the delegation blanket impl every wired context resolves through, the [`UseContext`](providers/use_context.md) and [`RedirectLookup`](providers/redirect_lookup.md) impls, and each provider's own impl of it — the whole set of things that can answer "which provider serves this component, and under what bounds?" Naming the *consumer* trait (`--item CanCalculateArea`) is the smaller companion: the consumer trait and the one blanket impl that routes it to the provider trait.

The **type** form is what you want for a context. Naming a context (`--item Rectangle`) gives its struct, the `HasField`/`HasFieldMut` impls its [`#[derive(HasField)]`](derives/derive_has_field.md) generated, and its `DelegateComponent` wiring entries with the real key and provider types — which is how you check that a wiring table produced the keys you intended rather than inferring them from the table's syntax.

If a path matches nothing you get an error saying so, naming the path, rather than a silent whole-crate expansion.

Two limits are worth knowing. `expand` is **not a check**: it stops as soon as the macros are expanded, so type-checking never runs and it reports nothing about wiring — a malformed macro invocation still fails, since that happens during expansion, but a missing field does not. And the output is meant to be *read* rather than compiled: the `cgp::macro_prelude::` qualifier the macros emit is stripped for legibility, and an `open` statement's per-key wiring entry keeps its raw `PathCons<…>` key, since its tail is a generic parameter that no `Path!` spelling covers.

`expand` is newer than the v0.1.0-alpha release, so a `cargo install cargo-cgp` from crates.io does not yet carry it. Until the next release, get it by dropping the tag from the Nix reference (`nix run github:contextgeneric/cargo-cgp -- expand --lib`, which tracks `main`) or by [building from a checkout](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/reference/installation.md#installing-from-source).

## When cargo-cgp is not available

If the tool is not installed and cannot be run, fall back to reading the raw compiler output directly. Every class in the [error catalog](../errors/README.md) documents that raw diagnostic — its code, shape, and whether the root cause is present — as the fallback for exactly this case, and the [error-extraction sub-skill](https://github.com/contextgeneric/cgp/blob/main/docs/skills/cgp/references/error-extraction.md) is the technique for reducing a raw cascade to its cause by hand. The key difference to remember: without `cargo-cgp`'s next-gen solver, the [hidden classes](../errors/hidden/unsatisfied-dependency.md) genuinely omit the cause, so promote them with a `check_components!` at the wiring site to make it appear.
Expand All @@ -86,7 +145,7 @@ This documentation is written for **`cgp` v0.8.0** and **`cargo-cgp` v0.1.0-alph

All in the `cargo-cgp` repository (link as GitHub URLs, read from `../cargo-cgp` locally when checked out):

- [Usage](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/reference/usage.md) — running the check, reading output, editor integration.
- [Usage](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/reference/usage.md) — running the check, reading output, expanding a target, editor integration.
- [Installation](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/reference/installation.md) — every install/update/uninstall path.
- [Error-code catalog](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/error-code.md) — what each `[CGP-Exxx]` means.
- [Troubleshooting](https://github.com/contextgeneric/cargo-cgp/blob/main/docs/reference/troubleshooting.md) — when the tool itself will not run.
Loading
Loading