Skip to content

Commit cd8c578

Browse files
authored
Merge adjacent StringLiterals before yielding (#16427)
Joins adjacent `StringLiteral` values before yielding within interpolations to ensure string is properly escaped if needed.
1 parent fbc55f6 commit cd8c578

File tree

3 files changed

+25
-6
lines changed

3 files changed

+25
-6
lines changed

spec/compiler/parser/to_s_spec.cr

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ describe "ASTNode#to_s" do
330330
expect_to_s %q(`\n\0`), %q(`\n\u0000`)
331331
expect_to_s %q(`#{1}\n\0`), %q(`#{1}\n\u0000`)
332332
expect_to_s Call.new("`", Call.new("String".path, "interpolation", "x".var, global: true)), %q(`#{::String.interpolation(x)}`)
333+
expect_to_s StringInterpolation.new(["#".string, "{foo}".string] of ASTNode), %q("\#{foo}")
334+
expect_to_s StringInterpolation.new([2.int32, " ".string, "#".string, "{".string] of ASTNode), %q("#{2} \#{")
335+
expect_to_s StringInterpolation.new(["a".string, "b".string] of ASTNode), %q("ab")
333336
expect_to_s "macro foo\n{% verbatim do %}1{% end %}\nend"
334337
expect_to_s Assign.new("x".var, Expressions.new([1.int32, 2.int32] of ASTNode)), "x = (1\n2\n)"
335338
expect_to_s "foo.*"

spec/compiler/semantic/macro_spec.cr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,6 +1685,19 @@ describe "Semantic: macro" do
16851685
CRYSTAL
16861686
end
16871687

1688+
it "preserves escaped interpolation in verbatim (#16413)" do
1689+
assert_type(<<-'CRYSTAL') { nil_type }
1690+
{% begin %}
1691+
{% verbatim do %}
1692+
{%
1693+
name = "FOO"
1694+
"\#{get_env(#{name})}"
1695+
%}
1696+
{% end %}
1697+
{% end %}
1698+
CRYSTAL
1699+
end
1700+
16881701
it "can use macro in instance var initializer (#7666)" do
16891702
assert_type(<<-CRYSTAL) { string }
16901703
class Foo

src/compiler/crystal/syntax/to_s.cr

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,16 @@ module Crystal
136136
end
137137

138138
def visit_interpolation(node, &)
139-
node.expressions.each do |exp|
140-
if exp.is_a?(StringLiteral)
141-
@str << yield exp.value
139+
node.expressions.chunks(&.is_a?(StringLiteral)).each do |(is_string, exps)|
140+
if is_string
141+
value = exps.join(&.as(StringLiteral).value)
142+
@str << yield value
142143
else
143-
@str << "\#{"
144-
exp.accept(self)
145-
@str << '}'
144+
exps.each do |exp|
145+
@str << "\#{"
146+
exp.accept(self)
147+
@str << '}'
148+
end
146149
end
147150
end
148151
end

0 commit comments

Comments
 (0)