AI Updates #7: span the data-derive impls, and catalog the equality-pinned abstract-type error#260
Merged
Merged
Conversation
cargo-cgp reshapes the orphan-rule namespace-registration class into a [CGP-E011] message naming the foreign namespace and key. Update the class doc's "How cargo-cgp presents it", "Notes for tooling", raw-diagnostic framing, and backing-fixture paths (usability/ -> acceptable/wiring/orphan/) to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cargo-cgp collapses a check-trait-failure cascade to a single block: it drops the intermediate provider-trait noise and coalesces the checked components that share one root cause into one headline listing every affected consumer trait over a single root-cause tree. Update the class doc's "How cargo-cgp presents it", "Notes for tooling", and backing-fixture entries (usability/ -> acceptable/duplication/) to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The HasField/HasFieldMut impls generated by `#[derive(HasField)]` carried
the macro's `call_site` span on their `impl`/`{ … }` boundary, so any error
about one field's impl — a coherence conflict (E0119), or the "but trait
`HasField<…>` is implemented for it" near-miss hint in a missing-field check
error — underlined the whole `#[derive(HasField)]` instead of the field.
Re-span each generated impl onto its field via `override_item_span` (the
field identifier for a named field, the whole `syn::Field` for a tuple
field), matching the delegate_components!/default_impl technique. Only the
boundary tokens move, so interior references stay at call_site and
go-to-definition on the field is unaffected.
The raw-diagnostic caret is pinned by the cargo-cgp UI fixtures (22
`.rust.stderr` baselines re-blessed there); documents the mechanism in the
HasField implementation doc's new Error spans section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the HasField span fix to every other extensible-data derive:
HasFields, FromVariant, ExtractField, BuildField, and the CgpData/
CgpRecord/CgpVariant umbrellas that compose them. Each generated impl
carried the derive's `call_site` span on its `impl`/`{ … }` boundary, so
any error about it underlined the whole `#[derive(...)]` rather than the
field, variant, or type it derives from.
Re-span each impl via `override_item_span`, keyed by kind: per-field
impls (`UpdateField`, builder `HasField`) onto their field, per-variant
impls (`FromVariant`, `ExtractField`) onto their variant, and whole-type
impls (`HasFields`/`FromFields`/`ToFields`/…, `HasBuilder`, the
`HasExtractor` family, `FinalizeBuild`/`FinalizeExtract`, `PartialData`)
onto the struct/enum name. Re-spanning lives in the shared codegen
helpers, so every composing entry point inherits it. The `__Partial…`
companion types are cloned from user input and keep their own spans.
Only the boundary tokens move, so interior references stay at call_site
and go-to-definition is unaffected. Documents the mechanism in each
affected implementation doc's new Error spans section.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A provider that pins an abstract type with `#[use_type(Trait.{Assoc = Concrete})]`
while the context binds it elsewhere fails as an `E0271` projection mismatch
rather than the `E0277` the rest of this class produces: the trait half of the
bound holds, so only the associated type disagrees. Record it as a sub-case with
its own tells — the caret landing on the `#[cgp_type]` attribute that defined the
type, the supplied type absent from the message, and the cascade one shared
abstract type produces — and note that cargo-cgp now reshapes it into
[CGP-E017], naming both types and the wiring entry to change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
AI Overview
This PR makes CGP's data derives point their compiler errors at the right place, and documents one new error class. It is a diagnostics-quality change: no macro accepts different input, no expansion changes shape, and no runtime behavior moves. Three commits carry it — two fix the spans the data-derive family stamps on its generated impls, and one adds an error-catalog section for an abstract-type mismatch the derives can surface. It touches 14 codegen files and 9 documentation files, all under cgp, with no change to any crate's public API or version.
High-level concepts
The core change is that every impl the data-derive family generates is now re-spanned onto the token the user actually wrote, instead of onto the whole
#[derive(...)]attribute. A proc macro builds its output from quasi-quoted tokens, and those tokens all carry the macro'scall_sitespan — the entire derived type. Left alone, a compiler error on a generated impl's header underlines the whole derive and says nothing about which field, variant, or type is at fault. Routing each finished impl through the existingoverride_item_spanhelper moves only the impl's boundary tokens — theimplkeyword and the{ … }body — onto a narrower span, collapsing the caret onto the entry the user wrote.The re-span is deliberately confined to boundary tokens so the editor keeps working. rust-analyzer maps a source token to its expansion by source range alone, ignoring hygiene, so dragging a resolvable reference like
HasFieldonto a user token would hijack go-to-definition on that token. The two boundary tokens moved here are structural — a keyword and a delimiter group, never references — so the compiler caret lands correctly while every interior reference stays atcall_siteand every user token keeps its own span. Correctness for the compiler and correctness for the IDE are the same concern handled together, exactly as the repository's span guidance requires.The second concept is a newly documented failure mode: the equality-pinned abstract-type mismatch. A provider can pin an abstract type to a concrete one —
#[use_type(HasErrorType.{Error = AppError})], which emitsSelf: HasErrorType<Error = AppError>— while the context binds that same abstract type to a different concrete type through its wiring. When the two disagree, the trait half of the bound still holds and only the associated-type projection fails, so the compiler reports anE0271type mismatch rather than anE0277unimplemented-trait error, with a caret on the#[cgp_type]attribute that defined the type — nowhere near either side of the disagreement. This PR records that class in the error catalog and describes howcargo-cgpreshapes it into the[CGP-E017]/[CGP-E112]form that names both types and the wiring entry to change.Structural changes
On the code side, every generator in crates/macros/cgp-macro-core/src/types/cgp_data/ now calls
override_item_spanbefore returning its impls. The helper itself is unchanged — the PR reuses infrastructure that already backeddelegate_components!and#[cgp_impl], extending it to the data-derive family for the first time. Per-field and per-variant impls are aimed at their field or variant (the identifier, or the wholesyn::Fieldfor an unnamed tuple field), and whole-type impls at the struct or enum name. The change spans the builder slice (has_builder_impl,into_builder_impl,partial_data,finalize_build_impl,has_field_impls,update_field_impls), the extractor slice (has_extractor_impl,extract_field_impls,finalize_extract_impl,partial_data), and the standalonederive_from_variant,derive_has_field, andderive_has_fieldsgenerators.On the documentation side, derive_has_field.md gains the canonical Error spans section that explains the mechanism, the two diagnostics it improves, and why only boundary tokens move. Matching sections are added to the other slice docs — derive_build_field.md, derive_extract_field.md, and derive_from_variant.md — and to derive_has_fields.md. The three umbrella-derive docs (derive_cgp_data.md, derive_cgp_record.md, derive_cgp_variant.md) each note that they inherit the spans because they run the same slice helpers. Finally, errors/checks/check-trait-failure.md grows two subsections and a fixture link documenting the equality-pinned abstract-type class.
Impacts
The change touches diagnostics and documentation only, and its effects fall into the following distinct areas.
E0119from a hand-written impl clashing with a derived one —HasField,HasFields,FromVariant, and the builder and extractor impls — lands on the specific field, variant, or type name rather than on the whole#[derive(...)]attribute.HasField<…>is implemented for it" hint now points at the nearest existing field's own line. A struct that derivesHasFieldfor several fields shows each near-miss on its own field, instead of collapsing them all onto the derive attribute where the encodedSymbol<len, …>tags were the only way to tell them apart.call_site, go-to-definition on a field, variant, or wired name still resolves to the one correct definition. This half has no automated fixture and is verified by hand.CgpData/CgpRecord/CgpVariantumbrella derives inherit the behavior automatically because they run the same helpers.no_stdand on the same versions.cargo-cgpUI fixtures'.rust.stderrbaselines —fields/base_area_1, the multi-fieldduplication/density_3, and others — so a regression that drops a re-span changes those snapshots there rather than here.E0271, reshaped to[CGP-E017]) is now documented with its diagnostic shape, its cascade behavior across every consumer that shares the abstract type, and its resolution. Its backing fixture,acceptable/types/abstract_type_mismatch.rs, already exists in the siblingcargo-cgpcheckout, so the new documentation links resolve.