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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy