require "../../spec_helper" describe "Visibility modifiers" do it "disallows invoking private method" do assert_error <<-CRYSTAL, "private method 'foo' called for Foo" class Foo private def foo 1 end end Foo.new.foo CRYSTAL end it "allows setting visibility modifier to macro" do assert_error <<-CRYSTAL, "private method 'foo' called for Foo" class Object macro x def foo end end end class Foo private x end Foo.new.foo CRYSTAL end it "allows setting visibility modifier to macro that generates many methods (1)" do assert_error <<-CRYSTAL, "private method 'foo' called for Foo" class Object macro x def foo end def bar end end end class Foo private x end Foo.new.foo CRYSTAL end it "allows setting visibility modifier to macro that generates many methods (2)" do assert_error <<-CRYSTAL, "private method 'bar' called for Foo" class Object macro x def foo end def bar end end end class Foo private x end Foo.new.bar CRYSTAL end it "allows invoking private method from the same class" do assert_type(<<-CRYSTAL) { int32 } class Foo private def foo 1 end def bar self.foo end end Foo.new.bar CRYSTAL end it "allows invoking protected method from the same class" do assert_type(<<-CRYSTAL) { int32 } class Foo protected def foo 1 end def bar self.foo end end Foo.new.bar CRYSTAL end it "allows invoking protected method from subclass" do assert_type(<<-CRYSTAL) { int32 } class Foo protected def foo 1 end end class Bar < Foo def bar Foo.new.foo end end Bar.new.bar CRYSTAL end it "allows invoking protected method from subclass (2)" do assert_type(<<-CRYSTAL) { int32 } class Foo protected def foo 1 end end class Bar < Foo end class Baz < Foo def baz Bar.new.foo end end Baz.new.baz CRYSTAL end it "errors if invoking protected method from top-level" do assert_error <<-CRYSTAL, "protected method 'foo' called for Foo" class Foo protected def foo end end Foo.new.foo CRYSTAL end it "errors if invoking protected method from non-subclass" do assert_error <<-CRYSTAL, "protected method 'foo' called for Foo" class Foo protected def foo end end class Bar def bar Foo.new.foo end end Bar.new.bar CRYSTAL end it "errors if invoking protected method from non-subclass, generated with macro that generates a macro" do assert_error <<-CRYSTAL, "protected method 'foo' called for Foo" class Object macro y def foo end end macro x y end end class Foo protected x end Foo.new.foo CRYSTAL end it "errors if applying visibility modifier to non-def or non-call" do assert_error <<-CRYSTAL, "can't apply visibility modifier" class Foo private 1 end CRYSTAL end it "allows invoking protected from instance to class" do assert_type(<<-CRYSTAL) { int32 } class Foo def instance_foo Foo.class_foo end protected def self.class_foo 1 end end Foo.new.instance_foo CRYSTAL end it "automatically makes initialize be protected" do assert_error <<-CRYSTAL, "protected method 'initialize' called for Foo" class Foo def initialize(x) end end foo = Foo.new(1) foo.initialize(2) CRYSTAL end it "allows invoking private setter with self" do assert_type(<<-CRYSTAL) { int32 } class Foo private def x=(x) x end def foo self.x = 1 end end Foo.new.foo CRYSTAL end it "allows invoking protected method from namespace to namespaced type" do assert_type(<<-CRYSTAL) { int32 } class Foo def foo Bar.new.bar end class Bar protected def bar 1 end end end Foo.new.foo CRYSTAL end it "allows invoking protected method from namespaced type to namespace" do assert_type(<<-CRYSTAL) { int32 } class Foo protected def foo 1 end class Bar def bar Foo.new.foo end end end Foo::Bar.new.bar CRYSTAL end it "allows invoking protected method between types in the same namespace" do assert_type(<<-CRYSTAL) { int32 } module NS1 class NS2 class Foo def foo Bar.new.bar end end class Bar protected def bar 1 end end end end NS1::NS2::Foo.new.foo CRYSTAL end it "allows invoking protected method between types in the same namespace when inheriting" do assert_type(<<-CRYSTAL) { int32 } module NS1 class NS2 class Foo def foo Bar.new.bar end end class Bar protected def bar 1 end end end end class MyFoo < NS1::NS2::Foo end MyFoo.new.foo CRYSTAL end it "allows invoking protected method from virtual type" do assert_type(<<-CRYSTAL) { union_of int32, float64 } abstract class Foo def foo bar end end class Bar < Foo protected def bar 1 end end class Baz < Foo protected def bar 1.5 end end (Bar.new || Baz.new).foo CRYSTAL end it "allows calling protected method from nested generic class (1)" do assert_type(<<-CRYSTAL) { int32 } class Foo class Bar(U) def bar Foo.new.foo end end protected def foo 1 end end Foo::Bar(Int32).new.bar CRYSTAL end it "allows calling protected method from nested generic class (2)" do assert_type(<<-CRYSTAL) { int32 } class Foo(T) class Bar(U) def bar Foo(Int32).new.foo end end protected def foo 1 end end Foo::Bar(Int32).new.bar CRYSTAL end it "gives correct error on unknown call (#2838)" do assert_error <<-CRYSTAL, "undefined local variable or method 'foo'" private foo CRYSTAL end it "defines protected initialize (#7501)" do assert_error <<-CRYSTAL, "protected method 'new' called for Foo.class" class Foo protected def initialize end end Foo.new CRYSTAL end it "handles virtual types (#8561)" do assert_no_errors <<-CRYSTAL module Namespace class Foo protected def foo end end class Bar def bar Foo.new.foo end end class Baz < Bar def initialize @bar = Bar.new end def bar @bar.bar end end end Namespace::Baz.new.bar CRYSTAL end end