-
Notifications
You must be signed in to change notification settings - Fork 6
Fix missing/invalid class/module names #816
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: desugar-remaining
Are you sure you want to change the base?
Conversation
5087195 to
0eedc09
Compare
82e1ca2 to
e7d29a1
Compare
| def foo; end # Parsed as constant path | ||
| def bar; end # Parsed as body |
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.
It differs from the legacy parser, but I think we can make this even by better by not discarding def foo, but instead making it the start of the body
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, possible, but I'm not sure it's always obvious that this node should be part of the body. These are the examples from Prism:
class (return)::A; end
^~~~~~ unexpected void value expressionclass 0.X end
^~~ unexpected constant path after `class`; class/module name must be CONSTANTmodule Parent module end
^~~~~~ unexpected constant path after `module`; class/module name must be CONSTANT
^~~ unexpected 'end', assuming it is closing the parent module definitionI think here it doesn't necessarily make sense to take that unexpected node and shove it into the body. What do you think?
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.
Hmm idk, it seems like there's time when it's the obvious thing to do, and these cases where it's clearly wrong. Not sure which is more prevalent.
One of the potential benefits is minimizing churn in the tree, which then means Sorbet's incremental type checking has to do less. E.g. if you remove the class name, the first member will disappear temporarily until you write the new name in. In the mean time, you could cause a lot of type checking errors. Though now that I think about it, you probably had those errors anyway, from the class being temporarily undefined.
IDK, we can revisit this later as a potential improvement. The original pipeline dropped more than Prism, so we're already "ahead" anyway
parser/prism/Translator.cc
Outdated
|
|
||
| auto name = desugar(classNode->constant_path); | ||
| ast::ExpressionPtr name; | ||
| if (classNode->constant_path == nullptr || |
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 think this is only repeated twice, but could you still extract a shared function anyway? I imagine this will always want to be kept the same between the class and module cases
| def foo; end # Parsed as constant path | ||
| def bar; end # Parsed as body |
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.
Hmm idk, it seems like there's time when it's the obvious thing to do, and these cases where it's clearly wrong. Not sure which is more prevalent.
One of the potential benefits is minimizing churn in the tree, which then means Sorbet's incremental type checking has to do less. E.g. if you remove the class name, the first member will disappear temporarily until you write the new name in. In the mean time, you could cause a lot of type checking errors. Though now that I think about it, you probably had those errors anyway, from the class being temporarily undefined.
IDK, we can revisit this later as a potential improvement. The original pipeline dropped more than Prism, so we're already "ahead" anyway
d96acf3 to
7f35d5d
Compare
d24b10e to
33d252f
Compare
This ensures running Sorbet with the Prism parser mode does not crash on missing or invalid class/model constant paths. See the regression tests for examples that were crashing without the fix.
The fix is to check
constant_pathexists and is one of the expected node types (ConstantReadNodeorConstantPathNode). If it doesn't exist or is an unexpected node type, we construct a "constant missing" constant to use in its place.For example:
In this case,
constant_pathis not set and the body is empty.Here
constant_pathis aDefNodeand the body is empty.Here
constant_pathis aDefNodeand the body contains the secondDefNodeinside aStatementsNode.The behavior is different to the original parser, which throws away much more of the class/module. Here we're able to keep most of it and only throw away whatever node was incorrectly in the
constant_pathfield.See here for more information. I closed that issue because I thought it was no longer crashing, but upon testing I can see that it still is.