File tree Expand file tree Collapse file tree 2 files changed +53
-2
lines changed
spec/unit/unparser/writer Expand file tree Collapse file tree 2 files changed +53
-2
lines changed Original file line number Diff line number Diff 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
Original file line number Diff line number Diff line change 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\n c\# {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\n a\n b\n c\# {1}d\n HEREDOC\n " )
22+ end
23+ end
24+ end
You can’t perform that action at this time.
0 commit comments