lib/tapioca/runtime/reflection.rb in tapioca-0.10.2 vs lib/tapioca/runtime/reflection.rb in tapioca-0.10.3
- old
+ new
@@ -181,8 +181,63 @@
singleton_class_of(T.cast(klass, Module)) == singleton_class
end
T.cast(result, Module)
end
+
+ sig { params(constant: Module).returns(T::Set[String]) }
+ def file_candidates_for(constant)
+ relevant_methods_for(constant).filter_map do |method|
+ method.source_location&.first
+ end.to_set
+ end
+
+ private
+
+ sig { params(constant: Module).returns(T::Array[UnboundMethod]) }
+ def relevant_methods_for(constant)
+ methods = methods_for(constant).select(&:source_location)
+ .reject { |x| method_defined_by_forwardable_module?(x) }
+
+ return methods unless methods.empty?
+
+ constants_of(constant).flat_map do |const_name|
+ if (mod = child_module_for_parent_with_name(constant, const_name.to_s))
+ relevant_methods_for(mod)
+ else
+ []
+ end
+ end
+ end
+
+ sig { params(constant: Module).returns(T::Array[UnboundMethod]) }
+ def methods_for(constant)
+ modules = [constant, singleton_class_of(constant)]
+ method_list_methods = [
+ PUBLIC_INSTANCE_METHODS_METHOD,
+ PROTECTED_INSTANCE_METHODS_METHOD,
+ PRIVATE_INSTANCE_METHODS_METHOD,
+ ]
+
+ modules.product(method_list_methods).flat_map do |mod, method_list_method|
+ method_list_method.bind_call(mod, false).map { |name| mod.instance_method(name) }
+ end
+ end
+
+ sig { params(parent: Module, name: String).returns(T.nilable(Module)) }
+ def child_module_for_parent_with_name(parent, name)
+ return if parent.autoload?(name)
+
+ child = constantize(name, inherit: true, namespace: parent)
+ return unless Module === child
+ return unless name_of(child) == "#{name_of(parent)}::#{name}"
+
+ child
+ end
+
+ sig { params(method: UnboundMethod).returns(T::Boolean) }
+ def method_defined_by_forwardable_module?(method)
+ method.source_location&.first == Object.const_source_location(:Forwardable)&.first
+ end
end
end
end