DAP000: separate refused-with-diagnostics from skipped-silently - #211
Merged
Conversation
The scorecard had two buckets, "unsupported API" and "skipped due to diagnostics", and the second was not true of everything in it. A call-site can be dropped with nothing said at all, and those were being counted as though a diagnostic had explained them. Measured on the Dapper suite, the old line read "82 unsupported API, 110 skipped due to diagnostics". It now reads "82 unsupported API, 75 refused with diagnostics, 35 skipped silently" - so a third of the skips had no explanation attached, and the instrument we have been steering by said otherwise. Three sources, all now visible rather than inferred: - CommandDefinition overloads. The analyzer only inspects call-sites with a string `sql` parameter (or one marked [Sql]); these carry the SQL inside the struct, so it never validates them and never reports. The generator sees them - it filters by method name - so they are counted, just not explained. IsVisibleToAnalyzer mirrors that entry condition so the generator can tell which of its skips anything will have reported. - the self-binding guards from #197 and #198 (multi-exec over an expandable member; expandable alongside an output parameter). Documented in the fixture comments, invisible to consumers. - GetRowParser's concreteType overload. Adds CommandDefinitionOverloads as a pinned fixture, so the count moves if that changes. Every golden .txt shifts with the message, and DAP004 asserts DAP000's arguments explicitly, so it gains the new one. Not fixed here: driving the silent count to zero. That needs a diagnostic at each of those sites, which is a separate change - this one makes the number visible so it can be driven down and kept there.
… it (#212) The parity table's statuses were remembered rather than checked, and the two I probed by hand this week were both understated. This replaces the guessing for the question that can be answered mechanically - "what does Dapper.AOT do with this overload?" - and leaves parity.md the judgement it is actually good for: impact, complexity, and decisions like the runtime-registration non-goal. The dispatch decision turns out to depend only on the method symbol, never on the call-site, so IsDapperMethod gains a symbol overload and the test classifies all 110 public SqlMapper extension methods without synthesising a single call. No database, no harness, runs in a second. The five dispositions, and what they found: candidate 40 generation is attempted unsupported API (diagnosed) 16 refused, DAP001 names it unsupported API (undiagnosed) 13 refused, nothing says so skipped silently 27 dropped mute - vanilla under JIT, runtime failure under AOT, no build-time signal not inspected 9 outside the name filter: AsList, Parse, GetTypeName and friends, which is correct - they need no interception So 40 of 110 overloads give a consumer nothing to act on, almost all of them CommandDefinition-shaped. That is the finding; fixing it is separate work. The report is checked in and compared on every run, so an overload added upstream fails the test rather than passing unnoticed, and a row moving into 'skipped silently' shows up in a diff. Bounded deliberately: this says what happens to an *overload*, not to every call of one - a supported overload can still be refused at a call-site for reasons of its own. And it says nothing about behaviour; only the Dapper suite does that.
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 DAP000 scorecard had two buckets — "unsupported API" and "skipped due to diagnostics" — and the second was not true of everything in it. A call-site can be dropped with nothing said at all, and those were counted as though a diagnostic had explained them.
On the Dapper test suite, the old line read:
and now reads:
A third of the skips had no explanation attached. The instrument we have been steering by since #185 said otherwise, which makes every ❓ row in
parity.mdthat rested on "the harness didn't flag it" unreliable.Where the 35 come from
CommandDefinitionoverloads. The analyzer only inspects call-sites with a string parameter namedsql(or marked[Sql]); these carry the SQL inside the struct, so it never validates them and never reports anything. The generator does see them — it filters by method name — so they are counted, just not explained.IsVisibleToAnalyzermirrors the analyzer's entry condition so the generator can tell which of its skips anything will have reported. Consequence for users: silent fallback to reflection-based Dapper, which works under JIT and fails under native AOT with no build-time signal (issues Support Dapper overloads with CommandDefinition #112, No code generation with Dapper.AOT despite following documentation #158, TypeHanderCache issue #165).GetRowParser'sconcreteTypeoverload.What is in here
SkippedSourceStategainsDiagnosed, set at each of the four bail paths; the count splits three ways; DAP000's message and argument list grow by one.CommandDefinitionOverloadsis added as a pinned fixture so the count moves if that behaviour changes.Every golden
.txtshifts with the message.DAP004asserts DAP000's arguments explicitly, so it gains the new one.What is deliberately not in here
Driving the silent count to zero. That needs a diagnostic at each of those sites — DAP001 for the analyzer-invisible overloads, and something new for the parse-time guards — which is a separate change with its own design questions (does the analyzer learn to inspect
CommandDefinition, or does the generator report directly?). This PR makes the number visible so it can be driven down and then kept at zero.Suites green on net8.0 (367) and net48 (360); solution builds clean.