Closed amomchilov closed 1 week ago
We decided on option 2, because keeping track of a parsing context will be necessary anyway, once we start removing our translation layer (related: #240). Have a look at against DesugarContext
for comparison.
Implemented in #295.
This code crashes Sorbet, and block us from type-checking the monolith:
It should raise a
SyntaxError
at runtime, but it shouldn't crash Sorbet statically.stacktrace
``` + exec test/pipeline_test_runner --single_test=test/prism_regression/dynamic_constant_assignment.rb --parser=prism [doctest] doctest version is "2.4.9" [doctest] run with "--help" for options Parsing with prism [2024-10-22 17:02:41.276] [fatalFallback] [error] Exception::raise(): ast/verifier/Verifier.cc:29 enforced condition methodDepth == 0 has failed: Found constant definition inside method definition [2024-10-22 17:02:41.705] [fatalFallback] [error] Backtrace: sorbet::ast::VerifierWalker::postTransformAssign(sorbet::core::Context, sorbet::ast::Assign const&) (in pipeline_test_runner) + 272 decltype(auto) sorbet::ast::MapFunctions<(sorbet::ast::TreeMapKind)2>::CALL_MEMBER_impl_postTransformAssignSorbet's handling
Sorbet's parser handles by diagnosing a syntax error early on, and rewriting the dynamic constant assignment into a fake write to a dummy local variable called
dynamic-const-assign
(core::Names::dynamicConstAssign()
).https://github.com/Shopify/sorbet/blob/97d7fffcb75facccf75306efe8d3850be5bab6d3/parser/Builder.cc#L348-L354
This is the expected parse tree:
Thus, by the time this tree reaches the Verifier, there's no more dynamic constant assignment, and this
ENFORCE
doesn't trip.https://github.com/Shopify/sorbet/blob/97d7fffcb75facccf75306efe8d3850be5bab6d3/ast/verifier/Verifier.cc#L27-L31
How Prism models this
Prism doesn't model this in the AST itself, but in the
Prism::Result#errors
field.Solution ideas
1. Add a flag to Prism's
ConstantWriteNode
andConstantTargetNode
We could define new a new flag that's tagged on the relevant node:
This would let Sorbet's translator inspect the node directly for this information, without needing any other parser/lexing context.
2. Track lexical scopes in
Prism::Translator
There's no direct way to infer the currently lexical scope for a Prism AST node, because the nodes only have one-way references "down" the tree, and no upwards references to their parent.
So if we want the lexical information, we'll need to track it ourselves. We can add state to
Prism::Translator
that we update any time we enter/exit methods and classes, to keep track of which lexical scope we're in.3. Correlate to the Parser errors
We can pass the Parser
errors
array to the Translator. Every time we do a constant assignment, we can search the errors (by the location information) to see if that assignment is a dynamic constant assignment.