Skip to content

Commit

Permalink
Be more picky about int/float arguments in Ruby
Browse files Browse the repository at this point in the history
There are implicit integer conversions using `to_int` and no conversions
for floats. This is correct according to
https://idiosyncratic-ruby.com/54-try-converting.html and Ruby's Discord
https://discord.com/channels/518658712081268738/788748516352458752/1118618993276760105.
  • Loading branch information
heinrich5991 committed Jun 15, 2023
1 parent 2eb3975 commit ce23a21
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 3 deletions.
21 changes: 21 additions & 0 deletions fixtures/type-limits/tests/bindings/test_type_limits.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ def test_larger_numbers(self):
self.assertEqual(take_u64(10**19), 10**19)

def test_non_integer(self):
self.assertRaises(TypeError, lambda: take_i8(None))
self.assertRaises(TypeError, lambda: take_i16(None))
self.assertRaises(TypeError, lambda: take_i32(None))
self.assertRaises(TypeError, lambda: take_i64(None))
self.assertRaises(TypeError, lambda: take_u8(None))
self.assertRaises(TypeError, lambda: take_u16(None))
self.assertRaises(TypeError, lambda: take_u32(None))
self.assertRaises(TypeError, lambda: take_u64(None))

self.assertRaises(TypeError, lambda: take_i8("0"))
self.assertRaises(TypeError, lambda: take_i16("0"))
self.assertRaises(TypeError, lambda: take_i32("0"))
Expand Down Expand Up @@ -97,6 +106,15 @@ class A:
self.assertRaises(TypeError, lambda: take_u64(A()))

def test_integer_like(self):
self.assertEqual(take_i8(123), 123.0)
self.assertEqual(take_i16(123), 123.0)
self.assertEqual(take_i32(123), 123.0)
self.assertEqual(take_i64(123), 123.0)
self.assertEqual(take_u8(123), 123.0)
self.assertEqual(take_u16(123), 123.0)
self.assertEqual(take_u32(123), 123.0)
self.assertEqual(take_u64(123), 123.0)

self.assertEqual(take_i8(False), 0)
self.assertEqual(take_i16(False), 0)
self.assertEqual(take_i32(False), 0)
Expand Down Expand Up @@ -129,6 +147,9 @@ def __index__(self):
self.assertEqual(take_u64(A()), 123)

def test_non_float(self):
self.assertRaises(TypeError, lambda: take_f32(None))
self.assertRaises(TypeError, lambda: take_f64(None))

self.assertRaises(TypeError, lambda: take_f32("0"))
self.assertRaises(TypeError, lambda: take_f64("0"))

Expand Down
149 changes: 149 additions & 0 deletions fixtures/type-limits/tests/bindings/test_type_limits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,154 @@ def test_larger_numbers
assert_equal(UniffiTypeLimits.take_u16(10**4), 10**4)
assert_equal(UniffiTypeLimits.take_u32(10**9), 10**9)
assert_equal(UniffiTypeLimits.take_u64(10**19), 10**19)

