Skip to content

Commit b9430c7

Browse files
Add test for Rule 1, which requires Meta.parse(string(ex)) == ex.
1 parent 7d8c4b6 commit b9430c7

File tree

2 files changed

+70
-33
lines changed

2 files changed

+70
-33
lines changed

base/show.jl

+10-4
Original file line numberDiff line numberDiff line change
@@ -797,9 +797,15 @@ const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, SSAValue,
797797
Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode}
798798
# Operators have precedence levels from 1-N, and show_unquoted defaults to a
799799
# precedence level of 0 (the fourth argument). The top-level print and show
800-
# methods use a precedence of -1 to specially allow space-separated macro syntax
801-
print( io::IO, ex::ExprNode) = (show_unquoted(io, ex, 0, -1); nothing)
802-
show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(io, ex, 0, -1, 0)
800+
# methods use a precedence of -1 to specially allow space-separated macro syntax.
801+
# IOContext(io, :unquote_fallback => false) tells show_unquoted to treat any
802+
# Expr whose head is :$ as if it is inside a quote, preventing fallback to the
803+
# "unhandled" case: this is used by print/string to be lawful to Rule 1 above.
804+
# On the countrary, show/repr have to follow Rule 2, requiring any Expr whose
805+
# head is :$ and which is not inside a quote to fallback to the "unhandled" case:
806+
# this is behavior is triggered by IOContext(io, :unquote_fallback => true)
807+
print( io::IO, ex::ExprNode) = (show_unquoted(IOContext(io, :unquote_fallback => false), ex, 0, -1); nothing)
808+
show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(IOContext(io, :unquote_fallback => true), ex, 0, -1, 0)
803809
show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0)
804810
show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0)
805811
show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex)
@@ -1569,7 +1575,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
15691575
if head === :$
15701576
quote_level -= 1
15711577
end
1572-
if head === :$ && quote_level < 0
1578+
if head === :$ && quote_level < 0 && get(io, :unquote_fallback, true)
15731579
unhandled = true
15741580
else
15751581
print(io, head)

test/show.jl

+60-29
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ macro test_repr(x)
5353
# this is a macro instead of function so we can avoid getting useful backtraces :)
5454
return :(test_repr($(esc(x))))
5555
end
56-
function test_repr(x::String)
56+
macro weak_test_repr(x)
57+
# this is a macro instead of function so we can avoid getting useful backtraces :)
58+
return :(test_repr($(esc(x)), true))
59+
end
60+
function test_repr(x::String, remove_linenums::Bool = false)
5761
# Note: We can't just compare x1 and x2 because interpolated
5862
# strings get converted to string Exprs by the first show().
5963
# This could produce a few false positives, but until string
@@ -64,14 +68,13 @@ function test_repr(x::String)
6468
x2 = eval(Meta.parse(repr(x1)))
6569
x3 = eval(Meta.parse(repr(x2)))
6670
if ! (x1 == x2 == x3)
67-
# error(string(
68-
print(string(
69-
"repr test failed:",
71+
error(string(
72+
"\nrepr test (Rule 2) failed:",
7073
"\noriginal: ", x,
7174
"\n\npreparsed: ", x1, "\n", sprint(dump, x1),
7275
"\n\nparsed: ", x2, "\n", sprint(dump, x2),
73-
"\n\nreparsed: ", x3, "\n", sprint(dump, x3)
74-
))
76+
"\n\nreparsed: ", x3, "\n", sprint(dump, x3),
77+
"\n\n"))
7578
end
7679
@test x1 == x2 == x3
7780

