Skip to content

Weld concatenated literals before scanning store SQL, and make the PostgreSQL parse-check population what the reader files declare - #3223

Merged
erikdarlingdata merged 5 commits into
devfrom
fix/source-scan-population-coverage
Sep 9, 2026
Merged

Weld concatenated literals before scanning store SQL, and make the PostgreSQL parse-check population what the reader files declare#3223
erikdarlingdata merged 5 commits into
devfrom
fix/source-scan-population-coverage

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 9, 2026

Copy link
Copy Markdown
Owner

Two source-scanning guards each reported a clean pass over a population that had a real thing outside it. Neither weakness came from #3216; both are pre-existing, and both are the shape where the guard's own output is indistinguishable from success.

1. StoreSqlClockDisciplineTests scanned per literal BODY, so a concatenated predicate was split for the scan

The pin flags store SQL comparing a naive collector timestamp against a bare clock function, because PostgreSQL resolves the mixed comparison at the store session's TimeZone — a documented one-hour predicate spanning five under America/New_York. It iterated CSharpSourceWalker.StringLiteralBodies one body at a time, so a statement assembled as @"… WHERE collection_time < " + Bound + @"now() - interval '1 hour'" put the column in one body and the clock in another. The comparison is in neither, and the second body is not even SQL-shaped, so it never reached the discriminator at all.

The scan now reads maximal runs of literals welded by C# + concatenation, rendered as the string the runtime builds: nothing between a bare + (which contributes nothing, so inserting a space would invent a token boundary in a statement merely split across source lines), and a single space where the glue carries an expression whose value is unknown — the substitution StringLiteralBodies already makes for an interpolation hole, for the same reason.

Reachability, measured, not argued. 815 runs in the corpus weld two or more literals, and 8 are units no member body was SQL-shaped enough to reach the discriminator. Four of those eight are the config_alert_log reads in DarlingAlertReader and ViewerDataService.AlertHistory — the exact site class this pin's own remarks name as its reason for existing ("two of the sites this pin was written for are alert reads and an alert that returns no rows never fires"). All four bind their bounds and are clean. None had ever been read.

No false positive. Offenders are 1 before and 1 after (the arithmetic-waived DarlingModuleMap.RefreshSql). That holds with #3216's three composed TouchAndProbeSql sites in the tree, which this scan now welds and reads for the first time — confirming they are inert rather than taking it on trust.

The LooksLikeSql remarks that stated this limit are replaced rather than left to contradict the code.

