From e3ea968693b7367c0b0de64e21446c751d99bafc Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 6 Mar 2025 08:34:12 -0800 Subject: [PATCH 1/2] Enable Windows tests in GitHub Actions Fixes #2992 --- .github/workflows/pull_request.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 72267a9bbe7..09fa81ac036 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -8,9 +8,6 @@ jobs: tests: name: Test uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main - with: - # https://github.com/swiftlang/swift-syntax/issues/2992 - enable_windows_checks: false soundness: name: Soundness uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main From 33a5ad453c4572c761723320cffd514515386af6 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 6 Mar 2025 13:45:57 -0800 Subject: [PATCH 2/2] Normalize line ending to `\n` in `StringLiteralExprSyntax.representedLiteralValue` This matches the specification of multi-line string literal handling: From https://docs.swift.org/swift-book/documentation/the-swift-programming-language/lexicalstructure/#String-Literals: > Line breaks in a multiline string literal are normalized to use the line feed character. Even if your source file has a mix of carriage returns and line feeds, all of the line breaks in the string will be the same. From https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170417/035923.html: > The quoted string should normalize newlines to \n in the value of the literal, regardless of whether the source file uses \n (Unix), \r\n (Windows), or \r (classic Mac) line endings. Likewise, when the compiler strips the initial and final newline from the literal value, it will strip one of any of the \n, \r\n, or \r line-ending sequences from both ends of the literal. --- Sources/SwiftParser/Lexer/Cursor.swift | 2 +- .../StringLiteralRepresentedLiteralValue.swift | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftParser/Lexer/Cursor.swift b/Sources/SwiftParser/Lexer/Cursor.swift index 28ed57f8947..bb947d80ecf 100644 --- a/Sources/SwiftParser/Lexer/Cursor.swift +++ b/Sources/SwiftParser/Lexer/Cursor.swift @@ -1594,7 +1594,7 @@ extension Lexer.Cursor { // MARK: Lexing a character in a string literal extension Lexer.Cursor { - enum CharacterLex { + enum CharacterLex: Equatable { /// A normal character as it occurs in the source file case success(Unicode.Scalar) diff --git a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift index 084538b128e..ad7c9d6e944 100644 --- a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift +++ b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift @@ -83,8 +83,8 @@ extension StringSegmentSyntax { precondition(!hasError, "appendUnescapedLiteralValue relies on properly parsed literals") let rawText = content.rawText - if !rawText.contains("\\") { - // Fast path. No escape sequence. + if !rawText.contains(where: { $0 == "\\" || $0 == "\r" }) { + // Fast path. No escape sequence that need to be interpreted or line endings that need to be normalized to \n. output.append(String(syntaxText: rawText)) return } @@ -105,6 +105,18 @@ extension StringSegmentSyntax { ) switch lex { + case .success(Unicode.Scalar("\r")): + // Line endings in multi-line string literals are normalized to line feeds even if the source file has a + // different encoding for new lines. + output.append("\n") + if cursor.peek() == "\n" { + // If we have \r\n, eat the \n as well and leave + let consumed = cursor.lexCharacterInStringLiteral( + stringLiteralKind: stringLiteralKind, + delimiterLength: delimiterLength + ) + assert(consumed == .success(Unicode.Scalar("\n"))) + } case .success(let scalar), .validatedEscapeSequence(let scalar): output.append(Character(scalar))