From 89d9dbe965376e7226cfe7cbe05ed9ccf03ac159 Mon Sep 17 00:00:00 2001 From: Evangelos Kassos Date: Sat, 20 Jun 2026 14:00:18 -0400 Subject: [PATCH] Fix forward progress detection for lookaround groups When a quantified concatenation begins with a lookaround group, the forward progress checker fails to skip over the assertion's child subtree, leaving the analysis cursor misaligned and leading to false positives. This change skips the lookaround's child subtree, matching the treatment added for nullable quantifications in #851. --- Sources/_StringProcessing/ByteCodeGen+DSLList.swift | 2 ++ Tests/RegexTests/CompileTests.swift | 1 + Tests/RegexTests/MatchTests.swift | 1 + 3 files changed, 4 insertions(+) diff --git a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift index 4abf2b90e..4d1290cd0 100644 --- a/Sources/_StringProcessing/ByteCodeGen+DSLList.swift +++ b/Sources/_StringProcessing/ByteCodeGen+DSLList.swift @@ -369,6 +369,8 @@ fileprivate extension Compiler.ByteCodeGen { case .nonCapturingGroup(let kind): switch kind.ast { case .lookahead, .negativeLookahead, .lookbehind, .negativeLookbehind: + list.skipNode(&position) + position += 1 return false default: return _guaranteesForwardProgressImpl(list, position: &position) diff --git a/Tests/RegexTests/CompileTests.swift b/Tests/RegexTests/CompileTests.swift index 62812087a..50cd675b4 100644 --- a/Tests/RegexTests/CompileTests.swift +++ b/Tests/RegexTests/CompileTests.swift @@ -531,6 +531,7 @@ extension RegexTests { expectProgram(for: #"(?:\w|(?i))+"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:A*(?:b|c*))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) expectProgram(for: #"(?:[^/]*(?:/|$))*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) + expectProgram(for: #"(?:(?!a)\d*)*"#, contains: [.moveCurrentPosition, .condBranchSamePosition]) // Bounded quantification, don't emit position checking expectProgram(for: #"(?:(?=a)){1,4}"#, doesNotContain: [.moveCurrentPosition, .condBranchSamePosition]) diff --git a/Tests/RegexTests/MatchTests.swift b/Tests/RegexTests/MatchTests.swift index 97532ff34..e1cfb298b 100644 --- a/Tests/RegexTests/MatchTests.swift +++ b/Tests/RegexTests/MatchTests.swift @@ -2856,6 +2856,7 @@ extension RegexTests { expectCompletion(regex: #"(?:A*(?:b|c*))*"#, in: "ABC") expectCompletion(regex: #"^(?:(?:[^/]*(?:/|$))*)(?:[^/]*)$"#, in: "Sources/main.swift") + expectCompletion(regex: #"(?:(?!a)\d*)*"#, in: "A") } func testQuantifyOptimization() throws {