assert_raise FloatDomainError do UniffiTypeLimits.take_i8(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i16(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i32(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i64(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u8(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u16(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u32(-Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u64(-Float::INFINITY) end

assert_raise FloatDomainError do UniffiTypeLimits.take_i8(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i16(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i32(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i64(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u8(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u16(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u32(Float::INFINITY) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u64(Float::INFINITY) end

assert_raise FloatDomainError do UniffiTypeLimits.take_i8(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i16(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i32(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_i64(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u8(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u16(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u32(Float::NAN) end
assert_raise FloatDomainError do UniffiTypeLimits.take_u64(Float::NAN) end
end
class NonInteger
end
def test_non_integer
assert_raise TypeError do UniffiTypeLimits.take_i8(nil) end
assert_raise TypeError do UniffiTypeLimits.take_i16(nil) end
assert_raise TypeError do UniffiTypeLimits.take_i32(nil) end
assert_raise TypeError do UniffiTypeLimits.take_i64(nil) end
assert_raise TypeError do UniffiTypeLimits.take_u8(nil) end
assert_raise TypeError do UniffiTypeLimits.take_u16(nil) end
assert_raise TypeError do UniffiTypeLimits.take_u32(nil) end
assert_raise TypeError do UniffiTypeLimits.take_u64(nil) end

assert_raise TypeError do UniffiTypeLimits.take_i8("0") end
assert_raise TypeError do UniffiTypeLimits.take_i16("0") end
assert_raise TypeError do UniffiTypeLimits.take_i32("0") end
assert_raise TypeError do UniffiTypeLimits.take_i64("0") end
assert_raise TypeError do UniffiTypeLimits.take_u8("0") end
assert_raise TypeError do UniffiTypeLimits.take_u16("0") end
assert_raise TypeError do UniffiTypeLimits.take_u32("0") end
assert_raise TypeError do UniffiTypeLimits.take_u64("0") end

assert_raise TypeError do UniffiTypeLimits.take_i8(false) end
assert_raise TypeError do UniffiTypeLimits.take_i16(false) end
assert_raise TypeError do UniffiTypeLimits.take_i32(false) end
assert_raise TypeError do UniffiTypeLimits.take_i64(false) end
assert_raise TypeError do UniffiTypeLimits.take_u8(false) end
assert_raise TypeError do UniffiTypeLimits.take_u16(false) end
assert_raise TypeError do UniffiTypeLimits.take_u32(false) end
assert_raise TypeError do UniffiTypeLimits.take_u64(false) end

assert_raise TypeError do UniffiTypeLimits.take_i8(true) end
assert_raise TypeError do UniffiTypeLimits.take_i16(true) end
assert_raise TypeError do UniffiTypeLimits.take_i32(true) end
assert_raise TypeError do UniffiTypeLimits.take_i64(true) end
assert_raise TypeError do UniffiTypeLimits.take_u8(true) end
assert_raise TypeError do UniffiTypeLimits.take_u16(true) end
assert_raise TypeError do UniffiTypeLimits.take_u32(true) end
assert_raise TypeError do UniffiTypeLimits.take_u64(true) end

assert_raise TypeError do UniffiTypeLimits.take_i8(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_i16(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_i32(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_i64(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_u8(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_u16(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_u32(NonInteger.new) end
assert_raise TypeError do UniffiTypeLimits.take_u64(NonInteger.new) end
end
class IntegerLike
def to_int
123
end
end
def test_integer_like
assert_equal(UniffiTypeLimits.take_i8(123.0), 123)
assert_equal(UniffiTypeLimits.take_i16(123.0), 123)
assert_equal(UniffiTypeLimits.take_i32(123.0), 123)
assert_equal(UniffiTypeLimits.take_i64(123.0), 123)
assert_equal(UniffiTypeLimits.take_u8(123.0), 123)
assert_equal(UniffiTypeLimits.take_u16(123.0), 123)
assert_equal(UniffiTypeLimits.take_u32(123.0), 123)
assert_equal(UniffiTypeLimits.take_u64(123.0), 123)

assert_equal(UniffiTypeLimits.take_i8(-0.5), 0)
assert_equal(UniffiTypeLimits.take_i16(-0.5), 0)
assert_equal(UniffiTypeLimits.take_i32(-0.5), 0)
assert_equal(UniffiTypeLimits.take_i64(-0.5), 0)
assert_equal(UniffiTypeLimits.take_u8(-0.5), 0)
assert_equal(UniffiTypeLimits.take_u16(-0.5), 0)
assert_equal(UniffiTypeLimits.take_u32(-0.5), 0)
assert_equal(UniffiTypeLimits.take_u64(-0.5), 0)

assert_equal(UniffiTypeLimits.take_i8(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_i16(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_i32(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_i64(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_u8(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_u16(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_u32(IntegerLike.new), 123)
assert_equal(UniffiTypeLimits.take_u64(IntegerLike.new), 123)
end
class NonFloat
end
def test_non_float
assert_raise TypeError do UniffiTypeLimits.take_f32(nil) end
assert_raise TypeError do UniffiTypeLimits.take_f64(nil) end

assert_raise TypeError do UniffiTypeLimits.take_f32("0") end
assert_raise TypeError do UniffiTypeLimits.take_f64("0") end

assert_raise TypeError do UniffiTypeLimits.take_f32(false) end
assert_raise TypeError do UniffiTypeLimits.take_f64(false) end

assert_raise TypeError do UniffiTypeLimits.take_f32(true) end
assert_raise TypeError do UniffiTypeLimits.take_f64(true) end

assert_raise RangeError do UniffiTypeLimits.take_f32(1i) end
assert_raise RangeError do UniffiTypeLimits.take_f64(1i) end

assert_raise TypeError do UniffiTypeLimits.take_f32(NonFloat.new) end
assert_raise TypeError do UniffiTypeLimits.take_f64(NonFloat.new) end
end
def test_float_like
assert_equal(UniffiTypeLimits.take_f32(456), 456.0)
assert_equal(UniffiTypeLimits.take_f64(456), 456.0)
end
def test_special_floats
assert_equal(UniffiTypeLimits.take_f32(Float::INFINITY), Float::INFINITY)
assert_equal(UniffiTypeLimits.take_f64(Float::INFINITY), Float::INFINITY)

assert_equal(UniffiTypeLimits.take_f32(-Float::INFINITY), -Float::INFINITY)
assert_equal(UniffiTypeLimits.take_f64(-Float::INFINITY), -Float::INFINITY)

assert_equal(UniffiTypeLimits.take_f32(0.0).to_s, "0.0")
assert_equal(UniffiTypeLimits.take_f64(0.0).to_s, "0.0")

assert_equal(UniffiTypeLimits.take_f32(-0.0).to_s, "-0.0")
assert_equal(UniffiTypeLimits.take_f64(-0.0).to_s, "-0.0")

assert(UniffiTypeLimits.take_f32(Float::NAN).nan?)
assert(UniffiTypeLimits.take_f64(Float::NAN).nan?)
end
end
4 changes: 2 additions & 2 deletions uniffi_bindgen/src/bindings/ruby/gen_ruby/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ mod filters {
Type::UInt16 => format!("{ns}::uniffi_in_range({nm}, \"u16\", 0, 2**16)"),
Type::UInt32 => format!("{ns}::uniffi_in_range({nm}, \"u32\", 0, 2**32)"),
Type::UInt64 => format!("{ns}::uniffi_in_range({nm}, \"u64\", 0, 2**64)"),
Type::Float32 | Type::Float64 => format!("{nm}.to_f"),
Type::Float32 | Type::Float64 => nm.to_string(),
Type::Boolean => format!("{nm} ? true : false"),
Type::Object { .. } | Type::Enum(_) | Type::Record(_) => nm.to_string(),
Type::String | Type::Bytes => format!("{nm}.to_s"),
Type::String | Type::Bytes => format!("{nm}.to_str"),
Type::Timestamp | Type::Duration => nm.to_string(),
Type::CallbackInterface(_) => panic!("No support for coercing callback interfaces yet"),
Type::Optional(t) => format!("({nm} ? {} : nil)", coerce_rb(nm, ns, t)?),
Expand Down
3 changes: 2 additions & 1 deletion uniffi_bindgen/src/bindings/ruby/templates/Helpers.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
def self.uniffi_in_range(i, type_name, min, max)
i = i.to_i
raise TypeError, "no implicit conversion of #{i} into Integer" unless i.respond_to?(:to_int)
i = i.to_int
raise RangeError, "#{type_name} requires #{min} <= value < #{max}" unless (min <= i && i < max)
i
end

0 comments on commit ce23a21

Please sign in to comment.