diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 493fd070744a..a41068be40d0 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -591,6 +591,11 @@ module Crystal assert_macro %({{"hello world".titleize}}), %("Hello World") end + it "executes to_utf16" do + assert_macro %({{"hello".to_utf16}}), "(::Slice(::UInt16).literal(104_u16, 101_u16, 108_u16, 108_u16, 111_u16, 0_u16))[0, 5]" + assert_macro %({{"TEST 😐🐙 ±∀ の".to_utf16}}), "(::Slice(::UInt16).literal(84_u16, 69_u16, 83_u16, 84_u16, 32_u16, 55357_u16, 56848_u16, 55357_u16, 56345_u16, 32_u16, 177_u16, 8704_u16, 32_u16, 12398_u16, 0_u16))[0, 14]" + end + it "executes to_i" do assert_macro %({{"1234".to_i}}), %(1234) end diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 2c45f49701fd..2b6ac1c1d25b 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -820,6 +820,21 @@ module Crystal else raise "StringLiteral#to_i: #{@value} is not an integer" end + when "to_utf16" + interpret_check_args do + slice = @value.to_utf16 + + # include the trailing zero that isn't counted in the slice but was + # generated by String#to_utf16 so the literal can be passed to C + # functions that expect a null terminated UInt16* + args = Slice(UInt16).new(slice.to_unsafe, slice.size + 1).to_a do |codepoint| + NumberLiteral.new(codepoint).as(ASTNode) + end + literal_node = Call.new(Generic.new(Path.global("Slice"), [Path.global("UInt16")] of ASTNode), "literal", args) + + # but keep the trailing zero hidden in the exposed slice + Call.new(literal_node, "[]", [NumberLiteral.new("0", :i32), NumberLiteral.new(slice.size)] of ASTNode) + end when "tr" interpret_check_args do |first, second| raise "first argument to StringLiteral#tr must be a string, not #{first.class_desc}" unless first.is_a?(StringLiteral)