Skip to content

Commit 1192656

Browse files
committed
Fix missing newline on dstr footer
1 parent 10fb02d commit 1192656

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

lib/unparser/writer/dynamic_string.rb

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class DynamicString
2727
# mutant:disable
2828
def dispatch
2929
if heredoc?
30-
write(HEREDOC_HEADER)
30+
write(heredoc_header)
3131
buffer.push_heredoc(heredoc_body)
3232
elsif round_tripping_segmented_source
3333
write(round_tripping_segmented_source)
@@ -106,8 +106,34 @@ def heredoc_body
106106
end
107107
memoize :heredoc_body
108108

109+
def heredoc_header
110+
needs_chomp? ? "#{HEREDOC_HEADER}.chomp" : HEREDOC_HEADER
111+
end
112+
memoize :heredoc_header
113+
114+
def needs_chomp?
115+
# Check if the content naturally ends with a newline
116+
# If not, we need .chomp to maintain semantic equivalence
117+
!content_ends_with_newline?
118+
end
119+
memoize :needs_chomp?
120+
121+
def content_ends_with_newline?
122+
# The last child determines if the content ends with a newline
123+
return false if children.empty?
124+
125+
last_child = children.last
126+
case last_child.type
127+
when :str
128+
last_child.children.first.end_with?("\n")
129+
else
130+
false # Interpolations don't add newlines
131+
end
132+
end
133+
memoize :content_ends_with_newline?
134+
109135
def heredoc_source
110-
"#{HEREDOC_HEADER}\n#{heredoc_body}"
136+
"#{heredoc_header}\n#{heredoc_body}"
111137
end
112138
memoize :heredoc_source
113139

@@ -116,6 +142,7 @@ class Heredoc
116142

117143
def emit
118144
emit_heredoc_body
145+
write("\n") unless buffer.content.end_with?("\n")
119146
write(HEREDOC_FOOTER)
120147
end
121148

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
RSpec.describe Unparser::Writer::DynamicString do
6+
context 'when dynamic string doesn not end with newline' do
7+
let(:original_code) { "\"a\\nb\nc\#{1}d\"" }
8+
9+
it 'maintains semantic equivalence when evaluated' do
10+
original_ast = Unparser.parse(original_code)
11+
generated_code = Unparser.unparse(original_ast)
12+
13+
# Must be parseable
14+
expect { Unparser.parse(generated_code) }.not_to raise_error
15+
16+
# Both should evaluate to the same result
17+
original_result = eval(original_code)
18+
generated_result = eval(generated_code)
19+
expect(generated_result).to eq(original_result)
20+
21+
expect(generated_code).to eq("<<-HEREDOC.chomp\na\nb\nc\#{1}d\nHEREDOC\n")
22+
end
23+
end
24+
end

0 commit comments

Comments
 (0)