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

Translate rescue node #277

Merged
merged 4 commits into from
Oct 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
source "https://rubygems.org"
12 changes: 12 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
GEM
remote: https://rubygems.org/
specs:

PLATFORMS
arm64-darwin-23
ruby

DEPENDENCIES

BUNDLED WITH
2.5.17
63 changes: 60 additions & 3 deletions parser/prism/Translator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,17 @@ unique_ptr<parser::Node> Translator::translate(pm_node_t *node) {

NodeVec statements;

if (auto prismStatements = beginNode->statements; prismStatements != nullptr) {
statements = translateMulti(prismStatements->body);
if (beginNode->rescue_clause != nullptr) {
// Handle `begin ... rescue ... end` and `begin ... rescue ... else ... end`
auto bodyNode = translateStatements(beginNode->statements, true);
auto elseNode = translate(reinterpret_cast<pm_node_t *>(beginNode->else_clause));
auto beginRescueNode = translateRescue(reinterpret_cast<pm_rescue_node *>(beginNode->rescue_clause),
move(bodyNode), move(elseNode));

statements.emplace_back(move(beginRescueNode));
} else if (beginNode->statements != nullptr) {
// Handle `begin ... end`
statements = translateMulti(beginNode->statements->body);
}

return make_unique<parser::Kwbegin>(location, move(statements));
Expand Down Expand Up @@ -850,6 +859,9 @@ unique_ptr<parser::Node> Translator::translate(pm_node_t *node) {

return make_unique<parser::Rescue>(location, move(body), move(cases), nullptr);
}
case PM_RESCUE_NODE: {
unreachable("PM_RESCUE_NODE is handled separately in translateRescue, see its docs for details.");
}
case PM_REST_PARAMETER_NODE: { // A rest parameter, like `def foo(*rest)`
auto restParamNode = reinterpret_cast<pm_rest_parameter_node *>(node);
core::LocOffsets nameLoc;
Expand Down Expand Up @@ -1041,7 +1053,6 @@ unique_ptr<parser::Node> Translator::translate(pm_node_t *node) {
case PM_NUMBERED_REFERENCE_READ_NODE:
case PM_POST_EXECUTION_NODE:
case PM_PRE_EXECUTION_NODE:
case PM_RESCUE_NODE:
case PM_SHAREABLE_CONSTANT_NODE:
case PM_SCOPE_NODE:
auto type_id = PM_NODE_TYPE(node);
Expand Down Expand Up @@ -1337,6 +1348,52 @@ unique_ptr<parser::Node> Translator::translateCallWithBlock(pm_block_node *prism
return make_unique<parser::Block>(sendNode->loc, move(sendNode), move(blockParametersNode), move(body));
}

// Prism represents a `begin ... rescue ... end` construct using a `pm_begin_node` that may contain:
// - `statements`: the code before the `rescue` clauses (the main body).
// - `rescue_clause`: a `pm_rescue_node` representing the first `rescue` clause.
// - `else_clause`: an optional `pm_else_node` representing the `else` clause.
//
// Each `pm_rescue_node` represents a single `rescue` clause and is linked to subsequent `rescue` clauses via its
// `consequent` pointer. Each `pm_rescue_node` contains:
// - `exceptions`: the exceptions to rescue (e.g., `RuntimeError`).
// - `reference`: the exception variable (e.g., `=> e`).
// - `statements`: the body of the rescue clause.
//
// In contrast, Sorbet's legacy parser represents the same construct using a `Rescue` node that contains:
// - `body`: the code before the `rescue` clauses (the main body).
// - `rescue`: a list of `Resbody` nodes, each representing a `rescue` clause.
// - `else_`: an optional node representing the `else` clause.
//
// This function and the PM_BEGIN_NODE case translate between the two representations by processing the `pm_rescue_node`
// (and its linked `subsequent` nodes) and assembling the corresponding `Rescue` and `Resbody` nodes in Sorbet's AST.
unique_ptr<parser::Node> Translator::translateRescue(pm_rescue_node *prismRescueNode, unique_ptr<parser::Node> bodyNode,
unique_ptr<parser::Node> elseNode) {
NodeVec rescueBodies;

// Each `rescue` clause generates a `Resbody` node, which is a child of the `Rescue` node.
for (pm_rescue_node *currentRescueNode = prismRescueNode; currentRescueNode != nullptr;
currentRescueNode = currentRescueNode->consequent) {
// Translate the exception variable (e.g. the `=> e` in `rescue => e`)
auto var = translate(currentRescueNode->reference);

// Translate the body of the rescue clause
auto rescueBody = translateStatements(currentRescueNode->statements, true);

// Translate the exceptions being rescued (e.g., `RuntimeError` in `rescue RuntimeError`)
auto exceptions = translateMulti(currentRescueNode->exceptions);
auto exceptionsArray =
exceptions.empty()
? nullptr
: make_unique<parser::Array>(translateLoc(currentRescueNode->base.location), move(exceptions));

rescueBodies.emplace_back(make_unique<parser::Resbody>(translateLoc(currentRescueNode->base.location),
move(exceptionsArray), move(var), move(rescueBody)));
}

// The `Rescue` node combines the main body, the rescue clauses, and the else clause.
return make_unique<parser::Rescue>(bodyNode->loc, move(bodyNode), move(rescueBodies), move(elseNode));
}

// Translates the given Prism Statements Node into a `parser::Begin` node or an inlined `parser::Node`.
// @param inlineIfSingle If enabled and there's 1 child node, we skip the `Begin` and just return the one `parser::Node`
unique_ptr<parser::Node> Translator::translateStatements(pm_statements_node *stmtsNode, bool inlineIfSingle) {
Expand Down
3 changes: 3 additions & 0 deletions parser/prism/Translator.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class Translator final {
bool isUsedForKeywordArguments);
std::unique_ptr<parser::Node> translateCallWithBlock(pm_block_node *prismBlockNode,
std::unique_ptr<parser::Node> sendNode);
std::unique_ptr<parser::Node> translateRescue(pm_rescue_node *prismRescueNode,
std::unique_ptr<parser::Node> beginNode,
std::unique_ptr<parser::Node> elseNode);
std::unique_ptr<parser::Node> translateStatements(pm_statements_node *stmtsNode, bool inlineIfSingle);

template <typename SorbetNode> std::unique_ptr<SorbetNode> translateSimpleKeyword(pm_node_t *untypedNode);
Expand Down
Loading
Loading