Skip to content

Commit 1545c78

Browse files
authored
Pad Hash#inspect, Tuple#inspect before { from first element (#16245)
This ensures the output is valid Crystal code and doesn't get confused with a macro expansion.
1 parent 0c5737a commit 1545c78

File tree

4 files changed

+67
-3
lines changed

4 files changed

+67
-3
lines changed

spec/std/hash_spec.cr

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,24 @@ describe "Hash" do
596596
it do
597597
h = {} of RecursiveHash => RecursiveHash
598598
h[h] = h
599-
h.to_s.should eq("{{...} => {...}}")
599+
h.to_s.should eq("{ {...} => {...} }")
600+
end
601+
602+
context "when the first key starts with '{'" do
603+
it "inserts a space after '{' and before '}' when first key is a Hash, preventing macro interpolation ({{ ... }})" do
604+
hash = { {1 => 2} => 3 }
605+
hash.to_s.should eq("{ {1 => 2} => 3 }")
606+
end
607+
608+
it "inserts a space after '{' and before '}' when first key is a Tuple, preventing macro interpolation ({{ ... }})" do
609+
hash = { {1, 2, 3} => 4 }
610+
hash.to_s.should eq("{ {1, 2, 3} => 4 }")
611+
end
612+
613+
it "inserts a space after '{' and before '}' when first key is a NamedTuple, preventing macro interpolation ({{ ... }})" do
614+
hash = { {a: 1} => 2 }
615+
hash.to_s.should eq("{ {a: 1} => 2 }")
616+
end
600617
end
601618
end
602619

spec/std/tuple_spec.cr

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,27 @@ describe "Tuple" do
168168
[a, b, c, d, e].min.should eq(d)
169169
end
170170

171-
it "does to_s" do
172-
{1, 2, 3}.to_s.should eq("{1, 2, 3}")
171+
describe "#to_s" do
172+
it "returns string representation" do
173+
{1, 2, 3}.to_s.should eq("{1, 2, 3}")
174+
end
175+
176+
context "when the first element starts with '{'" do
177+
it "inserts a space after '{' and before '}' when the first element is a Hash, preventing macro interpolation ({{ ... }})" do
178+
tuple = { {1 => 2} }
179+
tuple.to_s.should eq("{ {1 => 2} }")
180+
end
181+
182+
it "inserts a space after '{' and before '}' when the first element is a Tuple, preventing macro interpolation ({{ ... }})" do
183+
tuple = { {1, 2, 3} }
184+
tuple.to_s.should eq("{ {1, 2, 3} }")
185+
end
186+
187+
it "inserts a space after '{' and before '}' when the first element is a NamedTuple, preventing macro interpolation ({{ ... }})" do
188+
tuple = { {a: 1} }
189+
tuple.to_s.should eq("{ {a: 1} }")
190+
end
191+
end
173192
end
174193

175194
it "does each" do

src/hash.cr

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,7 +2044,10 @@ class Hash(K, V)
20442044
# ```
20452045
def to_s(io : IO) : Nil
20462046
executed = exec_recursive(:to_s) do
2047+
needs_padding = curly_like?(self.first_key?)
2048+
20472049
io << '{'
2050+
io << ' ' if needs_padding
20482051
found_one = false
20492052
each do |key, value|
20502053
io << ", " if found_one
@@ -2053,11 +2056,21 @@ class Hash(K, V)
20532056
value.inspect(io)
20542057
found_one = true
20552058
end
2059+
io << ' ' if needs_padding
20562060
io << '}'
20572061
end
20582062
io << "{...}" unless executed
20592063
end
20602064

2065+
private def curly_like?(object)
2066+
case object
2067+
when Hash, Tuple, NamedTuple
2068+
true
2069+
else
2070+
false
2071+
end
2072+
end
2073+
20612074
def pretty_print(pp) : Nil
20622075
executed = exec_recursive(:pretty_print) do
20632076
pp.list("{", self, "}") do |key, value|

src/tuple.cr

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,26 @@ struct Tuple
587587
# tuple.to_s # => "{1, \"hello\"}"
588588
# ```
589589
def to_s(io : IO) : Nil
590+
needs_padding = curly_like?(self.first?)
591+
590592
io << '{'
593+
io << ' ' if needs_padding
594+
591595
join io, ", ", &.inspect(io)
596+
597+
io << ' ' if needs_padding
592598
io << '}'
593599
end
594600

601+
private def curly_like?(object)
602+
case object
603+
when Hash, Tuple, NamedTuple
604+
true
605+
else
606+
false
607+
end
608+
end
609+
595610
def pretty_print(pp) : Nil
596611
pp.list("{", self, "}")
597612
end

0 commit comments

Comments
 (0)