diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 38b08f44568a..385e165a3504 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -928,6 +928,16 @@ module Crystal assert_macro %({{["c".id, "b", "a".id].sort}}), %([a, "b", c]) end + it "executes sort_by" do + assert_macro %({{["abc", "a", "ab"].sort_by { |x| x.size }}}), %(["a", "ab", "abc"]) + end + + it "calls block exactly once for each element in #sort_by" do + assert_macro <<-CRYSTAL, %(5) + {{ (i = 0; ["abc", "a", "ab", "abcde", "abcd"].sort_by { i += 1 }; i) }} + CRYSTAL + end + it "executes uniq" do assert_macro %({{[1, 1, 1, 2, 3, 1, 2, 3, 4].uniq}}), %([1, 2, 3, 4]) end @@ -1020,10 +1030,6 @@ module Crystal assert_macro %({{{:a => 1, :b => 3}.size}}), "2" end - it "executes sort_by" do - assert_macro %({{["abc", "a", "ab"].sort_by { |x| x.size }}}), %(["a", "ab", "abc"]) - end - it "executes empty?" do assert_macro %({{{:a => 1}.empty?}}), "false" end diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index d3a1a1cc15a6..8a7aa569fa95 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -3232,12 +3232,17 @@ end private def sort_by(object, klass, block, interpreter) block_arg = block.args.first? - klass.new(object.elements.sort { |x, y| - block_arg.try { |arg| interpreter.define_var(arg.name, x) } - x_result = interpreter.accept(block.body) - block_arg.try { |arg| interpreter.define_var(arg.name, y) } - y_result = interpreter.accept(block.body) - - x_result.interpret_compare(y_result) - }) + klass.new(object.elements.sort_by do |elem| + block_arg.try { |arg| interpreter.define_var(arg.name, elem) } + result = interpreter.accept(block.body) + InterpretCompareWrapper.new(result) + end) +end + +private record InterpretCompareWrapper, node : Crystal::ASTNode do + include Comparable(self) + + def <=>(other : self) + node.interpret_compare(other.node) + end end