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
11 changes: 10 additions & 1 deletion crates/tests/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,9 @@ implementation document, so the fixture tree mirrors [docs/implementation/entryp
Within a subdirectory, write one fixture file per case, named for the failure mode it
probes (`duplicate_key.rs`, `missing_dependency.rs`), and open each with a comment
stating what it exercises and why it must not compile — exactly as the main suite
requires. The driver [tests/compile_fail_tests.rs](cgp-compile-fail-tests/tests/compile_fail_tests.rs)
requires. **Keep each fixture subdirectory small — no more than about a dozen cases —
and split into further nested subdirectories when one grows past that**; the driver's
`**` glob picks up the new level with no registration. The driver [tests/compile_fail_tests.rs](cgp-compile-fail-tests/tests/compile_fail_tests.rs)
globs both trees with `**`, so the two `t.compile_fail(...)` calls pick up a new
fixture with no per-file registration. A single `trybuild::TestCases` runs both globs
— do not split them across two `#[test]` functions, which would race on the shared
Expand All @@ -192,6 +194,13 @@ capture the expanded code as an `insta` inline string snapshot in the
compiles even though the code would not), with a comment explaining **why** the
output is wrong and **what the correct output should be**.

Post-codegen compile-fail cases are additionally cataloged in the [error catalog](../../docs/errors/README.md)
under `docs/errors/`, which is becoming the canonical, reader-facing documentation for
them (see [docs/errors/AGENTS.md](../../docs/errors/AGENTS.md)): a fixture cross-links to
the error *class* it exercises there, and the catalog indexes the fixture. This migration
proceeds class by class — until a class is migrated, its fixture continues to cross-link
to the owning macro's implementation document as described below.

Every failure case must also be recorded in the owning macro's **implementation
document** under `docs/implementation/`, and *which section* holds it is what the
acceptable/problematic split decides. An **acceptable** fixture documents *intended*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! whole `#[cgp_component(..)]` attribute — the leak the span fix removed so that
//! cross-crate go-to-definition on the marker resolves to the provider name alone.
//!
//! See docs/implementation/entrypoints/cgp_component.md (Failure modes).
//! See docs/errors/wiring/conflicting-wiring.md.

use cgp::prelude::*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! cgp-macro-core/src/types/attributes/default_impl/attribute.rs). A regression
//! that dropped the re-span would move the carets back onto the macro attribute.
//!
//! See docs/implementation/entrypoints/cgp_impl.md (Failure modes).
//! See docs/errors/wiring/conflicting-wiring.md.

use cgp::core::component::DefaultImpls1;
use cgp::prelude::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! The E0119 carets fall on each provider `impl` block, since those impls are the
//! user's own `#[cgp_impl]` blocks rewritten in place.
//!
//! See docs/implementation/entrypoints/cgp_impl.md (Failure modes).
//! See docs/errors/wiring/conflicting-wiring.md.

use cgp::prelude::*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! `impl AppNamespace<_> for PathCons<Symbol<"app">, PathCons<AnnouncerComponent, Nil>>`,
//! whose trait (`AppNamespace`) and every element of the `Self` type (`PathCons`
//! and `Symbol` from `cgp`, `AnnouncerComponent` from crate-a) are all foreign to
//! this crate — an orphan-rule violation (E0117). A per-component default keyed on
//! this crate — an orphan-rule violation (E0210). A per-component default keyed on
//! a *prefix path* can therefore only be written in the crate that owns the
//! namespace; a downstream crate must own the key, which for a prefixed component
//! it does not. (The orphan-safe counterpart — a *local* component key registered
Expand All @@ -17,7 +17,7 @@
//! crate and why the guide recommends namespace *body* entries for wiring that
//! must live downstream of the namespace.
//!
//! See docs/implementation/entrypoints/cgp_namespace.md (Failure modes).
//! See docs/errors/wiring/orphan-rule.md.

use cgp::prelude::*;
use cgp_test_crate_a::{Announcer, AnnouncerComponent, AppNamespace, HasName};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! `build_namespace_impl` (`mapping/eval.rs`) regresses, the caret snaps back to
//! the block and this `.stderr` changes.
//!
//! See docs/implementation/entrypoints/cgp_namespace.md (Error spans).
//! See docs/errors/wiring/conflicting-wiring.md; error-span mechanics in docs/implementation/entrypoints/cgp_namespace.md.

