Regex: negated character class loop followed by \b incorrectly made atomic
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 54/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- csharp
- Ambito
- testing-qa
Direzione di ricerca
Start by reading RegexCharClass.IsKnownWordClassSubset in src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs and the fix in PR 125636. Add regression coverage for the listed negated character class patterns, including the source generator if the existing test structure supports it. Confirm the tests fail on .NET 10 and pass on the fixed code; the expected matches are given in the issue.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Description
On .NET 10, a + loop over a negated character class followed by \b is incorrectly made atomic,
so the backtracking engines and the source generator fail to find matches that exist. The
NonBacktracking engine is correct. .NET 9 was correct, and .NET 11 is already correct.
The pattern ^[^a-z]+\b(?:\W+|$) matches nothing at all on .NET 10.
PR 125636 fixed this in .net 11 but doesn't have a dedicated test.
This is not the same as https://github.com/dotnet/runtime/issues/135108
Reproduction Steps
using System.Text.RegularExpressions;
Console.WriteLine(new Regex(@"^[^a-z]+\b\W").IsMatch("A ghi")); // False on .NET 10; True on 9 and 11
Full three-runtime Docker repro (one image per SDK, identical source):
docker build --build-arg SDK_TAG=10.0 --build-arg TFM=net10.0 -t r . && docker run --rm r
| Pattern | Input | .NET 9.0.19 | .NET 10.0.11 | .NET 11.0.0-p7 |
|---|---|---|---|---|
^[^a-z]+\b |
A ghi |
"A " |
"A " |
"A " |
^[^a-z]+\b\W |
A ghi |
"A " |
no match | "A " |
^[^a-z]+\b(?:\W+|$) |
A ghi |
"A " |
no match | "A " |
^[^a-z]*\b(?:\W+|$) |
A ghi |
"A " |
"A " |
"A " |
^[^a-z]{1,}\b\W |
A ghi |
"A " |
no match | "A " |
^[^a-z]{1,9}\b\W |
A ghi |
"A " |
no match | "A " |
^[A-Z ]+\b(?:\W+|$) |
A ghi |
"A " |
"A " |
"A " |
^[^0-9]+\b\W |
A ghi |
"A " |
no match | "A " |
^\W+\b\W |
-- ghi |
no match | no match | no match |
Trigger requires both a loop minimum ≥ 1 (the node.M > 0 guard) and a negated set whose stated
characters are all word characters. RegexOptions.None, Compiled and [GeneratedRegex] are all
affected; NonBacktracking is not.
Expected behavior
Expected true
Actual behavior
.NET 10 returns false.
Regression?
Yes, .NET 9 → .NET 10. Introduced by #117892; incidentally fixed on main by #125636, which replaced
IsKnownWordClassSubset with IsSubsetOf guarded by !IsNegated(subset). Root-cause detail and the
proposed one-line patch for release/10.0 are above.
Known Workarounds
RegexOptions.NonBacktracking— correct on all versions, no pattern change.- Move the boundary into the alternation:
^[^a-z]+(?:\b\W+|$). Note^[^a-z]+(?:\b\W+|\b$)still reproduces, so it only helps when not every branch begins with the boundary. - Avoid the negated class, or use
*where semantics permit.
Configuration
.NET 10.0.14 - incorrect
.NET 10.0.11 (SDK 10.0.400) - incorrect
.NET 9.0.19 (SDK 9.0.317) - correct
.NET 11.0.0-preview.7.26381.103 - correct
Reproduced in the official mcr.microsoft.com/dotnet/sdk images on both linux/arm64 and linux/amd64, and on macOS 15 arm64 directly.
Other information
One guard, mirroring what the category path above it already does:
--- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
+++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs
@@ public static bool IsKnownWordClassSubset(string set)
// If we can enumerate every character in the set quickly, do so, checking to see whether they're all in \w.
- if (CanEasilyEnumerateSetContents(set))
+ // Note: for a negated set this would enumerate the characters that are EXCLUDED, so bail.
+ if (!IsNegated(set) && CanEasilyEnumerateSetContents(set))
{
Plus a regression test, which is missing everywhere today:
// [^a-z] is not a subset of \w - it contains spaces, punctuation, digits and uppercase -
// so [^a-z]+ must not be made atomic ahead of the \b.
yield return (@"^[^a-z]+\b(?:\W+|$)", "CA Actress", RegexOptions.None, 0, 10, true, "CA ");
yield return (@"^[^a-z]+\b\W", "A ghi", RegexOptions.None, 0, 5, true, "A ");
yield return (@"^[^0-9]+\b\W", "A ghi", RegexOptions.None, 0, 5, true, "A ");
Worth landing that test on main too, so .NET 11's currently-incidental correctness is actually pinned.
- Lingua principale
- C#
- Stelle
- 18.4k
- Fork
- 5.8k
- Merge medio
- 2g 17h
- PR unite (30g)
- 581
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di dotnet/runtime
-
DbTransaction.SupportsSavepoints docs say Release/ReleaseAsync are expected to throw, but they are no-ops by designForse già presa @alaricd l’ha presa 1 giorno fa. Apertaarea-System.Transactions untriaged
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
dotnet/runtime#137040 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
FileSystemGlobbing: Matcher throws ArgumentOutOfRangeException when the rootDir is the same value as the fileForse già presa @makazeu l’ha presa 1 giorno fa. Apertaarea-Extensions-FileSystem
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/runtime#136989 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Configuration binder source generator emits unqualified List<T> when binding arrays without implicit usingsForse già presa @makazeu l’ha presa 1 giorno fa. Apertaarea-Extensions-Configuration bug source-generator
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
dotnet/runtime#136972 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
[wasi] .NET 10: WasmEnableSIMD with AOT fails because WasiApp.targets has -msimd128 commented outApertaarea-Build-mono untriaged
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
PropertyChangedEventArgs should have a nice DebuggerDisplayForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertaarea-System.ComponentModel untriaged
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
dotnet/runtime#136608 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di dotnet/runtime
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
NethermindEth/nethermind#15684 ·
I maintainer di solito rispondono entro 1 giorno
-
area/ai-assistant squad/flyt
Difficoltà 2/5 1-3 ore Idoneità per principianti 83/100
Altinn/altinn-studio#21798 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
arastella625/DAORework#181 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
BEagle1984/silverback#280 ·
-
bug P1
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
endercodezz/BYDLO#417 ·
I maintainer di solito rispondono entro 1 giorno