-
Notifications
You must be signed in to change notification settings - Fork 14
Resolve scopes of referenced types when merging trees #513
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
base: main
Are you sure you want to change the base?
Conversation
Merging trees where the rhs refers to a type only defined in the lhs gets confused about module namespaces.
Move monkeypatched compatible_with? and merg_with methods out of Node subclasses to TreeMerger. This makes more sense because those methods now need a reference to the merge tree index.
|
(Legal is reviewing the CLA currently.) |
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.
High level this solution makes sense to me. I can't immediately think of any issues but we'll test it. Let us know how CLA goes and also I triggered CI.
|
Thanks @KaanOzkan. I'm a bit stumped by the type checking error that remains, where I'm passing a Hash into a method using the keyword splat syntax: Got any tips on how to express this typing correctly? It does function correctly despite the error. |
|
@bcdanieldickison Yeah looks like the API is designed to be used with kwargs. We could either create another setter function that takes in a non splatted Hash type or use |
|
Thanks for the tip. I like using |
|
Legal tells me they should be able to take a look at the CLA later this week… ⏳ |
KaanOzkan
left a comment
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 tried to test the performance of this feature but ran into errors. I'd like to make sure it doesn't effect RBI generation too negatively.
| right_sigs.each do |sig| | ||
| left_sigs << sig unless left_sigs.include?(sig) | ||
| end | ||
| left.sigs = left_sigs |
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.
Mutating in a ? method doesn't feel right but maybe it's okay since it's simpler than alternatives.
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.
Yeah I wasn't a fan of this either. Maybe the method can instead return :compatible (Symbol) rather than a boolean, and that way the linter won't demand the method be named with ?.
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.
Lmk what you think of 3d2efed
And add a test
At least without creating an artificial tree that wasn’t parsed from rbi text.
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 don't think this is the right approach, we're shoving way too much logic into the merging and I'd prefer for it to stay as simple as possible: purely syntactic, no name resolution.
I think your previous approach was better but I would tweak it a bit:
- Provide a flatten rewriter in
rbilike you attempted in #509 - Let Tapioca apply the flatten rewriter onto the imported RBI file
- Merge the flattened RBIs together
That way we limit the complexity of names handling to the flatten rewriter and we do not need to touch the merge rewriter at all.
|
Thanks for the review @Morriar. Unfortunately, I don't think any flavor of purely syntactic merging is feasible. I wrote about it in a comment when closing the other ticket, but the gist is that non-fully-qualified names can only be resolved by actually looking it up in the scope chain. E.g. when given this in the rhs: module Foo
class Bar
# could be ::Foo::Bar::Baz, ::Foo::Baz, or ::Baz
sig { returns(Baz) }
def baz; end
end
endit's ambiguous whether class Foo::Bar
# can only be ::Foo::Bar::Baz or ::Baz
sig { returns(Baz) }
def baz; end
endIt we add the constraint that all type references must be defined in the same rbi tree, then I think a flatten rewriter can unambiguously resolve these types to fully-qualified names by only referencing each individual tree. Alas the Stripe gem rbi doesn't follow this convention—not sure if it's reasonable to say it's a bug with the exported rbi from the gem. I am curious how others are able to effectively use the Stripe gem with typings. |
|
CLA has been signed. Can I get another look at this PR wrt the last comment, please? |
|
@bcdanieldickison sorry for the wait. After some internal discussion, this approach of resolving scopes by walking the chain was deemed too complex. We also think that the usage of nested namespaces in As a result, we think a better solution would be to change the RBI Generator The generator appears to be private to Stripe, but what do you think about bringing this up in the stripe-ruby repository with this PR and Shopify/tapioca#2248 as context? Thank you for all your investigation and the fix efforts, sorry again for the delay. |
|
@KaanOzkan no problem. Yes, I think it's reasonable to ask Stripe to generate their exported rbi without nesting, using fully scoped names. We have an open issue that includes this problem stripe/stripe-ruby#1577 so I'll forward this comment along there. It'll probably be a good idea to explicitly disallow nested scoping in rbi files to avoid this problem elsewhere. I'm not sure if this should be at the rbi level or in tapioca, but would need to be a soft deprecation initially. |
|
@bcdanieldickison Thank you!
I think displaying a warning will be useful but we do want to generate the RBI even if we can't import definitions from the Alternatively, we could have a simpler "approximate" solution in tapioca that looks at a subset of the exported RBI, do a string comparison and output a warning if it observed nested namespacing in that sample. |
This is another attempt to fix tapioca's rbi incorrectly merging of the Stripe gem, as described here: Shopify/tapioca#2248. Previous attempt: #509
Problem
When merging two trees that refer to the same scope using nested vs namespaced syntax, the merge algorithm was treating those as different scopes. For example:
The two definitions of
Foo::Bazwere considered incompatible due to mismatched superclasses even though they technically both refer toFoo::Bar. Note 1: Additionally, in the case of the Stripe SDK, the superclass in right hand tree (the gem's exported rbi) could refer to a type only defined in the left hand tree (tapioca's generated rbi).This Solution
Wherever there's a type reference in the right hand tree during a merge, it is looked up in the left hand tree using rules mimicking Ruby's semantics. This is applied to the following places where types can be referenced:
The output then uses fully-qualified names for those references.
Note that this makes the merge asymetric: the left-hand tree is expected to contain all the types that could be referenced by either tree.
TODO
For further improvement, consider merging
Attrand same-namedMethod. In the case of Stripe SDK, tapioca generates methods from source but the exported rbi defined attr accessors, which should be merged (along with their signatures). This is implemented in a second PR here: bandcampdotcom#2