1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

[ruby/date] Check for numeric arguments in constructors

Previously, the type of these arguments were not checked, leading to
NoMethodErrors in some cases, and TypeErrors in other cases, but not
showing what field was having the problems.  This change makes it so
the field with the problem is included in the error message.

For the valid_*? methods, this changes them to return false if one
of the arguments that should be numeric is not.

Fixes Ruby Bug 11935
Fixes Ruby Misc 15298

https://github.com/ruby/date/commit/a2f4b665f8
This commit is contained in:
Jeremy Evans 2019-07-24 08:03:27 -07:00 committed by Hiroshi SHIBATA
parent 9eb798a3f1
commit 4e40ca301c
No known key found for this signature in database
GPG key ID: F9CF13417264FAC2
2 changed files with 99 additions and 0 deletions

View file

@ -54,6 +54,14 @@ static double positive_inf, negative_inf;
static VALUE date_initialize(int argc, VALUE *argv, VALUE self);
static VALUE datetime_initialize(int argc, VALUE *argv, VALUE self);
#define RETURN_FALSE_UNLESS_NUMERIC(obj) if(!RTEST(rb_obj_is_kind_of((obj), rb_cNumeric))) return Qfalse
inline static void
check_numeric(VALUE obj, const char* field) {
if(!RTEST(rb_obj_is_kind_of(obj, rb_cNumeric))) {
rb_raise(rb_eTypeError, "invalid %s (not numeric)", field);
}
}
inline static int
f_cmp(VALUE x, VALUE y)
{
@ -2470,6 +2478,7 @@ date_s_valid_jd_p(int argc, VALUE *argv, VALUE klass)
rb_scan_args(argc, argv, "11", &vjd, &vsg);
RETURN_FALSE_UNLESS_NUMERIC(vjd);
argv2[0] = vjd;
if (argc < 2)
argv2[1] = INT2FIX(DEFAULT_SG);
@ -2562,6 +2571,9 @@ date_s_valid_civil_p(int argc, VALUE *argv, VALUE klass)
rb_scan_args(argc, argv, "31", &vy, &vm, &vd, &vsg);
RETURN_FALSE_UNLESS_NUMERIC(vy);
RETURN_FALSE_UNLESS_NUMERIC(vm);
RETURN_FALSE_UNLESS_NUMERIC(vd);
argv2[0] = vy;
argv2[1] = vm;
argv2[2] = vd;
@ -2643,6 +2655,8 @@ date_s_valid_ordinal_p(int argc, VALUE *argv, VALUE klass)
rb_scan_args(argc, argv, "21", &vy, &vd, &vsg);
RETURN_FALSE_UNLESS_NUMERIC(vy);
RETURN_FALSE_UNLESS_NUMERIC(vd);
argv2[0] = vy;
argv2[1] = vd;
if (argc < 3)
@ -2725,6 +2739,9 @@ date_s_valid_commercial_p(int argc, VALUE *argv, VALUE klass)
rb_scan_args(argc, argv, "31", &vy, &vw, &vd, &vsg);
RETURN_FALSE_UNLESS_NUMERIC(vy);
RETURN_FALSE_UNLESS_NUMERIC(vw);
RETURN_FALSE_UNLESS_NUMERIC(vd);
argv2[0] = vy;
argv2[1] = vw;
argv2[2] = vd;
@ -2906,6 +2923,7 @@ date_s_julian_leap_p(VALUE klass, VALUE y)
VALUE nth;
int ry;
check_numeric(y, "year");
decode_year(y, +1, &nth, &ry);
return f_boolcast(c_julian_leap_p(ry));
}
@ -2927,6 +2945,7 @@ date_s_gregorian_leap_p(VALUE klass, VALUE y)
VALUE nth;
int ry;
check_numeric(y, "year");
decode_year(y, -1, &nth, &ry);
return f_boolcast(c_gregorian_leap_p(ry));
}
@ -3281,6 +3300,7 @@ date_s_jd(int argc, VALUE *argv, VALUE klass)
case 2:
val2sg(vsg, sg);
case 1:
check_numeric(vjd, "jd");
num2num_with_frac(jd, positive_inf);
}
@ -3333,8 +3353,10 @@ date_s_ordinal(int argc, VALUE *argv, VALUE klass)
case 3:
val2sg(vsg, sg);
case 2:
check_numeric(vd, "yday");
num2int_with_frac(d, positive_inf);
case 1:
check_numeric(vy, "year");
y = vy;
}
@ -3413,10 +3435,13 @@ date_initialize(int argc, VALUE *argv, VALUE self)
case 4:
val2sg(vsg, sg);
case 3:
check_numeric(vd, "day");
num2int_with_frac(d, positive_inf);
case 2:
check_numeric(vd, "month");
m = NUM2INT(vm);
case 1:
check_numeric(vy, "year");
y = vy;
}
@ -3483,10 +3508,13 @@ date_s_commercial(int argc, VALUE *argv, VALUE klass)
case 4:
val2sg(vsg, sg);
case 3:
check_numeric(vd, "cwday");
num2int_with_frac(d, positive_inf);
case 2:
check_numeric(vw, "cweek");
w = NUM2INT(vw);
case 1:
check_numeric(vy, "year");
y = vy;
}
@ -7244,12 +7272,16 @@ datetime_s_jd(int argc, VALUE *argv, VALUE klass)
case 5:
val2off(vof, rof);
case 4:
check_numeric(vs, "second");
num2int_with_frac(s, positive_inf);
case 3:
check_numeric(vmin, "minute");
num2int_with_frac(min, 3);
case 2:
check_numeric(vh, "hour");
num2int_with_frac(h, 2);
case 1:
check_numeric(vjd, "jd");
num2num_with_frac(jd, 1);
}
@ -7313,14 +7345,19 @@ datetime_s_ordinal(int argc, VALUE *argv, VALUE klass)
case 6:
val2off(vof, rof);
case 5:
check_numeric(vs, "second");
num2int_with_frac(s, positive_inf);
case 4:
check_numeric(vmin, "minute");
num2int_with_frac(min, 4);
case 3:
check_numeric(vh, "hour");
num2int_with_frac(h, 3);
case 2:
check_numeric(vd, "yday");
num2int_with_frac(d, 2);
case 1:
check_numeric(vy, "year");
y = vy;
}
@ -7401,16 +7438,22 @@ datetime_initialize(int argc, VALUE *argv, VALUE self)
case 7:
val2off(vof, rof);
case 6:
check_numeric(vs, "second");
num2int_with_frac(s, positive_inf);
case 5:
check_numeric(vmin, "minute");
num2int_with_frac(min, 5);
case 4:
check_numeric(vh, "hour");
num2int_with_frac(h, 4);
case 3:
check_numeric(vd, "day");
num2int_with_frac(d, 3);
case 2:
check_numeric(vm, "month");
m = NUM2INT(vm);
case 1:
check_numeric(vy, "year");
y = vy;
}
@ -7499,16 +7542,22 @@ datetime_s_commercial(int argc, VALUE *argv, VALUE klass)
case 7:
val2off(vof, rof);
case 6:
check_numeric(vs, "second");
num2int_with_frac(s, positive_inf);
case 5:
check_numeric(vmin, "minute");
num2int_with_frac(min, 5);
case 4:
check_numeric(vh, "hour");
num2int_with_frac(h, 4);
case 3:
check_numeric(vd, "cwday");
num2int_with_frac(d, 3);
case 2:
check_numeric(vw, "cweek");
w = NUM2INT(vw);
case 1:
check_numeric(vy, "year");
y = vy;
}

View file

@ -32,6 +32,56 @@ class TestDateNew < Test::Unit::TestCase
end
end
def test_valid_with_invalid_types
o = Object.new
assert_equal(false, Date.valid_jd?(o))
assert_equal(false, Date.valid_civil?(o, 1, 1))
assert_equal(false, Date.valid_civil?(1, o, 1))
assert_equal(false, Date.valid_civil?(1, 1, o))
assert_equal(false, Date.valid_ordinal?(o, 1))
assert_equal(false, Date.valid_ordinal?(1, o))
assert_equal(false, Date.valid_commercial?(o, 1, 1))
assert_equal(false, Date.valid_commercial?(1, o, 1))
assert_equal(false, Date.valid_commercial?(1, 1, o))
end
def test_invalid_types
o = Object.new
assert_raise(TypeError) { Date.julian_leap?(o) }
assert_raise(TypeError) { Date.gregorian_leap?(o) }
assert_raise(TypeError) { Date.jd(o) }
assert_raise(TypeError) { Date.new(o) }
assert_raise(TypeError) { Date.new(1, o) }
assert_raise(TypeError) { Date.new(1, 1, o) }
assert_raise(TypeError) { Date.ordinal(o) }
assert_raise(TypeError) { Date.ordinal(1, o) }
assert_raise(TypeError) { Date.commercial(o) }
assert_raise(TypeError) { Date.commercial(1, o) }
assert_raise(TypeError) { Date.commercial(1, 1, o) }
assert_raise(TypeError) { DateTime.jd(o) }
assert_raise(TypeError) { DateTime.jd(1, o) }
assert_raise(TypeError) { DateTime.jd(1, 1, o) }
assert_raise(TypeError) { DateTime.jd(1, 1, 1, o) }
assert_raise(TypeError) { DateTime.new(o) }
assert_raise(TypeError) { DateTime.new(1, o) }
assert_raise(TypeError) { DateTime.new(1, 1, o) }
assert_raise(TypeError) { DateTime.new(1, 1, 1, o) }
assert_raise(TypeError) { DateTime.new(1, 1, 1, 1, o) }
assert_raise(TypeError) { DateTime.new(1, 1, 1, 1, 1, o) }
assert_raise(TypeError) { DateTime.ordinal(o) }
assert_raise(TypeError) { DateTime.ordinal(1, o) }
assert_raise(TypeError) { DateTime.ordinal(1, 1, o) }
assert_raise(TypeError) { DateTime.ordinal(1, 1, 1, o) }
assert_raise(TypeError) { DateTime.ordinal(1, 1, 1, 1, o) }
assert_raise(TypeError) { DateTime.commercial(o) }
assert_raise(TypeError) { DateTime.commercial(1, o) }
assert_raise(TypeError) { DateTime.commercial(1, 1, o) }
assert_raise(TypeError) { DateTime.commercial(1, 1, 1, o) }
assert_raise(TypeError) { DateTime.commercial(1, 1, 1, 1, o) }
assert_raise(TypeError) { DateTime.commercial(1, 1, 1, 1, 1, o) }
end
def test_ordinal
d = Date.ordinal
dt = DateTime.ordinal