use cgp::prelude::*;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//! Acceptable failure: a `for` loop that wires a **bare key** (`Key: Value`)
//! instead of embedding it in a path, in a context that also joins a namespace —
//! the two blanket impls overlap and the compiler rejects them with `E0119`.
//!
//! `namespace DefaultNamespace;` emits a blanket `impl<Key, Value>
//! DelegateComponent<Key> for App where Key: DefaultNamespace<App, ..>` (plus the
//! matching `IsProviderFor` forwarding) that covers *every* key. A `for <Key, Value>
//! in GreeterTable { Key: Value }` loop emits a second blanket `impl<Key, Value>
//! DelegateComponent<Key> for App where Key: GreeterTable<App, ..>` — also over every
//! key — and the two overlap because a key could satisfy both `where` clauses, so
//! coherence rejects the pair (`E0119`, fully generic `DelegateComponent<_>` /
//! `IsProviderFor<_, _, _>`). This is why a loop key must sit inside a path
//! (`@app.SomeComponent.Key: Value`), which keys the impl on a concrete path rather
//! than on every key. CGP lowers both blanket impls faithfully; only the whole
//! program reveals the overlap, so it defers to the compiler.
//!
//! This is the blanket-vs-blanket shape of the conflicting-wiring class; contrast
//! the specific-key override in override_registered_path.rs.
//!
//! See docs/errors/wiring/conflicting-wiring.md.

use cgp::prelude::*;

#[cgp_component(Greeter)]
pub trait CanGreet {
fn greet(&self) -> String;
}

#[cgp_impl(new GreetHello)]
impl Greeter {
fn greet(&self) -> String {
"Hello".to_owned()
}
}

cgp_namespace! {
new GreeterTable {
GreeterComponent: GreetHello,
}
}

pub struct App;

delegate_components! {
App {
namespace DefaultNamespace;

for <Key, Value> in GreeterTable {
Key: Value,
}
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0119]: conflicting implementations of trait `IsProviderFor<_, _, _>` for type `App`
--> tests/acceptable/cgp_namespace/for_loop_bare_key.rs:49:13
|
46 | namespace DefaultNamespace;
| ---------------- first implementation here
...
49 | Key: Value,
| ^^^ conflicting implementation for `App`

error[E0119]: conflicting implementations of trait `DelegateComponent<_>` for type `App`
--> tests/acceptable/cgp_namespace/for_loop_bare_key.rs:49:13
|
46 | namespace DefaultNamespace;
| ---------------- first implementation here
...
49 | Key: Value,
| ^^^ conflicting implementation for `App`
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Acceptable failure: two namespaces that inherit from each other, so resolving
//! any key through either one chases the other and back with no terminating step —
//! a trait-resolution cycle the compiler reports as `E0275` overflow.
//!
//! `new NamespaceA: NamespaceB` emits the inheritance blanket impl
//! `impl<Table, Key, Value> NamespaceA<Table> for Key where Key: NamespaceB<..>, ..`,
//! and `new NamespaceB: NamespaceA` emits the mirror impl. Evaluating the `where`
//! clause of either impl requires evaluating the other's, which requires the first
//! again — an infinite chain. Unlike the lazy `UseContext` wiring cycle (which is
//! accepted and only overflows when forced through a check), this cycle is caught
//! **eagerly at the two `cgp_namespace!` definitions**: the compiler evaluates each
//! inheritance impl's own `where` bound and overflows, so both definitions carry an
//! `E0275`, with no joining context required. A self-inheriting `new A: A` fails the
//! same way. CGP cannot see that the parent chain is circular from one macro
//! invocation, so it lowers each namespace faithfully and defers to the compiler.
//!
//! See docs/errors/wiring/namespace-inheritance-cycle.md.

use cgp::prelude::*;

cgp_namespace! {
new NamespaceA: NamespaceB {}
}

cgp_namespace! {
new NamespaceB: NamespaceA {}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
error[E0275]: overflow evaluating the requirement `__Key__: NamespaceA<__NamespaceBComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:21:1
|
21 | / cgp_namespace! {
22 | | new NamespaceA: NamespaceB {}
23 | | }
| |_^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`)
note: required for `__Key__` to implement `NamespaceB<__NamespaceAComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:25:1
|
25 | / cgp_namespace! {
26 | | new NamespaceB: NamespaceA {}
27 | | }
| |_^
note: required for `__Key__` to implement `NamespaceA<__NamespaceAComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:21:1
|
21 | / cgp_namespace! {
22 | | new NamespaceA: NamespaceB {}
23 | | }
| |_^
= note: 3 redundant requirements hidden
= note: required for `__Key__` to implement `NamespaceB<__Table__>`
= note: this error originates in the macro `cgp_namespace` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0275]: overflow evaluating the requirement `__Key__: NamespaceB<__NamespaceAComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:25:1
|
25 | / cgp_namespace! {
26 | | new NamespaceB: NamespaceA {}
27 | | }
| |_^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`$CRATE`)
note: required for `__Key__` to implement `NamespaceA<__NamespaceBComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:21:1
|
21 | / cgp_namespace! {
22 | | new NamespaceA: NamespaceB {}
23 | | }
| |_^
note: required for `__Key__` to implement `NamespaceB<__NamespaceBComponents>`
--> tests/acceptable/cgp_namespace/inheritance_cycle.rs:25:1
|
25 | / cgp_namespace! {
26 | | new NamespaceB: NamespaceA {}
27 | | }
| |_^
= note: 3 redundant requirements hidden
= note: required for `__Key__` to implement `NamespaceA<__Table__>`
= note: this error originates in the macro `cgp_namespace` (in Nightly builds, run with -Z macro-backtrace for more info)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! or wire the override on a path the namespace never registers. A namespace that
//! registers the leaf path leaves nothing for the context to override there.
//!
//! See docs/implementation/entrypoints/cgp_namespace.md (Failure modes).
//! See docs/errors/wiring/conflicting-wiring.md.

