require "../../spec_helper"

describe "Code gen: macro" do
  it "expands macro" do
    run("macro foo; 1 &+ 2; end; foo").to_i.should eq(3)
  end

  it "expands macro with arguments" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo(n)
        {{n}} &+ 2
      end

      foo(1)
      CRYSTAL
  end

  it "expands macro that invokes another macro" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo
        def x
          1 &+ 2
        end
      end

      macro bar
        foo
      end

      bar
      x
      CRYSTAL
  end

  it "expands macro defined in class" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        macro foo
          def bar
            1
          end
        end

        foo
      end

      foo = Foo.new
      foo.bar
      CRYSTAL
  end

  it "expands macro defined in base class" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Object
        macro foo
          def bar
            1
          end
        end
      end

      class Foo
        foo
      end

      foo = Foo.new
      foo.bar
      CRYSTAL
  end

  it "expands inline macro" do
    run(<<-CRYSTAL).to_i.should eq(1)
      a = {{ 1 }}
      a
      CRYSTAL
  end

  it "expands inline macro for" do
    run(<<-CRYSTAL).to_i.should eq(6)
      a = 0
      {% for i in [1, 2, 3] %}
        a &+= {{i}}
      {% end %}
      a
      CRYSTAL
  end

  it "expands inline macro if (true)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      a = 0
      {% if 1 == 1 %}
        a &+= 1
      {% end %}
      a
      CRYSTAL
  end

  it "expands inline macro if (false)" do
    run(<<-CRYSTAL).to_i.should eq(0)
      a = 0
      {% if 1 == 2 %}
        a &+= 1
      {% end %}
      a
      CRYSTAL
  end

  it "finds macro in class" do
    run(<<-CRYSTAL).to_i.should eq(3)
      class Foo
        macro foo
          1 &+ 2
        end

        def bar
          foo
        end
      end

      Foo.new.bar
      CRYSTAL
  end

  it "expands def macro" do
    run(<<-CRYSTAL).to_i.should eq(1)
      def bar_baz
        1
      end

      def foo : Int32
        {% begin %}
          bar_{{ "baz".id }}
        {% end %}
      end

      foo
      CRYSTAL
  end

  it "expands def macro with var" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def foo : Int32
          {{ @type }}
          a = {{ 1 }}
        end
      end

      Foo.new.foo
      CRYSTAL
  end

  it "expands def macro with @type.instance_vars" do
    run(<<-CRYSTAL).to_string.should eq("x")
      class Foo
        def initialize(@x : Int32)
        end

        def to_s : String
          {{ @type.instance_vars.first.stringify }}
        end
      end

      foo = Foo.new(1)
      foo.to_s
      CRYSTAL
  end

  it "expands def macro with @type.instance_vars with subclass" do
    run(<<-CRYSTAL).to_string.should eq("y")
      class Reference
        def to_s : String
          {{ @type.instance_vars.last.stringify }}
        end
      end

      class Foo
        def initialize(@x : Int32)
        end
      end

      class Bar < Foo
        def initialize(@x : Int32, @y : Int32)
        end
      end

      Bar.new(1, 2).to_s
      CRYSTAL
  end

  it "expands def macro with @type.instance_vars with virtual" do
    run(<<-CRYSTAL).to_string.should eq("y")
      class Reference
        def to_s : String
          {{ @type.instance_vars.last.stringify }}
        end
      end

      class Foo
        def initialize(@x : Int32)
        end
      end

      class Bar < Foo
        def initialize(@x : Int32, @y : Int32)
        end
      end

      (Bar.new(1, 2) || Foo.new(1)).to_s
      CRYSTAL
  end

  it "expands def macro with @type.name" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Foo
        def initialize(@x : Int32)
        end

        def to_s : String
          {{@type.name.stringify}}
        end
      end

      foo = Foo.new(1)
      foo.to_s
      CRYSTAL
  end

  it "expands macro and resolves type correctly" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def foo : Int32
          {{ @type }}
          1
        end
      end

      class Bar < Foo
        Int32 = 2
      end

      Bar.new.foo
      CRYSTAL
  end

  it "expands def macro with @type.name with virtual" do
    run(<<-CRYSTAL).to_string.should eq("Bar")
      class Reference
        def to_s : String
          {{ @type.name.stringify }}
        end
      end

      class Foo
      end

      class Bar < Foo
      end

      (Bar.new || Foo.new).to_s
      CRYSTAL
  end

  it "expands def macro with @type.name with virtual (2)" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Reference
        def to_s : String
          {{ @type.name.stringify }}
        end
      end

      class Foo
      end

      class Bar < Foo
      end

      (Foo.new || Bar.new).to_s
      CRYSTAL
  end

  it "allows overriding macro definition when redefining base class" do
    run(<<-CRYSTAL).to_string.should eq("OH NO")
      class Foo
        def inspect : String
          {{@type.name.stringify}}
        end
      end

      class Bar < Foo
      end

      class Foo
        def inspect
          "OH NO"
        end
      end

      Bar.new.inspect
      CRYSTAL
  end

  it "uses invocation context" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      macro foo
        def bar
          {{@type.name.stringify}}
        end
      end

      class Foo
        foo
      end

      Foo.new.bar
      CRYSTAL
  end

  it "allows macro with default arguments" do
    run(<<-CRYSTAL).to_i.should eq(3)
      def bar
        2
      end

      macro foo(x, y = :bar)
        {{x}} &+ {{y.id}}
      end

      foo(1)
      CRYSTAL
  end

  it "expands def macro with instance var and method call (bug)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      struct Nil
        def to_i!
          0
        end
      end

      class Foo
        @name : Int32?

        def foo : Int32
          {{ @type }}
          name = 1
          @name = name
        end
      end

      Foo.new.foo.to_i!
      CRYSTAL
  end

  it "expands @type.name in virtual metaclass (1)" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Class
        def to_s : String
          {{ @type.name.stringify }}
        end
      end

      class Foo
      end

      class Bar < Foo
      end

      p = Pointer(Foo.class).malloc(1_u64)
      p.value = Bar
      p.value = Foo
      p.value.to_s
      CRYSTAL
  end

  it "expands @type.name in virtual metaclass (2)" do
    run(<<-CRYSTAL).to_string.should eq("Bar")
      class Class
        def to_s : String
          {{ @type.name.stringify }}
        end
      end

      class Foo
      end

      class Bar < Foo
      end

      p = Pointer(Foo.class).malloc(1_u64)
      p.value = Foo
      p.value = Bar
      p.value.to_s
      CRYSTAL
  end

  it "doesn't skip abstract classes when defining macro methods" do
    run(<<-CRYSTAL).to_i.should eq(2)
      class Object
        def foo : Int32
          {{ @type }}
          1
        end
      end

      class Type
      end

      class ModuleType < Type
        def foo
          2
        end
      end

      class Type1 < ModuleType
      end

      class Type2 < Type
      end

      t = Type1.new || Type2.new
      t.foo
      CRYSTAL
  end

  it "doesn't reuse macro nodes (bug)" do
    run(<<-CRYSTAL).to_i.should eq(2)
      struct Float
        def &+(other)
          self + other
        end
      end

      def foo(x)
        {% for y in [1, 2] %}
          x &+ 1
        {% end %}
      end

      foo 1
      foo(1.5).to_i!
      CRYSTAL
  end

  it "can use constants" do
    run(<<-CRYSTAL).to_i.should eq(1)
      CONST = 1
      {{ CONST }}
      CRYSTAL
  end

  it "can refer to types" do
    run(<<-CRYSTAL).to_string.should eq("y")
      class Foo
        def initialize(@x : Int32, @y : Int32)
        end

        def foo : String
          {{ @type }}
          {{ Foo.instance_vars.last.name.stringify }}
        end

      end

      Foo.new(1, 2).foo
      CRYSTAL
  end

  it "runs macro with splat" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo(*args)
        {{args.size}}
      end

      foo 1, 1, 1
      CRYSTAL
  end

  it "runs macro with arg and splat" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo(name, *args)
        {{args.size}}
      end

      foo bar, 1, 1, 1
      CRYSTAL
  end

  it "expands macro that yields" do
    run(<<-CRYSTAL).to_i.should eq(3)
      def foo
        {% for i in 0 .. 2 %}
          yield {{i}}
        {% end %}
      end

      a = 0
      foo do |x|
        a &+= x
      end
      a
      CRYSTAL
  end

  it "can refer to abstract (1)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo
      end

      {{ Foo.abstract? }}
      CRYSTAL
  end

  it "can refer to abstract (2)" do
    run(<<-CRYSTAL).to_b.should be_true
      abstract class Foo
      end

      {{ Foo.abstract? }}
      CRYSTAL
  end

  it "can refer to @type" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Foo
        def foo : String
          {{@type.name.stringify}}
        end
      end

      Foo.new.foo
      CRYSTAL
  end

  it "can refer to union (1)" do
    run(<<-CRYSTAL).to_b.should be_false
      {{Int32.union?}}
      CRYSTAL
  end

  it "can refer to union (2)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
        def initialize
          @x = 1; @x = 1.1
        end
        def foo
          {{ @type.instance_vars.first.type.union? }}
        end
      end
      Foo.new.foo
      CRYSTAL
  end

  it "can iterate union types" do
    run(<<-CRYSTAL).to_string.should eq("Float64-Int32")
      class Foo
        def initialize
          @x = 1; @x = 1.1
        end
        def foo
          {{ @type.instance_vars.first.type.union_types.map(&.name).sort.join("-") }}
        end
      end
      Foo.new.foo
      CRYSTAL
  end

  it "can access type variables" do
    run(<<-CRYSTAL).to_string.should eq("Int32")
      class Foo(T)
        def foo
          {{ @type.type_vars.first.name.stringify }}
        end
      end
      Foo(Int32).new.foo
      CRYSTAL
  end

  it "can access type variables of a module" do
    run(<<-CRYSTAL).to_string.should eq("Int32")
      module Foo(T)
        def self.foo
          {{ @type.type_vars.first.name.stringify }}
        end
      end
      Foo(Int32).foo
      CRYSTAL
  end

  it "can access type variables that are not types" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo(T)
        def foo
          {{ @type.type_vars.first.is_a?(NumberLiteral) }}
        end
      end
      Foo(1).new.foo
      CRYSTAL
  end

  it "can access type variables of a tuple" do
    run(<<-CRYSTAL).to_string.should eq("Int32")
      struct Tuple
        def foo
          {{ @type.type_vars.first.name.stringify }}
        end
      end
      {1, 2, 3}.foo
      CRYSTAL
  end

  it "can access type variables of a generic type" do
    run(<<-CRYSTAL).to_string.should eq("T-K")
      class Foo(T, K)
        def self.foo : String
          {{ @type.type_vars.map(&.stringify).join("-") }}
        end
      end
      Foo.foo
      CRYSTAL
  end

  it "receives &block" do
    run(<<-CRYSTAL).to_i.should eq(2)
      macro foo(&block)
        bar {{block}}
      end

      def bar
        yield 1
      end

      foo do |x|
        x &+ 1
      end
      CRYSTAL
  end

  it "executes with named arguments" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo(x = 1)
        {{x}} &+ 1
      end

      foo x: 2
      CRYSTAL
  end

  it "gets correct class name when there are classes in the middle" do
    run(<<-CRYSTAL).to_string.should eq("Qux")
      class Foo
        def class_desc : String
          {{@type.name.stringify}}
        end
      end

      class Bar < Foo
      end

      class Baz < Bar
      end

      class Qux < Bar
      end

      a = Pointer(Foo).malloc(1_u64)
      a.value = Qux.new
      a.value.class_desc
      CRYSTAL
  end

  it "transforms hooks (bug)" do
    codegen(<<-CRYSTAL)
      require "prelude"

      module GC
        def self.add_finalizer(object : T)
          object.responds_to?(:finalize)
        end
      end

      abstract class Foo
        ALL = Pointer(Foo).malloc(1_u64)

        macro inherited
          ALL.value = new
        end
      end

      class Bar < Foo
      end
      CRYSTAL
  end

  it "executes subclasses" do
    run(<<-CRYSTAL).to_string.should eq("Bar-Baz")
      class Foo
      end

      class Bar < Foo
      end

      class Baz < Foo
      end

      class Qux < Baz
      end

      {{ Foo.subclasses.map(&.name).join("-") }}
      CRYSTAL
  end

  it "executes all_subclasses" do
    run(<<-CRYSTAL).to_string.should eq("Bar-Baz")
      class Foo
      end

      class Bar < Foo
      end

      class Baz < Bar
      end

      {{ Foo.all_subclasses.map(&.name).join("-") }}
      CRYSTAL
  end

  it "gets enum members with @type.constants" do
    run(<<-CRYSTAL).to_i.should eq(0 + 1 + 2)
      enum Color
        Red
        Green
        Blue

        def self.red
          {{@type.constants[0]}}
        end

        def self.green
          {{@type.constants[1]}}
        end

        def self.blue
          {{@type.constants[2]}}
        end
      end

      Color.red.value &+ Color.green.value &+ Color.blue.value
      CRYSTAL
  end

  it "gets enum members as constants" do
    run(<<-CRYSTAL).to_string.should eq("Green")
      enum Color
        Red
        Green
        Blue
      end

      {{Color.constants[1].stringify}}
      CRYSTAL
  end

  it "says that enum has Flags annotation" do
    run(<<-CRYSTAL).to_b.should be_true
      @[Flags]
      enum Color
        Red
        Green
        Blue
      end

      {{Color.annotation(Flags) ? true : false}}
      CRYSTAL
  end

  it "says that enum doesn't have Flags annotation" do
    run(<<-CRYSTAL).to_b.should be_false
      enum Color
        Red
        Green
        Blue
      end

      {{Color.annotation(Flags) ? true : false}}
      CRYSTAL
  end

  it "gets methods" do
    run(<<-CRYSTAL).to_string.should eq("bar")
      class Foo
        def bar
          1
        end

        def first_method_name : String
          {{ @type.methods.map(&.name.stringify).first }}
        end
      end

      Foo.new.first_method_name
      CRYSTAL
  end

  it "copies base macro def to sub-subtype even after it was copied to a subtype (#448)" do
    run(<<-CRYSTAL).to_string.should eq("Baz")
      class Object
        def class_name : String
          {{@type.name.stringify}}
        end
      end

      class Foo
        @@children : Pointer(Foo)
        @@children = Pointer(Foo).malloc(1_u64)

        def self.children
          @@children
        end
      end

      Foo.children.value = Foo.new
      Foo.children.value.class_name

      class Bar < Foo; end

      Foo.children.value = Bar.new
      Foo.children.value.class_name

      class Baz < Bar; end
      Foo.children.value = Baz.new
      Foo.children.value.class_name
      CRYSTAL
  end

  it "recalculates method when virtual metaclass type is added" do
    run(<<-CRYSTAL).to_string.should eq("Test, RunnableTest")
      require "prelude"

      class Global
        @@x = [] of String
        @@runnables = [] of Runnable.class

        def self.x=(@@x)
        end

        def self.x
          @@x
        end

        def self.runnables
          @@runnables
        end
      end

      def run
        Global.runnables.each &.run
      end

      class Runnable
      end

      class Runnable
        macro inherited
          Global.runnables << self
        end

        def self.run : Nil
          Global.x << {{@type.name.stringify}}
          nil
        end
      end

      class Test < Runnable
      end

      run
      Global.x.clear

      class RunnableTest < Test
      end

      run
      Global.x.join(", ")
      CRYSTAL
  end

  it "correctly recomputes call (bug)" do
    run(<<-CRYSTAL).to_string.should eq("Baz")
      class Object
        def in_object
          in_class(1)
        end
      end

      class Class
        def in_class(x)
          bar
        end

        def bar : String
          {{@type.name.stringify}}
        end
      end

      class Foo
      end

      class Bar < Foo
      end

      f = Bar.new || Foo.new
      f.class.in_object

      class Baz < Foo
      end

      f2 = Baz.new || Foo.new
      f2.class.in_object
      CRYSTAL
  end

  it "doesn't override local variable when using macro variable" do
    run(<<-CRYSTAL).to_i.should eq(1)
      macro foo(x)
        %a = {{x}}
        %a
      end

      a = 1
      foo(2)
      foo(3)
      a
      CRYSTAL
  end

  it "doesn't override local variable when using macro variable (2)" do
    run(<<-CRYSTAL).to_i.should eq(26)
      macro foo(x)
        %a = {{x}} &+ 10
        %a
      end

      a = 1
      z = foo(2)
      w = foo(3)
      a &+ z &+ w
      CRYSTAL
  end

  it "uses indexed macro variable" do
    run(<<-CRYSTAL).to_i.should eq(4 + 5 + 6 + 40 + 50 + 60)
      macro foo(*elems)
        {% for elem, i in elems %}
          %var{i} = {{elem}}
        {% end %}

        %total = 0
        {% for elem, i in elems %}
          %total &+= %var{i}
        {% end %}
        %total
      end

      z = 0
      z &+= foo 4, 5, 6
      z &+= foo 40, 50, 60
      z
      CRYSTAL
  end

  it "uses indexed macro variable with many keys" do
    run(<<-CRYSTAL).to_i.should eq(4 + 5 + 6)
      macro foo(*elems)
        {% for elem, i in elems %}
          %var{elem, i} = {{elem}}
        {% end %}

        %total = 0
        {% for elem, i in elems %}
          %total &+= %var{elem, i}
        {% end %}
        %total
      end

      z = foo 4, 5, 6
      z
      CRYSTAL
  end

  it "codegens macro def with splat (#496)" do
    run(<<-CRYSTAL).to_i.should eq(6)
      class Foo
        def bar(*args) : Int32
          {{ @type }}
          args[0] &+ args[1] &+ args[2]
        end
      end

      Foo.new.bar(1, 2, 3)
      CRYSTAL
  end

  it "codegens macro def with default arg (similar to #496)" do
    run(<<-CRYSTAL).to_i.should eq(3)
      class Foo
        def bar(foo = 1) : Int32
          {{ @type }}
          foo &+ 2
        end
      end

      Foo.new.bar
      CRYSTAL
  end

  it "expands macro with default arg and splat (#784)" do
    run(<<-CRYSTAL).to_string.should eq("5")
      macro some_macro(a=5, *args)
        {{a.stringify}}
      end

      some_macro
      CRYSTAL
  end

  it "expands macro with default arg and splat (2) (#784)" do
    run(<<-CRYSTAL).to_string.should eq("1")
      macro some_macro(a=5, *args)
        {{a.stringify}}
      end

      some_macro 1, 2, 3, 4
      CRYSTAL
  end

  it "expands macro with default arg and splat (3) (#784)" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro some_macro(a=5, *args)
        {{args.size}}
      end

      some_macro 1, 2, 3, 4
      CRYSTAL
  end

  it "checks if macro expansion returns (#821)" do
    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(123)
      macro pass
        return 123
      end

      def me
        pass
        nil
      end

      me || 0
      CRYSTAL
  end

  it "passes #826" do
    run(<<-CRYSTAL).to_i.should eq(123)
      macro foo
        macro bar
          {{yield}}
        end
      end

      foo do
        123
      end

      bar
      CRYSTAL
  end

  it "declares constant in macro (#838)" do
    run(<<-CRYSTAL).to_i.should eq(123)
      macro foo
        {{yield}}
      end

      foo do
        CONST = 123
      end

      CONST
      CRYSTAL
  end

  it "errors if dynamic constant assignment after macro expansion" do
    assert_error <<-CRYSTAL, "dynamic constant assignment. Constants can only be declared at the top level or inside other types."
      macro foo
        X = 123
      end

      def bar
        foo
      end

      bar
      CRYSTAL
  end

  it "finds macro from virtual type" do
    run(<<-CRYSTAL).to_i.should eq(123)
      class Foo
        macro foo
          123
        end

        def bar
          foo
        end
      end

      class Bar < Foo
      end

      a = Pointer(Foo).malloc(1_u64)
      a.value = Foo.new
      a.value.bar
      CRYSTAL
  end

  it "expands macro with escaped quotes (#895)" do
    run(<<-CRYSTAL).to_string.should eq(%(hello"))
      macro foo(x)
        "{{x}}\\""
      end

      foo hello
      CRYSTAL
  end

  it "expands macro def with return (#1040)" do
    run(<<-CRYSTAL).to_i.should eq(123)
      class Foo
        def a : Int32
          {{ @type }}
          return 123
        end
      end

      Foo.new.a
      CRYSTAL
  end

  it "fixes empty types of macro expansions (#1379)" do
    run(<<-CRYSTAL).to_i.should eq(123)
      macro lala(exp)
        {{exp}}
      end

      def foo
        bar do
          return 123
        end
      end

      def bar
        return yield
      end

      lala foo
      CRYSTAL
  end

  it "expands macro as class method" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        macro bar
          1
        end
      end

      Foo.bar
      CRYSTAL
  end

  it "expands macro as class method and accesses @type" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Foo
        macro bar
          {{@type.stringify}}
        end
      end

      Foo.bar
      CRYSTAL
  end

  it "codegens macro with comment (bug) (#1396)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      macro my_macro
        # {{ 1 }}
        {{ 1 }}
      end

      my_macro
      CRYSTAL
  end

  it "correctly resolves constant inside block in macro def" do
    run(<<-CRYSTAL).to_i.should eq(123)
      def foo
        yield
      end

      class Foo
        Const = 123

        def self.bar : Int32
          {{ @type }}
          foo { Const }
        end
      end

      Foo.bar
      CRYSTAL
  end

  it "can access free variables" do
    run(<<-CRYSTAL).to_string.should eq("Int32")
      def foo(x : T) forall T
        {{ T.stringify }}
      end

      foo(1)
      CRYSTAL
  end

  it "types macro expansion bug (#1734)" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        def foo : Int32
          {{ @type }}
          1 || 2
        end
      end

      class Bar < Foo
      end

      x = true ? Foo.new : Bar.new
      x.foo
      CRYSTAL
  end

  it "expands Path with resolve method" do
    run(<<-CRYSTAL).to_i.should eq(1)
      CONST = 1

      macro id(path)
        {{path.resolve}}
      end

      id(CONST)
      CRYSTAL
  end

  it "can use macro inside array literal" do
    run(<<-CRYSTAL).to_i.should eq(42)
      require "prelude"

      macro foo
        42
      end

      ary = [foo]
      ary[0]
      CRYSTAL
  end

  it "can use macro inside hash literal" do
    run(<<-CRYSTAL).to_i.should eq(42)
      require "prelude"

      macro foo
        42
      end

      hash = {foo => foo}
      hash[foo]
      CRYSTAL
  end

  it "executes with named arguments for positional arg (1)" do
    run(<<-CRYSTAL).to_i.should eq(3)
      macro foo(x)
        {{x}} &+ 1
      end

      foo x: 2
      CRYSTAL
  end

  it "executes with named arguments for positional arg (2)" do
    run(<<-CRYSTAL).to_i.should eq(6)
      macro foo(x, y)
        {{x}} &+ {{y}} &+ 1
      end

      foo x: 2, y: 3
      CRYSTAL
  end

  it "executes with named arguments for positional arg (3)" do
    run(<<-CRYSTAL).to_i.should eq(6)
      class String
        def bytesize
          @bytesize
        end
      end

      macro foo(x, y)
        {{x}} &+ {{y}}.bytesize &+ 1
      end

      foo y: "foo", x: 2
      CRYSTAL
  end

  it "stringifies type without virtual marker" do
    run(<<-CRYSTAL).to_i.should eq(2)
      class Foo
        def foo_m : Int32
          {{ @type }}.foo
        end

        def self.foo
          1
        end
      end

      class Bar < Foo
        def self.foo
          2
        end
      end

      (Bar.new || Foo.new).foo_m
      CRYSTAL
  end

  it "uses tuple T in method with free vars" do
    run(<<-CRYSTAL).to_i.should eq(2)
      struct Tuple
        def foo(x : U) forall U
          {{T.size}}
        end
      end

      {1, 3}.foo(1)
      CRYSTAL
  end

  it "implicitly marks method as macro def when using @type" do
    run(<<-CRYSTAL).to_string.should eq("Bar")
      class Foo
        def method
          {{@type.stringify}}
        end
      end

      class Bar < Foo
      end

      Bar.new.as(Foo).method
      CRYSTAL
  end

  it "doesn't replace %s in string (#2178)" do
    run(<<-CRYSTAL).to_string.should eq("hello %s")
      {% begin %}
        "hello %s"
      {% end %}
      CRYSTAL
  end

  it "doesn't replace %q() (#2178)" do
    run(<<-CRYSTAL).to_string.should eq("hello")
      {% begin %}
        %q(hello)
      {% end %}
      CRYSTAL
  end

  it "replaces %s inside string inside interpolation (#2178)" do
    run(<<-CRYSTAL).to_string.should eq("hello world")
      require "prelude"

      {% begin %}
        %a = "world"
        "hello \#{ %a }"
      {% end %}
      CRYSTAL
  end

  it "replaces %s inside string inside interpolation, with braces (#2178)" do
    run(<<-CRYSTAL).to_string.should eq(%(hello [{"world", "world"}, "world"]))
      require "prelude"

      {% begin %}
        %a = "world"
        "hello \#{ [{ %a, %a }, %a] }"
      {% end %}
      CRYSTAL
  end

  it "retains original yield expression (#2923)" do
    run(<<-CRYSTAL).to_string.should eq("hi")
      macro foo
        def bar(baz)
          {{yield}}
        end
      end

      foo do
        baz
      end

      bar("hi")
      CRYSTAL
  end

  it "surrounds {{yield}} with begin/end" do
    run(<<-CRYSTAL).to_i.should eq(2)
      macro foo
        a = {{yield}}
      end

      a = 0
      foo do
        1
        2
      end
      a
      CRYSTAL
  end

  it "initializes instance var in macro" do
    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)
      class Foo
        {% begin %}
          @x = 1
        {% end %}
      end

      Foo.new.@x
      CRYSTAL
  end

  it "initializes class var in macro" do
    run(<<-CRYSTAL, inject_primitives: false).to_i.should eq(1)
      class Foo
        {% begin %}
          @@x = 1
        {% end %}

        def self.x
          @@x
        end
      end

      Foo.x
      CRYSTAL
  end

  it "expands @def in inline macro" do
    run(<<-CRYSTAL).to_string.should eq("foo")
      def foo
        {{@def.name.stringify}}
      end

      foo
      CRYSTAL
  end

  it "expands @def in macro" do
    run(<<-CRYSTAL).to_string.should eq("bar")
      macro foo
        {{@def.name.stringify}}
      end

      def bar
        foo
      end

      bar
      CRYSTAL
  end

  it "gets constant" do
    run(<<-CRYSTAL).to_i.should eq(42)
      class Foo
        Bar = 42
      end

      {{ Foo.constant("Bar") }}
      CRYSTAL
  end

  it "determines if overrides (false)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo
        def foo
          1
        end
      end

      class Bar < Foo
      end

      {{ Bar.overrides?(Foo, "foo") }}
      CRYSTAL
  end

  it "determines if overrides (true)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
        def foo
          1
        end
      end

      class Bar < Foo
        def foo
          2
        end
      end

      {{ Bar.overrides?(Foo, "foo") }}
      CRYSTAL
  end

  it "determines if overrides, through another class (true)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
        def foo
          1
        end
      end

      class Bar < Foo
        def foo
          2
        end
      end

      class Baz < Bar
      end

      {{ Baz.overrides?(Foo, "foo") }}
      CRYSTAL
  end

  it "determines if overrides, through module (true)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
        def foo
          1
        end
      end

      module Moo
        def foo
          2
        end
      end

      class Bar < Foo
        include Moo
      end

      class Baz < Bar
      end

      {{ Baz.overrides?(Foo, "foo") }}
      CRYSTAL
  end

  it "determines if overrides, with macro method (false)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo
        def foo
          {{ @type }}
        end
      end

      class Bar < Foo
      end

      (Foo.new || Bar.new).foo

      def x
        {{ Bar.overrides?(Foo, "foo") }}
      end

      x
      CRYSTAL
  end

  it "determines if method exists (true)" do
    run(<<-CRYSTAL).to_b.should be_true
      class Foo
        def foo
          42
        end
      end

      {{ Foo.has_method?(:foo) }}
      CRYSTAL
  end

  it "determines if method exists (false)" do
    run(<<-CRYSTAL).to_b.should be_false
      class Foo
        def foo
          42
        end
      end

      {{ Foo.has_method?(:bar) }}
      CRYSTAL
  end

  it "forwards file location" do
    run(<<-CRYSTAL, filename: "bar.cr").to_string.should eq("bar.cr")
      macro foo
        bar
      end

      macro bar(file = __FILE__)
        {{file}}
      end

      foo
      CRYSTAL
  end

  it "forwards dir location" do
    run(<<-CRYSTAL, filename: "somedir/bar.cr").to_string.should eq("somedir")
      macro foo
        bar
      end

      macro bar(dir = __DIR__)
        {{dir}}
      end

      foo
      CRYSTAL
  end

  it "forwards line number" do
    run(<<-CRYSTAL, filename: "somedir/bar.cr", inject_primitives: false).to_i.should eq(9)
      macro foo
        bar
      end

      macro bar(line = __LINE__)
        {{line}}
      end

      foo
      CRYSTAL
  end

  it "keeps line number with no block" do
    run(<<-CRYSTAL, filename: "somedir/bar.cr", inject_primitives: false).to_i.should eq(6)
      macro foo
        {{ yield }}
        __LINE__
      end

      foo
      CRYSTAL
  end

  it "keeps line number with a block" do
    run(<<-CRYSTAL, filename: "somedir/bar.cr", inject_primitives: false).to_i.should eq(6)
      macro foo
        {{ yield }}
        __LINE__
      end

      foo do
        1
      end
      CRYSTAL
  end

  it "resolves alias in macro" do
    run(<<-CRYSTAL).to_i.should eq(2)
      alias Foo = Int32 | String

      {{ Foo.union_types.size }}
      CRYSTAL
  end

  it "gets default value of instance variable" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        @x = 1

        def default
          {{@type.instance_vars.first.default_value}}
        end
      end

      Foo.new.default
      CRYSTAL
  end

  it "gets default value of instance variable of generic type" do
    run(<<-CRYSTAL).to_i.should eq(10)
      require "prelude"

      struct Int32
        def self.foo
          10
        end
      end

      class Foo(T)
        @x : T = T.foo

        def default
          {{@type.instance_vars.first.default_value}}
        end
      end

      Foo(Int32).new.default
      CRYSTAL
  end

  it "gets default value of instance variable of inherited type that also includes module" do
    run(<<-CRYSTAL).to_i.should eq(10)
      module Moo
        @moo = 10
      end

      class Foo
        include Moo

        def foo
          {{ @type.instance_vars.first.default_value }}
        end
      end

      class Bar < Foo
      end

      Bar.new.foo
      CRYSTAL
  end

  it "determines if variable has default value" do
    run(<<-CRYSTAL).to_i.should eq(1)
      class Foo
        @x = 1
        @y : Int32

        def initialize(@y)
        end

        def defaults
          {
            {{ @type.instance_vars.find { |i| i.name == "x" }.has_default_value? }},
            {{ @type.instance_vars.find { |i| i.name == "y" }.has_default_value? }},
          }
        end
      end

      x, y = Foo.new(2).defaults
      a = 0
      a &+= 1 if x
      a &+= 2 if y
      a
      CRYSTAL
  end

  it "expands macro with op assign inside assign (#5568)" do
    run(<<-CRYSTAL).to_string.chomp.should eq("2")
      require "prelude"

      macro expand
        {{ yield }}
      end

      def foo
        {:foo => 1}
      end

      expand do
        x = foo[:foo] += 1
        puts x
      end
      CRYSTAL
  end

  it "devirtualizes @type" do
    run(<<-CRYSTAL).to_string.should eq("Foo")
      class Foo
        def foo
          {{@type.id.stringify}}
        end
      end

      class Bar < Foo
      end

      (Foo.new || Bar.new).foo
      CRYSTAL
  end

  it "keeps heredoc contents inside macro" do
    run(<<-CRYSTAL).to_string.should eq("  %foo")
      macro foo
        <<-FOO
          %foo
        FOO
      end

      foo
      CRYSTAL
  end

  it "keeps heredoc contents with interpolation inside macro" do
    run(<<-CRYSTAL).to_string.should eq("  42")
      require "prelude"

      macro foo
        %foo = 42
        <<-FOO
          \#{ %foo }
        FOO
      end

      foo
      CRYSTAL
  end

  it "access to the program with @top_level" do
    run(<<-CRYSTAL).to_string.should eq("main")
      class Foo
        def bar
          {{@top_level.name.stringify}}
        end
      end

      Foo.new.bar
      CRYSTAL
  end

  it "responds correctly to has_constant? with @top_level" do
    run(<<-CRYSTAL).to_b.should be_true
      FOO = 1
      class Foo
        def bar
          {{@top_level.has_constant?("FOO")}}
        end
      end

      Foo.new.bar
      CRYSTAL
  end

  it "does block unpacking inside macro expression (#13707)" do
    run(<<-CRYSTAL).to_i.should eq(10)
      {% begin %}
        {%
          data = [{1, 2}, {3, 4}]
          value = 0
          data.each do |(k, v)|
            value += k
            value += v
          end
        %}
        {{ value }}
      {% end %}
      CRYSTAL
  end

  it "accepts compile-time flags" do
    run("{{ flag?(:foo) ? 1 : 0 }}", flags: %w(foo)).to_i.should eq(1)
    run("{{ flag?(:foo) ? 1 : 0 }}", Int32, flags: %w(foo)).should eq(1)
  end
end
