require "../../spec_helper" describe "Code gen: method_missing" do it "does method_missing macro without args" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def foo_something 1 end macro method_missing(call) {{call.name.id}}_something end end Foo.new.foo CRYSTAL end it "does method_missing macro with args" do run(<<-CRYSTAL).to_i.should eq(6) class Foo macro method_missing(call) {{call.args.join(" &+ ").id}} end end Foo.new.foo(1, 2, 3) CRYSTAL end it "does method_missing macro with block" do run(<<-CRYSTAL).to_i.should eq(6) class Foo def foo_something yield 1 yield 2 yield 3 end macro method_missing(call) {{call.name.id}}_something {{call.block}} end end a = 0 Foo.new.foo do |x| a &+= x end a CRYSTAL end it "does method_missing macro with block but not using it" do run(<<-CRYSTAL).to_i.should eq(3) class Foo def foo_something 1 &+ 2 end macro method_missing(call) {{call.name.id}}_something {{call.block}} end end Foo.new.foo CRYSTAL end it "does method_missing macro with virtual type (1)" do run(<<-CRYSTAL).to_string.should eq("Foococo") class Foo macro method_missing(call) "{{@type.name.id}}{{call.name.id}}" end end class Bar < Foo end foo = Foo.new || Bar.new foo.coco CRYSTAL end it "does method_missing macro with virtual type (2)" do run(<<-CRYSTAL).to_string.should eq("Barcoco") class Foo macro method_missing(call) "{{@type.name.id}}{{call.name.id}}" end end class Bar < Foo end foo = Bar.new || Foo.new foo.coco CRYSTAL end it "does method_missing macro with virtual type (3)" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def lala 1 end macro method_missing(call) 2 end end class Bar < Foo end foo = Bar.new || Foo.new foo.lala CRYSTAL end it "does method_missing macro with virtual type (4)" do run(<<-CRYSTAL).to_i.should eq(2) class Foo macro method_missing(call) 1 end end class Bar < Foo macro method_missing(call) 2 end end foo = Bar.new || Foo.new foo.lala CRYSTAL end it "does method_missing macro with virtual type (5)" do run(<<-CRYSTAL).to_i.should eq(3) class Foo macro method_missing(call) 1 end end class Bar < Foo macro method_missing(call) 2 end end class Baz < Bar macro method_missing(call) 3 end end foo = Baz.new || Bar.new || Foo.new foo.lala CRYSTAL end it "does method_missing macro with virtual type (6)" do run(<<-CRYSTAL).to_i.should eq(2) abstract class Foo end class Bar < Foo macro method_missing(call) 2 end end class Baz < Bar def lala 3 end end foo = Bar.new || Baz.new foo.lala CRYSTAL end it "does method_missing macro with virtual type (7)" do run(<<-CRYSTAL).to_i.should eq(3) abstract class Foo end class Bar < Foo macro method_missing(call) 2 end end class Baz < Bar def lala 3 end end foo = Baz.new || Bar.new foo.lala CRYSTAL end it "does method_missing macro with virtual type (8)" do run(<<-CRYSTAL).to_string.should eq("Bar") class Foo macro method_missing(call) {{@type.name.stringify}} end end class Bar < Foo end foo = Foo.new foo.coco bar = Bar.new bar.coco CRYSTAL end it "does method_missing macro with module involved" do run(<<-CRYSTAL).to_i.should eq(1) module Moo def lala 1 end end class Foo include Moo macro method_missing(call) 2 end end Foo.new.lala CRYSTAL end it "does method_missing macro with top level method involved" do run(<<-CRYSTAL).to_i.should eq(1) def lala 1 end class Foo macro method_missing(call) 2 end def bar lala end end foo = Foo.new foo.bar CRYSTAL end it "does method_missing macro with included module" do run(<<-CRYSTAL).to_string.should eq("Foo") module Moo macro method_missing(call) {{@type.name.stringify}} end end class Foo include Moo end Foo.new.coco CRYSTAL end it "does method_missing with assignment (bug)" do run(<<-CRYSTAL).to_i.should eq(1) class Foo macro method_missing(call) x = {{call.args[0]}} x end end foo = Foo.new foo.bar(1) CRYSTAL end it "does method_missing with assignment (2) (bug)" do run(<<-CRYSTAL).to_i.should eq(1) struct Nil def to_i! 0 end end class Foo @x : Int32? macro method_missing(call) @x = {{call.args[0]}} @x end end foo = Foo.new foo.bar(1).to_i! CRYSTAL end it "does method_missing macro without args (with call)" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def foo_something 1 end macro method_missing(call) {{call.name.id}}_something end end Foo.new.foo CRYSTAL end it "does method_missing macro with args (with call)" do run(<<-CRYSTAL).to_i.should eq(6) class Foo macro method_missing(call) {{call.args.join(" &+ ").id}} end end Foo.new.foo(1, 2, 3) CRYSTAL end it "forwards" do run(<<-CRYSTAL).to_i.should eq(6) class Wrapped def foo(x, y, z) x &+ y &+ z end end class Foo def initialize(@wrapped : Wrapped) end macro method_missing(call) @wrapped.{{call}} end end Foo.new(Wrapped.new).foo(1, 2, 3) CRYSTAL end it "does method_missing generating method" do run(<<-CRYSTAL).to_string.should eq("bar") class Foo macro method_missing(call) def {{call.name}} {{call.name.stringify}} end end end Foo.new.bar CRYSTAL end it "works with named arguments (#3654)" do run(<<-CRYSTAL).to_i.should eq(3) class A macro method_missing(call) {{call.named_args[0].value}} &+ {{call.named_args[1].value}} end end a = A.new a.b(x: 1, y: 2) CRYSTAL end it "works with named arguments that aren't legal variable names (#10381)" do run(<<-CRYSTAL).to_i.should eq(3) class A macro method_missing(call) {{call.named_args[0].value}} &+ {{call.named_args[1].value}} end end a = A.new a.b("@x": 1, Y: 2) CRYSTAL end it "finds method_missing with 'with ... yield'" do run(<<-CRYSTAL).to_i.should eq(10) class Foo def initialize(@x : Int32) end macro method_missing(call) @{{call.name.id}} end end def bar foo = Foo.new(10) with foo yield end bar do x end CRYSTAL end end