mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Defer escaping control char in error messages
* eval_error.c (print_errinfo): defer escaping control char in error messages until writing to stderr, instead of quoting at building the message. [ruby-core:90853] [Bug #15497] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@66753 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
e52b102c36
commit
50784a0a44
8 changed files with 106 additions and 39 deletions
3
error.c
3
error.c
|
@ -1675,7 +1675,6 @@ name_err_mesg_to_str(VALUE obj)
|
|||
d = rb_any_to_s(obj);
|
||||
}
|
||||
singleton = (RSTRING_LEN(d) > 0 && RSTRING_PTR(d)[0] == '#');
|
||||
d = QUOTE(d);
|
||||
break;
|
||||
}
|
||||
if (!singleton) {
|
||||
|
@ -1685,7 +1684,7 @@ name_err_mesg_to_str(VALUE obj)
|
|||
else {
|
||||
c = s = FAKE_CSTR(&s_str, "");
|
||||
}
|
||||
args[0] = QUOTE(rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]));
|
||||
args[0] = rb_obj_as_string(ptr[NAME_ERR_MESG__NAME]);
|
||||
args[1] = d;
|
||||
args[2] = s;
|
||||
args[3] = c;
|
||||
|
|
48
eval_error.c
48
eval_error.c
|
@ -79,6 +79,44 @@ error_print(rb_execution_context_t *ec)
|
|||
rb_ec_error_print(ec, ec->errinfo);
|
||||
}
|
||||
|
||||
static void
|
||||
write_warnq(VALUE out, VALUE str, const char *ptr, long len)
|
||||
{
|
||||
if (NIL_P(out)) {
|
||||
const char *beg = ptr;
|
||||
const long olen = len;
|
||||
for (; len > 0; --len, ++ptr) {
|
||||
unsigned char c = *ptr;
|
||||
if (rb_iscntrl(c)) {
|
||||
char buf[5];
|
||||
const char *cc = 0;
|
||||
if (ptr > beg) rb_write_error2(beg, ptr - beg);
|
||||
beg = ptr + 1;
|
||||
cc = ruby_escaped_char(c);
|
||||
if (cc) {
|
||||
rb_write_error2(cc, strlen(cc));
|
||||
}
|
||||
else {
|
||||
rb_write_error2(buf, snprintf(buf, sizeof(buf), "\\x%02X", c));
|
||||
}
|
||||
}
|
||||
else if (c == '\\') {
|
||||
rb_write_error2(beg, ptr - beg + 1);
|
||||
beg = ptr;
|
||||
}
|
||||
}
|
||||
if (ptr > beg) {
|
||||
if (beg == RSTRING_PTR(str) && olen == RSTRING_LEN(str))
|
||||
rb_write_error_str(str);
|
||||
else
|
||||
rb_write_error2(beg, ptr - beg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
rb_str_cat(out, ptr, len);
|
||||
}
|
||||
}
|
||||
|
||||
#define CSI_BEGIN "\033["
|
||||
#define CSI_SGR "m"
|
||||
|
||||
|
@ -134,11 +172,11 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
|
|||
if (RSTRING_PTR(epath)[0] == '#')
|
||||
epath = 0;
|
||||
if ((tail = memchr(einfo, '\n', elen)) != 0) {
|
||||
write_warn2(str, einfo, tail - einfo);
|
||||
write_warnq(str, emesg, einfo, tail - einfo);
|
||||
tail++; /* skip newline */
|
||||
}
|
||||
else {
|
||||
write_warn_str(str, emesg);
|
||||
write_warnq(str, emesg, einfo, elen);
|
||||
}
|
||||
if (epath) {
|
||||
write_warn(str, " (");
|
||||
|
@ -154,7 +192,7 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
|
|||
}
|
||||
if (tail && einfo+elen > tail) {
|
||||
if (!highlight) {
|
||||
write_warn2(str, tail, einfo+elen-tail);
|
||||
write_warnq(str, emesg, tail, einfo+elen-tail);
|
||||
if (einfo[elen-1] != '\n') write_warn2(str, "\n", 1);
|
||||
}
|
||||
else {
|
||||
|
@ -164,7 +202,7 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
|
|||
tail = memchr(einfo, '\n', elen);
|
||||
if (!tail || tail > einfo) {
|
||||
write_warn(str, bold);
|
||||
write_warn2(str, einfo, tail ? tail-einfo : elen);
|
||||
write_warnq(str, emesg, einfo, tail ? tail-einfo : elen);
|
||||
write_warn(str, reset);
|
||||
if (!tail) {
|
||||
write_warn2(str, "\n", 1);
|
||||
|
@ -174,7 +212,7 @@ print_errinfo(const VALUE eclass, const VALUE errat, const VALUE emesg, const VA
|
|||
elen -= tail - einfo;
|
||||
einfo = tail;
|
||||
do ++tail; while (tail < einfo+elen && *tail == '\n');
|
||||
write_warn2(str, einfo, tail-einfo);
|
||||
write_warnq(str, emesg, einfo, tail-einfo);
|
||||
elen -= tail - einfo;
|
||||
einfo = tail;
|
||||
}
|
||||
|
|
|
@ -2021,6 +2021,7 @@ VALUE rb_sym_to_proc(VALUE sym);
|
|||
char *rb_str_to_cstr(VALUE str);
|
||||
VALUE rb_str_eql(VALUE str1, VALUE str2);
|
||||
VALUE rb_obj_as_string_result(VALUE str, VALUE obj);
|
||||
const char *ruby_escaped_char(int c);
|
||||
|
||||
/* symbol.c */
|
||||
#ifdef RUBY_ENCODING_H
|
||||
|
|
9
ruby.c
9
ruby.c
|
@ -1367,16 +1367,9 @@ proc_options(long argc, char **argv, ruby_cmdline_options_t *opt, int envopt)
|
|||
|
||||
default:
|
||||
{
|
||||
if (ISPRINT(*s)) {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"invalid option -%c (-h will show valid options)",
|
||||
(int)(unsigned char)*s);
|
||||
}
|
||||
else {
|
||||
rb_raise(rb_eRuntimeError,
|
||||
"invalid option -\\x%02X (-h will show valid options)",
|
||||
(int)(unsigned char)*s);
|
||||
}
|
||||
}
|
||||
goto switch_end;
|
||||
|
||||
|
|
37
string.c
37
string.c
|
@ -5818,6 +5818,24 @@ rb_str_buf_cat_escaped_char(VALUE result, unsigned int c, int unicode_p)
|
|||
return l;
|
||||
}
|
||||
|
||||
const char *
|
||||
ruby_escaped_char(int c)
|
||||
{
|
||||
switch (c) {
|
||||
case '\0': return "\\0";
|
||||
case '\n': return "\\n";
|
||||
case '\r': return "\\r";
|
||||
case '\t': return "\\t";
|
||||
case '\f': return "\\f";
|
||||
case '\013': return "\\v";
|
||||
case '\010': return "\\b";
|
||||
case '\007': return "\\a";
|
||||
case '\033': return "\\e";
|
||||
case '\x7f': return "\\c?";
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VALUE
|
||||
rb_str_escape(VALUE str)
|
||||
{
|
||||
|
@ -5832,7 +5850,8 @@ rb_str_escape(VALUE str)
|
|||
int asciicompat = rb_enc_asciicompat(enc);
|
||||
|
||||
while (p < pend) {
|
||||
unsigned int c, cc;
|
||||
unsigned int c;
|
||||
const char *cc;
|
||||
int n = rb_enc_precise_mbclen(p, pend, enc);
|
||||
if (!MBCLEN_CHARFOUND_P(n)) {
|
||||
if (p > prev) str_buf_cat(result, prev, p - prev);
|
||||
|
@ -5849,22 +5868,10 @@ rb_str_escape(VALUE str)
|
|||
n = MBCLEN_CHARFOUND_LEN(n);
|
||||
c = rb_enc_mbc_to_codepoint(p, pend, enc);
|
||||
p += n;
|
||||
switch (c) {
|
||||
case '\n': cc = 'n'; break;
|
||||
case '\r': cc = 'r'; break;
|
||||
case '\t': cc = 't'; break;
|
||||
case '\f': cc = 'f'; break;
|
||||
case '\013': cc = 'v'; break;
|
||||
case '\010': cc = 'b'; break;
|
||||
case '\007': cc = 'a'; break;
|
||||
case 033: cc = 'e'; break;
|
||||
default: cc = 0; break;
|
||||
}
|
||||
cc = ruby_escaped_char(c);
|
||||
if (cc) {
|
||||
if (p - n > prev) str_buf_cat(result, prev, p - n - prev);
|
||||
buf[0] = '\\';
|
||||
buf[1] = (char)cc;
|
||||
str_buf_cat(result, buf, 2);
|
||||
str_buf_cat(result, cc, strlen(cc));
|
||||
prev = p;
|
||||
}
|
||||
else if (asciicompat && rb_enc_isascii(c, enc) && ISPRINT(c)) {
|
||||
|
|
|
@ -1071,6 +1071,43 @@ $stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
|
|||
end;
|
||||
end
|
||||
|
||||
def assert_null_char(src, *args, **opts)
|
||||
begin
|
||||
eval(src)
|
||||
rescue => e
|
||||
end
|
||||
assert_not_nil(e)
|
||||
assert_include(e.message, "\0")
|
||||
assert_in_out_err([], src, [], [], *args, **opts) do |_, err,|
|
||||
err.each do |e|
|
||||
assert_not_include(e, "\0")
|
||||
end
|
||||
end
|
||||
e
|
||||
end
|
||||
|
||||
def test_control_in_message
|
||||
bug7574 = '[ruby-dev:46749]'
|
||||
assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
|
||||
begin;
|
||||
Object.const_defined?("String\0")
|
||||
end;
|
||||
assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
|
||||
begin;
|
||||
Object.const_get("String\0")
|
||||
end;
|
||||
end
|
||||
|
||||
def test_encoding_in_message
|
||||
name = "\u{e9}t\u{e9}"
|
||||
e = EnvUtil.with_default_external("US-ASCII") do
|
||||
assert_raise(NameError) do
|
||||
Object.const_get(name)
|
||||
end
|
||||
end
|
||||
assert_include(e.message, name)
|
||||
end
|
||||
|
||||
def test_method_missing_reason_clear
|
||||
bug10969 = '[ruby-core:68515] [Bug #10969]'
|
||||
a = Class.new {def method_missing(*) super end}.new
|
||||
|
|
|
@ -745,10 +745,6 @@ class TestModule < Test::Unit::TestCase
|
|||
assert_raise(NameError) { c1.const_get(:foo) }
|
||||
bug5084 = '[ruby-dev:44200]'
|
||||
assert_raise(TypeError, bug5084) { c1.const_get(1) }
|
||||
bug7574 = '[ruby-dev:46749]'
|
||||
assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
|
||||
Object.const_get("String\0")
|
||||
}
|
||||
end
|
||||
|
||||
def test_const_defined_invalid_name
|
||||
|
@ -756,10 +752,6 @@ class TestModule < Test::Unit::TestCase
|
|||
assert_raise(NameError) { c1.const_defined?(:foo) }
|
||||
bug5084 = '[ruby-dev:44200]'
|
||||
assert_raise(TypeError, bug5084) { c1.const_defined?(1) }
|
||||
bug7574 = '[ruby-dev:46749]'
|
||||
assert_raise_with_message(NameError, "wrong constant name \"String\\u0000\"", bug7574) {
|
||||
Object.const_defined?("String\0")
|
||||
}
|
||||
end
|
||||
|
||||
def test_const_get_no_inherited
|
||||
|
|
|
@ -310,7 +310,7 @@ class TestRubyOptions < Test::Unit::TestCase
|
|||
|
||||
assert_in_out_err(%W(-\r -e) + [""], "", [], [])
|
||||
|
||||
assert_in_out_err(%W(-\rx), "", [], /invalid option -\\x0D \(-h will show valid options\) \(RuntimeError\)/)
|
||||
assert_in_out_err(%W(-\rx), "", [], /invalid option -\\r \(-h will show valid options\) \(RuntimeError\)/)
|
||||
|
||||
assert_in_out_err(%W(-\x01), "", [], /invalid option -\\x01 \(-h will show valid options\) \(RuntimeError\)/)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue