|
| 1 | +## Summary |
| 2 | +- Closes #259 |
| 3 | +- Turns three separate "author-experience against the guide" DX gaps into clear, authoring-time Quarry diagnostics instead of cryptic Roslyn errors. |
| 4 | +- Full scope: generator diagnostics (QRY043), generator behavior change (nested row types), NuGet packaging (auto-opt-in `.targets`), analyzer (QRY044), and documentation. |
| 5 | + |
| 6 | +## Reason for Change |
| 7 | +Issue #259 consolidates three unrelated-looking build failures that all share the same root cause: the guide didn't warn the author and the failure mode was a generic Roslyn error (CS7036 / CS8852 / CS0138 / CS9137) against generated code rather than a Quarry diagnostic pointing at the real issue. |
| 8 | + |
| 9 | +## Impact |
| 10 | +Authors adopting Quarry on a new project now see: |
| 11 | +1. **QRY043** (error) naming the row-entity type when it can't be materialized — positional record, init-only property, abstract class, or interface — with the recommended workaround (`Select(x => new Dto { ... })` on a chain query). |
| 12 | +2. **Compiling generator output for nested row types** — row records declared inside an enclosing class no longer hit CS0138. The generator emits the `global::`-prefixed FQN in the interceptor body. |
| 13 | +3. **`Quarry.Generated` auto-opted-in** via the shipped `build/Quarry.targets` — authors no longer hit CS9137 for the Quarry-internal namespace they can't reasonably discover. |
| 14 | +4. **QRY044** (warning) pointing at each `[QuarryContext]` class whose namespace is missing from `<InterceptorsNamespaces>`, with the exact csproj line to paste. |
| 15 | + |
| 16 | +## Plan items implemented as specified |
| 17 | +- Phase 1: **QRY043** diagnostic, detection in `DisplayClassEnricher`, reporting via `PipelineOrchestrator.CollectTranslatedDiagnostics`, emission suppressed for affected sites so QRY043 is the only error reported. |
| 18 | +- Phase 2: Nested row type support via new `IsNestedType` / `FullyQualifiedResultTypeName` fields on `RawSqlTypeInfo`. Emitters branch on `IsNestedType` to pick the FQN; struct-reader identifier is sanitized. |
| 19 | +- Phase 3: `build/Quarry.targets` auto-registers `Quarry.Generated` in `<InterceptorsNamespaces>`. `Quarry.Generator.props` exposes `InterceptorsNamespaces` as `CompilerVisibleProperty` for the Phase 4 analyzer. |
| 20 | +- Phase 4: **QRY044** analyzer in `Quarry.Analyzers`, diagnostic-only (no code fix — the fix target is the `.csproj`, not a source document). |
| 21 | +- Phase 5: `llm.md` gains an updated `InterceptorsNamespaces` paragraph and a row-entity shape note under Raw SQL. `src/Quarry.Generator/README.md` and `llm.md` diagnostic tables updated. |
| 22 | + |
| 23 | +## Deviations from plan implemented |
| 24 | +- The plan called for adding `RawCallSite.EntityNamespace` populated from `typeArgSymbol.ContainingNamespace`. The implementation instead flags nested sites via `RawSqlTypeInfo.IsNestedType` and skips them in the namespace-collection path. Functionally equivalent, smaller surface area, avoids a parallel namespace representation on `RawCallSite`. |
| 25 | + |
| 26 | +## Gaps in original plan implemented |
| 27 | +- `CheckRowEntityMaterializability` also rejects **abstract classes** and **interfaces** used as `T` (added during REMEDIATE after review). The plan only listed parameterless-ctor and init-only property cases. Without this guard, abstract/interface `T` would fail downstream with CS0144 against generated code — exactly the kind of cryptic error the issue was filed to eliminate. |
| 28 | + |
| 29 | +## Migration Steps |
| 30 | +None for consumers of the existing shipped Quarry packages — all changes are additive. The new `build/Quarry.targets` takes effect automatically on package upgrade; consumers whose `.csproj` already listed `Quarry.Generated` in `<InterceptorsNamespaces>` compose with the targets file (semicolon-separated list deduplicates semantically). Existing in-repo sample/test projects that manually list `Quarry.Generated` are left alone — redundant but harmless. |
| 31 | + |
| 32 | +## Performance Considerations |
| 33 | +No runtime change. Generator-time: one additional `ITypeSymbol`-level check per `RawSqlAsync<T>` call site (O(properties on T) for the init-only scan, which already ran). Analyzer QRY044 is symbol-based and runs once per class declaration with an early syntactic filter on the attribute name. |
| 34 | + |
| 35 | +## Security Considerations |
| 36 | +`InterceptorsNamespacesAnalyzer` reads `build_property.InterceptorsNamespaces` and interpolates the caller's namespace symbol (not the raw property value) into the diagnostic message — no injection vector. No new runtime surface, no new dependencies. |
| 37 | + |
| 38 | +## Breaking Changes |
| 39 | +### Consumer-facing |
| 40 | +None. |
| 41 | + |
| 42 | +### Internal |
| 43 | +- `RawSqlTypeInfo` ctor gains two optional parameters (`isNestedType = false`, `fullyQualifiedResultTypeName = null`). All existing callers compile unchanged and opt into the default. |
| 44 | +- `RawCallSite` gains a mutable post-construction `MaterializabilityError` property, propagated through all three `With*` copy methods. Excluded from `Equals`/`GetHashCode` to avoid incremental-generator cache instability — consistent with how `DisplayClassName`, `RawSqlTypeInfo`, and other enrichment fields are handled. |
0 commit comments