Skip to content

Commit

Permalink
Merge pull request #35309 from JuliaLang/jb/fix35305
Browse files Browse the repository at this point in the history
fix #35305, need escaping when printing string macro calls
  • Loading branch information
JeffBezanson authored Mar 31, 2020
2 parents 33df293 + b6dd448 commit 351d7e9
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 2 deletions.
6 changes: 4 additions & 2 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1500,11 +1500,13 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::In
is_core_macro(args[1], "@big_str")
print(io, args[3])
# x"y" and x"y"z
elseif isa(args[1], Symbol) &&
elseif isa(args[1], Symbol) && nargs >= 3 && isa(args[3], String) &&
startswith(string(args[1]::Symbol), "@") &&
endswith(string(args[1]::Symbol), "_str")
s = string(args[1]::Symbol)
print(io, s[2:prevind(s,end,4)], "\"", args[3], "\"")
print(io, s[2:prevind(s,end,4)], "\"")
escape_raw_string(io, args[3])
print(io, "\"")
if nargs == 4
print(io, args[4])
end
Expand Down
49 changes: 49 additions & 0 deletions base/strings/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,55 @@ julia> println(raw"\\\\x \\\\\\"")
"""
macro raw_str(s); s; end

"""
escape_raw_string(s::AbstractString)
escape_raw_string(io, s::AbstractString)
Escape a string in the manner used for parsing raw string literals.
For each double-quote (`"`) character in input string `s`, this
function counts the number _n_ of preceeding backslash (`\\`) characters,
and then increases there the number of backslashes from _n_ to 2_n_+1
(even for _n_ = 0). It also doubles a sequence of backslashes at the end
of the string.
This escaping convention is used in raw strings and other non-standard
string literals. (It also happens to be the escaping convention
expected by the Microsoft C/C++ compiler runtime when it parses a
command-line string into the argv[] array.)
See also: [`escape_string`](@ref)
"""
function escape_raw_string(io, str::AbstractString)
escapes = 0
for c in str
if c == '\\'
escapes += 1
else
if c == '"'
# if one or more backslashes are followed by
# a double quote then escape all backslashes
# and the double quote
escapes = escapes * 2 + 1
end
while escapes > 0
write(io, '\\')
escapes -= 1
end
escapes = 0
write(io, c)
end
end
# also escape any trailing backslashes,
# so they do not affect the closing quote
while escapes > 0
write(io, '\\')
write(io, '\\')
escapes -= 1
end
end
escape_raw_string(str::AbstractString) = sprint(escape_raw_string, str;
sizehint = lastindex(str) + 2)

## multiline strings ##

"""
Expand Down
3 changes: 3 additions & 0 deletions test/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,9 @@ z856739 = [:a, :b]
@test sprint(show, Meta.parse("a\"b\"c")) == ":(a\"b\"c)"
@test sprint(show, Meta.parse("aa\"b\"")) == ":(aa\"b\")"
@test sprint(show, Meta.parse("a\"b\"cc")) == ":(a\"b\"cc)"
@test sprint(show, Meta.parse("a\"\"\"issue \"35305\" \"\"\"")) == ":(a\"issue \\\"35305\\\" \")"
@test sprint(show, Meta.parse("a\"\$\"")) == ":(a\"\$\")"
@test sprint(show, Meta.parse("a\"\\b\"")) == ":(a\"\\b\")"
# 11111111111111111111, 0xfffffffffffffffff, 1111...many digits...
@test sprint(show, Meta.parse("11111111111111111111")) == ":(11111111111111111111)"
# @test_repr "Base.@int128_str \"11111111111111111111\""
Expand Down
1 change: 1 addition & 0 deletions test/strings/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
@test "aaa \\g \n" == unescape_string(str, ['g'])
@test "aaa \\g \\n" == unescape_string(str, ['g', 'n'])
end
@test Base.escape_raw_string(raw"\"\\\"\\-\\") == "\\\"\\\\\\\"\\\\-\\\\"
end
@testset "join()" begin
@test join([]) == join([],",") == ""
Expand Down

2 comments on commit 351d7e9

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily package evaluation, I will reply here when finished:

@nanosoldier runtests(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your package evaluation job has completed - possible new issues were detected. A full report can be found here. cc @maleadt

Please sign in to comment.