55require_relative "type_toolkit/dsl"
66require_relative "type_toolkit/method_def_recorder"
77require_relative "type_toolkit/interface"
8+ require_relative "type_toolkit/abstract_class"
89
910# Raised when a call is made to an abstract method that never had a real implementation.
1011AbstractMethodNotImplementedError = Class . new ( Exception ) # rubocop:disable Lint/InheritException
@@ -29,29 +30,20 @@ def __register_abstract_method(method_name) # :nodoc:
2930 # TODO: change semantics to only return methods that are actually abstract and unimplemented.
3031 #: (?bool) -> Array[Symbol]
3132 def abstract_instance_methods ( include_super = true )
32- #: self as Module[HasAbstractMethods]
33-
34- result = @__abstract_methods
35-
36- return result . to_a unless include_super
37-
38- if defined? ( super ) && ( super_abstract_methods = super )
39- if result
40- result . merge ( super_abstract_methods )
41- else
42- result = super_abstract_methods
43- end
44- end
45-
46- abstract_methods_in_interfaces = included_modules . flat_map do |m |
47- m . is_a? ( HasAbstractMethods ) ? m . abstract_instance_methods : [ ]
48- end
49-
50- if abstract_methods_in_interfaces . any?
51- if result &.any?
52- result . merge ( abstract_methods_in_interfaces )
53- else
54- result = abstract_methods_in_interfaces
33+ #: self as Module[top] & HasAbstractMethods
34+
35+ result = @__abstract_methods #: Set[Symbol]?
36+
37+ if include_super
38+ ancestors . each do |m |
39+ methods = m . instance_variable_get ( :@__abstract_methods )
40+ if methods &.any?
41+ if result
42+ result . merge ( methods )
43+ else
44+ result = methods
45+ end
46+ end
5547 end
5648 end
5749
@@ -73,11 +65,16 @@ def abstract_instance_methods(include_super = true)
7365 #
7466 #: (Symbol) -> bool
7567 def abstract_method_declared? ( method_name )
76- #: self as Module[untyped]
77-
78- @__abstract_methods &.include? ( method_name ) ||
79- included_modules . any? { |m | m . is_a? ( HasAbstractMethods ) && m . abstract_method_declared? ( method_name ) } ||
80- ( defined? ( super ) && super )
68+ #: self as Module[top]
69+
70+ # FIXME: Allocating the `ancestors` array is not great.
71+ # I tried a recursive approach, but that didn't quite work.
72+ # There is only one implementation of `abstract_method_declared?` in the ancestor chain, so there is no `super` to call.
73+ # This method always checked the ivar of the current class, which might not be set. What we actually want is to
74+ # walk up the ancestor chain, and check the ivar of each ancestor.
75+ ancestors . any? do |m |
76+ m . instance_variable_get ( :@__abstract_methods ) &.include? ( method_name )
77+ end
8178 end
8279
8380 # Returns true if the given method is abstract, and has not been implemented.
@@ -168,5 +165,24 @@ def make_interface!(mod)
168165 mod . extend ( TypeToolkit ::MethodDefRecorder )
169166 mod . extend ( TypeToolkit ::HasAbstractMethods )
170167 end
168+
169+ def make_abstract! ( mod )
170+ case mod
171+ when Class
172+ # We need to save the original implementation of `new`, so we can restore it on the subclasses later.
173+ mod . singleton_class . alias_method ( :__original_new_impl , :new )
174+
175+ mod . extend ( TypeToolkit ::AbstractClass )
176+ mod . extend ( TypeToolkit ::DSL )
177+ mod . extend ( TypeToolkit ::MethodDefRecorder )
178+ mod . extend ( TypeToolkit ::HasAbstractMethods )
179+
180+ mod . include ( TypeToolkit ::AbstractInstanceMethodReceiver )
181+ when Module
182+ raise NotImplementedError , "Abstract modules are not implemented yet."
183+ else
184+ raise TypeError , "Expected a Class or Module, got #{ mod . class } ."
185+ end
186+ end
171187 end
172188end
0 commit comments