Skip to content

Commit 11ae581

Browse files
committed
Fix handling of control/meta escapes in literal regexps
Ruby uses a recursive algorithm for handling control/meta escapes in strings (read_escape). However, the equivalent code for regexps (tokadd_escape) in did not use a recursive algorithm. Due to this, Handling of control/meta escapes in regexp did not have the same behavior as in strings, leading to behavior such as the following returning nil: ```ruby /\c\xFF/ =~ "\c\xFF" ``` Switch the code for handling \c, \C and \M in literal regexps to use the same code as for strings (read_escape), to keep behavior consistent between the two. Fixes [Bug #14367]
1 parent 9484f9e commit 11ae581

File tree

3 files changed

+36
-34
lines changed

3 files changed

+36
-34
lines changed

parse.y

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6902,10 +6902,8 @@ static int
69026902
tokadd_escape(struct parser_params *p, rb_encoding **encp)
69036903
{
69046904
int c;
6905-
int flags = 0;
69066905
size_t numlen;
69076906

6908-
first:
69096907
switch (c = nextc(p)) {
69106908
case '\n':
69116909
return 0; /* just ignore */
@@ -6928,37 +6926,6 @@ tokadd_escape(struct parser_params *p, rb_encoding **encp)
69286926
}
69296927
return 0;
69306928

6931-
case 'M':
6932-
if (flags & ESCAPE_META) goto eof;
6933-
if ((c = nextc(p)) != '-') {
6934-
pushback(p, c);
6935-
goto eof;
6936-
}
6937-
tokcopy(p, 3);
6938-
flags |= ESCAPE_META;
6939-
goto escaped;
6940-
6941-
case 'C':
6942-
if (flags & ESCAPE_CONTROL) goto eof;
6943-
if ((c = nextc(p)) != '-') {
6944-
pushback(p, c);
6945-
goto eof;
6946-
}
6947-
tokcopy(p, 3);
6948-
goto escaped;
6949-
6950-
case 'c':
6951-
if (flags & ESCAPE_CONTROL) goto eof;
6952-
tokcopy(p, 2);
6953-
flags |= ESCAPE_CONTROL;
6954-
escaped:
6955-
if ((c = nextc(p)) == '\\') {
6956-
goto first;
6957-
}
6958-
else if (c == -1) goto eof;
6959-
tokadd(p, c);
6960-
return 0;
6961-
69626929
eof:
69636930
case -1:
69646931
yyerror0("Invalid escape character syntax");
@@ -7151,6 +7118,23 @@ tokadd_string(struct parser_params *p,
71517118
goto non_ascii;
71527119
}
71537120
if (func & STR_FUNC_REGEXP) {
7121+
switch (c) {
7122+
case 'c':
7123+
case 'C':
7124+
case 'M': {
7125+
pushback(p, c);
7126+
c = read_escape(p, 0, enc);
7127+
7128+
int i;
7129+
char escbuf[5];
7130+
snprintf(escbuf, sizeof(escbuf), "\\x%02X", c);
7131+
for(i = 0; i < 4; i++) {
7132+
tokadd(p, escbuf[i]);
7133+
}
7134+
continue;
7135+
}
7136+
}
7137+
71547138
if (c == term && !simple_re_meta(c)) {
71557139
tokadd(p, c);
71567140
continue;

spec/ruby/language/regexp/interpolation_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def o.to_s
3636

3737
it "gives precedence to escape sequences over substitution" do
3838
str = "J"
39-
/\c#{str}/.to_s.should == '(?-mix:\c#' + '{str})'
39+
/\c#{str}/.to_s.should include('{str}')
4040
end
4141

4242
it "throws RegexpError for malformed interpolation" do

test/ruby/test_regexp.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,24 @@ def test_initialize
496496
assert_raise(RegexpError) { Regexp.new("((?<v>))\\g<0>") }
497497
end
498498

499+
def test_match_control_meta_escape
500+
assert_equal(0, /\c\xFF/ =~ "\c\xFF")
501+
assert_equal(0, /\c\M-\xFF/ =~ "\c\M-\xFF")
502+
assert_equal(0, /\C-\xFF/ =~ "\C-\xFF")
503+
assert_equal(0, /\C-\M-\xFF/ =~ "\C-\M-\xFF")
504+
assert_equal(0, /\M-\xFF/ =~ "\M-\xFF")
505+
assert_equal(0, /\M-\C-\xFF/ =~ "\M-\C-\xFF")
506+
assert_equal(0, /\M-\c\xFF/ =~ "\M-\c\xFF")
507+
508+
assert_nil(/\c\xFE/ =~ "\c\xFF")
509+
assert_nil(/\c\M-\xFE/ =~ "\c\M-\xFF")
510+
assert_nil(/\C-\xFE/ =~ "\C-\xFF")
511+
assert_nil(/\C-\M-\xFE/ =~ "\C-\M-\xFF")
512+
assert_nil(/\M-\xFE/ =~ "\M-\xFF")
513+
assert_nil(/\M-\C-\xFE/ =~ "\M-\C-\xFF")
514+
assert_nil(/\M-\c\xFE/ =~ "\M-\c\xFF")
515+
end
516+
499517
def test_unescape
500518
assert_raise(ArgumentError) { s = '\\'; /#{ s }/ }
501519
assert_equal(/\xFF/n, /#{ s="\\xFF" }/n)

0 commit comments

Comments
 (0)