@@ -80,19 +83,47 @@ function test_repr(x::String)
8083
x6 = eval(Base.remove_linenums!(Meta.parse(repr(x5))))
8184
if ! (x4 == x5 == x6)
8285
error(string(
83-
"repr test without line numbers failed:",
86+
"\nrepr test (Rule 2) without line numbers failed:",
8487
"\noriginal: ", x,
8588
"\n\npreparsed: ", x4, "\n", sprint(dump, x4),
8689
"\n\nparsed: ", x5, "\n", sprint(dump, x5),
87-
"\n\nreparsed: ", x6, "\n", sprint(dump, x6)
88-
))
90+
"\n\nreparsed: ", x6, "\n", sprint(dump, x6),
91+
"\n\n"))
8992
end
9093
@test x4 == x5 == x6
9194

9295
@test Base.remove_linenums!(x1) ==
9396
Base.remove_linenums!(x2) ==
9497
Base.remove_linenums!(x3) ==
9598
x4 == x5 == x6
99+
100+
if isa(x1, Expr) && remove_linenums
101+
if Base.remove_linenums!(Meta.parse(string(x1))) != x1
102+
error(string(
103+
"\nstring test (Rule 1) failed:",
104+
"\noriginal: ", x,
105+
"\n\npreparsed: ", x1, "\n", sprint(dump, x4),
106+
"\n\nstring(preparsed): ", string(x1),
107+
"\n\nBase.remove_linenums!(Meta.parse(string(preparsed))): ",
108+
Base.remove_linenums!(Meta.parse(string(x1))), "\n",
109+
sprint(dump, Base.remove_linenums!(Meta.parse(string(x1)))),
110+
"\n\n"))
111+
end
112+
@test Base.remove_linenums!(Meta.parse(string(x1))) == x1
113+
elseif isa(x1, Expr)
114+
if Meta.parse(string(x1)) != x1
115+
error(string(
116+
"\nstring test (Rule 1) failed:",
117+
"\noriginal: ", x,
118+
"\n\npreparsed: ", x1, "\n", sprint(dump, x4),
119+
"\n\nstring(preparsed): ", string(x1),
120+
"\n\nMeta.parse(string(preparsed)): ",
121+
Meta.parse(string(x1)), "\n",
122+
sprint(dump, Meta.parse(string(x1))),
123+
"\n\n"))
124+
end
125+
@test Meta.parse(string(x1)) == x1
126+
end
96127
end
97128

98129
# primitive types
@@ -207,7 +238,7 @@ end
207238

208239

209240
# control structures (shamelessly stolen from base/bitarray.jl)
210-
@test_repr """mutable struct BitArray{N} <: AbstractArray{Bool, N}
241+
@weak_test_repr """mutable struct BitArray{N} <: AbstractArray{Bool, N}
211242
# line meta
212243
chunks::Vector{UInt64}
213244
# line meta
@@ -254,7 +285,7 @@ end
254285
end
255286
end"""
256287

257-
@test_repr """function copy_chunks(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer)
288+
@weak_test_repr """function copy_chunks(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer)
258289
# line meta
259290
if numbits == 0
260291
# line meta
@@ -333,13 +364,13 @@ end"""
333364
return
334365
end"""
335366

336-
@test_repr """if a
367+
@weak_test_repr """if a
337368
# line meta
338369
b
339370
end
340371
"""
341372

342-
@test_repr """if a
373+
@weak_test_repr """if a
343374
# line meta
344375
b
345376
elseif c
@@ -348,7 +379,7 @@ d
348379
end
349380
"""
350381

351-
@test_repr """if a
382+
@weak_test_repr """if a
352383
# line meta
353384
b
354385
elseif c
@@ -360,7 +391,7 @@ e
360391
end
361392
"""
362393

363-
@test_repr """if a
394+
@weak_test_repr """if a
364395
# line meta
365396
b
366397
elseif c
@@ -372,15 +403,15 @@ f
372403
end
373404
"""
374405

375-
@test_repr """f(x, y) do z, w
406+
@weak_test_repr """f(x, y) do z, w
376407
# line meta
377408
a
378409
# line meta
379410
b
380411
end
381412
"""
382413

383-
@test_repr """f(x, y) do z
414+
@weak_test_repr """f(x, y) do z
384415
# line meta
385416
a
386417
# line meta
@@ -441,7 +472,7 @@ end
441472

