From 61ada0106a8bc39272083f33eaaeec2e40c136b1 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 4 Nov 2026 09:48:03 +0000 Subject: [PATCH 1/5] unified: Cover Swift string literal values before decoding Record plain, escaped, raw, multiline, and interpolated literals so the string decoding required by regex parsing can be reviewed separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../string-literals/test.expected | 9 ++++++ .../library-tests/string-literals/test.ql | 5 ++++ .../library-tests/string-literals/test.swift | 30 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 unified/ql/test/library-tests/string-literals/test.expected create mode 100644 unified/ql/test/library-tests/string-literals/test.ql create mode 100644 unified/ql/test/library-tests/string-literals/test.swift diff --git a/unified/ql/test/library-tests/string-literals/test.expected b/unified/ql/test/library-tests/string-literals/test.expected new file mode 100644 index 000000000000..0d83ad52fc55 --- /dev/null +++ b/unified/ql/test/library-tests/string-literals/test.expected @@ -0,0 +1,9 @@ +| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:9:20:9:42 | "Dummy implementation." | Dummy implementation. | +| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:46:20:46:42 | "Dummy implementation." | Dummy implementation. | +| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:100:20:100:42 | "Dummy implementation." | Dummy implementation. | +| test.swift:1:13:1:14 | "" | | +| test.swift:2:14:2:18 | "abc" | abc | +| test.swift:4:24:4:28 | "\\\\n" | \\\\n | +| test.swift:5:15:5:31 | "\\u{61}\\u{1F600}" | \\u{61}\\u{1F600} | +| test.swift:6:11:6:21 | #"\\n\\"abc"# | #"\\n\\"abc"# | +| test.swift:8:20:8:30 | #"abc#def"# | #"abc#def"# | diff --git a/unified/ql/test/library-tests/string-literals/test.ql b/unified/ql/test/library-tests/string-literals/test.ql new file mode 100644 index 000000000000..668a8c5c3509 --- /dev/null +++ b/unified/ql/test/library-tests/string-literals/test.ql @@ -0,0 +1,5 @@ +import unified + +from StringLiteral literal +where not literal.getParent() instanceof StringInterpolationExpr +select literal, literal.getStringValue() diff --git a/unified/ql/test/library-tests/string-literals/test.swift b/unified/ql/test/library-tests/string-literals/test.swift new file mode 100644 index 000000000000..1fc694242349 --- /dev/null +++ b/unified/ql/test/library-tests/string-literals/test.swift @@ -0,0 +1,30 @@ +let empty = "" +let simple = "abc" +let escaped = "a\nb\rc\td\0e\\f\"g\'h" +let escapedBackslash = "\\n" +let unicode = "\u{61}\u{1F600}" +let raw = #"\n\"abc"# +let rawEscapes = ##"\n\##n\##u{61}\##"\##\"## +let hashesInside = #"abc#def"# +let multiline = """ + aa| + bb + """ +let multilineIndent = """ + aa + bb + """ +let multilineEmptyLine = """ + aa + + bb + """ +let multilineContinuation = """ + aa\ + bb + """ +let rawMultiline = #""" + aa\n + bb + """# +let interpolation = "a\(simple)b" From bddbb6a9a881b0f9f55a6081a21360b7516f1a4d Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 4 Nov 2026 09:48:40 +0000 Subject: [PATCH 2/5] unified: Restrict literal regression output to its fixture Exclude extractor-provided builtins so expected results do not depend on the extractor pack location. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- unified/ql/test/library-tests/string-literals/test.expected | 3 --- unified/ql/test/library-tests/string-literals/test.ql | 4 +++- unified/ql/test/library-tests/string-literals/test.swift | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/unified/ql/test/library-tests/string-literals/test.expected b/unified/ql/test/library-tests/string-literals/test.expected index 0d83ad52fc55..c93ae7880f46 100644 --- a/unified/ql/test/library-tests/string-literals/test.expected +++ b/unified/ql/test/library-tests/string-literals/test.expected @@ -1,6 +1,3 @@ -| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:9:20:9:42 | "Dummy implementation." | Dummy implementation. | -| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:46:20:46:42 | "Dummy implementation." | Dummy implementation. | -| file:///Users/geoffw0/semmle-code/ql/unified/extractor-pack/tools/builtins/types.swift:100:20:100:42 | "Dummy implementation." | Dummy implementation. | | test.swift:1:13:1:14 | "" | | | test.swift:2:14:2:18 | "abc" | abc | | test.swift:4:24:4:28 | "\\\\n" | \\\\n | diff --git a/unified/ql/test/library-tests/string-literals/test.ql b/unified/ql/test/library-tests/string-literals/test.ql index 668a8c5c3509..3d0d9488c427 100644 --- a/unified/ql/test/library-tests/string-literals/test.ql +++ b/unified/ql/test/library-tests/string-literals/test.ql @@ -1,5 +1,7 @@ import unified from StringLiteral literal -where not literal.getParent() instanceof StringInterpolationExpr +where + literal.getFile().getRelativePath() = "test.swift" and + not literal.getParent() instanceof StringInterpolationExpr select literal, literal.getStringValue() diff --git a/unified/ql/test/library-tests/string-literals/test.swift b/unified/ql/test/library-tests/string-literals/test.swift index 1fc694242349..b12ee6fb4aff 100644 --- a/unified/ql/test/library-tests/string-literals/test.swift +++ b/unified/ql/test/library-tests/string-literals/test.swift @@ -3,7 +3,7 @@ let simple = "abc" let escaped = "a\nb\rc\td\0e\\f\"g\'h" let escapedBackslash = "\\n" let unicode = "\u{61}\u{1F600}" -let raw = #"\n\"abc"# +let raw = #"\n\"abc"# let rawEscapes = ##"\n\##n\##u{61}\##"\##\"## let hashesInside = #"abc#def"# let multiline = """ From 41d387f3241b68ddf1a8e8a6ce48ce83c0f4bb47 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 4 Nov 2026 10:00:28 +0000 Subject: [PATCH 3/5] unified: Cover multi-segment constant string extraction Exercise full literal expressions, including currently missing multiline and escaped values, and retain an extractor corpus case distinguishing constants from interpolation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../swift/literals/string-escapes.output | 157 ++++++++++++++++++ .../swift/literals/string-escapes.swift | 7 + .../string-literals/test.expected | 20 ++- .../library-tests/string-literals/test.ql | 8 +- 4 files changed, 181 insertions(+), 11 deletions(-) create mode 100644 unified/extractor/tests/corpus/swift/literals/string-escapes.output create mode 100644 unified/extractor/tests/corpus/swift/literals/string-escapes.swift diff --git a/unified/extractor/tests/corpus/swift/literals/string-escapes.output b/unified/extractor/tests/corpus/swift/literals/string-escapes.output new file mode 100644 index 000000000000..49dc2339d78b --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-escapes.output @@ -0,0 +1,157 @@ +let escaped = "a\nb\rc" +let raw = #"\n"# +let multiline = """ + aa| + bb + """ +let interpolated = "a\(raw)b" + +--- + +sourceFile + endOfFileToken: endOfFile + statements: + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + stringLiteralExpr + closingQuote: " + openingQuote: " + segments: + stringSegment + content: stringSegment "a\\n" + stringSegment + content: stringSegment "b\\r" + stringSegment + content: stringSegment "c" + pattern: + identifierPattern + identifier: identifier "escaped" + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + stringLiteralExpr + closingPounds: rawStringPoundDelimiter "#" + openingPounds: rawStringPoundDelimiter "#" + closingQuote: " + openingQuote: " + segments: + stringSegment + content: stringSegment "\\n" + pattern: + identifierPattern + identifier: identifier "raw" + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + stringLiteralExpr + closingQuote: """ + openingQuote: """ + segments: + stringSegment + content: stringSegment "aa|\n" + stringSegment + content: stringSegment "bb" + pattern: + identifierPattern + identifier: identifier "multiline" + codeBlockItem + item: + variableDecl + attributes: + modifiers: + bindingSpecifier: let + bindings: + patternBinding + initializer: + initializerClause + equal: = + value: + stringLiteralExpr + closingQuote: " + openingQuote: " + segments: + stringSegment + content: stringSegment "a" + expressionSegment + leftParen: ( + rightParen: ) + backslash: \ + expressions: + labeledExpr + expression: + declReferenceExpr + baseName: identifier "raw" + stringSegment + content: stringSegment "b" + pattern: + identifierPattern + identifier: identifier "interpolated" + +--- + +top_level source="⟨body⟩" + body: + block source="⟨stmt⟩\n⟨stmt⟩\n⟨stmt⟩\n⟨stmt⟩" + stmt: + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "escaped" source="escaped" + value: + string_interpolation_expr source="\"⟨element⟩⟨element⟩⟨element⟩\"" + element: + string_literal "a\\n" source="a\\n" + string_literal "b\\r" source="b\\r" + string_literal "c" source="c" + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "raw" source="raw" + value: string_literal "#\"\\n\"#" source="#\"\\n\"#" + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "multiline" source="multiline" + value: + string_interpolation_expr source="\"\"\"\n ⟨element⟩ ⟨element⟩\n \"\"\"" + element: + string_literal "aa|\n" source="aa|\n" + string_literal "bb" source="bb" + variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" + modifier: modifier "let" source="let" + pattern: identifier "interpolated" source="interpolated" + value: + string_interpolation_expr source="\"⟨element⟩⟨element⟩⟨element⟩\"" + element: + string_literal "a" source="a" + call_expr source="\\(⟨argument⟩)" + callee: builtin_expr "interpolation" source="" + argument: + argument source="⟨value⟩" + value: identifier "raw" source="raw" + string_literal "b" source="b" diff --git a/unified/extractor/tests/corpus/swift/literals/string-escapes.swift b/unified/extractor/tests/corpus/swift/literals/string-escapes.swift new file mode 100644 index 000000000000..44b9ba6e5b85 --- /dev/null +++ b/unified/extractor/tests/corpus/swift/literals/string-escapes.swift @@ -0,0 +1,7 @@ +let escaped = "a\nb\rc" +let raw = #"\n"# +let multiline = """ + aa| + bb + """ +let interpolated = "a\(raw)b" diff --git a/unified/ql/test/library-tests/string-literals/test.expected b/unified/ql/test/library-tests/string-literals/test.expected index c93ae7880f46..74ae833e9821 100644 --- a/unified/ql/test/library-tests/string-literals/test.expected +++ b/unified/ql/test/library-tests/string-literals/test.expected @@ -1,6 +1,14 @@ -| test.swift:1:13:1:14 | "" | | -| test.swift:2:14:2:18 | "abc" | abc | -| test.swift:4:24:4:28 | "\\\\n" | \\\\n | -| test.swift:5:15:5:31 | "\\u{61}\\u{1F600}" | \\u{61}\\u{1F600} | -| test.swift:6:11:6:21 | #"\\n\\"abc"# | #"\\n\\"abc"# | -| test.swift:8:20:8:30 | #"abc#def"# | #"abc#def"# | +| test.swift:1:5:1:9 | empty | | +| test.swift:2:5:2:10 | simple | abc | +| test.swift:3:5:3:11 | escaped | | +| test.swift:4:5:4:20 | escapedBackslash | \\\\n | +| test.swift:5:5:5:11 | unicode | \\u{61}\\u{1F600} | +| test.swift:6:5:6:7 | raw | #"\\n\\"abc"# | +| test.swift:7:5:7:14 | rawEscapes | | +| test.swift:8:5:8:16 | hashesInside | #"abc#def"# | +| test.swift:9:5:9:13 | multiline | | +| test.swift:13:5:13:19 | multilineIndent | | +| test.swift:17:5:17:22 | multilineEmptyLine | | +| test.swift:22:5:22:25 | multilineContinuation | | +| test.swift:26:5:26:16 | rawMultiline | | +| test.swift:30:5:30:17 | interpolation | | diff --git a/unified/ql/test/library-tests/string-literals/test.ql b/unified/ql/test/library-tests/string-literals/test.ql index 3d0d9488c427..3e5e592ed862 100644 --- a/unified/ql/test/library-tests/string-literals/test.ql +++ b/unified/ql/test/library-tests/string-literals/test.ql @@ -1,7 +1,5 @@ import unified -from StringLiteral literal -where - literal.getFile().getRelativePath() = "test.swift" and - not literal.getParent() instanceof StringInterpolationExpr -select literal, literal.getStringValue() +from VariableDeclaration decl +where decl.getFile().getRelativePath() = "test.swift" +select decl.getPattern(), concat(decl.getValue().getStringValue()) From c69135cf34e9c3401e7c76d4118cc372d7279dd6 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 4 Nov 2026 10:21:24 +0000 Subject: [PATCH 4/5] unified: Decode Swift string literals for regex parsing Preserve multi-segment constants as literals while keeping interpolations separate. Decode escaped, raw, Unicode, and multiline strings so regex parsing receives runtime pattern text rather than source syntax. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../extractor/src/languages/swift/swift.rs | 24 +++-- .../swift/literals/string-escapes.output | 13 +-- .../lib/codeql/unified/internal/FacadeAst.qll | 15 ++-- .../unified/internal/SwiftStringLiterals.qll | 90 +++++++++++++++++++ .../string-literals/test.expected | 22 ++--- 5 files changed, 124 insertions(+), 40 deletions(-) create mode 100644 unified/ql/lib/codeql/unified/internal/SwiftStringLiterals.qll diff --git a/unified/extractor/src/languages/swift/swift.rs b/unified/extractor/src/languages/swift/swift.rs index 3cdfb0e12a99..178355bb6d4b 100644 --- a/unified/extractor/src/languages/swift/swift.rs +++ b/unified/extractor/src/languages/swift/swift.rs @@ -168,17 +168,23 @@ fn translation_rules() -> Vec> { tree!((builtin_expr #{node})) }), rule!((simpleStringLiteralExpr) @@node => (string_literal #{node})), - // String literal with a single constant segment (for some reason not typed as simpleStringLiteralExpr) rule!( - (stringLiteralExpr segments: (stringSegment) segments: _* @@rest) @@node - where rest.is_empty() + (stringLiteralExpr segments: _* @@segments) @@node => - (string_literal #{node}) - ), - rule!( - (stringLiteralExpr segments: _* @segments) - => - (string_interpolation_expr element: {segments}) + expr { + // Escapes and line breaks can split a constant literal into several segments. + if segments.iter().all(|segment| { + ctx.ast() + .get_node(*segment) + .expect("matched string segment must exist") + .kind_name() == "stringSegment" + }) { + tree!((string_literal #{node})) + } else { + let segments = ctx.translate(segments)?; + tree!((string_interpolation_expr element: {segments})) + } + } ), rule!((stringSegment content: @@content) => (string_literal #{content})), rule!( diff --git a/unified/extractor/tests/corpus/swift/literals/string-escapes.output b/unified/extractor/tests/corpus/swift/literals/string-escapes.output index 49dc2339d78b..f99303dcbe1e 100644 --- a/unified/extractor/tests/corpus/swift/literals/string-escapes.output +++ b/unified/extractor/tests/corpus/swift/literals/string-escapes.output @@ -124,12 +124,7 @@ top_level source="⟨body⟩" variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "let" source="let" pattern: identifier "escaped" source="escaped" - value: - string_interpolation_expr source="\"⟨element⟩⟨element⟩⟨element⟩\"" - element: - string_literal "a\\n" source="a\\n" - string_literal "b\\r" source="b\\r" - string_literal "c" source="c" + value: string_literal "\"a\\nb\\rc\"" source="\"a\\nb\\rc\"" variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "let" source="let" pattern: identifier "raw" source="raw" @@ -137,11 +132,7 @@ top_level source="⟨body⟩" variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "let" source="let" pattern: identifier "multiline" source="multiline" - value: - string_interpolation_expr source="\"\"\"\n ⟨element⟩ ⟨element⟩\n \"\"\"" - element: - string_literal "aa|\n" source="aa|\n" - string_literal "bb" source="bb" + value: string_literal "\"\"\"\n aa|\n bb\n \"\"\"" source="\"\"\"\n aa|\n bb\n \"\"\"" variable_declaration source="⟨modifier⟩ ⟨pattern⟩ = ⟨value⟩" modifier: modifier "let" source="let" pattern: identifier "interpolated" source="interpolated" diff --git a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll index 8d40d3f6b099..a9fe9f507122 100644 --- a/unified/ql/lib/codeql/unified/internal/FacadeAst.qll +++ b/unified/ql/lib/codeql/unified/internal/FacadeAst.qll @@ -8,6 +8,7 @@ private import unified as U private import AstPlugin private import codeql.files.FileSystem private import codeql.unified.internal.NameBinding as NameBinding +private import codeql.unified.internal.SwiftStringLiterals as SwiftStringLiterals module Unified { private import Ast::Unified as G @@ -109,15 +110,11 @@ module Unified { class Expr extends G::Expr { /** Gets the string value of this expression, if it is a known string constant. */ string getStringValue() { - // TODO: we'll want to cook the string literals extractor-side, but for now - // just strip the quotes here and ignore escape sequences. - exists(string text | text = this.(StringLiteral).getValue() | - result = text.regexpCapture("\"(.*)\"", 1) - or - // Constant-segments of string interpolations are represented as string literals, but their raw text does not have quotes - not exists(text.regexpCapture("\"(.*)\"", 1)) and - result = text - ) + result = SwiftStringLiterals::getStringValue(this.(StringLiteral)) + or + // Interpolation segments have no delimiters; their raw text is not a complete literal. + this.getParent() instanceof StringInterpolationExpr and + result = this.(StringLiteral).getValue() } /** Gets the immediately-enclosing expression, skipping over intermediate sub-nodes like `Argument`, and without crossing a function boundary. */ diff --git a/unified/ql/lib/codeql/unified/internal/SwiftStringLiterals.qll b/unified/ql/lib/codeql/unified/internal/SwiftStringLiterals.qll new file mode 100644 index 000000000000..312bb8e85856 --- /dev/null +++ b/unified/ql/lib/codeql/unified/internal/SwiftStringLiterals.qll @@ -0,0 +1,90 @@ +/** Provides decoded values of Swift string literals. */ +overlay[local?] +module; + +private import unified +private import codeql.util.Numbers + +private predicate literalContents(StringLiteral literal, string text, string escapePrefix) { + exists(string raw, string hashes, string quote, string contents | + raw = literal.getValue().replaceAll("\r\n", "\n").replaceAll("\r", "\n") and + hashes = raw.regexpCapture("(?s)(#*)(\"\"\"|\")(.*)\\2\\1", 1) and + quote = raw.regexpCapture("(?s)(#*)(\"\"\"|\")(.*)\\2\\1", 2) and + contents = raw.regexpCapture("(?s)(#*)(\"\"\"|\")(.*)\\2\\1", 3) and + escapePrefix = "\\" + hashes + | + quote = "\"" and text = contents + or + quote = "\"\"\"" and + exists(string body, string indent | + body = contents.regexpCapture("(?s)\n(.*)\n([ \t]*)", 1) and + indent = contents.regexpCapture("(?s)\n(.*)\n([ \t]*)", 2) and + text = body.regexpReplaceAll("(?m)^" + indent, "") + ) + ) +} + +private predicate decodedPart(StringLiteral literal, int start, int end, string value) { + exists(string text, string prefix | + literalContents(literal, text, prefix) and + start in [0 .. text.length() - 1] + | + not text.substring(start, start + prefix.length()) = prefix and + end = start + 1 and + value = text.charAt(start) + or + text.substring(start, start + prefix.length()) = prefix and + exists(int next | next = start + prefix.length() | + end = next + 1 and + ( + text.charAt(next) = "0" and value = 0.toUnicode() + or + text.charAt(next) = "t" and value = "\t" + or + text.charAt(next) = "n" and value = "\n" + or + text.charAt(next) = "r" and value = "\r" + or + value = text.charAt(next) and value = ["\\", "\"", "'"] + ) + or + exists(string hex | + hex = + text.substring(next, text.length()).regexpCapture("(?s)u\\{([0-9a-fA-F]{1,8})\\}.*", 1) and + end = next + hex.length() + 3 and + value = parseHexInt(hex).toUnicode() + ) + or + exists(string continuation | + continuation = text.substring(next, text.length()).regexpCapture("(?s)([ \t]*\n).*", 1) and + end = next + continuation.length() and + value = "" + ) + ) + ) +} + +private predicate decodedOffset(StringLiteral literal, int offset) { + offset = 0 + or + decodedPart(literal, _, offset, _) and + exists(int previous | + decodedOffset(literal, previous) and + decodedPart(literal, previous, offset, _) + ) +} + +/** Gets the decoded value of `literal`, if it is a complete, valid string literal. */ +string getStringValue(StringLiteral literal) { + exists(string text | + literalContents(literal, text, _) and + decodedOffset(literal, text.length()) + | + result = + concat(int start, int end, string value | + decodedOffset(literal, start) and decodedPart(literal, start, end, value) + | + value order by start + ) + ) +} diff --git a/unified/ql/test/library-tests/string-literals/test.expected b/unified/ql/test/library-tests/string-literals/test.expected index 74ae833e9821..58927276a808 100644 --- a/unified/ql/test/library-tests/string-literals/test.expected +++ b/unified/ql/test/library-tests/string-literals/test.expected @@ -1,14 +1,14 @@ | test.swift:1:5:1:9 | empty | | | test.swift:2:5:2:10 | simple | abc | -| test.swift:3:5:3:11 | escaped | | -| test.swift:4:5:4:20 | escapedBackslash | \\\\n | -| test.swift:5:5:5:11 | unicode | \\u{61}\\u{1F600} | -| test.swift:6:5:6:7 | raw | #"\\n\\"abc"# | -| test.swift:7:5:7:14 | rawEscapes | | -| test.swift:8:5:8:16 | hashesInside | #"abc#def"# | -| test.swift:9:5:9:13 | multiline | | -| test.swift:13:5:13:19 | multilineIndent | | -| test.swift:17:5:17:22 | multilineEmptyLine | | -| test.swift:22:5:22:25 | multilineContinuation | | -| test.swift:26:5:26:16 | rawMultiline | | +| test.swift:3:5:3:11 | escaped | a\nb\rc\td\u0000e\\f"g'h | +| test.swift:4:5:4:20 | escapedBackslash | \\n | +| test.swift:5:5:5:11 | unicode | a\ud83d\ude00 | +| test.swift:6:5:6:7 | raw | \\n\\"abc | +| test.swift:7:5:7:14 | rawEscapes | \\n\na"\\ | +| test.swift:8:5:8:16 | hashesInside | abc#def | +| test.swift:9:5:9:13 | multiline | aa\|\nbb | +| test.swift:13:5:13:19 | multilineIndent | aa\nbb | +| test.swift:17:5:17:22 | multilineEmptyLine | aa\n\nbb | +| test.swift:22:5:22:25 | multilineContinuation | aabb | +| test.swift:26:5:26:16 | rawMultiline | aa\\n\nbb | | test.swift:30:5:30:17 | interpolation | | From 374b36ecb1f9d70fdcdcc12986b1c79422c90b17 Mon Sep 17 00:00:00 2001 From: Geoffrey White <40627776+geoffw0@users.noreply.github.com> Date: Wed, 4 Nov 2026 12:02:10 +0000 Subject: [PATCH 5/5] unified: Use fromSource() in the test. --- unified/ql/test/library-tests/string-literals/test.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unified/ql/test/library-tests/string-literals/test.ql b/unified/ql/test/library-tests/string-literals/test.ql index 3e5e592ed862..2f6fc3c59c39 100644 --- a/unified/ql/test/library-tests/string-literals/test.ql +++ b/unified/ql/test/library-tests/string-literals/test.ql @@ -1,5 +1,5 @@ import unified from VariableDeclaration decl -where decl.getFile().getRelativePath() = "test.swift" +where decl.getFile().fromSource() select decl.getPattern(), concat(decl.getValue().getStringValue())