Skip to content

Fix NU5128 pack warning for analyzer project - #7

Merged
DJGosnell merged 1 commit into
masterfrom
fix/analyzer-pack-warning
Mar 14, 2026
Merged

Fix NU5128 pack warning for analyzer project#7
DJGosnell merged 1 commit into
masterfrom
fix/analyzer-pack-warning

Conversation

@DJGosnell

Copy link
Copy Markdown
Member

Summary

  • Suppress NU5128 NuGet warning when packing Quarry.Analyzers by adding SuppressDependenciesWhenPacking=true

Reason for Change

The CI build produces a NU5128 warning because the analyzer package declares a netstandard2.0 dependency group (from PackageReference items) but outputs to analyzers/dotnet/cs instead of lib/netstandard2.0. NuGet flags the mismatch between the dependency group and the missing lib folder.

Impact

  • Eliminates the NU5128 warning during dotnet pack
  • The analyzer's dependencies (Microsoft.CodeAnalysis.*) are already PrivateAssets="all", so suppressing the dependency group from the nuspec is correct — Roslyn loads analyzer assemblies directly with no runtime dependency resolution

Migration Steps

  • None

Performance Considerations

  • None

Security Considerations

  • None

Breaking Changes

  • Consumer-facing: None
  • Internal: None

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@DJGosnell
DJGosnell merged commit d4039df into master Mar 14, 2026
1 check passed
@DJGosnell
DJGosnell deleted the fix/analyzer-pack-warning branch March 20, 2026 03:54
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Resume bookkeeping only — no code changes. Closes the review classification
loop by filling Action Taken for B items #3/#5/#6/#7 with references to
commit be224dd; adds session log entry covering the resume.
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Addresses review.md classifications from session 2:
- A/#4 Correctness: extend HasQuarryContextAttributeSyntactic with an
  AliasQualifiedNameSyntax branch so [global::QuarryContextAttribute]
  and extern-alias attribute forms pass the syntactic pre-filter.
- B/#1 Plan: drop RawSqlTypeInfo.FullyQualifiedResultTypeName — written
  but never read; ResultTypeName already carries the final display form
  (FQN for nested, short for not).
- B/#7 Tests: add alias-qualified attribute QRY044 coverage.
- B/#8 Tests: add struct-with-init-only-property QRY043 coverage.
- B/#9 Tests: add mixed RawSqlScalarAsync<int> + failing RawSqlAsync<T>
  test exercising the scalar branch in PipelineOrchestrator + FileEmitter.

3259 tests pass (+3 new).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Address review findings from _sessions/256-fix-sql-raw-select-projection/review.md:

