Fix false-positive DAP214 for {=XXX} literal-replacement syntax - #191
Conversation
SqlTools.GetParameters() only matched @/:/?/$ prefixed parameters via
ParameterRegex, ignoring the {=XXX} literal-replacement syntax handled
by the existing LiteralTokens regex. That meant a member referenced
only via {=XXX} was filtered out of the known-parameters set built in
DapperAnalyzer.SharedGetParametersToInclude, so when TSqlProcessor
later rewrote {=XXX} to @xxx for AST parsing, the variable appeared
undeclared, triggering a false DAP214.
Fix merges ParameterRegex and LiteralTokens matches in GetParameters().
Root cause diagnosed by @andreasblueher in DapperLib/Dapper#2181.
|
Thanks for this — the root-cause analysis is spot on (and thanks @andreasblueher for the original diagnosis): One catch, though: So the shape I'd suggest: keep the literal names out of the path the generator consumes, and include them only where the analyzer builds the set handed to Happy to help get this over the line whichever way suits you — if you'd like, I can push the adjustment onto your branch (if "allow edits by maintainers" is enabled) and you can review, or leave it with you with the notes above. Either way this is a good catch and I'd like to land it. |
|
Side note: the feature parity (the missing literal injection) is what I'm working on right now, so... timely. |
… generator
SharedGetParametersToInclude fed two consumers that need different
answers: the analyzer's DAP214 validation (which must know about
{=XXX} literal tokens to avoid the false positive) and the
generator's BuildParameterMap (which must NOT treat literal-only
members as bound parameters, since literal injection isn't
implemented yet and {=x} values need to be injected into the SQL
text rather than bound as a DbParameter).
Threads an includeLiteralTokens flag through GetParameters,
GetUniqueParameters and SharedGetParametersToInclude, defaulting to
false (pre-fix behavior). The analyzer call site opts in explicitly;
the generator keeps the default.
Adds a regression case mixing a bound parameter and a literal token
in the same query, plus an InterceptorTests golden fixture confirming
the generated parameter map only binds the real parameter.
Per review from @mgravell on DapperLib#191.
|
Good catch, thanks for the detailed breakdown. Pushed a fix along exactly the lines you suggested: threaded an Added a regression case mixing No need to push to the branch yourself, but appreciate the offer. Let me know if the shape looks right or if you'd rather see it split differently. |
Two resolutions: - DapperAnalyzer.cs: main appended SuppliesSqlParameters at the end of the class, where this branch had only added the missing trailing newline. Kept both; the two changes do not interact (includeLiteralTokens is consumed earlier in SharedGetParametersToInclude, SuppliesSqlParameters gates the no-parameters-detected report in a branch this work does not touch). - LiteralTokens.output.txt and LiteralTokens.output.netfx.txt: refreshed for the reworded DAP000 scorecard on main. These snapshots are new on this branch, so they merged cleanly while carrying the old wording.
|
merging with thanks |
Summary
Fixes a false-positive
DAP214(Variable @X is not declared and no corresponding parameter exists) when SQL uses the{=XXX}literal-replacement syntax, e.g.:Related issue: DapperLib/Dapper#2181
Root cause
Diagnosed by @andreasblueher in a comment on the issue:
SqlTools.GetParameters(string?)only scanned SQL withParameterRegex(the@/:/?/$prefixed forms), ignoring the already-existingLiteralTokensregex that matches{=XXX}. That method feedsSqlTools.GetUniqueParameters, whichDapperAnalyzer.SharedGetParametersToIncludeuses to build the filter of which anonymous-type/parameter-object members get included as known SQL parameters (ParameterMode.Filter).Because
Adminwas invisible to that filter, it never made it into theImmutableArray<SqlParameter>passed toTSqlProcessor.Execute. Separately,TSqlProcessordoes rewrite{=Admin}to@Adminbefore AST parsing (via the sameLiteralTokensregex) — so the parser sees a reference to@Admin, finds it isn't in the known-parameters set, and reportsDAP214, even though the caller supplied it.Fix
SqlTools.GetParametersnow merges matches from bothParameterRegexand the existingLiteralTokensregex, so members referenced only via{=XXX}are correctly recognized as used/known parameters. No regex patterns were changed — only how their results are combined.Test plan
dotnet build— analyzer project builds clean[InlineData("select Id from Users where UserTypeId = {=Admin}", "Admin")]toSqlTests.DetectParametersDAP214.NoFalsePositive_Issue2181regression test: verifies{=Admin}with a matching parameter member produces zero diagnosticsdotnet testontest/Dapper.AOT.Test(net10.0): all 158Verifiersdiagnostic tests pass, including all otherDAP21xcases (no regressions)DateOnlyTimeOnlyPostgreSqlTests.ReadDateOnly, aDateOnly→IConvertiblecast issue in Postgres integration tests, unrelated to this change)