Skip to content
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

More effectively determine method ownership for RBI generation #896

Merged
merged 1 commit into from Apr 13, 2022

Conversation

egiurleo
Copy link
Contributor

@egiurleo egiurleo commented Apr 12, 2022

Motivation

This issue was discovered by @paracycle while investigating #890, but it is technically unrelated to mixin generation.

While generating RBIs for a method, tapioca implements a check to determine which constant "owns" or defines that method. We only want to generate RBIs for a method on the module or class that defines it, not any that just happen to inherit it.

Historically, we have been checking the method.owner and verifying that it matches the class/module for which we are generating RBIs. However, this check misses one particular case: when a method is also defined on a prepended module, it will always appear as if it belongs to the prepended module, even when it is also defined on the class that prepends the module.

This means that tapioca will not generate RBI for that method on the class that defines it.

Implementation

This code was originally written by @paracycle on commit 80faabc. I've just cleaned it up and moved it to the correct spot.

Instead of just checking the method owner, this implementation walks up the ancestor chain via the super_method method, checking if any of the super methods are owned by the constant. If that is the case, that means the constant also defines the method, and we should generate RBIs for this method on the constant.

Tests

I have added a test which fails without the changes but passes with the changes included.

Tophatting

I tested this change by generating gem and dsl RBIs on Shopify core using this branch of tapioca and verifying that the change did not introduce any new typing issues.

@egiurleo egiurleo marked this pull request as ready for review Apr 12, 2022
@egiurleo egiurleo requested a review from Apr 12, 2022
@egiurleo egiurleo self-assigned this Apr 12, 2022
# Widen the type of `method` to be nilable
method = T.let(method, T.nilable(UnboundMethod))

loop do
Copy link
Member

@vinistock vinistock Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would while method here work and allow us to remove the break?

#
# This method implements a better way of checking whether a constant defines a method.
# It walks up the ancestor tree via the `super_method` method; if any of the super
# methods are owned by the constant, it means that the constant declares the method.
Copy link
Collaborator

@Morriar Morriar Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have this comment on top of the method as documentation rather than inside?

add_ruby_file("foo.rb", <<~RUBY)
module Foo
def bar
super
Copy link
Collaborator

@Morriar Morriar Apr 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an example that does not call super? 🙏

@@ -66,7 +66,7 @@ def compile_directly_owned_methods(tree, module_name, mod, for_visibility = [:pu
end
def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public.new)
return unless method
return unless method.owner == constant
return unless method_defined_by_constant?(method, constant)
Copy link
Member

@paracycle paracycle Apr 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that I had named this method but looking at it again, it feels like method_owned_by_constant? would be a more appropriate name, since we are doing ownerships checks.

@egiurleo egiurleo force-pushed the method-defined-by-constant branch from 5b4bf06 to a0ab5d9 Compare Apr 13, 2022
Copy link
Collaborator

@Morriar Morriar left a comment

Amazing thanks! 🎉

@egiurleo egiurleo merged commit 8564ea7 into main Apr 13, 2022
6 checks passed
@egiurleo egiurleo deleted the method-defined-by-constant branch Apr 13, 2022
@paracycle paracycle added the enhancement label Apr 29, 2022
@shopify-shipit shopify-shipit bot temporarily deployed to production May 13, 2022 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants