Skip to content

Commit 28cdc1a

Browse files
jesse-shopifyamomchilov
authored andcommitted
Align string / HEREDOC locations
1 parent cef3ac8 commit 28cdc1a

File tree

1 file changed

+55
-3
lines changed

1 file changed

+55
-3
lines changed

parser/prism/Translator.cc

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,9 @@ ast::ExpressionPtr Translator::desugarSendOpAssign(pm_node_t *untypedNode) {
12721272
template <typename PrismAssignmentNode, ast::UnresolvedIdent::Kind IdentKind>
12731273
ast::ExpressionPtr Translator::desugarAssignment(pm_node_t *untypedNode) {
12741274
auto node = down_cast<PrismAssignmentNode>(untypedNode);
1275-
auto location = translateLoc(untypedNode->location);
1275+
// For heredocs, Prism's location only includes the opening tag (e.g., "x = <<~EOF").
1276+
// Extend to include the full heredoc body by using endLoc() on the value.
1277+
auto location = translateLoc(startLoc(untypedNode), endLoc(node->value));
12761278
auto rhs = desugar(node->value);
12771279

12781280
ast::ExpressionPtr lhs;
@@ -1374,6 +1376,7 @@ ast::ExpressionPtr Translator::desugarMethodCall(ast::ExpressionPtr receiver, co
13741376

13751377
// For heredoc xstrings in arguments, Prism's call node location only includes the opening.
13761378
// Extend the location to include the full heredoc using endLoc(), which handles heredocs.
1379+
// NOTE: Only xstrings extend the call location; regular string heredocs do not.
13771380
if (!prismArgs.empty()) {
13781381
auto lastArg = prismArgs.back();
13791382
if (PM_NODE_TYPE_P(lastArg, PM_X_STRING_NODE) || PM_NODE_TYPE_P(lastArg, PM_INTERPOLATED_X_STRING_NODE)) {
@@ -2970,8 +2973,11 @@ ast::ExpressionPtr Translator::desugar(pm_node_t *node) {
29702973
case PM_INTERPOLATED_STRING_NODE: { // An interpolated string like `"foo #{bar} baz"`
29712974
auto interpolatedStringNode = down_cast<pm_interpolated_string_node>(node);
29722975

2976+
// For heredocs, endLoc() extends to the closing delimiter.
2977+
auto strLoc = translateLoc(startLoc(node), endLoc(node));
2978+
29732979
// Desugar `"a #{b} c"` to `::Magic.<string-interpolate>("a ", b, " c")`
2974-
return desugarDString(location, interpolatedStringNode->parts);
2980+
return desugarDString(strLoc, interpolatedStringNode->parts);
29752981
}
29762982
case PM_INTERPOLATED_SYMBOL_NODE: { // A symbol like `:"a #{b} c"`
29772983
auto interpolatedSymbolNode = down_cast<pm_interpolated_symbol_node>(node);
@@ -3402,10 +3408,13 @@ ast::ExpressionPtr Translator::desugar(pm_node_t *node) {
34023408
case PM_STRING_NODE: { // A string literal, e.g. `"foo"`
34033409
auto strNode = down_cast<pm_string_node>(node);
34043410

3411+
// For heredocs, endLoc() extends to the closing delimiter.
3412+
auto strLoc = translateLoc(startLoc(node), endLoc(node));
3413+
34053414
auto unescaped = &strNode->unescaped;
34063415
auto content = ctx.state.enterNameUTF8(parser.extractString(unescaped));
34073416

3408-
return MK::String(location, content);
3417+
return MK::String(strLoc, content);
34093418
}
34103419
case PM_SUPER_NODE: { // A `super` call with explicit args, like `super()`, `super(a, b)`
34113420
// If there's no arguments (except a literal block argument), then it's a `PM_FORWARDING_SUPER_NODE`.
@@ -3617,6 +3626,7 @@ const uint8_t *endLoc(pm_node_t *anyNode) {
36173626
case PM_CALL_NODE: {
36183627
// For call nodes with heredoc xstring arguments, Prism's location only includes the opening.
36193628
// Extend to include the full heredoc by checking the last argument.
3629+
// NOTE: Only xstrings extend the call location; regular string heredocs do not.
36203630
auto *node = down_cast<pm_call_node>(anyNode);
36213631
if (node->arguments && node->arguments->arguments.size > 0) {
36223632
auto *lastArg = node->arguments->arguments.nodes[node->arguments->arguments.size - 1];
@@ -3627,6 +3637,23 @@ const uint8_t *endLoc(pm_node_t *anyNode) {
36273637
}
36283638
return anyNode->location.end;
36293639
}
3640+
case PM_STRING_NODE: {
3641+
// For heredoc strings, Prism's base.location only includes the opening delimiter.
3642+
// Use closing_loc to get the full heredoc span, excluding the trailing newline.
3643+
auto *node = down_cast<pm_string_node>(anyNode);
3644+
if (auto end = closingLocEnd(node->closing_loc)) {
3645+
return end;
3646+
}
3647+
return anyNode->location.end;
3648+
}
3649+
case PM_INTERPOLATED_STRING_NODE: {
3650+
// Same as PM_STRING_NODE - extend to closing delimiter for heredocs.
3651+
auto *node = down_cast<pm_interpolated_string_node>(anyNode);
3652+
if (auto end = closingLocEnd(node->closing_loc)) {
3653+
return end;
3654+
}
3655+
return anyNode->location.end;
3656+
}
36303657
case PM_X_STRING_NODE: {
36313658
// For heredoc xstrings, Prism's base.location only includes the opening delimiter.
36323659
// Use closing_loc to get the full heredoc span, excluding the trailing newline.
@@ -3644,6 +3671,31 @@ const uint8_t *endLoc(pm_node_t *anyNode) {
36443671
}
36453672
return anyNode->location.end;
36463673
}
3674+
case PM_LOCAL_VARIABLE_WRITE_NODE: {
3675+
// For assignments with heredoc values, extend to the heredoc closing delimiter.
3676+
auto *node = down_cast<pm_local_variable_write_node>(anyNode);
3677+
return endLoc(node->value);
3678+
}
3679+
case PM_INSTANCE_VARIABLE_WRITE_NODE: {
3680+
auto *node = down_cast<pm_instance_variable_write_node>(anyNode);
3681+
return endLoc(node->value);
3682+
}
3683+
case PM_CLASS_VARIABLE_WRITE_NODE: {
3684+
auto *node = down_cast<pm_class_variable_write_node>(anyNode);
3685+
return endLoc(node->value);
3686+
}
3687+
case PM_GLOBAL_VARIABLE_WRITE_NODE: {
3688+
auto *node = down_cast<pm_global_variable_write_node>(anyNode);
3689+
return endLoc(node->value);
3690+
}
3691+
case PM_CONSTANT_WRITE_NODE: {
3692+
auto *node = down_cast<pm_constant_write_node>(anyNode);
3693+
return endLoc(node->value);
3694+
}
3695+
case PM_CONSTANT_PATH_WRITE_NODE: {
3696+
auto *node = down_cast<pm_constant_path_write_node>(anyNode);
3697+
return endLoc(node->value);
3698+
}
36473699
default: {
36483700
return anyNode->location.end;
36493701
}

0 commit comments

Comments
 (0)