Skip to content

Commit 351d7e9

Browse files
authored
Merge pull request #35309 from JuliaLang/jb/fix35305
fix #35305, need escaping when printing string macro calls
2 parents 33df293 + b6dd448 commit 351d7e9

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

base/show.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,11 +1500,13 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
15001500
is_core_macro(args[1], "@big_str")
15011501
print(io, args[3])
15021502
# x"y" and x"y"z
1503-
elseif isa(args[1], Symbol) &&
1503+
elseif isa(args[1], Symbol) && nargs >= 3 && isa(args[3], String) &&
15041504
startswith(string(args[1]::Symbol), "@") &&
15051505
endswith(string(args[1]::Symbol), "_str")
15061506
s = string(args[1]::Symbol)
1507-
print(io, s[2:prevind(s,end,4)], "\"", args[3], "\"")
1507+
print(io, s[2:prevind(s,end,4)], "\"")
1508+
escape_raw_string(io, args[3])
1509+
print(io, "\"")
15081510
if nargs == 4
15091511
print(io, args[4])
15101512
end

base/strings/io.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,55 @@ julia> println(raw"\\\\x \\\\\\"")
523523
"""
524524
macro raw_str(s); s; end
525525

526+
"""
527+
escape_raw_string(s::AbstractString)
528+
escape_raw_string(io, s::AbstractString)
529+
530+
Escape a string in the manner used for parsing raw string literals.
531+
For each double-quote (`"`) character in input string `s`, this
532+
function counts the number _n_ of preceeding backslash (`\\`) characters,
533+
and then increases there the number of backslashes from _n_ to 2_n_+1
534+
(even for _n_ = 0). It also doubles a sequence of backslashes at the end
535+
of the string.
536+
537+
This escaping convention is used in raw strings and other non-standard
538+
string literals. (It also happens to be the escaping convention
539+
expected by the Microsoft C/C++ compiler runtime when it parses a
540+
command-line string into the argv[] array.)
541+
542+
See also: [`escape_string`](@ref)
543+
"""
544+
function escape_raw_string(io, str::AbstractString)
545+
escapes = 0
546+
for c in str
547+
if c == '\\'
548+
escapes += 1
549+
else
550+
if c == '"'
551+
# if one or more backslashes are followed by
552+
# a double quote then escape all backslashes
553+
# and the double quote
554+
escapes = escapes * 2 + 1
555+
end
556+
while escapes > 0
557+
write(io, '\\')
558+
escapes -= 1
559+
end
560+
escapes = 0
561+
write(io, c)
562+
end
563+
end
564+
# also escape any trailing backslashes,
565+
# so they do not affect the closing quote
566+
while escapes > 0
567+
write(io, '\\')
568+
write(io, '\\')
569+
escapes -= 1
570+
end
571+
end
572+
escape_raw_string(str::AbstractString) = sprint(escape_raw_string, str;
573+
sizehint = lastindex(str) + 2)
574+
526575
## multiline strings ##
527576

528577
"""

test/show.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,6 +1133,9 @@ z856739 = [:a, :b]
11331133
@test sprint(show, Meta.parse("a\"b\"c")) == ":(a\"b\"c)"
11341134
@test sprint(show, Meta.parse("aa\"b\"")) == ":(aa\"b\")"
11351135
@test sprint(show, Meta.parse("a\"b\"cc")) == ":(a\"b\"cc)"
1136+
@test sprint(show, Meta.parse("a\"\"\"issue \"35305\" \"\"\"")) == ":(a\"issue \\\"35305\\\" \")"
1137+
@test sprint(show, Meta.parse("a\"\$\"")) == ":(a\"\$\")"
1138+
@test sprint(show, Meta.parse("a\"\\b\"")) == ":(a\"\\b\")"
11361139
# 11111111111111111111, 0xfffffffffffffffff, 1111...many digits...
11371140
@test sprint(show, Meta.parse("11111111111111111111")) == ":(11111111111111111111)"
11381141
# @test_repr "Base.@int128_str \"11111111111111111111\""

test/strings/io.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
@test "aaa \\g \n" == unescape_string(str, ['g'])
149149
@test "aaa \\g \\n" == unescape_string(str, ['g', 'n'])
150150
end
151+
@test Base.escape_raw_string(raw"\"\\\"\\-\\") == "\\\"\\\\\\\"\\\\-\\\\"
151152
end
152153
@testset "join()" begin
153154
@test join([]) == join([],",") == ""

0 commit comments

Comments
 (0)