-
Notifications
You must be signed in to change notification settings - Fork 133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Filter out mixins that weren't performed in current gem #1012
Conversation
b9aca82
to
50ada09
Compare
@@ -24,23 +24,25 @@ def on_scope(event) | |||
# The way we identify these "foreign constants" is by asking the mixin tracker which | |||
# constants have mixed in the current module that we are handling. We add all the | |||
# constants that we discover to the pipeline to be processed. | |||
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |constant, location| | |||
next unless mixed_in_by_gem?(location) | |||
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |_, location_info| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes in this file are a result of the refactor to constants_with_mixin
. Functionality is not changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be:
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |_, location_info| | |
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each_value do |location_info| |
instead?
lib/tapioca/gem/listeners/mixins.rb
Outdated
# Some extend mixins are actually includes or prepend on the singleton class. | ||
# If an extend mixin is not found in the tracker, these lines search for a prepend | ||
# or include on the singleton class and use the resulting file as the mixin location. | ||
if mixin_location.nil? && mixin_type == Runtime::Trackers::Mixin::Type::Extend |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes, extension mixins are actually tracked in the mixin tracker as prepends or includes to the singleton class. If the mixin is not found under the Runtime::Trackers::Mixin::Type::Extend
type, these lines search the mixin tracker for a prepend or include on the singleton class.
This feels a little bit hacky to me...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something we can rectify at mixin registration time, I guess. If a mixin is being prepended/included into a singleton class, then we can record that as an extend on the attached class at that time.
# actionpack RBI should have nothing in it about `TypedParameters` | ||
# refute_includes(actionpack_rbi, "TypedParameters") | ||
refute_includes(actionpack_rbi, "TypedParameters") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assertion passes now 🎉
lib/tapioca/gem/listeners/mixins.rb
Outdated
mods | ||
.select do |mod| | ||
name = @pipeline.name_of(mod) | ||
|
||
name && !filtered_mixin?(name) | ||
end | ||
.map do |mod| | ||
mixin_location = mixin_location(constant, mod, mixin_type) | ||
|
||
# Some extend mixins are actually includes or prepend on the singleton class. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Some extend mixins are actually includes or prepend on the singleton class. | |
# Some extend mixins are actually includes or prepends on the singleton class. |
lib/tapioca/gem/listeners/mixins.rb
Outdated
location: String, | ||
).returns(T::Boolean) | ||
end | ||
def mixed_in_by_gem?(location) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method is mixed_in_by_gem?
but works with a location
only, which doesn't seem strictly related to mixins, should we rename it location_in_gem?
?
Or maybe mixed_in_by_gem?
should take a constant?
lib/tapioca/gem/listeners/mixins.rb
Outdated
sig do | ||
params( | ||
location: String, | ||
).returns(T::Boolean) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's inline signatures < 120 chars please 🙏
@@ -24,23 +24,25 @@ def on_scope(event) | |||
# The way we identify these "foreign constants" is by asking the mixin tracker which | |||
# constants have mixed in the current module that we are handling. We add all the | |||
# constants that we discover to the pipeline to be processed. | |||
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |constant, location| | |||
next unless mixed_in_by_gem?(location) | |||
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |_, location_info| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be:
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each do |_, location_info| | |
Runtime::Trackers::Mixin.constants_with_mixin(mixin).each_value do |location_info| |
instead?
lib/tapioca/gem/listeners/mixins.rb
Outdated
).returns(T.nilable(String)) | ||
end | ||
def mixin_location(constant, mixin, mixin_type) | ||
T.must(Runtime::Trackers::Mixin.constants_with_mixin(mixin)[mixin_type])[constant] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use dig
instead?
T.must(Runtime::Trackers::Mixin.constants_with_mixin(mixin)[mixin_type])[constant] | |
Runtime::Trackers::Mixin.constants_with_mixin(mixin).dig(mixin_type, constant) |
lib/tapioca/gem/listeners/mixins.rb
Outdated
T.must(mixin_locations[Runtime::Trackers::Mixin::Type::Include])[singleton_class] || | ||
T.must(mixin_locations[Runtime::Trackers::Mixin::Type::Prepend])[singleton_class] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, dig
would be much nicer:
T.must(mixin_locations[Runtime::Trackers::Mixin::Type::Include])[singleton_class] || | |
T.must(mixin_locations[Runtime::Trackers::Mixin::Type::Prepend])[singleton_class] | |
mixin_locations.dig(Runtime::Trackers::Mixin::Type::Include, singleton_class) || | |
mixin_locations.dig(Runtime::Trackers::Mixin::Type::Prepend, singleton_class) |
lib/tapioca/gem/listeners/mixins.rb
Outdated
# Some extend mixins are actually includes or prepend on the singleton class. | ||
# If an extend mixin is not found in the tracker, these lines search for a prepend | ||
# or include on the singleton class and use the resulting file as the mixin location. | ||
if mixin_location.nil? && mixin_type == Runtime::Trackers::Mixin::Type::Extend |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is something we can rectify at mixin registration time, I guess. If a mixin is being prepended/included into a singleton class, then we can record that as an extend on the attached class at that time.
Refactor the Trackers::Mixin#constants_with_mixin method to keep track of the type of each mixin. This will be used to filter mixins that were not performed in the current gem.
Uses `Trackers::Mixin` to find the location of each mixin and filter out any that did not occur in the current gem to avoid generating unnecessary RBI.
50ada09
to
445d9ef
Compare
@@ -168,6 +168,17 @@ def required_from_location | |||
|
|||
required_location.absolute_path || "" | |||
end | |||
|
|||
sig { params(constant: Module).returns(T.nilable(String)) } | |||
def constant_name_from_singleton_class(constant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved these two methods into reflection because I had to use them in Trackers::Mixin
to find the attached class based on a singleton class.
sig { params(constant: Module).returns(T.nilable(String)) } | ||
def constant_name_from_singleton_class(constant) | ||
constant.to_s.match("#<Class:(.+)>")&.captures&.first | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method was moved into Runtime::Reflection
# attached class. Registering the mixin as an extend on the attached class ensures that | ||
# this mixin can be found whether searching for an include/prepend on the singleton class | ||
# or an extend on the attached class. | ||
def register_extend_on_attached_class(constant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This functionality was moved from Listeners::Mixins
so that an include/prepend on a singleton class can also be found as an extend on the attached class.
.gsub(/^\s+include ::Minitest::Expectations\s/, "") | ||
.gsub(/^\s+include ::JSON::Ext::Generator::GeneratorMethods::Object\s/, "") | ||
.gsub(/^\s+include ::PP::ObjectMixin\s/, "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@paracycle Is this gsub you were talking about?
Closes #890
Motivation
This is the culmination of all the dynamic mixin work!
Currently, when RBIs are generated for mixins, tapioca doesn't take into account whether a mixin was performed in the current gem or in another gem. This results in strange scenarios where one gem will re-open a class to perform a mixin, and then that mixin is reflected in another gem's RBI. We should only generate RBI for mixins that are performed in the current gem.
Implementation
This PR can be read commit-by-commit.
mixin_locations_for
method, which is no longer used, and modify theconstants_with_mixin
method to keep track of mixin locations.constant_name_from_singleton_class
method intoRuntime::Reflection
so it can be shared with the Module extension coming from the mixin tracker.mixin_locations_for
method to skip any mixins that weren't performed in the current gem.Tests
Modified existing tests to remove mixins that should be filtered out. Un-commented two lines in a cli test that now pass because of these changes!
Tophatting 🎩
Can generate gem RBIs on Shopify core with these changes.