Skip to content

RepoFileAdoptionTests' adopter census cannot see qualified reader calls, so the exact-set equality passes with an undeclared LF-reading file in the tree #3129

Description

@erikdarlingdata

RepoFileAdoptionTests' adopter census cannot see a qualified call to either repo-file reader, so TheLfReadingPins_AreExactlyTheOnesDeclaredHere — an exact-set equality — passes while an undeclared LF-reading file exists in the tree today.

The two regexes, and what they miss

Darling/Darling.Tests/RepoFileAdoptionTests.cs:

private static readonly Regex RawCall = new(
    @"(?<![\w.])ReadRepoFile\s*\(", RegexOptions.Compiled);

private static readonly Regex LfCall = new(
    @"(?<![\w.])ReadRepoFileLf\s*\(", RegexOptions.Compiled);

The lookbehind excludes a preceding ., which is what stops ReadRepoFileLf( being double-counted as a raw call. It also makes the qualified spelling invisible to both:

spelling RawCall LfCall
ReadRepoFile( true false
RepoFile.ReadRepoFile( false false
ReadRepoFileLf( false true
RepoFile.ReadRepoFileLf( false false

And the raw-adopter decision compounds it — a file is raw only when RawCall.Matches(code).Count > LfCall.Matches(code).Count. With qualified calls both counts are 0, so 0 > 0 is false and the file is neither kind of adopter. It is not miscategorised; it is absent.

The declaration census is unaffected and worth stating so the fix is scoped: that pattern uses \bReadRepoFile(?:Lf)?\s*\( with a word boundary rather than the lookbehind, so ExactlyOneFile_DeclaresTheSharedRepoFileReader still works. Only call sites are blind.

There are TWO live instances, and they break different assertions

Measured on origin/dev (01fdeae92), both surviving comment and string stripping:

file form lines breaks
RepoFileResolutionEquivalenceTests.cs RepoFile.ReadRepoFileLf( 164, 184 the LF exact-set equality
PostgresCancelOriginTests.cs (added by #3127) RepoFile.ReadRepoFile( 304, 342 the adopter floor

They are not the same defect twice. The LF caller makes TheLfReadingPins_AreExactlyTheOnesDeclaredHere pass with an undeclared LF adopter in the tree. The raw caller does not enter any declared set, but it does not count toward adopters.Length >= 30 either — so the floor is measured against an undercount that is now growing, since #3127 added to it yesterday.

The LF instance passes for the wrong reason

Darling/Darling.Tests/RepoFileResolutionEquivalenceTests.cs makes two qualified LF calls that survive comment and string stripping:

164:  var text = lf ? RepoFile.ReadRepoFileLf(segments) : RepoFile.ReadRepoFile(segments);
184:  Assert.Throws<FileNotFoundException>(() => RepoFile.ReadRepoFileLf(missing));

It is not in s_lfReaders (eight entries, and this is not one), and the set equality passes anyway.

Its exclusion is substantively correct — it is the equivalence test for the reader, parameterised over both forms, and it normalises line endings itself at line 171 (onDisk.Replace("\r\n", "\n", ...)) rather than relying on LF anchors. So the right file is excluded for the wrong reason: the census cannot see it, not that it isn't LF-anchored.

Why that matters rather than being a curiosity

A future pin using the qualified LF form with multi-line anchors would be invisible to the set equality, so the assertion would pass while that pin sat undeclared — which is precisely the drift this class exists to catch. The guard would report the tree adopted correctly while it hadn't.

That is the class's own subject turned on itself. Its summary enumerates its known blind spots and does not mention this one.

Secondary, no live risk: the adopters.Length >= 30 floor is measured against the same undercount. It reads 38 today, so the floor is satisfied with margin, but the figure it is satisfied by is smaller than the truth.

Suggested direction

  1. Count qualified and unqualified separately rather than excluding . from the match. The lookbehind exists to stop the Lf spelling being double-counted as raw — a real problem, since the name is a prefix — but that is a disambiguation need, and it is being solved by making a whole legal form unmatchable. Match (?:RepoFile\.)?ReadRepoFileLf\s*\( and (?:RepoFile\.)?ReadRepoFile\s*\( and keep the existing count comparison to separate them.
  2. Then re-check the declared set, because RepoFileResolutionEquivalenceTests.cs will become visible and must be either declared or excluded on its actual grounds — that it normalises line endings itself. If it is excluded, the exclusion needs to be expressible in the assertion rather than accidental.
  3. Add the qualified form to the summary's blind-spot list if any part of it survives.

Acceptance criteria, both arms:

  • a file making only RepoFile.ReadRepoFileLf( calls is seen by the census, and the set equality reds if it is undeclared;
  • the existing eight declared readers still satisfy the equality, and no currently-passing adopter becomes miscategorised.

The second arm matters: a fix that simply drops the lookbehind reintroduces the double-count the lookbehind was added to prevent, which would classify every LF reader as also a raw one.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions