Skip to content

Split a glob class too large to list into its ranges [patch] - #196

Open
matt-edmondson wants to merge 2 commits into
mainfrom
fix/184-large-glob-class
Open

matt-edmondson wants to merge 2 commits into
mainfrom
fix/184-large-glob-class

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #184

What was wrong

ToGlobCharacterList returned null for a class that would list more than MaxGlobClassMembers (4096) characters. ResolveGlob and DoesMatchGlob read null as a half-typed token to ignore. So a complete class such as [가-힣a-z]* matched every item, and as an exclusion it hid nothing.

Change

  • ExpandGlobClasses now returns the token's alternatives (IReadOnlyList<string>?), and ResolveGlob parses each of them. The token matches if any alternative does, the same rule already used for the **/ expansions.
  • A class over the cap that is not negated is split. Each letter or digit range is matched natively ([가-힣], [a-z]), and the remaining members are listed ([_]). [가-힣a-z]* becomes [가-힣]* and [a-z]*.
  • A negated class can't be split that way, because it has to miss every piece at once. That kind of class, a class whose split would give the token more than 16 alternatives, and a class with no native range ([A-힣] spans \) are listed in full. Matching a full list is slower: I measured about 15 µs per match against a 55k-member list, versus sub-microsecond natively. In exchange, the token no longer matches everything.
  • A class with no members (every range reversed) still returns null. That case is A glob class whose only range is reversed (file[%-!]) matches every item, while the letter/digit equivalent (file[9-0]) matches none #173 and is left for its own change.

Tests

  • Added GlobClassTooLargeToListMatchesOnlyItsMembers, covering the issue's cases plus positive, negated, required and excluded variants, and GlobClassTooLargeToListStillLeavesAHalfTypedTokenIgnored.
  • Added ExpandGlobClassesSplitsAClassTooLargeToListIntoItsRanges and ExpandGlobClassesListsAClassTooLargeToListThatCannotBeSplit. [\u0001-�_] moved out of the "rejects" test into the latter.
  • Ran the new behavioural tests against an unchanged main worktree: 12 rows and the half-typed test fail there. On this branch they all pass (297 total, 296 passed).
  • One correction to the issue's table: IsMatch("README.md", "-[一-龥a-z]*") is true, not false. Glob matching is case-sensitive by default, so README.md isn't a member of a-z and isn't excluded. The test pins that, and also pins that -[一-龥A-Z]* does exclude it.
  • EveryMethodInTheReadmeApiReferenceExistsWithItsDocumentedDefaults also fails on main without this change. It is unrelated and untouched here.

The open PRs #165 and #168 touch nearby glob-class code, so whichever of them lands second may need a small rebase.


Generated by Claude Code

ToGlobCharacterList gave up on a class that would list more than 4096
characters, and ResolveGlob read that as a half-typed token to ignore, so a
complete class such as "[가-힣a-z]*" matched every item, and as an exclusion
hid nothing. A class that is not negated is now split instead: each letter or
digit range is matched natively, the rest is listed, and the token matches if
any piece does. A negated class, or one whose split would give the token more
than 16 alternatives, is listed in full however long it is, which is slower to
match but no longer matches everything. A half-typed token is still ignored.

Fixes #184

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW4tA1FYwsQrBAjGqs7
@azure-pipelines

Copy link
Copy Markdown
There was an error handling pipeline event 0de51f26-d7c3-42f0-bd95-aaa51dfe5fd9.

Comment thread TextFilter/TextFilter.cs Resolved
Comment thread TextFilter/TextFilter.cs Resolved
Ports the fix from #186 (for #183). The README API test failed on main
because the binary-compatibility overloads deliberately carry no defaults,
so this PR's CI was red for a reason outside its change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW4tA1FYwsQrBAjGqs7
@azure-pipelines

Copy link
Copy Markdown
There was an error handling pipeline event fc412c32-bb93-4c29-9b3b-b251a9ccf69c.

Copy link
Copy Markdown
Contributor Author

The first CI run failed in EveryMethodInTheReadmeApiReferenceExistsWithItsDocumentedDefaults on ubuntu-latest. That failure isn't from this change. The same test fails on main (CI run on 5f420d9), because the binary-compatibility overloads deliberately have no defaults (#183).

I ported the fix from open PR #186 into this branch (5ffdca0, identical diff), so this PR can go green on its own. With it, the full suite passes locally: 297 of 297. Whichever of #186 and this PR merges second will see the change already on main.


Generated by Claude Code

@sonarqubecloud

sonarqubecloud Bot commented Nov 7, 2026

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

2 participants