[Lldb-commits] [lldb] [llvm] [Support] Allow \n, \t, \x in regexes (PR #223024)
Harald van Dijk via lldb-commits
lldb-commits at lists.llvm.org
Fri Sep 11 22:15:56 PDT 2026
https://github.com/hvdijk updated https://github.com/llvm/llvm-project/pull/223024
>From a37715527a6d156911abc5a14219e5d2b355c859 Mon Sep 17 00:00:00 2001
From: Harald van Dijk <hdijk at accesssoftek.com>
Date: Fri, 11 Sep 2026 20:35:27 +0100
Subject: [PATCH 1/5] [Support] Allow \n, \t, \x in regexes
\n and \t were already permitted in Regex::sub, but not in the regex
pattern itself. This commit allows them there too.
\x was not supported at all. This commit allows it both in regex
patterns and in Regex::sub.
The goal is to allow \x to be used in FileCheck patterns and to that
end, a test is also included that this works in FileCheck as expected.
---
llvm/lib/Support/Regex.cpp | 12 ++++++++
llvm/lib/Support/regcomp.c | 46 +++++++++++++++++++++++-----
llvm/test/FileCheck/escape.txt | 5 +++
llvm/unittests/Support/RegexTest.cpp | 35 +++++++++++++++++++++
4 files changed, 91 insertions(+), 7 deletions(-)
create mode 100644 llvm/test/FileCheck/escape.txt
diff --git a/llvm/lib/Support/Regex.cpp b/llvm/lib/Support/Regex.cpp
index 5a96f1974341d..039108be9ad89 100644
--- a/llvm/lib/Support/Regex.cpp
+++ b/llvm/lib/Support/Regex.cpp
@@ -12,6 +12,7 @@
#include "llvm/Support/Regex.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "regex_impl.h"
@@ -202,6 +203,17 @@ std::string Regex::sub(StringRef Repl, StringRef String,
Repl = Repl.substr(1);
break;
+ // Hex character escapes.
+ case 'x':
+ if (Repl.size() >= 3 && isHexDigit(Repl[1]) && isHexDigit(Repl[2])) {
+ Res += char(hexDigitValue(Repl[1]) * 16 + hexDigitValue(Repl[2]));
+ Repl = Repl.substr(3);
+ } else {
+ Res += Repl[0];
+ Repl = Repl.substr(1);
+ }
+ break;
+
// Decimal escapes are backreferences.
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
diff --git a/llvm/lib/Support/regcomp.c b/llvm/lib/Support/regcomp.c
index 6838b96a0e5df..92dfff397056a 100644
--- a/llvm/lib/Support/regcomp.c
+++ b/llvm/lib/Support/regcomp.c
@@ -211,6 +211,7 @@ static char p_b_symbol(struct parse *);
static char p_b_coll_elem(struct parse *, int);
static char othercase(int);
static void bothcases(struct parse *, int);
+static void uncased(struct parse *, int);
static void ordinary(struct parse *, int);
static void nonnewline(struct parse *);
static void repeat(struct parse *, sopno, int, int);
@@ -519,6 +520,31 @@ static void p_ere_exp(struct parse *p) {
backrefnum = backrefnum * 10 + c - '0';
}
MUSTEAT('}', REG_BADRPT);
+ } else if (c == 'n') {
+ ordinary(p, '\n');
+ break;
+ } else if (c == 't') {
+ ordinary(p, '\t');
+ break;
+ } else if (c == 'x') {
+ /* Support \xAA hexadecimal escape sequences. \x must be followed by
+ * exactly two hex digits, otherwise it is interpreted literally.
+ */
+ char hexstr[3] = {0};
+ char *hexp;
+ int val;
+ if (MORE2()) {
+ hexstr[0] = PEEK();
+ hexstr[1] = PEEK2();
+ val = strtol(hexstr, &hexp, 16);
+ if (*hexp == '\0') {
+ NEXT2();
+ uncased(p, (char)val);
+ break;
+ }
+ }
+ ordinary(p, 'x');
+ break;
} else {
/* Other chars are simply themselves when escaped with a backslash.
*/
@@ -1063,18 +1089,24 @@ static void bothcases(struct parse *p, int ch) {
}
/*
- - ordinary - emit an ordinary character
+ - uncased - emit an ordinary character that is never treated as cased
*/
-static void ordinary(struct parse *p, int ch) {
+static void uncased(struct parse *p, int ch) {
cat_t *cap = p->g->categories;
+ EMIT(OCHAR, (uch)ch);
+ if (cap[ch] == 0)
+ cap[ch] = p->g->ncategories++;
+}
+
+/*
+ - ordinary - emit an ordinary character
+ */
+static void ordinary(struct parse *p, int ch) {
if ((p->g->cflags & REG_ICASE) && isalpha((uch)ch) && othercase(ch) != ch)
bothcases(p, ch);
- else {
- EMIT(OCHAR, (uch)ch);
- if (cap[ch] == 0)
- cap[ch] = p->g->ncategories++;
- }
+ else
+ uncased(p, ch);
}
/*
diff --git a/llvm/test/FileCheck/escape.txt b/llvm/test/FileCheck/escape.txt
new file mode 100644
index 0000000000000..6f04e89aab56b
--- /dev/null
+++ b/llvm/test/FileCheck/escape.txt
@@ -0,0 +1,5 @@
+; RUN: printf '\t\n\xC2\xA1\n' > %t.1
+; RUN: FileCheck --strict-whitespace --match-full-lines %s < %t.1
+
+CHECK:{{\t}}
+CHECK:{{\xC2\xA1}}
diff --git a/llvm/unittests/Support/RegexTest.cpp b/llvm/unittests/Support/RegexTest.cpp
index 18a78b731f3e9..c21e294854b7c 100644
--- a/llvm/unittests/Support/RegexTest.cpp
+++ b/llvm/unittests/Support/RegexTest.cpp
@@ -68,6 +68,37 @@ TEST_F(RegexTest, EmptyPattern) {
EXPECT_FALSE(r.match(""));
}
+TEST_F(RegexTest, Escapes) {
+ Regex r1("\\n");
+ EXPECT_TRUE(r1.match("\n"));
+ EXPECT_FALSE(r1.match("n"));
+ EXPECT_FALSE(r1.match("\\n"));
+
+ Regex r2("\\t");
+ EXPECT_TRUE(r2.match("\t"));
+ EXPECT_FALSE(r2.match("t"));
+ EXPECT_FALSE(r2.match("\\t"));
+
+ Regex r3("\\x40");
+ EXPECT_TRUE(r3.match("\x40"));
+ EXPECT_FALSE(r3.match("x40"));
+ EXPECT_FALSE(r3.match("\\x40"));
+
+ Regex r4("A\\x41", Regex::IgnoreCase);
+ EXPECT_TRUE(r4.match("AA"));
+ EXPECT_FALSE(r4.match("Aa"));
+ EXPECT_TRUE(r4.match("aA"));
+ EXPECT_FALSE(r4.match("aa"));
+
+ Regex r5("\\q");
+ EXPECT_TRUE(r5.match("q"));
+ EXPECT_FALSE(r5.match("\\q"));
+
+ Regex r6("\\xjq");
+ EXPECT_TRUE(r6.match("xjq"));
+ EXPECT_FALSE(r6.match("\\xjq"));
+}
+
TEST_F(RegexTest, Backreferences) {
Regex r1("([a-z]+)_\\1");
SmallVector<StringRef, 4> Matches;
@@ -139,6 +170,10 @@ TEST_F(RegexTest, Substitution) {
EXPECT_EQ("", Error);
EXPECT_EQ("a\tber", Regex("[0-9]+").sub("\\t", "a1234ber", &Error));
EXPECT_EQ("", Error);
+ EXPECT_EQ("a\100ber", Regex("[0-9]+").sub("\\x40", "a1234ber", &Error));
+ EXPECT_EQ("", Error);
+ EXPECT_EQ("axjqber", Regex("[0-9]+").sub("\\xjq", "a1234ber", &Error));
+ EXPECT_EQ("", Error);
EXPECT_EQ("ajber", Regex("[0-9]+").sub("\\j", "a1234ber", &Error));
EXPECT_EQ("", Error);
>From fe5fc9ada06dba56913aff428881319a440781ca Mon Sep 17 00:00:00 2001
From: Harald van Dijk <hdijk at accesssoftek.com>
Date: Fri, 11 Sep 2026 20:42:23 +0100
Subject: [PATCH 2/5] Apply clang-format suggestion
---
llvm/lib/Support/Regex.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Support/Regex.cpp b/llvm/lib/Support/Regex.cpp
index 039108be9ad89..045a9d17116f3 100644
--- a/llvm/lib/Support/Regex.cpp
+++ b/llvm/lib/Support/Regex.cpp
@@ -11,11 +11,11 @@
//===----------------------------------------------------------------------===//
#include "llvm/Support/Regex.h"
+#include "regex_impl.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-#include "regex_impl.h"
#include <cassert>
#include <string>
>From e3330a384c1f715228b75678fd77bacdeb8f4d04 Mon Sep 17 00:00:00 2001
From: Harald van Dijk <hdijk at accesssoftek.com>
Date: Sat, 12 Sep 2026 00:14:15 +0100
Subject: [PATCH 3/5] Fix lldb tests
These tests were using {{.*\n}} which, due to \n being unsupported,
previously only checked that there was an n anywhere in the line. The
change to make \n match a newline character, as these tests had intended
it to mean, meant that the match now ends on the next line. That is not
how these tests were intended to work, they were not intended to span
multiple lines, so just remove the \n.
---
lldb/test/Shell/Settings/Inputs/EchoCommandsAll.out | 6 +++---
lldb/test/Shell/Settings/Inputs/EchoCommandsNoComments.out | 6 +++---
lldb/test/Shell/Settings/Inputs/EchoCommandsNone.out | 6 +++---
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/lldb/test/Shell/Settings/Inputs/EchoCommandsAll.out b/lldb/test/Shell/Settings/Inputs/EchoCommandsAll.out
index 45b7729197607..d4583f3dea3cb 100644
--- a/lldb/test/Shell/Settings/Inputs/EchoCommandsAll.out
+++ b/lldb/test/Shell/Settings/Inputs/EchoCommandsAll.out
@@ -1,6 +1,6 @@
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK-NEXT: Executing commands in {{.*\n}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK-NEXT: Executing commands in {{.*}}
# CHECK-NEXT: Evaluate expr 1+2
# CHECK-NEXT: expr 1+2
# CHECK-NEXT: (int) $0 = 3
diff --git a/lldb/test/Shell/Settings/Inputs/EchoCommandsNoComments.out b/lldb/test/Shell/Settings/Inputs/EchoCommandsNoComments.out
index a0b653a8f4cca..08c37bd5bbe13 100644
--- a/lldb/test/Shell/Settings/Inputs/EchoCommandsNoComments.out
+++ b/lldb/test/Shell/Settings/Inputs/EchoCommandsNoComments.out
@@ -1,5 +1,5 @@
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK-NEXT: Executing commands in {{.*\n}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK-NEXT: Executing commands in {{.*}}
# CHECK-NEXT: expr 1+2
# CHECK-NEXT: (int) $0 = 3
diff --git a/lldb/test/Shell/Settings/Inputs/EchoCommandsNone.out b/lldb/test/Shell/Settings/Inputs/EchoCommandsNone.out
index fca1bbb50ca0a..fdad0c0daf34d 100644
--- a/lldb/test/Shell/Settings/Inputs/EchoCommandsNone.out
+++ b/lldb/test/Shell/Settings/Inputs/EchoCommandsNone.out
@@ -1,4 +1,4 @@
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK: (lldb) command source -s {{.*\n}}
-# CHECK-NEXT: Executing commands in {{.*\n}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK: (lldb) command source -s {{.*}}
+# CHECK-NEXT: Executing commands in {{.*}}
# CHECK-NEXT: (int) $0 = 3
>From d2d727d75399dfa66410caf46e51e435b48a645a Mon Sep 17 00:00:00 2001
From: Harald van Dijk <hdijk at accesssoftek.com>
Date: Sat, 12 Sep 2026 01:27:03 +0100
Subject: [PATCH 4/5] Fix test to not fail because of partial match
---
llvm/unittests/Support/RegexTest.cpp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/llvm/unittests/Support/RegexTest.cpp b/llvm/unittests/Support/RegexTest.cpp
index c21e294854b7c..845b831aced04 100644
--- a/llvm/unittests/Support/RegexTest.cpp
+++ b/llvm/unittests/Support/RegexTest.cpp
@@ -90,13 +90,13 @@ TEST_F(RegexTest, Escapes) {
EXPECT_TRUE(r4.match("aA"));
EXPECT_FALSE(r4.match("aa"));
- Regex r5("\\q");
- EXPECT_TRUE(r5.match("q"));
- EXPECT_FALSE(r5.match("\\q"));
+ Regex r5("p\\q");
+ EXPECT_TRUE(r5.match("pq"));
+ EXPECT_FALSE(r5.match("p\\q"));
- Regex r6("\\xjq");
- EXPECT_TRUE(r6.match("xjq"));
- EXPECT_FALSE(r6.match("\\xjq"));
+ Regex r6("w\\xjq");
+ EXPECT_TRUE(r6.match("wxjq"));
+ EXPECT_FALSE(r6.match("w\\xjq"));
}
TEST_F(RegexTest, Backreferences) {
>From da0347d79054882eaf7a4c8c64f85701fd92aa67 Mon Sep 17 00:00:00 2001
From: Harald van Dijk <hdijk at accesssoftek.com>
Date: Sat, 12 Sep 2026 06:15:37 +0100
Subject: [PATCH 5/5] Use octal escapes with printf
---
llvm/test/FileCheck/escape.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/FileCheck/escape.txt b/llvm/test/FileCheck/escape.txt
index 6f04e89aab56b..e082fbdf58914 100644
--- a/llvm/test/FileCheck/escape.txt
+++ b/llvm/test/FileCheck/escape.txt
@@ -1,4 +1,4 @@
-; RUN: printf '\t\n\xC2\xA1\n' > %t.1
+; RUN: printf '\t\n\302\241\n' > %t.1
; RUN: FileCheck --strict-whitespace --match-full-lines %s < %t.1
CHECK:{{\t}}
More information about the lldb-commits
mailing list