diff --git a/lib/type_toolkit/dsl.rb b/lib/type_toolkit/dsl.rb index 7dd6d98..5074d0d 100644 --- a/lib/type_toolkit/dsl.rb +++ b/lib/type_toolkit/dsl.rb @@ -26,6 +26,12 @@ def abstract(method_name) MSG end + if is_singleton_method && is_a?(TypeToolkit::Interface) + singleton_class.remove_method(method_name) + raise TypeError, + "Interfaces can't declare abstract singleton methods. Define instance methods instead." + end + # The `method_owner` is the class whose method table stores the abstract method. # # Example: diff --git a/lib/type_toolkit/interface.rb b/lib/type_toolkit/interface.rb index a548f8b..b5d8bbb 100644 --- a/lib/type_toolkit/interface.rb +++ b/lib/type_toolkit/interface.rb @@ -18,6 +18,18 @@ def make_interface!(mod) mod.extend(TypeToolkit::DSL) mod.extend(TypeToolkit::MethodDefRecorder) mod.extend(TypeToolkit::HasAbstractMethods) + mod.singleton_class.extend(TypeToolkit::InterfaceSingletonClass) + end + end + + module InterfaceSingletonClass + #: (Symbol) -> bot + def abstract(method_name) + #: self as Module[top] + + remove_method(method_name) if method_defined?(method_name) || private_method_defined?(method_name) + raise TypeError, + "Interfaces can't declare abstract singleton methods. Define instance methods instead." end end diff --git a/spec/interface_spec.rb b/spec/interface_spec.rb index 24ddc54..74e5a50 100644 --- a/spec/interface_spec.rb +++ b/spec/interface_spec.rb @@ -64,6 +64,38 @@ def m2 = "PartiallyInheritsItsImpl#m2" end end + it "raises when declaring an abstract singleton method" do + mod = Module.new + + error = assert_raises(TypeError) do + mod.module_eval do + interface! + + abstract def self.the_method_name = assert_never_called! + end + end + + assert_match("Interfaces can't declare abstract singleton methods.", error.message) + refute_respond_to mod, :the_method_name + end + + it "raises when declaring an abstract method on the interface singleton class" do + mod = Module.new + + error = assert_raises(TypeError) do + mod.module_eval do + interface! + + class << self + abstract def the_method_name = assert_never_called! + end + end + end + + assert_match("Interfaces can't declare abstract singleton methods.", error.message) + refute_respond_to mod, :the_method_name + end + it "raises when it sees a different method name than what MethodDefRecorder recorded" do error = assert_raises(RuntimeError) do Module.new do