Skip to content

Commit 357c298

Browse files
committed
[utils] escape backslashes in diagnostics strings
DiagnosticVerifier lexes the contents of expected diagnostics the same way Swift parses string literals, to allow for escape codes. This makes for problems when backslashes are followed by something that makes for a valid escape. In particular, it reaches an llvm_unreachable if it encounters `\(`, which would create a string interpolation in a string literal.
1 parent 9c98997 commit 357c298

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

test/Utils/update-verify-tests/expansion.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
// RUN: not %target-swift-frontend-verify -I %t -plugin-path %swift-plugin-dir -typecheck %t/unparsed.swift 2>%t/output.txt -Rmacro-expansions
4343
// RUN: not %update-verify-tests < %t/output.txt | %FileCheck --check-prefix CHECK-UNPARSED %s
4444

45+
// RUN: not %target-swift-frontend-verify -load-plugin-library %t/%target-library-name(UnstringifyMacroDefinition) -typecheck %t/escaped.swift 2>%t/output.txt -Rmacro-expansions
46+
// RUN: %update-verify-tests < %t/output.txt
47+
// RUN: %target-swift-frontend-verify -load-plugin-library %t/%target-library-name(UnstringifyMacroDefinition) -typecheck %t/escaped.swift -Rmacro-expansions
48+
// RUN: %diff %t/escaped.swift %t/escaped.swift.expected
49+
4550
//--- single.swift
4651
@attached(peer, names: overloaded)
4752
macro unstringifyPeer(_ s: String) =
@@ -260,3 +265,43 @@ func bar() {
260265
foo(a, &b)
261266
}
262267

268+
//--- escaped.swift
269+
@attached(peer, names: overloaded)
270+
macro unstringifyPeer(_ s: String) =
271+
#externalMacro(module: "UnstringifyMacroDefinition", type: "UnstringifyPeerMacro")
272+
273+
@unstringifyPeer("""
274+
func foo(_ x: Int) {
275+
let a = "\\(x)"
276+
let b = "\\(x)"
277+
}
278+
""")
279+
// NB: DiagnosticVerifier interprets "\\(x)" as "\(x)"
280+
// expected-expansion@+3:30{{
281+
// expected-remark@2{{macro content: |let a = "\\(x)"|}}
282+
// }}
283+
func foo() { let _ = "\(2)" }
284+
285+
//--- escaped.swift.expected
286+
@attached(peer, names: overloaded)
287+
macro unstringifyPeer(_ s: String) =
288+
#externalMacro(module: "UnstringifyMacroDefinition", type: "UnstringifyPeerMacro")
289+
290+
// expected-note@+1 6{{in expansion of macro 'unstringifyPeer' on global function 'foo()' here}}
291+
@unstringifyPeer("""
292+
func foo(_ x: Int) {
293+
let a = "\\(x)"
294+
let b = "\\(x)"
295+
}
296+
""")
297+
// NB: DiagnosticVerifier interprets "\\(x)" as "\(x)"
298+
// expected-expansion@+8:30{{
299+
// expected-remark@1{{macro content: |func foo(_ x: Int) {|}}
300+
// expected-warning@2{{initialization of immutable value 'a' was never used; consider replacing with assignment to '_' or removing it}}
301+
// expected-remark@2{{macro content: | let a = "\\(x)"|}}
302+
// expected-warning@3{{initialization of immutable value 'b' was never used; consider replacing with assignment to '_' or removing it}}
303+
// expected-remark@3{{macro content: | let b = "\\(x)"|}}
304+
// expected-remark@4{{macro content: |}|}}
305+
// }}
306+
func foo() { let _ = "\(2)" }
307+

utils/update_verify_tests/core.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
import re
3+
from codecs import encode, decode
34

45
DEBUG = False
56

@@ -172,7 +173,9 @@ def render(self):
172173
if self.category == "expansion":
173174
return base_s + "{{"
174175
else:
175-
return base_s + "{{" + self.diag_content + "}}"
176+
# python trivia: raw strings can't end with a backslash
177+
escaped_diag_s = self.diag_content.replace("\\", "\\\\")
178+
return base_s + "{{" + escaped_diag_s + "}}"
176179

177180

178181
class ExpansionDiagClose:
@@ -245,9 +248,12 @@ def parse_diag(line, filename, prefix):
245248
count = int(count_s) if count_s else 1
246249
line.content = matched_re.sub("{{DIAG}}", s)
247250

251+
unescaped_diag_s = decode(
252+
encode(diag_s, "utf-8", "backslashreplace"), "unicode-escape"
253+
)
248254
return Diag(
249255
check_prefix,
250-
diag_s,
256+
unescaped_diag_s,
251257
category_s,
252258
target_line_n,
253259
is_absolute,

0 commit comments

Comments
 (0)