Parse the crude second opinion's XML instead of guessing its quote delimiters - #3146
Conversation
…limiters An apostrophe anywhere in a project file opened a single-quote alternation that ran to the next apostrophe, so every literal between them left the crude answer. Reading the document with XDocument keeps the attribute-value and element-text population and gets the quoting grammar right by construction; a comment is still text-scanned, because a commented-out path counting is the over-read this read's doc rests on. The floor assertions now name which side lost the path, so a crude-parse failure stops presenting as an evaluated-side regression.
|
Reviewed. This is entirely test-infrastructure ( Verified the core claim against the actual repo files rather than taking the PR body's numbers on faith:
Traced No correctness, parity, security, or performance issues found. Left one inline nit — a doc-wording imprecision in a summary line ( |
ReviewScope: single file, Not applicable here, checked and ruled out:
Correctness walkthrough:
Caveat: I could not execute No correctness, parity, or security issues found. |
|
Reviewed the diff (Lite.Tests/CrossAppGuardCiGateTests.cs only). Scope check: no T-SQL touched, so the T-SQL style rules don't apply here. No Correctness: traced the new
No security, performance, or parity issues found. The doc-comment rewrites on |
Closes #3140.
ParsedProjectXmlPathsis the deliberately-crude second opinion thatTheEvaluatedSet_ContainsEverythingAParseOfTheSameXmlFindsfloors MSBuild's evaluated item set against. It read the project XML with a regex alternation whose single-quote arm treated an apostrophe anywhere — in a comment, in prose — as an attribute delimiter: it paired with the next apostrophe and every literal between them left the answer. That is an under-read of arbitrary literals, which is precisely what the doc claimed twice it could not do.What was swallowed on
devMeasured with the shipped read, not reasoned about:
Lite.Tests/Lite.Tests.csproj<None Include="Fixtures\SystemHealth\*.xml" CopyToOutputDirectory="PreserveNewest" />itemDarling/Darling.Tests/Darling.Tests.csproj<None>items —DarlingStoreUpgrade.cs,Mcp\DarlingMcpHostService.cs,Mcp\DarlingWebHostService.cs,..\tools\fetch-pg-runtime.ps1and each one'sLinkNeither file's swallowed region held a cross-app path, which is the only reason the guard was green — green by position, not by correctness. And the file that lost the most has an EVEN apostrophe count, so a check on parity would have called it clean. Position relative to the item is the invariant; parity never was.
The fix, and what was rejected
LiteralXmlValuesreads the document withXDocumentand yields each attribute value and each element's text. Crude now means UNEVALUATED — no property expansion, no globbing, noConditions — rather than mis-parsed, and the quoting grammar is the XML parser's problem, because a regex that gets XML quoting right is most of an XML parser.XDocument.Parseon a.csprojis already this repo's idiom (WriteLockBudgetTests).A comment's body is still text-scanned by
CommentedOutValues, for double-quoted values and>-to-<spans, so a path inside a comment still counts. That over-read is the positionImportElementstakes for the same stated reason — a commented-out construct is a construct arriving unnoticed — so a reader that skipped comments would have narrowed a documented guarantee while claiming to fix a bug. Dropping only the apostrophe arm there is what makes prose harmless; the price is a single-quoted attribute inside a comment, which is now stated in the doc and asserted.Rejected:
ImportElementsboth rest on.Include='…', so this trades a silent under-read of arbitrary literals for a silent under-read of single-quoted ones.XDocumentreads that spelling correctly, and the pin asserts it.Darling.Tests.csprojwas even and lost four items, andCondition="'$(X)' != ''"carries four apostrophes inside one correct value. A parity check fails toward "clean" in the case that matters.The floor assertions now name which side lost the path
Assert.Contains(floor, parsedCross)andAssert.Contains(floor, evaluated)sat adjacent and message-less, so a crude-parse failure presented as an evaluated-side regression — which is how #3138's red was read. Each is now anAssert.Truenaming its own side. The crude one stays first because it is the precondition, and it says so.Reproduced both directions: with
dev's file plus one apostrophe each side of the floor item, the failure wasAssert.Contains() Failure … Set: [] … Not found: "Darling/Darling.Tests/CSharpSourceWalker.cs". The same scenario now says the CRUDE parse is broken and thatEvaluateis not implicated; droppingCompilefromEvaluatedItemTypesinstead says the EVALUATED read has lost the item.The pin
TheCrudeParse_SeesEveryLiteralValueHoweverItIsQuotedstates the property — every path the XML names literally is in the answer, and the only misses are values MSBuild has to evaluate — then enumerates the one way it can become false: something in the text is taken for a delimiter. Each route gets its own document and its own file name, so a broken route reds by name rather than hiding behind a sibling. Both negative arms carry a literal in the same document asserted PRESENT, so an absence cannot be the absence of a read. Nothing touches the disk; the read resolves and anchors by path arithmetic.Lite.Testsgained nobuild.ymlfilter entry and needs none.Changelog entry for the coordinator