use cgp::prelude::*;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Acceptable failure: a context joins a namespace that *routes* a prefixed
//! component to a path, but no entry ever *terminates* that path with a provider,
//! so the namespace lookup finds no delegate.
//!
//! `CanGreet` carries `#[prefix(@app in DefaultNamespace)]`, so `DefaultNamespace`
//! resolves `GreeterComponent` to `RedirectLookup<_, @app.GreeterComponent>`. `App`
//! joins `DefaultNamespace` with `namespace DefaultNamespace;`, so its
//! `GreeterComponent` lookup follows that redirect — but nothing (no `#[default_impl]`,
//! no namespace body entry, no direct `@app.GreeterComponent:` line) ever binds a
//! provider at that path. The defined `GreetHello` is never wired there. Resolving
//! the component therefore requires `App: DelegateComponent<@app.GreeterComponent>`,
//! for which there is no impl, and the `check_components!` surfaces that as an
//! `E0277` on `PathCons<app, GreeterComponent>: DefaultNamespace<App>`.
//!
//! This is the *lookup-failed* class — no provider is found at all — distinct from
//! an unsatisfied *dependency*, where a provider is found but its `where` clause is
//! unmet. The forgotten binding (usually a missing `#[default_impl]` or body entry)
//! is the common namespace mistake it captures. CGP lowers the wiring faithfully;
//! only the whole program reveals the path is unbound, so it defers to the compiler.
//!
//! See docs/errors/checks/unregistered-namespace-path.md.

use cgp::prelude::*;

#[cgp_component(Greeter)]
#[prefix(@app in DefaultNamespace)]
pub trait CanGreet {
fn greet(&self) -> String;
}

#[cgp_impl(new GreetHello)]
impl Greeter {
fn greet(&self) -> String {
"Hello".to_owned()
}
}

pub struct App;

delegate_components! {
App {
namespace DefaultNamespace;
}
}

check_components! {
App {
GreeterComponent,
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error[E0277]: the trait bound `PathCons<Symbol<3, cgp::prelude::Chars<'a', cgp::prelude::Chars<'p', cgp::prelude::Chars<'p', Nil>>>>, PathCons<GreeterComponent, Nil>>: cgp::prelude::DefaultNamespace<App>` is not satisfied
--> tests/acceptable/cgp_namespace/unregistered_prefix_path.rs:48:9
|
48 | GreeterComponent,
| ^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
= help: the trait `cgp::prelude::DefaultNamespace<App>` is not implemented for `PathCons<Symbol<3, cgp::prelude::Chars<'a', cgp::prelude::Chars<'p', cgp::prelude::Chars<'p', Nil>>>>, PathCons<GreeterComponent, Nil>>`
= help: the following other types implement trait `cgp::prelude::DefaultNamespace<Components>`:
AsyncComputerComponent
AsyncComputerRefComponent
ComputerComponent
ComputerRefComponent
ErrorRaiserComponent
ErrorTypeProviderComponent
ErrorWrapperComponent
GreeterComponent
and $N others
note: required for `App` to implement `DelegateComponent<PathCons<Symbol<3, cgp::prelude::Chars<'a', cgp::prelude::Chars<'p', cgp::prelude::Chars<'p', Nil>>>>, PathCons<GreeterComponent, Nil>>>`
--> tests/acceptable/cgp_namespace/unregistered_prefix_path.rs:40:1
|
40 | / delegate_components! {
41 | | App {
| | ^^^
42 | | namespace DefaultNamespace;
43 | | }
44 | | }
| |_^
note: required for `RedirectLookup<App, PathCons<Symbol<3, cgp::prelude::Chars<'a', cgp::prelude::Chars<'p', cgp::prelude::Chars<'p', Nil>>>>, PathCons<GreeterComponent, Nil>>>` to implement `IsProviderFor<GreeterComponent, App>`
--> tests/acceptable/cgp_namespace/unregistered_prefix_path.rs:25:1
|
25 | #[cgp_component(Greeter)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: required for `App` to implement `CanUseComponent<GreeterComponent>`
note: required by a bound in `__CheckApp`
--> tests/acceptable/cgp_namespace/unregistered_prefix_path.rs:46:1
|
46 | / check_components! {
47 | | App {
48 | | GreeterComponent,
49 | | }
50 | | }
| |_^ required by this bound in `__CheckApp`
= note: this error originates in the macro `delegate_components` which comes from the expansion of the macro `check_components` (in Nightly builds, run with -Z macro-backtrace for more info)
Loading
Loading