M8: verification diagnostics (snapshot-based analyzer)#8
Merged
Conversation
Bump PackageVersion to 0.3.0-preview.2 so M8's verification analyzer work is distinguishable from the M7 preview in package feeds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extract the parsing core out of the GeneratorAttributeSyntaxContext overload so the upcoming verification analyzer can parse an attributed method from its IMethodSymbol without a generator pipeline. Pure refactor; the golden emission tests pin the behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QuerySnapshot models the analyzer-side view of one .sqlbound/ file and QuerySnapshotReader parses it with a small hand-rolled JSON parser. A library dependency was rejected deliberately: analyzer dependencies must ship bundled and are a known source of version conflicts inside the IDE, while this schema is tiny and written by SqlBound's own prepare step. Unknown fields are ignored for forward compatibility; malformed input returns false so the analyzer can diagnose, not crash. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
QueryVerifier compares a parsed query method with its snapshot and reports SQLB103-107: no result set at all, missing named columns, CLR type mismatches, database-nullable columns declared non-nullable, and unread result columns. Matching is by case-insensitive name because the generated code binds with GetOrdinal, and by mapped CLR type because the database's SQL type suggestions are inferences. The over-nullable direction (declared nullable, column non-nullable) is deliberately silent - it is safe at runtime. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SQLB108-111 complete the comparison rules: a statement parameter with no matching method parameter is an error (guaranteed runtime failure), a method scalar the SQL never uses is a warning, parameter CLR type mismatches are errors, and an [SqlExecute] statement that returns a result set warns that the rows are silently discarded. Infrastructure parameters (connection, transaction, cancellation token) are excluded from matching. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SqlQueryVerificationAnalyzer closes ADR 0001's loop: it parses each attributed method with the generator's own parser, pairs it with its .sqlbound/ snapshot by SHA-256 of the raw command text, and reports QueryVerifier findings at the method identifier. Verification is opt-in by presence: zero .sqlbound/ AdditionalFiles means total silence for codegen-only consumers, while any wired snapshot makes a query without one SQLB101 (stale coverage) and an unreadable or text-mismatched file SQLB102. Usage errors stay the generator's to report - the analyzer skips methods that produce no model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ADR 0003 closes the question ADR 0001 deferred: the committed .sqlbound/ directory itself is the opt-in switch. No snapshots wired means the analyzer is silent, so codegen-only consumers are never nagged; any snapshot present makes a query without one a warning, surfacing the forgot-to-re-run-prepare hole in the IDE immediately. docs/diagnostics.md catalogs SQLB001-010 and SQLB101-111 with the two non-obvious comparison rules (name-based column matching, CLR-type comparison). Co-Authored-By: Claude Fable 5 <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.
The analyzer side of ADR 0001: compare each
[SqlQuery]/[SqlExecute]method's declared signature against the database-described metadata committed under.sqlbound/— fast, offline, never a database connection. Ships insideSqlBound.Generators(0.3.0-preview.2).What's here
SqlQueryVerificationAnalyzer: parses attributed methods with the generator's own parser (a symbol-based entry point extracted as a pure refactor, pinned by the golden emission tests), pairs each query with its snapshot by SHA-256 of the raw command text (.sqlbound/query-<hash>.json, naming provisional until M9), and reports findings at the method identifier. Usage errors (SQLB0xx) remain the generator's — the analyzer never double-reports..sqlbound/AdditionalFiles→ total silence, so codegen-only consumers are never nagged; any snapshot present → a query without one is SQLB101 (warning), surfacing the forgot-to-re-run-preparehole in the IDE immediately. Severities retunable via.editorconfig.QueryVerifier(pure, location-free): columns match by case-insensitive name, not ordinal (generated code binds withGetOrdinal); types compare as mapped CLR types, not SQL type names (SQL Server's suggested types are inferences — thedecimal(38,19)lesson from M7); over-nullable declarations are deliberately silent.QuerySnapshotReader: snapshot JSON via a small hand-rolled parser instead of aSystem.Text.Jsondependency — analyzer dependencies must ship bundled and are a known source of IDE version conflicts. Malformed input becomes SQLB102, never a crash; unknown fields are ignored so M9 can extend the schema.docs/diagnostics.md: full SQLB catalog.Diagnostics added (
SqlBound.Verification)SQLB101/102 (no snapshot / invalid-stale snapshot, Warning), SQLB103–106 (no result set, missing column, column type, nullability — Error), SQLB107 (unread columns, Info), SQLB108/110 (parameter missing from method / parameter type — Error), SQLB109 (unused method parameter, Warning), SQLB111 (
[SqlExecute]discards a result set, Warning).One refinement over the approved plan: "statement produces no result set" got its own ID rather than one missing-column error per declared column, so the range is 101–111 instead of 101–110.
Testing
QueryVerifieras a pure function covering every rule and the silent directions, and a newAnalyzerHarness(CompilationWithAnalyzers+ in-memoryAdditionalText) running the analyzer end-to-end from source + snapshot files — opt-in silence, matching-snapshot silence, SQLB101/102/106/111 paths, and location assertions.prepareexists to write real snapshots.🤖 Generated with Claude Code