require "../../spec_helper" describe "Semantic: enum" do it "types enum" do assert_type(<<-CRYSTAL) { types["Foo"] } enum Foo A = 1 end Foo::A CRYSTAL end it "types enum value" do assert_type(<<-CRYSTAL) { int32 } enum Foo A = 1 end Foo::A.value CRYSTAL end it "disallows implicit conversion of int to enum" do assert_error <<-CRYSTAL, "expected argument #1 to 'foo' to be Foo, not Int32" enum Foo A = 1 end def foo(x : Foo) end foo 1 CRYSTAL end it "finds method in enum type" do assert_type(<<-CRYSTAL) { int32 } struct Enum def foo 1 end end enum Foo A = 1 end Foo::A.foo CRYSTAL end it "finds class method in enum type" do assert_type(<<-CRYSTAL) { int32 } struct Enum def self.foo 1 end end enum Foo A = 1 end Foo.foo CRYSTAL end it "errors if using a name twice" do assert_error <<-CRYSTAL, "enum 'Foo' already contains a member named 'A'" enum Foo A A end CRYSTAL end it "creates enum from value" do assert_type(<<-CRYSTAL) { types["Foo"] } enum Foo A B end Foo.new(1) CRYSTAL end it "defines method on enum" do assert_type(<<-CRYSTAL) { int32 } enum Foo A B def foo 1 end end Foo::A.foo CRYSTAL end it "defines class method on enum" do assert_type(<<-CRYSTAL) { int32 } enum Foo A B def self.foo 1 end end Foo.foo CRYSTAL end it "reopens an enum" do assert_type(<<-CRYSTAL) { int32 } enum Foo A B end enum Foo def foo 1 end end Foo::A.foo CRYSTAL end it "errors if reopen but not enum" do assert_error <<-CRYSTAL, "Foo is not a enum, it's a class" class Foo end enum Foo A B end CRYSTAL end it "errors if reopen and tries to define constant" do assert_error <<-CRYSTAL, "can't reopen enum and add more constants to it" enum Foo A B end enum Foo C end CRYSTAL end it "has None value when defined as @[Flags]" do assert_type(<<-CRYSTAL) { int32 } @[Flags] enum Foo A B end Foo::None.value CRYSTAL end it "has All value when defined as @[Flags]" do assert_type(<<-CRYSTAL) { int32 } @[Flags] enum Foo A B end Foo::All.value CRYSTAL end it "doesn't break assigned values in enum flags when a member has value 0 (#5767)" do result = semantic(<<-CRYSTAL) @[Flags] enum Foo OtherNone = 0 Bar Baz end CRYSTAL enum_type = result.program.types["Foo"].as(EnumType) enum_type.types["OtherNone"].as(Const).value.should eq(NumberLiteral.new("0", :i32)) enum_type.types["Bar"].as(Const).value.should eq(NumberLiteral.new("1", :i32)) enum_type.types["Baz"].as(Const).value.should eq(NumberLiteral.new("2", :i32)) end it "disallows redefining None to non-0 for @[Flags] enum" do assert_error <<-CRYSTAL, "flags enum can't redefine None member to non-0" @[Flags] enum Foo None = 42 Dummy end CRYSTAL assert_error <<-CRYSTAL, "flags enum can't redefine None member to non-0" @[Flags] enum Foo None # 1 Dummy end CRYSTAL end it "allows redefining None to 0 for @[Flags] enum" do assert_type(<<-CRYSTAL) { int32 } @[Flags] enum Foo None = 0 Dummy end Foo::None.value CRYSTAL end it "disallows All value for @[Flags] enum" do assert_error <<-CRYSTAL, "flags enum can't redefine All member" @[Flags] enum Foo All = 50 end CRYSTAL end it "doesn't error when defining a non-flags enum with None or All" do assert_type(<<-CRYSTAL) { int32 } enum Foo None All = 50 end Foo::None.value CRYSTAL end it "doesn't error when defining a flags enum in a lib with None or All" do assert_type(<<-CRYSTAL) { int32 } lib Lib @[Flags] enum Foo None Dummy All = 50 end end Lib::Foo::None.value CRYSTAL end it "doesn't error when defining a method for an enum with flags" do assert_type(<<-CRYSTAL) { types["Foo"] } @[Flags] enum Foo A B def foo self end end Foo::A.foo CRYSTAL end it "allows class vars in enum" do assert_type(<<-CRYSTAL) { int32 } enum Foo A @@class_var = 1 def self.class_var @@class_var end end Foo.class_var CRYSTAL end it "errors if invoking private enum method" do assert_error <<-CRYSTAL, "private method 'foo' called for Foo" enum Foo A private def foo 1 end end Foo::A.foo CRYSTAL end it "errors if enum value is too big for type (#678)" do assert_error <<-CRYSTAL, "invalid Int32: 2147486719" enum Foo A = 2147486719 end CRYSTAL end it "reports arithmetic overflow in enum value expression (#11746)" do assert_error <<-CRYSTAL, "Arithmetic overflow", inject_primitives: true enum Foo : UInt64 A = 0_u64 - 1_u64 end CRYSTAL end it "errors if using instance var inside enum (#991)" do assert_error <<-CRYSTAL, "can't use instance variables inside enums (at enum Foo)" enum Foo A def meth puts @value end end Foo::A.meth CRYSTAL end it "marks as flags with base type (#2185)" do result = semantic(<<-CRYSTAL) @[Flags] enum SomeFacts : UInt8 AppleLover PearLover CoolDude end SomeFacts::AppleLover CRYSTAL enum_type = result.program.types["SomeFacts"].as(EnumType) annotation_type = result.program.types["Flags"].as(AnnotationType) enum_type.annotation(annotation_type).should_not be_nil end it "reopens enum without base type (1)" do assert_no_errors <<-CRYSTAL enum Foo X end enum Foo end CRYSTAL end it "reopens enum without base type (2)" do assert_no_errors <<-CRYSTAL enum Foo : UInt8 X end enum Foo end CRYSTAL end it "reopens enum with same base type (1)" do assert_no_errors <<-CRYSTAL enum Foo X end enum Foo : Int32 end CRYSTAL end it "reopens enum with same base type (2)" do assert_no_errors <<-CRYSTAL enum Foo : UInt8 X end enum Foo : UInt8 end CRYSTAL end it "errors if reopening enum with different base type (1)" do assert_error <<-CRYSTAL, "enum Foo's base type is Int32, not UInt8" enum Foo X end enum Foo : UInt8 end CRYSTAL end it "errors if reopening enum with different base type (2)" do assert_error <<-CRYSTAL, "enum Foo's base type is UInt8, not UInt16" enum Foo : UInt8 X end enum Foo : UInt16 end CRYSTAL end it "can use macro expression inside enum" do assert_type(<<-CRYSTAL) { types["Foo"] } enum Foo {{ "A".id }} end Foo::A CRYSTAL end it "can use macro for inside enum" do assert_type(<<-CRYSTAL) { types["Foo"] } enum Foo {% for name in %w(A B C) %} {{name.id}} {% end %} end Foo::A CRYSTAL end it "errors if inheriting Enum (#3592)" do assert_error <<-CRYSTAL, "can't inherit Enum. Use the enum keyword to define enums" struct Foo < Enum end CRYSTAL end it "errors on enum without members (#3447)" do assert_error <<-CRYSTAL, "enum Foo must have at least one member" enum Foo end CRYSTAL assert_error <<-CRYSTAL, "enum Foo must have at least one member" @[Flags] enum Foo None = 0 end CRYSTAL end it "errors if declaring type inside enum (#3127)" do assert_error <<-CRYSTAL, "can't declare type inside enum Foo" enum Foo A end class Foo::Bar end CRYSTAL end it "errors if declaring type inside enum, nested (#3127)" do assert_error <<-CRYSTAL, "can't declare type inside enum" enum Foo A end class Foo::Bar::Baz end CRYSTAL end it "attaches annotation to enum method (#6690)" do result = semantic(<<-CRYSTAL) enum Foo X @[AlwaysInline] def bar end end CRYSTAL method = result.program.types["Foo"].lookup_first_def("bar", block: false).not_nil! method.always_inline?.should be_true end it "errors if defining initialize in Enum (#7238)" do assert_error <<-CRYSTAL, "enums can't define an `initialize` method, try using `def self.new`" enum Foo FOO = 1 def initialize end end CRYSTAL end it "can redefine Enum.new" do assert_type(<<-CRYSTAL) { string } enum Foo FOO = 1 def self.new(x : Int32) "hello" end end Foo.new(1) CRYSTAL end it "gives error on enum overflow" do assert_error <<-CRYSTAL, "value of enum member V129 would overflow the base type Int8" enum Foo : Int8 #{Array.new(129) { |i| "V#{i + 1}" }.join "\n"} end CRYSTAL end it "gives error on flags enum overflow" do assert_error <<-CRYSTAL, "value of enum member V9 would overflow the base type UInt8" @[Flags] enum Foo : UInt8 #{Array.new(9) { |i| "V#{i + 1}" }.join "\n"} end CRYSTAL end it "gives error on enum overflow after a member with value" do assert_error <<-CRYSTAL, "value of enum member B would overflow the base type Int32" enum Foo A = 0x7FFFFFFF B end CRYSTAL end it "gives error on signed flags enum overflow after a member with value" do assert_error <<-CRYSTAL, "value of enum member B would overflow the base type Int32" @[Flags] enum Foo A = 0x40000000 B end CRYSTAL end it "gives error on unsigned flags enum overflow after a member with value" do assert_error <<-CRYSTAL, "value of enum member B would overflow the base type UInt32" @[Flags] enum Foo : UInt32 A = 0x80000000 B end CRYSTAL end it "doesn't overflow when going from negative to zero (#7874)" do assert_no_errors <<-CRYSTAL enum Nums Zero = -2 One Two end CRYSTAL end it "doesn't overflow on flags member (#7877)" do assert_no_errors <<-CRYSTAL @[Flags] enum Filter A = 1 << 29 B end CRYSTAL end it "doesn't visit enum members generated by macros twice (#10104)" do result = semantic(<<-CRYSTAL) enum Foo A = 1 {% begin %} def foo end {% end %} end CRYSTAL a_def = result.program.types["Foo"].lookup_defs("foo").first a_def.previous.should be_nil end it "adds docs to helper methods" do result = top_level_semantic <<-CRYSTAL, wants_doc: true enum Foo # These are the docs for `Bar` Bar = 1 end CRYSTAL a_defs = result.program.types["Foo"].lookup_defs("bar?") a_defs.first.doc.should eq("Returns `true` if this enum value equals `Bar`") end it "marks helper methods with `:nodoc:` if the member is `:nodoc:`" do result = top_level_semantic <<-CRYSTAL, wants_doc: true enum Foo # :nodoc: Bar = 1 end CRYSTAL a_defs = result.program.types["Foo"].lookup_defs("bar?") a_defs.first.doc.should eq(":nodoc:") end end