Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2096,8 +2096,10 @@ private RegexNode ReduceLookaround()
Debug.Assert(ChildCount() == 1);

// Captures inside of negative lookarounds are undone after the lookaround. Thus, if there's nothing
// inside of the negative lookaround that needs that capture group (namely a backreference), we can
// remove the capture.
// inside of the negative lookaround that relies on or impacts persisted state, we can remove the capture.
// This includes backreferences (because backreferences within the lookaround still need to refer to that
// capture group) and balancing groups (because they can impact and are impacted by capture stacks from
// captures outside of the lookaround).
if (Kind is RegexNodeKind.NegativeLookaround && ContainsKind(Child(0), [RegexNodeKind.Backreference, RegexNodeKind.BackreferenceConditional]) is false)
{
if (RemoveCaptures(this, 0))
Expand All @@ -2111,7 +2113,9 @@ static bool RemoveCaptures(RegexNode parent, int nodeIndex)
{
RegexNode node = parent.Child(nodeIndex);

if (node.Kind is RegexNodeKind.Capture)
// Only remove captures that don't rely on or impact persisted state.
// Balancing groups (N != -1) impact capture stacks and must be preserved.
if (node is { Kind: RegexNodeKind.Capture, N: -1 })
{
parent.ReplaceChild(nodeIndex, node.Child(0));
RemoveCaptures(parent, nodeIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public static IEnumerable<object[]> Count_ReturnsExpectedCount_TestData()
yield return new object[] { engine, @"\b\w+\b", "abc def ghi jkl", 15, RegexOptions.RightToLeft, 4 };
yield return new object[] { RegexEngine.Interpreter, @"(?<=abc)\w", "abcxabcy", 8, RegexOptions.RightToLeft, 2 };
yield return new object[] { engine, @"(?<=abc)\w", "abcxabcy", 7, RegexOptions.RightToLeft, 1 };

// Balancing groups in negative lookarounds should not be removed
yield return new object[] { engine, @"()(?'-1')(?!(?'-1'))", "abc", 0, RegexOptions.None, 4 };
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,11 @@ public static IEnumerable<object[]> Match_MemberData()
yield return (@"(?<cat>cat)\w+(?<dog-0>dog)", "cat_Hello_World_dog", RegexOptions.None, 0, 19, false, string.Empty);
yield return (@"(.)(?'2-1'(?'-1'))", "cat", RegexOptions.None, 0, 3, false, string.Empty);
yield return (@"(?'2-1'(.))", "cat", RegexOptions.None, 0, 3, true, "c");

// Balancing groups in negative lookarounds should not be removed
// The pattern captures group 1, uncaptures it, then checks the negative lookahead
// The negative lookahead contains a balancing group that should not be removed
yield return (@"()(?'-1')(?!(?'-1'))", "a", RegexOptions.None, 0, 1, true, string.Empty);
}

// Atomic Zero-Width Assertions \A \Z \z \b \B
Expand Down
Loading