You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2025/02/13 11:39:41 UTC

(lucene) branch branch_10x updated: Fix failure found by TestOperations.testGetRandomAcceptedString (#14227)

This is an automated email from the ASF dual-hosted git repository.

rmuir pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/branch_10x by this push:
     new 7682ef096e2 Fix failure found by TestOperations.testGetRandomAcceptedString (#14227)
7682ef096e2 is described below

commit 7682ef096e269354b0f24152ae60dace152b866b
Author: Robert Muir <rm...@apache.org>
AuthorDate: Thu Feb 13 06:22:25 2025 -0500

    Fix failure found by TestOperations.testGetRandomAcceptedString (#14227)
    
    * Fix failure found by TestOperations.testGetRandomAcceptedString
    
    string: ?+½]+]+Ř*+[\]ᖴﴁ.
    
    expected: before #14193
    java.lang.IllegalArgumentException: expected ']' at position 17
    
    actual: after #14193
    REGEXP_CONCATENATION
      REGEXP_CONCATENATION
        REGEXP_CONCATENATION
          REGEXP_CONCATENATION
            REGEXP_CONCATENATION
              REGEXP_CONCATENATION
                REGEXP_CONCATENATION
                  REGEXP_CONCATENATION
                    REGEXP_REPEAT_MIN min=1
                      REGEXP_CHAR char=?
                    REGEXP_CHAR char=½
                  REGEXP_REPEAT_MIN min=1
                    REGEXP_CHAR char=]
                REGEXP_CHAR char=
              REGEXP_REPEAT_MIN min=1
                REGEXP_CHAR char=]
            REGEXP_REPEAT_MIN min=1
              REGEXP_REPEAT
                REGEXP_CHAR char=Ř
          REGEXP_CHAR_CLASS starts=[] ends=[]
        REGEXP_STRING string=ᖴﴁ
      REGEXP_ANYCHAR
    
    Problem is caused by RegExp accepting too much rather than throwing
    exceptions like it should have. The lenience in the parser comes from
    "expandPreDefined" which invades on escape character parsing for character
    classes (e.g. \s). This one adds a lot of complexity to parsing.
    
    Don't invoke expandPreDefined(), except for the set of characters that it
    explicitly handles. This is also consistent with the way
    expandPreDefined()'s complexity is managed elsewhere in the parser, such
    as in parseSimpleExp().
    
    Add parsing tests for testEmptyClass(), which is unchanged by this PR,
    but should be there, and testEscapedInvalidClass(), which fails without
    the change.
    
    When we consume an escape, either it matches the predefined special
    logic, or we emit range for next(), no other logic: it is an escape.
    
    Add test for escaped '-' in a character class.
---
 .../org/apache/lucene/util/automaton/RegExp.java   | 10 +++++++-
 .../lucene/util/automaton/TestOperations.java      |  5 ++--
 .../lucene/util/automaton/TestRegExpParsing.java   | 29 ++++++++++++++++++++++
 3 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/lucene/core/src/java/org/apache/lucene/util/automaton/RegExp.java b/lucene/core/src/java/org/apache/lucene/util/automaton/RegExp.java
index aaf50c8c180..8be1a0eb12c 100644
--- a/lucene/core/src/java/org/apache/lucene/util/automaton/RegExp.java
+++ b/lucene/core/src/java/org/apache/lucene/util/automaton/RegExp.java
@@ -1243,7 +1243,15 @@ public class RegExp {
     do {
       // look for escape
       if (match('\\')) {
-        expandPreDefined(starts, ends);
+        if (peek("\\ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")) {
+          // special "escape" or invalid escape
+          expandPreDefined(starts, ends);
+        } else {
+          // escaped character, don't parse it
+          int c = next();
+          starts.add(c);
+          ends.add(c);
+        }
       } else {
         // parse a character
         int c = parseCharExp();
diff --git a/lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java b/lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java
index 7fb1d679946..aedac499ecc 100644
--- a/lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java
+++ b/lucene/core/src/test/org/apache/lucene/util/automaton/TestOperations.java
@@ -132,10 +132,11 @@ public class TestOperations extends LuceneTestCase {
     final int ITER2 = atLeast(100);
     for (int i = 0; i < ITER1; i++) {
 
-      final RegExp re = new RegExp(AutomatonTestUtil.randomRegexp(random()), RegExp.NONE);
+      final String text = AutomatonTestUtil.randomRegexp(random());
+      final RegExp re = new RegExp(text, RegExp.NONE);
       // System.out.println("TEST i=" + i + " re=" + re);
       final Automaton a = Operations.determinize(re.toAutomaton(), DEFAULT_DETERMINIZE_WORK_LIMIT);
-      assertFalse(Operations.isEmpty(a));
+      assertFalse("empty: " + text, Operations.isEmpty(a));
 
       final AutomatonTestUtil.RandomAcceptedStrings rx =
           new AutomatonTestUtil.RandomAcceptedStrings(a);
diff --git a/lucene/core/src/test/org/apache/lucene/util/automaton/TestRegExpParsing.java b/lucene/core/src/test/org/apache/lucene/util/automaton/TestRegExpParsing.java
index 51a14761d38..c5e269b51f9 100644
--- a/lucene/core/src/test/org/apache/lucene/util/automaton/TestRegExpParsing.java
+++ b/lucene/core/src/test/org/apache/lucene/util/automaton/TestRegExpParsing.java
@@ -379,6 +379,15 @@ public class TestRegExpParsing extends LuceneTestCase {
     assertSameLanguage(expected, actual);
   }
 
+  public void testEscapedDashCharClass() {
+    RegExp re = new RegExp("[\\-]");
+    assertEquals("REGEXP_CHAR char=-\n", re.toStringTree());
+    Automaton actual = re.toAutomaton();
+    assertTrue(actual.isDeterministic());
+    Automaton expected = Automata.makeChar('-');
+    assertSameLanguage(expected, actual);
+  }
+
   public void testEmpty() {
     RegExp re = new RegExp("#", RegExp.EMPTY);
     assertEquals("#", re.toString());
@@ -391,6 +400,26 @@ public class TestRegExpParsing extends LuceneTestCase {
     assertSameLanguage(expected, actual);
   }
 
+  public void testEmptyClass() {
+    Exception expected =
+        expectThrows(
+            IllegalArgumentException.class,
+            () -> {
+              new RegExp("[]");
+            });
+    assertEquals("expected ']' at position 2", expected.getMessage());
+  }
+
+  public void testEscapedInvalidClass() {
+    Exception expected =
+        expectThrows(
+            IllegalArgumentException.class,
+            () -> {
+              new RegExp("[\\]");
+            });
+    assertEquals("expected ']' at position 3", expected.getMessage());
+  }
+
   public void testInterval() {
     RegExp re = new RegExp("<5-40>");
     assertEquals("<5-40>", re.toString());