442473
@test_repr "[1 2 3; 4 5 6; 7 8 9]'"
443474

444-
@test_repr "baremodule X
475+
@weak_test_repr "baremodule X
445476
# line meta
446477
# line meta
447478
import ...B.c
@@ -450,7 +481,7 @@ import D
450481
# line meta
451482
import B.C.D.E.F.g
452483
end"
453-
@test_repr "baremodule Y
484+
@weak_test_repr "baremodule Y
454485
# line meta
455486
# line meta
456487
export A, B, C
@@ -837,11 +868,11 @@ test_mt(show_f5, "show_f5(A::AbstractArray{T,N}, indices::Vararg{$Int,N})")
837868
@test_repr "macro m end"
838869
@test sprint(show, Expr(:macro, Expr(:call, :m, :ex), Expr(:block, :m))) ==
839870
":(macro m(ex)\n m\n end)"
840-
@test_repr """macro identity(ex)
871+
@weak_test_repr """macro identity(ex)
841872
# line meta
842873
esc(ex)
843874
end"""
844-
@test_repr """macro m(a,b)
875+
@weak_test_repr """macro m(a,b)
845876
# line meta
846877
quote
847878
# line meta
@@ -897,7 +928,7 @@ end""")) ==
897928
\$\$x
898929
end
899930
end)"""
900-
@test_repr """
931+
@weak_test_repr """
901932
quote
902933
#= none:2 =#
903934
quote
@@ -907,7 +938,7 @@ quote
907938
end"""
908939

909940
# fallback printing + nested quotes and unquotes
910-
@test_repr repr(Expr(:block, LineNumberNode(0, :none),
941+
@weak_test_repr repr(Expr(:block, LineNumberNode(0, :none),
911942
Expr(:exotic_head, Expr(:$, :x))))
912943
@test_repr repr(Expr(:exotic_head, Expr(:call, :+, 1, Expr(:quote, Expr(:$, Expr(:$, :y))))))
913944
@test_repr repr(Expr(:quote, Expr(:$, Expr(:exotic_head, Expr(:call, :+, 1, Expr(:$, :y))))))
@@ -945,20 +976,20 @@ end"""
945976

946977
# nested quotes and blocks
947978
@test_repr "Expr(:quote, Expr(:block, :a, :b))"
948-
@test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a, LineNumberNode(0, :none), :b)))
979+
@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a, LineNumberNode(0, :none), :b)))
949980
@test repr(Expr(:quote, Expr(:block, :a, :b))) ==
950981
":(quote
951982
a
952983
b
953984
end)"
954985
@test_repr "Expr(:quote, Expr(:block, :a))"
955-
@test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a)))
986+
@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a)))
956987
@test repr(Expr(:quote, Expr(:block, :a))) ==
957988
":(quote
958989
a
959990
end)"
960991
@test_repr "Expr(:quote, Expr(:block, :(a + b)))"
961-
@test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :(a + b))))
992+
@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :(a + b))))
962993
@test repr(Expr(:quote, Expr(:block, :(a + b)))) ==
963994
":(quote
964995
a + b
@@ -976,12 +1007,12 @@ end"""
9761007
# unquoting
9771008
@test_repr "\$y"
9781009
@test_repr "\$\$y"
979-
@test_repr """
1010+
@weak_test_repr """
9801011
begin
9811012
# line meta
9821013
\$y
9831014
end"""
984-
@test_repr """
1015+
@weak_test_repr """
9851016
begin
9861017
# line meta
9871018
\$\$y
@@ -1223,7 +1254,7 @@ test_repr("(+).:-")
12231254
test_repr("(!).:~")
12241255
test_repr("a.:(begin
12251256
#= none:3 =#
1226-
end)")
1257+
end)", true)
12271258
test_repr("a.:(=)")
12281259
test_repr("a.:(:)")
12291260
test_repr("(:).a")

0 commit comments

Comments
 (0)