-
Notifications
You must be signed in to change notification settings - Fork 558
Methods defined on anonymous classes cause typing errors #3609
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
Comments
@paracycle This is definitely related to #3505. I'll take a look to try and figure out what's going on. |
So, what's going on is that classes defined by
To:
The bare case:
Is not rewritten, instead we end up with a symbol table where
I just compiled a version of sorbet locally without the private-call check, and as expected (based on the symbol table above) it unfortunately doesn't error out:
So, the bug is actually that |
Oh wow, great find @bss, thanks for looking into this. We have some instances of bare anonymous class definitions in our codebase, that is mostly to do with some mock objects in test case. That means, those have been infecting our @jez @elliottt Is there a way we can fix this so that bare anonymous class definitions don't end up defining their methods on |
I think that the solution @bss suggests of getting rid of those nodes from the AST is the only way to support this in Sorbet. We can't stick the methods on anonymous class definitions in a rewriter pass because all the rewriter passes operate in parallel, so there's no easy way to coordinate to ensure that anonymous class names are globally unqiue. |
Do we need them to be globally unique and stable? If not, we could just generate some GUIDs during the rewriting process. Not sure about the performance implications of doing that though. |
So actually I thought about it longer and getting unique IDs is actually easy because the pass is parallelized by file, so we could get unique IDs using a scheme like But then the problem becomes that we can't put these class defs inside method defs, because that is a syntax error in Ruby. |
Yes, everything that ever ends up in the SymbolTable must be stable, otherwise LSP won't work. (The whole assumption of LSP mode is that if you run Sorbet twice on an identical codebase that the symbol table doesn't change.) |
The other thing: almost every time that x = 0
y = Class.new {p x} is ok, but x = 0
class AnonymousClass_1_1_y
p x
end is an error, because the local variable Sorbet only has one notion of class definitions—the So the naive approach to fix this would be:
but if that approach works in a rewriter pass, you may as well just do that yourself in the code verbatim. The reason why people don't do that is almost always because it's not possible, and they're accessing variables from the closure, which no amount of desugaring will help with. All of which is to say: Sorbet is somewhat fundamentally incompatible with anonymous classes, and we recommend avoiding them. There are creative solutions that we could add to Sorbet, for example:
but I don't feel like implementing those so if someone else wants to pick one I'll review it. |
From @alexevanczuk on the Sorbet slack:
|
I've noticed that this issue occurs with any #7111 mentions this, but it was closed as a duplicate. However, I think it points out that the affected scope is a bit larger than this thread would imply. # typed: true
class Dynamic
def define(&block)
end
end
dynamic = Dynamic.new.define do
def hi
end
end
class Greeter
end
greet = Greeter.new
greet.hi
In the code I'm working on, this tends to occur in rspec tests when defining test helper methods. Using |
Input
→ View on sorbet.run
Observed output
Expected behavior
I would have expected
match?
to not have been registered as a private instance method ofObject
, but instead that I got this error:This seems to be related to the changes in #3505 /cc @bss
The text was updated successfully, but these errors were encountered: