Skip to content

Commit

Permalink
WIP: use a MethodDefinition tracker to properly attribute methods d…
Browse files Browse the repository at this point in the history
…efinitions that are triggered by one gem but defined in another
  • Loading branch information
egiurleo committed Aug 9, 2024
1 parent aa2cfea commit 4cd8847
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 3 deletions.
2 changes: 1 addition & 1 deletion lib/tapioca/gem/listeners/methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public
signature = signature_of!(method)
method = T.let(signature.method, UnboundMethod) if signature

case @pipeline.method_in_gem?(method)
case @pipeline.method_in_gem?(constant, method)
when nil
# This means that this is a C-method. Thus, we want to
# skip it only if the constant is an ignored one, since
Expand Down
18 changes: 16 additions & 2 deletions lib/tapioca/gem/pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ def constant_in_gem?(name)
gem.contains_path?(source_file)
end

sig { params(method: UnboundMethod).returns(T.nilable(T::Boolean)) }
def method_in_gem?(method)
sig { params(constant: Module, method: UnboundMethod).returns(T.nilable(T::Boolean)) }
def method_in_gem?(constant, method)
source_file, _ = method.source_location
# Ruby 3.3 adds automatic definition of source location for evals if
# `file` and `line` arguments are not provided. This results in the source
Expand All @@ -163,6 +163,20 @@ def method_in_gem?(method)
# If the source location of the method is "(eval)", err on the side of caution and include the method.
return true if source_file == "(eval)"

return true if @gem.contains_path?(source_file)

method_definitions = Tapioca::Runtime::Trackers::MethodDefinition.method_definitions_for(constant)

source_file = method_definitions[method.name]

# If the source location of the method isn't available, signal that by returning nil.
return unless source_file

# If the source location of the method is "(eval)", err on the side of caution and include the method.
return true if source_file == "(eval)"

source_file = source_file&.sub(EVAL_SOURCE_FILE_PATTERN, "\\1")

@gem.contains_path?(source_file)
end

Expand Down
1 change: 1 addition & 0 deletions lib/tapioca/runtime/trackers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,4 @@ def register_tracker(tracker)
require "tapioca/runtime/trackers/constant_definition"
require "tapioca/runtime/trackers/autoload"
require "tapioca/runtime/trackers/required_ancestor"
require "tapioca/runtime/trackers/method_definition"
43 changes: 43 additions & 0 deletions lib/tapioca/runtime/trackers/method_definition.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# typed: true
# frozen_string_literal: true

module Tapioca
module Runtime
module Trackers
module MethodDefinition
extend Tracker
extend T::Sig

@method_definitions = {}.compare_by_identity

class << self
extend T::Sig

sig { params(constant: Module, method_name: Symbol).void }
def register(constant, method_name)
return unless enabled?

@method_definitions[constant] ||= {}
@method_definitions[constant][method_name] = Reflection.resolve_loc(caller_locations)
end

sig { params(constant: Module).returns(T::Hash[Symbol, T::Array[T::Hash[Symbol, String]]]) }
def method_definitions_for(constant)
@method_definitions[constant] || {}
end
end
end
end
end
end

class Module
prepend(Module.new do
def method_added(method_name)
Tapioca::Runtime::Trackers::MethodDefinition.register(self, method_name)
super
end
end)
end

# TODO: are there other methods I have to override here?

0 comments on commit 4cd8847

Please sign in to comment.