- Fail-loud fallback (#3): AnalyzeProjectedExpression returns null for a
  methodName=="Raw" whose GetAggregateInfo returned null, preventing the
  generic type-info fallback from producing an empty-column ProjectedColumn
  that would regress the original #256 bug. AnalyzeInvocation emits a
  specific CreateFailed message for the single-column path.

- Captured-var typing (#4, #8, #16): RenderRawArgToCanonical tries
  ResolveScalarArgSql first for simple scalar args (identifiers, literals,
  captured members). This consults the semantic model for authoritative CLR
  types rather than the SqlExprParser default of "object", so a captured
  DateTime landed in ParameterInfo as "object" becomes "DateTime".

- Dialect-aware booleans (#5): FormatLiteralForProjection emits canonical
  {@boolt}/{@boolf} placeholders rather than per-dialect bool text.
  QuoteSqlExpression resolves the placeholder to TRUE/FALSE on PostgreSQL
  and 1/0 elsewhere. Because projection analysis runs at a fixed discovery
  dialect, emitting dialect-rendered text at analysis time would embed the
  wrong value in the cached SqlExpression.

- String-concat guard (#6): RenderRawArgNode's Add walker bails to null if
  either operand is a string/char typed literal or captured; the projection
  fails loudly rather than emitting "a + b" which is invalid on MySQL/SqlServer.
  Users who want string concat should write it in the template text.

- Validation refactor (#7): Replaced the throwaway RawCallExpr shell with a
  dedicated IsRawTemplateValid helper. Transient shell is now isolated to
  one helper with clear scope.

- T="object" guard (#21): TryExtractSqlRawTypeArg returns null rather than
  "object" when T is unresolvable. BuildSqlRawInfo then fails the projection
  loudly instead of letting ChainAnalyzer.TryResolveAggregateTypeFromSql
  misinfer a CLR type from SUM/MIN/MAX substrings in the user's template.

- Tests: Add Select_SqlRaw_BooleanLiteralArg_DialectAware (#5, #13) and
  Select_SqlRaw_CapturedVariable_TypeInferredFromSemanticModel (#4, #12, #14).

Review classifications + Action Taken recorded in
_sessions/256-fix-sql-raw-select-projection/review.md §Classifications.
All 3253 tests pass (3242 baseline + 11 Sql.Raw projection tests).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Review session 2 findings #6, #7.

`FormatLiteralForProjection` previously treated `"TRUE"`, `"true"`, and
`"1"` as truthy, but `SqlExprParser.ParseLiteral` only ever emits
`"TRUE"` or `"FALSE"` for bool literals (SqlExprParser.cs:270-274). The
extra checks were dead and added cognitive load — now only `"TRUE"`
maps to `{@boolt}`, everything else to `{@boolf}`.

`GetRawBinaryOperator` used to return `"?"` for any unmapped
`SqlBinaryOperator`, which is the MySQL parameter placeholder and
would parse into unrelated runtime behavior — the exact silent-
wrong-SQL failure mode this PR fixes. It now throws
`ArgumentOutOfRangeException`, so any future operator addition that
forgets to update the table will fail the build rather than emit
garbage SQL.
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Review items addressed inline:
- #3 (B, Correctness): `CheckRowEntityMaterializability` now also rejects
  abstract classes and interfaces. CS0144 would otherwise fire against the
  generated `new T()` with no indication of which row type was at fault.
- #5 (B, Tests): adds a nested-row test driving the struct-reader fallback
  by using an expression SELECT list, exercising `SanitizeForIdentifier`
  and the FQN-in-`IRowReader<T>` path.
- #6 (B, Tests): strengthens the namespace-level-row regression to assert
  that `using TestApp.Rows;` IS emitted.
- #7 (B, Tests): adds a QRY044 test where `build_property.InterceptorsNamespaces`
  is entirely absent (null), confirming the diagnostic still fires.
- Plus QRY043 tests for abstract and interface row types.

Test totals: 3256 passing (+4 from Phase 5 green baseline).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Resume bookkeeping only — no code changes. Closes the review classification
loop by filling Action Taken for B items #3/#5/#6/#7 with references to
commit be224dd; adds session log entry covering the resume.
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Addresses review.md classifications from session 2:
- A/#4 Correctness: extend HasQuarryContextAttributeSyntactic with an
  AliasQualifiedNameSyntax branch so [global::QuarryContextAttribute]
  and extern-alias attribute forms pass the syntactic pre-filter.
- B/#1 Plan: drop RawSqlTypeInfo.FullyQualifiedResultTypeName — written
  but never read; ResultTypeName already carries the final display form
  (FQN for nested, short for not).
- B/#7 Tests: add alias-qualified attribute QRY044 coverage.
- B/#8 Tests: add struct-with-init-only-property QRY043 coverage.
- B/#9 Tests: add mixed RawSqlScalarAsync<int> + failing RawSqlAsync<T>
  test exercising the scalar branch in PipelineOrchestrator + FileEmitter.

3259 tests pass (+3 new).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
* Support Sql.Raw<T> in single-entity Select projections (#256)

Previously, Sql.Raw<T> used inside a Select tuple/DTO/object-init projection
silently rendered as an empty string literal in the generated SQL
(e.g., SELECT "OrderId", "" FROM "orders") because ProjectionAnalyzer fell
through to the generic fallback where columnName="" and the raw C# source
was placed in SqlExpression then later stripped.

Add a Raw case to GetAggregateInfo that parses each template argument via
SqlExprParser, walks the resulting SqlExpr tree with a projection-aware
renderer, and substitutes {0}/{1}/... template placeholders with canonical
{ColumnName} identifier placeholders (dialect-resolved later), @__proj{N}
parameter placeholders for captured runtime vars, and inline literals for
compile-time constants. Binary ops, unary ops, function calls, IS NULL, IN,
and LIKE expressions render inline so args like u.Price * 2 are supported.

The generic T type argument on Sql.Raw<T> determines the column CLR type;
template/arg count mismatches fail via RawCallExpr.Validate().

Covers tuple, DTO, object-initializer, and single-column projection forms
for single-entity Select (joined projections follow in a separate commit).

Task #1 of 3 in #256 workflow.

* Support Sql.Raw<T> in joined and single-column Select projections (#256)

Add a Raw case to GetJoinedAggregateInfo that resolves column references
to canonical {alias}.{ColumnName} placeholders using the per-parameter
lookup. The shared BuildSqlRawInfo helper and the SqlExpr tree walker from
Phase 1 are reused via a column-resolver delegate — only the column
resolution differs between single-entity and joined contexts.

Single-column projections (e.g., .Select(u => Sql.Raw<string>(...)))
already route through GetAggregateInfo via AnalyzeInvocation, so no
additional change is needed; joined single-column does the same via
AnalyzeJoinedInvocation → ResolveJoinedAggregate → GetJoinedAggregateInfo.

Add sanity tests for joined-tuple and single-column forms; the existing
column-reference sanity test from Phase 1 covers single-entity tuple form.

Task #2 of 3 in #256 workflow.

* Cross-dialect test coverage for Sql.Raw<T> in Select projections (#256)

Add 6 tests to complement the sanity tests from Phases 1+2, covering the
full surface of the fix:

- Multiple column references — Sql.Raw<string>("coalesce({0}, {1})", ...)
- Captured variable — verifies @__proj{N} local placeholders are remapped
  to dialect-specific parameter placeholders (@p0 / $1 / ?)
- Literal parameter — compile-time constant inlines as SQL literal
- No placeholders — template with no {N} substitutions passes through
- DTO initializer — new UserSummaryDto { ... = Sql.Raw<string>(...) }
- Binary op arg — u.UserId * 10 exercises the IR-based arg tree walker

All 9 tests (3 from Phases 1+2 + 6 added here) assert SQL output across
SQLite, PostgreSQL, MySQL, and SqlServer.

Task #3 of 3 in #256 workflow.

* Remediate review findings for Sql.Raw<T> Select projection (#256)

Address review findings from _sessions/256-fix-sql-raw-select-projection/review.md:

- Fail-loud fallback (#3): AnalyzeProjectedExpression returns null for a
  methodName=="Raw" whose GetAggregateInfo returned null, preventing the
  generic type-info fallback from producing an empty-column ProjectedColumn
  that would regress the original #256 bug. AnalyzeInvocation emits a
  specific CreateFailed message for the single-column path.

- Captured-var typing (#4, #8, #16): RenderRawArgToCanonical tries
  ResolveScalarArgSql first for simple scalar args (identifiers, literals,
  captured members). This consults the semantic model for authoritative CLR
  types rather than the SqlExprParser default of "object", so a captured
  DateTime landed in ParameterInfo as "object" becomes "DateTime".

- Dialect-aware booleans (#5): FormatLiteralForProjection emits canonical
  {@boolt}/{@boolf} placeholders rather than per-dialect bool text.
  QuoteSqlExpression resolves the placeholder to TRUE/FALSE on PostgreSQL
  and 1/0 elsewhere. Because projection analysis runs at a fixed discovery
  dialect, emitting dialect-rendered text at analysis time would embed the
  wrong value in the cached SqlExpression.

- String-concat guard (#6): RenderRawArgNode's Add walker bails to null if
  either operand is a string/char typed literal or captured; the projection
  fails loudly rather than emitting "a + b" which is invalid on MySQL/SqlServer.
  Users who want string concat should write it in the template text.

- Validation refactor (#7): Replaced the throwaway RawCallExpr shell with a
  dedicated IsRawTemplateValid helper. Transient shell is now isolated to
  one helper with clear scope.

- T="object" guard (#21): TryExtractSqlRawTypeArg returns null rather than
  "object" when T is unresolvable. BuildSqlRawInfo then fails the projection
  loudly instead of letting ChainAnalyzer.TryResolveAggregateTypeFromSql
  misinfer a CLR type from SUM/MIN/MAX substrings in the user's template.

- Tests: Add Select_SqlRaw_BooleanLiteralArg_DialectAware (#5, #13) and
  Select_SqlRaw_CapturedVariable_TypeInferredFromSemanticModel (#4, #12, #14).

Review classifications + Action Taken recorded in
_sessions/256-fix-sql-raw-select-projection/review.md §Classifications.
All 3253 tests pass (3242 baseline + 11 Sql.Raw projection tests).

* Record PR #262 in workflow state (#256)

* Back-step REMEDIATE → REVIEW for session 2 re-analysis (#256)

User declined to finalize PR #262 and requested a fresh full
review. Archived prior review.md to review-session1.md; workflow
phase reset to REVIEW.

* Make Sql.Raw projection walker fail loudly on unsupported args (#256)

Review session 2 findings #4, #8.

The projection-arg walker used to emit `SqlRawExpr.SqlText` verbatim
when it reached a node the parser could not translate. Since
`SqlExprParser` falls through to `new SqlRawExpr(expression.ToString())`
for C# ternaries, unknown invocations, postfix unary (other than `!`),
array-creation without initializer, element access, and interpolated
strings, any of those inside `Sql.Raw<T>(template, arg)` would leak the
C# source text — e.g. `/* unsupported: C# ternary expression */` or
`u.Foo(x)` — into the generated SQL. This reproduces the exact class of
silent-wrong-SQL bug this PR fixes. The walker now returns null for
`SqlRawExpr`, forcing the caller to bail out.

The string-concat Add guard now also checks `ColumnRefExpr` operands
via a new `isStringColumn` delegate threaded alongside the existing
column resolver. Previously only direct string literals and captured
string locals were caught, so `Sql.Raw<string>("{0}", u.FirstName + u.LastName)`
would still emit `("FirstName" + "LastName")` — invalid on MySQL and
SqlServer. The guard now bails on any string-typed operand: literal,
captured, or column.

Both `RenderRawArgToCanonical` and `RenderRawArgToCanonicalJoined`
receive an `isStringColumn` delegate that closes over the column
lookup for the current context; `IsStringColumnRef` and
`IsJoinedStringColumnRef` implement the two contexts.

* Propagate IsStaticField and ExpressionPath for Sql.Raw captured vars (#256)

Review session 2 findings #2, #9, #17.

`AddCapturedAsProjectionParameter` now mirrors the ParameterInfo
construction in `SqlExprClauseTranslator.ExtractParametersCore`
(SqlExprClauseTranslator.cs:88-92) so captured-variable metadata
propagates consistently across the Where-path and Select-projection
paths:

- `ExpressionPath = captured.ExpressionPath` — needed by the parameter
  binder to emit direct-path navigation code for deep captures like
  `obj.Inner.Field`.
- `IsStaticCapture = captured.IsStaticField` — controls whether
  UnsafeAccessor uses StaticField kind (null target) vs Field kind
  (func.Target).

Latent today: the walker path is only reached for captured variables
that are NOT direct identifiers or parameter-prefixed member accesses
(the fast-path in RenderRawArgToCanonical delegates those to
ResolveScalarArgSql). Remaining walker captures are rare (function-call
args, operator arms) and SqlExprParser never sets IsStaticField=true in
the current code. The fix aligns the projection path with the canonical
pattern so future parser changes or more complex Raw arg shapes don't
silently drop capture metadata.

* Tighten bool-literal detection and operator-table fallback (#256)

Review session 2 findings #6, #7.

`FormatLiteralForProjection` previously treated `"TRUE"`, `"true"`, and
`"1"` as truthy, but `SqlExprParser.ParseLiteral` only ever emits
`"TRUE"` or `"FALSE"` for bool literals (SqlExprParser.cs:270-274). The
extra checks were dead and added cognitive load — now only `"TRUE"`
maps to `{@boolt}`, everything else to `{@boolf}`.

`GetRawBinaryOperator` used to return `"?"` for any unmapped
`SqlBinaryOperator`, which is the MySQL parameter placeholder and
would parse into unrelated runtime behavior — the exact silent-
wrong-SQL failure mode this PR fixes. It now throws
`ArgumentOutOfRangeException`, so any future operator addition that
forgets to update the table will fail the build rather than emit
garbage SQL.

* Emit QRY029 for Sql.Raw template errors in Select projections (#256)

Review session 2 finding #1.

Where-path `Sql.Raw` calls surface template errors (placeholder/
argument count mismatch) as QRY029 compile-time errors via
`PipelineOrchestrator.CollectTranslatedDiagnostics`, because the raw
site's `Expression` is a `RawCallExpr` that runs `Validate()`. Select-
projection `Sql.Raw` calls were silent — `ProjectionAnalyzer.
BuildSqlRawInfo` validated via a transient `RawCallExpr` shell and, on
failure, discarded both the shell and the error, returning `(null, null)`.
The projection then failed analysis and the chain degraded to runtime
build, leaving no user-visible diagnostic.

`GetRawTemplateValidationError` now returns the `Validate()` message
(the same string the Where path shows). `BuildSqlRawInfo` records the
message via a thread-static accumulator modeled on `PipelineErrorBag`.
Public entry points in `ProjectionAnalyzer` drain the accumulator and
attach the messages to the returned `ProjectionInfo` via a new init-
only `SqlRawValidationErrors` property. `PipelineOrchestrator` emits
QRY029 per entry, scoped to the Select call's location.

Scope note: QRY029 is attached to the Select call location rather
than the individual `Sql.Raw` call site — less precise than the
Where path, but sufficient to locate the bad template in the lambda.

Added `ProjectionFailureReason.SqlRawValidationError` for future callers
that want to distinguish this degradation class; current pipeline
reads from `SqlRawValidationErrors` directly.

* Test remediation coverage for Sql.Raw Select projection (#256)

Review session 2 findings #11, #12, #14.

UsageSiteDiscoveryTests.cs (#11):
Three new QRY029 tests for projection-path Sql.Raw:
- Too many arguments
- Too few arguments
- Non-sequential placeholders ({0}, {2} skipping {1})
All assert QRY029 fires and the message matches the RawCallExpr.Validate
text, mirroring the existing Where-path tests.

CrossDialectMiscTests.cs (#12, #14):
- Select_SqlRaw_CapturedVariable_TypeInferredFromSemanticModel now also
  asserts Name, Value, IsCollection, IsEnum, and executes the query at
  runtime to verify the captured DateTime round-trips through the
  parameter binder (shallow-test remediation).
- Select_SqlRaw_Joined_MultipleArgs — joined projection with three
  args from a literal, u.UserName, and o.Status. Exercises
  ResolveJoinedColumnRefToPlaceholder for two distinct lambda params.
- Select_SqlRaw_Joined_WithCapturedVariable — joined projection with a
  captured int threshold in the Raw arg. Exercises
  IsScalarArgCandidateJoined and the fast-path delegation in the
  joined context.

Manifest regeneration reflects the three new cross-dialect queries.

* Share binary-operator table between SqlExprRenderer and Sql.Raw walker (#256)

Review session 2 finding #16 (scoped).

The Sql.Raw projection walker used to carry its own copy of the
SqlBinaryOperator → text table (`GetRawBinaryOperator`). Any future
operator addition would require updating both it and
`SqlExprRenderer.GetSqlOperator`, and any dialect-specific fix in the
renderer (e.g., the string-concat TODO at SqlExprRenderer.cs:290)
would bypass the walker. `GetSqlOperator` is now `internal` and
called directly from the walker.

The renderer's fallback was also tightened to throw
`ArgumentOutOfRangeException` rather than emit `"?"` — matching the
walker's stricter contract and preventing an unmapped operator from
silently rendering as the MySQL parameter placeholder.

Full consolidation of the remaining shape-level duplication
(IN/LIKE/IS NULL/function-call/unary emission) is out of scope: those
render paths in SqlExprRenderer invoke dialect-sensitive recursion
that the canonical walker must not enter. Tracked as a follow-up in
the walker's docstring: to share the rest, SqlExprRenderer would need
a canonical-projection output mode with delegate hooks for column
resolution, captured-value accumulation, and literal rendering.

* Record Action Taken for session 2 review findings (#256)

Populates the review.md Classifications table with a concise description
of how each A/B finding was addressed (or why D findings were left
alone). Session 2 remediation complete — ready to finalize.

* Save PR #262 body draft alongside session artifacts (#256)

* Update workflow.md for session 2 REMEDIATE completion (#256)

* chore: remove session artifacts before merge
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Review items addressed inline:
- #3 (B, Correctness): `CheckRowEntityMaterializability` now also rejects
  abstract classes and interfaces. CS0144 would otherwise fire against the
  generated `new T()` with no indication of which row type was at fault.
- #5 (B, Tests): adds a nested-row test driving the struct-reader fallback
  by using an expression SELECT list, exercising `SanitizeForIdentifier`
  and the FQN-in-`IRowReader<T>` path.
- #6 (B, Tests): strengthens the namespace-level-row regression to assert
  that `using TestApp.Rows;` IS emitted.
- #7 (B, Tests): adds a QRY044 test where `build_property.InterceptorsNamespaces`
  is entirely absent (null), confirming the diagnostic still fires.
- Plus QRY043 tests for abstract and interface row types.

Test totals: 3256 passing (+4 from Phase 5 green baseline).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Resume bookkeeping only — no code changes. Closes the review classification
loop by filling Action Taken for B items #3/#5/#6/#7 with references to
commit be224dd; adds session log entry covering the resume.
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Addresses review.md classifications from session 2:
- A/#4 Correctness: extend HasQuarryContextAttributeSyntactic with an
  AliasQualifiedNameSyntax branch so [global::QuarryContextAttribute]
  and extern-alias attribute forms pass the syntactic pre-filter.
- B/#1 Plan: drop RawSqlTypeInfo.FullyQualifiedResultTypeName — written
  but never read; ResultTypeName already carries the final display form
  (FQN for nested, short for not).
- B/#7 Tests: add alias-qualified attribute QRY044 coverage.
- B/#8 Tests: add struct-with-init-only-property QRY043 coverage.
- B/#9 Tests: add mixed RawSqlScalarAsync<int> + failing RawSqlAsync<T>
  test exercising the scalar branch in PipelineOrchestrator + FileEmitter.

3259 tests pass (+3 new).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Review items addressed inline:
- #3 (B, Correctness): `CheckRowEntityMaterializability` now also rejects
  abstract classes and interfaces. CS0144 would otherwise fire against the
  generated `new T()` with no indication of which row type was at fault.
- #5 (B, Tests): adds a nested-row test driving the struct-reader fallback
  by using an expression SELECT list, exercising `SanitizeForIdentifier`
  and the FQN-in-`IRowReader<T>` path.
- #6 (B, Tests): strengthens the namespace-level-row regression to assert
  that `using TestApp.Rows;` IS emitted.
- #7 (B, Tests): adds a QRY044 test where `build_property.InterceptorsNamespaces`
  is entirely absent (null), confirming the diagnostic still fires.
- Plus QRY043 tests for abstract and interface row types.

Test totals: 3256 passing (+4 from Phase 5 green baseline).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Resume bookkeeping only — no code changes. Closes the review classification
loop by filling Action Taken for B items #3/#5/#6/#7 with references to
commit be224dd; adds session log entry covering the resume.
DJGosnell added a commit that referenced this pull request Apr 23, 2026
Addresses review.md classifications from session 2:
- A/#4 Correctness: extend HasQuarryContextAttributeSyntactic with an
  AliasQualifiedNameSyntax branch so [global::QuarryContextAttribute]
  and extern-alias attribute forms pass the syntactic pre-filter.
- B/#1 Plan: drop RawSqlTypeInfo.FullyQualifiedResultTypeName — written
  but never read; ResultTypeName already carries the final display form
  (FQN for nested, short for not).
- B/#7 Tests: add alias-qualified attribute QRY044 coverage.
- B/#8 Tests: add struct-with-init-only-property QRY043 coverage.
- B/#9 Tests: add mixed RawSqlScalarAsync<int> + failing RawSqlAsync<T>
  test exercising the scalar branch in PipelineOrchestrator + FileEmitter.

3259 tests pass (+3 new).
DJGosnell added a commit that referenced this pull request Apr 23, 2026
* feat(generator): QRY043 diagnostic for un-materializable RawSqlAsync row types

Surface the real reason RawSqlAsync<T> row types fail to compile when T is a
positional record or has init-only properties. Previously authors saw cryptic
CS7036/CS8852 errors against generated code; now they see QRY043 naming their
type and the specific shape violation.

Detection runs in DisplayClassEnricher where the ITypeSymbol is already resolved
via the supplemental compilation. Emission is suppressed for affected sites so
QRY043 is the only error reported.

* feat(generator): support nested row-entity types in RawSqlAsync interceptors

Nested row types (row record declared inside an enclosing class) previously broke
the generator: it emitted `using <EnclosingType>;` which the compiler rejects
with CS0138. Fix tracks the nesting state on RawSqlTypeInfo and emits the
`global::`-prefixed fully qualified type name in generated bodies for nested
types so references resolve without a using directive. Namespace-level row
types still use their short name + using (unchanged codegen).

* feat(packaging): ship Quarry.targets auto-registering Quarry.Generated

Adds build/Quarry.targets to the Quarry NuGet package. It appends
Quarry.Generated to <InterceptorsNamespaces> so consumers no longer hit CS9137
for the Quarry-internal namespace they can't reasonably discover. Consumers
still add their own QuarryContext namespace — Phase 4's QRY044 analyzer
surfaces that gap at authoring time.

Also exposes InterceptorsNamespaces as a CompilerVisibleProperty so that
analyzer can read it from AnalyzerConfigOptions.

* feat(analyzers): QRY044 warns when QuarryContext namespace is missing from InterceptorsNamespaces

Surfaces the CS9137 project-setup gap at authoring time with the exact
<InterceptorsNamespaces> line to paste into the .csproj. Warning severity:
the build would fail with CS9137 anyway, so this is an earlier signal, not a
new error. Context classes in the global namespace are ignored because
Quarry.Generated (auto-registered by the shipped targets file) already
covers that path.

Descriptive diagnostic only — no CodeFixProvider, since the fix target is
the .csproj rather than a source document and standard Roslyn code fixes
can't reliably modify project files.

* docs: document row-entity shape requirements and interceptor opt-in behavior

Updates llm.md and Quarry.Generator/{README.md,llm.md} with:
- Revised InterceptorsNamespaces guidance: Quarry.Generated is now
  auto-registered by the shipped targets file, so the doc only prompts for
  the consumer's context namespace.
- New Row entity shape note under Raw SQL describing the parameterless ctor
  + public get/set property requirements, with QRY043 and the chain-query
  workaround for immutable shapes.
- Nested row types are explicitly called out as supported.
- QRY043 and QRY044 added to the diagnostic inventory tables.

* fix(generator): remediate review findings for #259

Review items addressed inline:
- #3 (B, Correctness): `CheckRowEntityMaterializability` now also rejects
  abstract classes and interfaces. CS0144 would otherwise fire against the
  generated `new T()` with no indication of which row type was at fault.
- #5 (B, Tests): adds a nested-row test driving the struct-reader fallback
  by using an expression SELECT list, exercising `SanitizeForIdentifier`
  and the FQN-in-`IRowReader<T>` path.
- #6 (B, Tests): strengthens the namespace-level-row regression to assert
  that `using TestApp.Rows;` IS emitted.
- #7 (B, Tests): adds a QRY044 test where `build_property.InterceptorsNamespaces`
  is entirely absent (null), confirming the diagnostic still fires.
- Plus QRY043 tests for abstract and interface row types.

Test totals: 3256 passing (+4 from Phase 5 green baseline).

* chore: record PR #260 in workflow.md + add pr-body.md session artifact

* docs: include abstract class and interface rejection in QRY043 docs

The REMEDIATE-phase extension of CheckRowEntityMaterializability added two
additional rejection cases (abstract classes and interfaces) that weren't
reflected in llm.md, src/Quarry.Generator/README.md, or
src/Quarry.Generator/llm.md. Brings the guide text and diagnostic inventory
tables in line with the actual QRY043 behavior.

* chore(session): record session 2 resume and populate review Action Taken

Resume bookkeeping only — no code changes. Closes the review classification
loop by filling Action Taken for B items #3/#5/#6/#7 with references to
commit be224dd; adds session log entry covering the resume.

* chore(session): back-step REMEDIATE -> REVIEW for re-analysis

* fix: remediate second-round review findings (A/#4, B/#1/#7/#8/#9)

Addresses review.md classifications from session 2:
- A/#4 Correctness: extend HasQuarryContextAttributeSyntactic with an
  AliasQualifiedNameSyntax branch so [global::QuarryContextAttribute]
  and extern-alias attribute forms pass the syntactic pre-filter.
- B/#1 Plan: drop RawSqlTypeInfo.FullyQualifiedResultTypeName — written
  but never read; ResultTypeName already carries the final display form
  (FQN for nested, short for not).
- B/#7 Tests: add alias-qualified attribute QRY044 coverage.
- B/#8 Tests: add struct-with-init-only-property QRY043 coverage.
- B/#9 Tests: add mixed RawSqlScalarAsync<int> + failing RawSqlAsync<T>
  test exercising the scalar branch in PipelineOrchestrator + FileEmitter.

3259 tests pass (+3 new).

* docs(pr): update PR body for second remediation round (A/#4, B/#1/#7/#8/#9)

* chore(session): log rebase on origin/master (4daf62d)

* chore(session): log second rebase on origin/master (e4354a4)

* chore(session): log third rebase on origin/master (95c4bbb + d2a6b1e)

* chore: remove session artifacts before merge
DJGosnell added a commit that referenced this pull request Apr 29, 2026
- Finding #2: Delete the registry-driven joined-projection island
  (Analyze, AnalyzeJoined, AnalyzeJoinedExpression, AnalyzeJoinedSingleColumn,
  AnalyzeJoinedInitializer, AnalyzeJoinedTuple, ResolveJoinedProjectedExpression,
  ResolveJoinedColumn) and the orphaned BuildColumnLookup. Production discovery
  only reaches AnalyzeJoinedSyntaxOnly, so the deleted methods carried zero traffic
  yet duplicated the same legacy strict-Kind FK .Id branch the placeholder path
  was already fixed for. Inlined InferResultTypeFromSyntax (live callers remain).

- Finding #3: Clear IsRefKeyAccess=false before falling through to generic
  enrichment, so the generic block doesn't observe a stale flag if it matches
  a non-FK column for some other reason.

- Finding #8: Pin the wrap-suppression contract in FkKeyProjection_DiagnosticsShape
  with IsForeignKey == false and ForeignKeyEntityName == null assertions.

- Finding #11: Align IsRefKeyAccess enrichment with generic enrichment by carrying
  CustomTypeMapping and IsEnum from the FK column.

Finding #7 (test for the FK-not-found fall-through) is documented as a known
test-infrastructure follow-up — reproducing the path produces broken interceptor
code, so a runnable test requires source-generator unit-test scaffolding that
doesn't exist in this repo yet.
DJGosnell added a commit that referenced this pull request Apr 29, 2026
- Finding #2: Delete the registry-driven joined-projection island
  (Analyze, AnalyzeJoined, AnalyzeJoinedExpression, AnalyzeJoinedSingleColumn,
  AnalyzeJoinedInitializer, AnalyzeJoinedTuple, ResolveJoinedProjectedExpression,
  ResolveJoinedColumn) and the orphaned BuildColumnLookup. Production discovery
  only reaches AnalyzeJoinedSyntaxOnly, so the deleted methods carried zero traffic
  yet duplicated the same legacy strict-Kind FK .Id branch the placeholder path
  was already fixed for. Inlined InferResultTypeFromSyntax (live callers remain).

- Finding #3: Clear IsRefKeyAccess=false before falling through to generic
  enrichment, so the generic block doesn't observe a stale flag if it matches
  a non-FK column for some other reason.

- Finding #8: Pin the wrap-suppression contract in FkKeyProjection_DiagnosticsShape
  with IsForeignKey == false and ForeignKeyEntityName == null assertions.

- Finding #11: Align IsRefKeyAccess enrichment with generic enrichment by carrying
  CustomTypeMapping and IsEnum from the FK column.

Finding #7 (test for the FK-not-found fall-through) is documented as a known
test-infrastructure follow-up — reproducing the path produces broken interceptor
code, so a runnable test requires source-generator unit-test scaffolding that
doesn't exist in this repo yet.
DJGosnell added a commit that referenced this pull request Apr 29, 2026
(A) findings — addressed in code:
- Doc comments on `LikeExpr.NeedsEscape` and `EscapeLikeMetaChars` updated
  to flag dialect-aware render-time escaping (#1)
- New `MaybeDoubleBackslashes` helper: prefix/suffix in non-folded LIKE
  concat path now defensively backslash-doubled for MySQL+default (#7)
- Inline comments at both literal-pattern emission sites explain the
  single-quote-first / backslash-double-second order (#9)
- `SqlDialectConfig.ParseAttribute` returns `(Config, Schema)` tuple in
  a single pass; `ContextParser` consumes the tuple (#28)
- Code comment in `ParseAttribute` documents the SQLite default for
  missing `Dialect=` as intentional pre-existing behavior (#11)
- All 11 method bodies in `SqlAssembler.cs` now uniformly use the
  `var dialect = config.Dialect;` shadow at entry (#25)
- Extracted `IsDockerUnavailable`, `TableExistsAsync`, `ExecAsync` from
  the two MySQL test containers into shared `TestContainerHelpers.cs` (#26)
- Added `<remarks>` footgun warning to the back-compat
  `Render(SqlDialect)` overload — silently defaults the carrier flag (#33)
- Updated `MySqlBackslashEscapes` XML doc to make non-MySQL no-op
  behavior explicit; future QRY rule noted as follow-up (#37)

(B) findings — added gap-filling tests:
- `Contains_ParameterBound_BackslashEscapesTrue_NoDoubling` proves the
  parameter-bound LIKE path bypasses doubling (#20)
- `Where_Contains_LiteralBackslash_DefaultMode_ReturnsMatch` integration
  test exercises the seeded-but-unqueried `"a\b"` row through the full
  pipeline against live default-mode MySQL (#21)
- `Where_Contains_AnsiForm_NoBackslashEscapesSession_ReturnsMatch`
  integration test exercises the opt-out roundtrip with new
  `MyAnsiSessionDb` context against default-mode container with
  session-level `SET sql_mode = ...,NO_BACKSLASH_ESCAPES` (#2)

All 3,384 tests pass.
DJGosnell added a commit that referenced this pull request May 6, 2026
…ves)

Addresses the four A/B findings from the REVIEW pass:

- Finding #1 (B): adds `Select_ProjectionMixedNestingDepths_OrderTotalAndItemTotal`
  to `CrossDialectNestedSubqueryTests` — closes the deep-projection-side gap
  that the sibling 1-level test left uncovered. The plan's 3-level
  Sum/Sum/Count variant exposed a generator projection-type resolver bug
  (nested int aggregates resolve as decimal, CS9144); tracked in #294 and
  worked around by keeping the new test on decimal-typed Sums.
- Findings #3 + #9 (B): adds `QRY075_UpdateSetAction_AssignToComputedColumn_Reports`
  and `QRY075_UpdateSetAction_AssignToWritableColumn_DoesNotReport` to
  `ComputedColumnDiagnosticTests` — provides the positive firing assertion
  that was missing after 1abdd63 trimmed the dead typed-lambda tests.
- Finding #7 (A): renames `ToAsyncEnumerable_BreakEarly_StopsAfterFirstRow`
  to `ToAsyncEnumerable_BreakAfterFirst_YieldsOrderedFirstRow` and rewrites
  the comment to admit the assertion is behavioral, not a streaming-vs-
  buffering proof.
- Finding #8 (A): tightens the `MultiContextPerFileTests` class docstring
  to state the test only proves file separation; carrier-name non-collision
  is attributed to C# `file`-scoped accessibility (language guarantee, not
  a generator behavior the test observes).

Manifest snapshots regenerate to pick up the new projection query shape.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant