summaryrefslogtreecommitdiff
path: root/lib/prism/translation
diff options
context:
space:
mode:
authorStan Lo <stan.lo@shopify.com>2024-04-02 21:41:40 +0100
committergit <svn-admin@ruby-lang.org>2024-04-12 13:55:35 +0000
commit0924ff2d390084922fbcebada20b968fa3e8238c (patch)
tree312413d4a145a8bac53ee358d34351225af61b65 /lib/prism/translation
parentabd05c848f437405e10410ded2a3d666e1b9bba5 (diff)
[ruby/prism] Fix parser translation's heredoc whitespace calculation
Given this example: ```rb <<~HEREDOC #{x} HEREDOC ``` Both the parser gem and Prism's translation layer would generate the following AST: ``` s(:dstr, s(:begin, s(:int, 1)), s(:str, " a\n")) ``` However, the parser gem inserts a empty string node into this node's location, like: ``` <Parser::Source::Map::Heredoc:0x0000000104ce73b8 @expression=#<Parser::Source::Range (string) 0...10>, @heredoc_body=#<Parser::Source::Range (string) 11...20>, @heredoc_end=#<Parser::Source::Range (string) 20...27>, @node=s(:dstr, s(:str, ""), s(:begin, s(:int, 1)), s(:str, " a\n"))> ``` This is required to calculate the correct whitespace for the heredoc body. We need to adjust the translation layer to account for this. With this fix, we also won't need to ignore the tilde heredoc fixture anymore. https://github.com/ruby/prism/commit/e7372e3ba5
Diffstat (limited to 'lib/prism/translation')
-rw-r--r--lib/prism/translation/parser/compiler.rb29
1 files changed, 28 insertions, 1 deletions
diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb
index 0b1893cade..9437589623 100644
--- a/lib/prism/translation/parser/compiler.rb
+++ b/lib/prism/translation/parser/compiler.rb
@@ -947,8 +947,35 @@ module Prism
def visit_interpolated_string_node(node)
if node.heredoc?
children, closing = visit_heredoc(node)
+ opening = token(node.opening_loc)
+
+ start_offset = node.opening_loc.end_offset + 1
+ end_offset = node.parts.first.location.start_offset
+
+ # In the below case, the offsets should be the same:
+ #
+ # <<~HEREDOC
+ # a #{b}
+ # HEREDOC
+ #
+ # But in this case, the end_offset would be greater than the start_offset:
+ #
+ # <<~HEREDOC
+ # #{b}
+ # HEREDOC
+ #
+ # So we need to make sure the result node's heredoc range is correct, without updating the children
+ result = if start_offset < end_offset
+ # We need to add a padding string to ensure that the heredoc has correct range for its body
+ padding_string_node = builder.string_internal(["", srange_offsets(start_offset, end_offset)])
+ node_with_correct_location = builder.string_compose(opening, [padding_string_node, *children], closing)
+ # But the padding string should not be included in the final AST, so we need to update the result's children
+ node_with_correct_location.updated(:dstr, children)
+ else
+ builder.string_compose(opening, children, closing)
+ end
- return builder.string_compose(token(node.opening_loc), children, closing)
+ return result
end
parts = if node.parts.one? { |part| part.type == :string_node }