Skip to content

Commit e58606d

Browse files
committed
Fix parser translator ast for heredoc with written newlines
Heredocs that contain "\\n" don't start a new string node.
1 parent a5dc98b commit e58606d

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

lib/prism/translation/parser/compiler.rb

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2079,27 +2079,49 @@ def visit_heredoc(node)
20792079

20802080
escaped_lengths = []
20812081
normalized_lengths = []
2082+
# Keeps track of where an unescaped line should start a new token. An unescaped
2083+
# \n would otherwise be indistinguishable from the actual newline at the end of
2084+
# of the line. The parser gem only emits a new string node at "real" newlines,
2085+
# line line continuations don't start a new node as well.
2086+
do_next_tokens = []
20822087

20832088
if node.opening.end_with?("'")
20842089
escaped.each do |line|
20852090
escaped_lengths << line.bytesize
20862091
normalized_lengths << chomped_bytesize(line)
2092+
do_next_tokens << true
20872093
end
20882094
else
20892095
escaped
2090-
.chunk_while { |before, after| before.match?(/(?<!\\)\\\r?\n$/) }
2096+
.chunk_while { |before, after| before[/(\\*)\r?\n$/, 1].length.odd? }
20912097
.each do |lines|
20922098
escaped_lengths << lines.sum(&:bytesize)
20932099
normalized_lengths << lines.sum { |line| chomped_bytesize(line) }
2100+
unescaped_lines_count = lines.sum do |line|
2101+
line.scan(/(\\*)n/).count { |(backslashes)| backslashes.length.odd? }
2102+
end
2103+
do_next_tokens.push(*Array.new(unescaped_lines_count + 1, false))
2104+
do_next_tokens[-1] = true
20942105
end
20952106
end
20962107

20972108
start_offset = part.location.start_offset
2098-
2099-
unescaped.map.with_index do |unescaped_line, index|
2100-
inner_part = builder.string_internal([unescaped_line, srange_offsets(start_offset, start_offset + normalized_lengths.fetch(index, 0))])
2101-
start_offset += escaped_lengths.fetch(index, 0)
2102-
inner_part
2109+
current_line = +""
2110+
current_normalized_length = 0
2111+
2112+
unescaped.filter_map.with_index do |unescaped_line, index|
2113+
current_line << unescaped_line
2114+
current_normalized_length += normalized_lengths.fetch(index, 0)
2115+
2116+
if do_next_tokens[index]
2117+
inner_part = builder.string_internal([current_line, srange_offsets(start_offset, start_offset + current_normalized_length)])
2118+
start_offset += escaped_lengths.fetch(index, 0)
2119+
current_line = +""
2120+
current_normalized_length = 0
2121+
inner_part
2122+
else
2123+
nil
2124+
end
21032125
end
21042126
else
21052127
[visit(part)]

test/prism/ruby/parser_test.rb

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,12 @@ class ParserTest < TestCase
6565
"seattlerb/heredoc_with_extra_carriage_returns_windows.txt",
6666
"seattlerb/heredoc_with_only_carriage_returns_windows.txt",
6767
"seattlerb/heredoc_with_only_carriage_returns.txt",
68-
"seattlerb/parse_line_heredoc_hardnewline.txt",
6968
"seattlerb/pctW_lineno.txt",
7069
"seattlerb/regexp_esc_C_slash.txt",
7170
"unparser/corpus/literal/literal.txt",
7271
"unparser/corpus/semantic/dstr.txt",
7372
"whitequark/dedenting_interpolating_heredoc_fake_line_continuation.txt",
7473
"whitequark/parser_slash_slash_n_escaping_in_literals.txt",
75-
"whitequark/ruby_bug_11989.txt"
7674
]
7775

7876
# Not sure why these files are failing on JRuby, but skipping them for now.

0 commit comments

Comments
 (0)