What is deliberately not welded, because welding it would invent a predicate the runtime never builds: separate statements, separate arguments or collection elements, separate blocks, and the two arms of a conditional. Two tests do that, and each is load-bearing on a shape the other clears:

  • A + at both ends of the gap. 2,175 gaps have one; the two the alternative "gap contains a +" rule would additionally weld are real corpus shapes (DarlingMcpPgWraparoundTools' &&Rank(multi)>Rank(xid)?worst+), and one of them is the fixture for this clause.
  • A separator at expression depth zero. … + x; var b = y + … has a + at both ends and is two statements; 10 corpus gaps are that shape, in ComposeCompiler and DarlingNetworkConfigEditor — files whose business is composing SQL, where a false weld could invent an offender.

Depth is why the separator test is a walk and not a Contains: a comma inside the glue's own call (+ DarlingToolExitCode.Diagnose(exitCode, exePath) +, four times here) is one concatenation, and a depth-blind rule splits it for no gain.

Nesting is respected — a literal inside an interpolation hole welds only with its own siblings, and 144 bodies are nested that way.

The DDL column scrape reads the same welded units. Measured, that changes nothing today (57 names either way, because every rung's DDL is one literal); it is there so the two sites cannot disagree about what a literal is, which is how the split went unnoticed.

2. DarlingPgReadSqlParsesLiveTests had a floor of 10 against a population of 49, and one shipped read was outside it

This is the suite that PREPAREs every shipped PostgreSQL read against a real server. The defect it exists for (#2554: an ambiguous LEFT JOIN column, 42702, throwing on every call for months while a dozen text assertions passed) is invisible to every other instrument, so a read that drops out of this population loses its only parse check.

The population claim was wrong. Its doc said "twelve constants exist today across nine readers". Measured: 28 reader types ship 49 reads. A floor of 10 against 49 tolerated a 39-read drop — 79% of the population could leave while the guard reported a clean pass.

And one read was already outside. Discovery filtered on f.IsLiteral, and a static readonly string composed by concatenation is not a literal. DarlingPgColumnStatsReader.CoverageEvidenceSql — a shipped read, executed at every column-stats coverage call, interpolating a status list and two collector thresholds — had never been parse-checked. Discovery now reads const and static readonly alike; that read passes PREPARE against PostgreSQL 17.11, verified rather than assumed.

The single floor is replaced by three clauses that fail differently:

  • a ratchet at the live population (49). Growth never trips it, so it still needs no edit when a read lands; a read removed reds, which is the direction a coverage loss travels in.
  • a per-type clause: every matched reader type must contribute at least one read. DarlingPgTrendReader alone ships 9, so no total with slack can see one type emptying.
  • a source census: every const string / static readonly string declared in the Storage project's DarlingPg*Reader*.cs files must be in the parse-checked population or in a named, per-site NotQueryFields set. This is the only clause whose denominator comes from outside reflection, so it is the only one that sees a read leave reflection's reach while its declaration sits in the file — a type renamed off the pattern, a field moved, a field kind the filter does not read. A stale allowlist entry reds too.

NotQueryFields holds three: two SQL fragments interpolated into a query, and DarlingPgTableBloatReader.StaleStatisticsChurnRatioSql, which is spelled like a query and holds "0.2" — the trap that makes any name-shaped rule useless here.

The census runs without a server. The old floor lived inside the DARLING_TEST_PG-gated test, so on the Windows build job nothing checked the discovery at all.

Census keying: by declaring type, not file stem

The census originally keyed source-declared fields by Path.GetFileNameWithoutExtension, and its comment justified that with a 1-file-per-type invariant nothing asserted. CONTRIBUTING.md endorses partial classes for large files, so splitting DarlingPgFooReader.cs would key half its fields DarlingPgFooReader.Extra.SomeSql, match nothing, and fail loudly for a field that reflection and the parse check both cover — a false failure in a pin whose whole subject is guards that report the wrong thing.

Reproduced both ways. With DarlingPgTrendReader made partial and an ExtraProbeSql added in DarlingPgTrendReader.Extra.cs:

Census keying Result
file stem 1 failed — "not in the parse-checked population: DarlingPgTrendReader.Extra.ExtraProbeSql"
declaring type 0 failed, read count rises to 50

Attribution runs through CSharpMemberMap, so it walks with CSharpSourceWalker like the other source pins, and takes the innermost enclosing type — a reader file also declares its row types, so nearest-declaration-above would get it wrong. A field that cannot be attributed asserts as an attribution failure rather than as an uncovered read, because reporting it as the latter sends the next reader to the wrong file.

One containment scan, not a third copy

Resolving the declaring type was first done with a private copy of CSharpMemberMap.EnclosingMember's containment scan, differing only by a DeclarationKind — a third copy of the shape #2913 consolidated from five and #3094 fixed a half-corrected copy of.

It was also not a faithful copy. EnclosingMember escalates an unterminated body (End < 0) to Unknown rather than guess; the copy treated one as running to EOF and would have attributed a field to a type the brace walk had lost. Inert today (0 unattributed either way), and precisely the quiet divergence between two copies that #3094 was about.

CSharpMemberMap now exposes EnclosingType beside EnclosingMember, both delegating to one private Enclosing(map, offset, kind), so the End < 0 judgement is made once. DeclarationKind's doc claimed types are "excluded from attribution", which EnclosingType makes untrue; it now says excluded from member attribution.

Proved behaviour-preserving for the six pins that already call EnclosingMember: the pre-refactor body and the delegated call agree at all 673,093 probed offsets across 2,229 files — every literal start and end, every declaration boundary, and a coarse sweep besides. EnclosingType resolves to a type at 633,459 of those offsets, so it is not degenerate.

The census pattern is anchored to a declaration

It first matched const string X = anywhere in the stripped source. C# allows a local const string, and reflection only ever sees static fields — so a local added to any reader method would sit in missing permanently, and the only way to quiet it would be putting a local into NotQueryFields, which is for fields that hold no query rather than for what the pattern over-matched.

The scan now walks the declarations CSharpMemberMap already found and asks which of them are static string fields, instead of pattern-matching positions of its own. DeclarationHead requires an access modifier, which a local cannot carry, so anchoring is what excludes them.

Reproduced with const string UnrelatedLocal = "not a query"; added inside DarlingPgXminReader's read method:

Census pattern Result
free regex over the file 1 failed — "not in the parse-checked population: DarlingPgXminReader.UnrelatedLocal"
anchored to a declaration 0 failed

The population is unchanged either way — 52 declared, 49 discovered, the 3 remaining being exactly the NotQueryFields entries — so anchoring costs no reach.

Mutation evidence

Baseline on this branch, xUnit v3 in-process runner over both guard files against live PostgreSQL 17.11 (timescale/timescaledb:latest-pg17, initdb -U darling as CI does): Total: 5, Errors: 0, Failed: 0, Skipped: 0, Not Run: 0.

Mutation Result
weld crippled to pre-change per-body behaviour 1 failed — "the scan MISSED a predicate split across a concatenation (bare + glue)"
depth-zero separator test removed 1 failed — two statements welded
both-ends + weakened to contains-a-+ 1 failed — a comparison-in-condition welded
depth-zero colon test removed 1 failed — a conditional's arms welded
nesting ignored (every body top level) 1 failed — the container's weld goes to the nested body
weld rendering collapsed to always-a-space 1 failed — bare-+ rendering invents a token boundary
discovery narrowed to const only (= dev before this PR) 2 failed — 48 against the 49 ratchet
same, both ratchets lowered to isolate the census 1 failed — names DarlingPgColumnStatsReader.CoverageEvidenceSql
syntax error injected into CoverageEvidenceSql 1 failed — "1 of 49 shipped PostgreSQL reads do not parse", so the widened population is really PREPAREd and not merely counted
one reader type leaves reflection's reach 2 failed — type ratchet at 27/28 and the read ratchet
same, both ratchets lowered to isolate the census 1 failed — names DarlingPgXminReader.PgXminHorizonSql
one read leaves the field-kind filter (const → plain static) 2 failed — read ratchet at 48/49
shared EnclosingType filters Member instead of Type 1 failed — fields keyed by their own member name, matching nothing
census file glob broken 1 failed — "only 0 static string fields were read out of the reader SOURCE"

Three results are green by design, and each says something:

Mutation Result What it establishes
const stringstatic readonly string (the shape this PR was briefed on) 0 failed widening the filter removes that failure mode rather than detecting it. It is no longer a coverage loss, so it cannot be its own red-proof — the pre-fix state above is.
separator test made depth-blind 0 failed its cost is precision, not coverage: 4 genuine concatenations split, none of them SQL. The corpus measurement is the evidence, not a test.
the new control fact disabled, grouping kept 0 failed the corpus scan is green before and after the grouping change, so it is not evidence for it. The control fact is the only instrument.

Every mutation applied by byte-level replace with a count == 1 anchor, verified changed by sha256, rebuilt, run, restored, restore confirmed by sha256. Tree ended byte-identical to HEAD on all five touched files.

Two mutations in an earlier pass did not compile (a type rename and a field deletion break their callers); both were replaced by shapes that isolate the same coverage loss and do compile, rather than reported as passes.

Three of my own controls were originally not load-bearing and the battery is what showed it: the separator test, the both-ends test and the nesting logic each had every fixture cleared by a different clause. The fixtures that now discriminate them are modelled on real corpus gaps, and the nesting case was rewritten as a composition assertion after the findings-based version turned out to be flagging pre-existing walker behaviour rather than anything the grouping did.

Scope

Test-only; no product code changes. Darling.Tests builds on macOS with EnableWindowsTargeting and each commit builds independently. The suites themselves cannot run on macOS, so both guard files were compiled and executed through a scratch net10.0 xUnit project that links the real source files[CallerFilePath] therefore resolves to the real tree and the scans read the real corpus. CI is the arbiter for the rest of the suite.

…r a bare clock

StoreSqlClockDisciplineTests scanned one string literal BODY at a time, so a
predicate assembled across a C# concatenation was split for the scan: a bare
now() on one side of a + with the naive collector column on the other is a
comparison in neither body, and the suite reported clean on it.

The scan now reads maximal runs of literals welded by concatenation, rendered
as the string the runtime builds - nothing between a bare +, a single space
where the glue carries an expression whose value is unknown, which is the
substitution CSharpSourceWalker already makes for an interpolation hole.

Measured on the corpus: 815 runs weld two or more literals and 8 are units no
member body was SQL-shaped enough to reach the discriminator at all. Four of
those 8 are the config_alert_log reads in DarlingAlertReader and
ViewerDataService.AlertHistory - the site class this pin's own remarks name as
its reason for existing. All four bind their bounds and are clean; none had
ever been read.

The offender count is unchanged at 1 (the arithmetic-waived DarlingModuleMap
RefreshSql), so the change adds reach without adding a false positive. That
holds with #3216's three composed TouchAndProbeSql sites in the tree, which
this scan now welds and reads for the first time.
DarlingPgReadSqlParsesLiveTests discovered reads through f.IsLiteral, so a
static readonly string composed by concatenation was outside the population:
DarlingPgColumnStatsReader.CoverageEvidenceSql, a shipped read, had never been
parse-checked. Discovery now reads const and static readonly alike.

Its anti-vacuity floor was 10 against a doc claim of "twelve constants across
nine readers" that had gone stale: measured, 28 reader types ship 49 reads, so
the floor tolerated a 39-read drop. Replaced by three clauses that fail
differently - a ratchet at the live population, a per-type clause (one reader
ships 9 reads, so no slack total can see a type emptying), and a census of what
the reader SOURCE declares, which is the only denominator from outside
reflection and so the only one that sees a read leave reflection's reach while
its declaration stays in the file.

The census runs without a server, so it covers the Windows build job too. The
old floor lived inside the DARLING_TEST_PG-gated test, which meant nothing
checked the discovery on half the CI matrix.
Comment thread Darling/Darling.Tests/DarlingPgReadSqlParsesLiveTests.cs Outdated
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown

Reviewed. Scope is entirely Darling.Tests source-scanning infrastructure (StoreSqlClockDisciplineTests.cs, DarlingPgReadSqlParsesLiveTests.cs) — no T-SQL, no production Lite/Darling code, and no Lite counterpart is expected since both guards are PostgreSQL/Darling-specific (naive-vs-timestamptz clock mixing, and PG reader reflection). No parity drift, no security-relevant surface (no user input, no network/process/file writes beyond reading repo source), no performance concern (small in-memory regex/text scans over the repo's own .cs files, run once per test).

Traced the new ConcatenatedLiteralUnits/WeldBetween literal-welding logic (the core of the StoreSqlClockDisciplineTests change) by hand against the fixture cases in TheScan_WeldsAPredicateSplitAcrossAConcatenation_AndWeldsNothingElse — the depth/bracket tracking, the +-at-both-ends + depth-zero-separator test, the nested-interpolation parent tracking, and the delimiter-blanking via CSharpSourceWalker.StripCommentsAndStrings all line up correctly with the documented shapes (bare + vs expression glue, separate statements/arguments/collection elements, conditional arms, global:: colons). This is a solid fix for the "read one literal body at a time" gap it describes.

Left one inline comment on DarlingPgReadSqlParsesLiveTests.cs about a latent fragility in the new source census: it keys declared fields by file-stem rather than by the CLR type name, resting on an unasserted "one file per reader type" assumption that today happens to hold (28 files, 28 types) but isn't actually checked, and would produce a false "missing" failure rather than a silent gap if a reader is ever split into a partial-class file (a pattern CONTRIBUTING.md endorses for large services). Not a live bug, but worth tightening given the whole point of this PR is closing exactly this class of guard-blind-spot.

The census compared source-declared fields against reflection using
Path.GetFileNameWithoutExtension as the type name, and its comment justified
that with a 1-file-per-type invariant nothing asserted. CONTRIBUTING.md
endorses partial classes for large files, so splitting DarlingPgFooReader.cs
would key half its fields DarlingPgFooReader.Extra.SomeSql, match nothing, and
fail LOUDLY for a field reflection and the parse check both cover.

Reproduced: with DarlingPgTrendReader made partial and an ExtraProbeSql added
in DarlingPgTrendReader.Extra.cs, file-stem keying reds naming
DarlingPgTrendReader.Extra.ExtraProbeSql; keyed by declaring type the same
split is green and the read count rises to 50.

Attribution now runs through CSharpMemberMap, so it walks with
CSharpSourceWalker like the rest of the source pins and picks the INNERMOST
enclosing type - a reader file also declares its row types. A field that cannot
be attributed asserts as an attribution failure rather than as an uncovered
read, because reporting it as the latter sends the next reader to the wrong
file.
Comment thread Darling/Darling.Tests/DarlingPgReadSqlParsesLiveTests.cs Outdated
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown

Reviewed. This is test-only infrastructure inside Darling.Tests (no product code, no T-SQL), so the T-SQL style rules don't apply, and I don't see a Lite/Darling parity gap — Lite has no equivalent PostgreSQL-clock or parse-checked-population test for this to drift from.

I traced the two changed guards in detail (ConcatenatedLiteralUnits/WeldBetween welding logic, the parent/nesting bookkeeping, the +-at-both-ends and depth-zero separator tests, and the source census in DarlingPgReadSqlParsesLiveTests) against the corpus numbers the PR states (28 reader types / 52 declared fields / 49 SQL reads / 3 NotQueryFields) and against several hand-built fixtures (container+following-literal weld, nested interpolation holes, comparison-in-a-conditional). Everything checks out — the weld correctly reconstructs the runtime string for bare-+ vs. expression glue, correctly refuses to weld across statement/argument/block boundaries and conditional arms, and the census's EnclosingType/regex extraction matches the actual field count in the 28 DarlingPg*Reader*.cs files exactly (52 = 49 + 3). No correctness bugs found in either guard.

One maintainability finding posted inline: EnclosingType in DarlingPgReadSqlParsesLiveTests.cs duplicates CSharpMemberMap.EnclosingMember's shape instead of extending the shared CSharpMemberMap, which is the exact pattern this codebase has fixed multiple times before (#2913, #3094, #2898, #3090) — and it silently diverges from EnclosingMember's "unterminated → escalate to Unknown" fallback in a way that's inert today but easy to lose track of with a third copy of the scan.

Non-blocking observation: several other source-scanning pins still read CSharpSourceWalker.StringLiteralBodies one literal at a time (PgColumnStatsCoverageTests, TsqlConventionGuardTests, CollectorMeasurementSeamTests, QueryStoreTouchGuardSingleSourceTests, plus Lite.Tests/AuroraOnlySqlIsGatedTests and LiteLogLevelGateTests), so the same "predicate split across a +" blind spot this PR closes for the clock-discipline and read-population guards may still exist in those. Explicitly out of scope per the PR's own Scope section, so just flagging it as a possible follow-up rather than something to fix here.

The census resolved a field's declaring type with a private copy of
EnclosingMember's containment scan, differing from it only by a DeclarationKind
- a third copy of the shape #2913 consolidated from five and #3094 fixed a
half-corrected copy of.

It was not even a faithful copy. EnclosingMember escalates an unterminated body
(End < 0) to Unknown rather than guess; the copy treated one as running to EOF
and would have attributed a field to a type the brace walk had lost. Inert
today (0 unattributed either way), and exactly the quiet divergence between two
copies that #3094 was about.

CSharpMemberMap now exposes EnclosingType beside EnclosingMember, both
delegating to one private Enclosing(map, offset, kind), so the End < 0
judgement is made once. Proved behaviour-preserving for the six pins that
already call EnclosingMember: the pre-refactor body and the delegated call
agree at all 673,093 probed offsets across 2,229 files - every literal start
and end, every declaration boundary, and a coarse sweep besides.

DeclarationKind's doc said types are "excluded from attribution", which
EnclosingType makes untrue; it now says excluded from MEMBER attribution.
Comment thread Darling/Darling.Tests/DarlingPgReadSqlParsesLiveTests.cs Outdated
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown

Reviewed. This is test-only (Darling.Tests) — no T-SQL, no stored procs, no product code, so the usual T-SQL style/OPTION(RECOMPILE)/missing-index conventions don't apply here.

Lite/Darling parity: No gap. StoreSqlClockDisciplineTests's StoreSourceFiles() already enumerates both Darling's and Lite's store trees (LiteDir(thisFile, "Services"|"Database"|"Analysis")), so the concatenation-welding fix in this PR automatically extends to Lite's SQL literals too — nothing needed on the Lite side. DarlingPgReadSqlParsesLiveTests's live-PREPARE census is inherently Postgres-specific (it leans on Npgsql's PREPARE for real name-resolution checking); there's no equivalent DuckDB primitive to port, so the absence of a Lite counterpart there is architectural, not a drift.

Security: None found — no new file/network/process boundaries, and the regex/reflection work operates only over the repo's own known-good source tree at test time.

Correctness: Left one inline comment on DarlingPgReadSqlParsesLiveTests.cs — the new DeclaredStringFields regex isn't scoped to field-declaration position, so a future local const string inside a DarlingPg*Reader*.cs method body would be miscounted as a declared field and could produce a spurious census failure. Not exercised by the current corpus, but worth tightening given the whole point of this PR is closing exactly this kind of silent-miscount gap.

Otherwise the welding logic (ConcatenatedLiteralUnits/WeldBetween) and the CSharpMemberMap.EnclosingType refactor both check out against their stated fixtures and corpus claims.

🤖 Generated with Claude Code

…nter it

The census matched `const string X =` anywhere in the stripped source. C#
allows a LOCAL const string, reflection only ever sees static fields, so a
local added to any reader method would sit in `missing` permanently - and the
only way to quiet it would be putting a local into NotQueryFields, which is for
fields holding no query rather than for what the pattern over-matched.

The scan now walks the declarations CSharpMemberMap already found and asks
which of them are static string fields, instead of pattern-matching positions
of its own. DeclarationHead requires an access modifier, which a local cannot
carry, so anchoring is what excludes them.

Reproduced: with `const string UnrelatedLocal = "not a query";` added inside
DarlingPgXminReader's read method, the free pattern reds naming
DarlingPgXminReader.UnrelatedLocal; anchored to declarations the same local is
clean. The population is unchanged either way - 52 declared, 49 discovered,
the 3 remaining being exactly the NotQueryFields entries - so anchoring costs
no reach.
@claude

claude Bot commented Sep 9, 2026

Copy link
Copy Markdown

Review

Scope: this PR only touches Darling.Tests (CSharpMemberMap.cs, DarlingPgReadSqlParsesLiveTests.cs, StoreSqlClockDisciplineTests.cs) — test infrastructure, no T-SQL, no production code, no Lite/Darling application-code parity concerns. StoreSqlClockDisciplineTests already scans both apps' source (StoreSourceFiles/LiteDir), and that dual-scan is unchanged here, so there's no new parity gap.

I traced the two new/changed algorithms by hand against the PR's own fixtures:

  • ConcatenatedLiteralUnits / WeldBetween (StoreSqlClockDisciplineTests.cs): walked the parent/innermost-container logic and the depth-tracked glue classifier against every case in TheScan_WeldsAPredicateSplitAcrossAConcatenation_AndWeldsNothingElse (bare +, expression glue, comment-in-gap, separate statements/arguments/collection elements, both conditional-arm shapes, and the &&...?worst+ real-corpus shape) — each traces to the expected weld/no-weld outcome. The nested-interpolation-hole handling (container-carries-the-weld) also checks out.
  • CSharpMemberMap.Enclosing/EnclosingType/EnclosingMember refactor: straightforward parameterization, DeclarationKind.Type already existed in the shared map, so this is a clean dedup with no behavior change for the Member path.
  • DarlingPgReadSqlParsesLiveTests census (QueryFields/FieldText/DeclaredStringFields/StaticStringField): verified against current source — DarlingPg*Reader*.cs glob matches exactly 28 files (= MinimumReaderTypes), and the three NotQueryFields entries (EvidenceStatusList, SessionScopedSources, StaleStatisticsChurnRatioSql) all still exist at those exact sites. CoverageEvidenceSql on DarlingPgColumnStatsReader is indeed static readonly (not const), confirming the stated gap the IsInitOnly addition closes. The new TheReadPopulationIsWhatTheReaderFilesDeclare test correctly runs without Assert.SkipWhen (no DB needed), matching its doc claim that it covers the Windows build job where DARLING_TEST_PG is unset.

No correctness bugs, parity drift, or security issues found. Two very minor, non-blocking observations:

  1. StaticStringField regex hardcodes the lowercase string keyword, so a field declared as String (capital) would silently fall out of both sides of the census (reflection's FieldType == typeof(string) check is type-based and unaffected, but the source-side regex wouldn't flag it as "declared", making a real gap here undetectable). Not an issue today — grep shows no String-cased static fields in DarlingPg*Reader*.cs — just a latent assumption worth a one-line comment if it's ever hit.
  2. ConcatenatedLiteralUnits now runs both StripCommentsAndStrings and StringLiteralBodies (two full walks) per file where the old code ran one; negligible given ~600 test-only files, just noting it as the cost of the more thorough scan.

Nice work on the fixture coverage for the "must-not-weld" shapes — the two "+ at both ends but still separate statements/arguments" cases and the unparenthesized-conditional-in-a-condition case are exactly the fixtures that make the depth/boundary logic non-trivial to get right.

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