diff --git a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift index a6e3feec7d2..2af415e61a4 100644 --- a/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift +++ b/Sources/SwiftParser/StringLiteralRepresentedLiteralValue.swift @@ -16,6 +16,19 @@ @_spi(RawSyntax) @_spi(BumpPtrAllocator) import SwiftSyntax #endif +private func getStringLiteralKind(quoteToken: TokenSyntax) -> StringLiteralKind? { + switch quoteToken.tokenKind { + case .stringQuote: + return .singleLine + case .multilineStringQuote: + return .multiLine + case .singleQuote: + return .singleQuote + default: + return nil + } +} + extension StringLiteralExprSyntax { /// Returns the string value of the literal as the parsed program would see @@ -55,16 +68,7 @@ extension StringLiteralExprSyntax { @_spi(Compiler) public var stringLiteralKind: StringLiteralKind? { - switch openingQuote.tokenKind { - case .stringQuote: - return .singleLine - case .multilineStringQuote: - return .multiLine - case .singleQuote: - return .singleQuote - default: - return nil - } + getStringLiteralKind(quoteToken: openingQuote) } @_spi(Compiler) @@ -73,6 +77,23 @@ extension StringLiteralExprSyntax { } } +extension SimpleStringLiteralExprSyntax { + public var representedLiteralValue: String? { + guard let stringLiteralKind else { return nil } + + var results = "" + for segment in segments { + segment.appendUnescapedLiteralValue(stringLiteralKind: stringLiteralKind, delimiterLength: 0, to: &results) + } + return results + } + + @_spi(Compiler) + public var stringLiteralKind: StringLiteralKind? { + getStringLiteralKind(quoteToken: openingQuote) + } +} + extension StringSegmentSyntax { @_spi(Compiler) public func appendUnescapedLiteralValue( diff --git a/Tests/SwiftParserTest/SimpleStringLiteralRepresentedLiteralValueTests.swift b/Tests/SwiftParserTest/SimpleStringLiteralRepresentedLiteralValueTests.swift new file mode 100644 index 00000000000..e971685b410 --- /dev/null +++ b/Tests/SwiftParserTest/SimpleStringLiteralRepresentedLiteralValueTests.swift @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import SwiftParser +import SwiftSyntax +import SwiftSyntaxBuilder +import XCTest + +class SimpleStringLiteralRepresentedLiteralValueTests: ParserTestCase { + func testBasic() { + test(###""" + #sourceLocation(file: "foo.swift", line: 0) + """###, "foo.swift") + } + + func testBasicUnicode() { + test(###""" + #sourceLocation(file: "foo\u{002E}swift", line: 0) + """###, "foo.swift") + } + + func testMultiline() { + test(###""" + #sourceLocation(file: """ + foo.swift + """, line: 0) + """###, "foo.swift") + } + + func testMultilineEscaped() { + test(###""" + #sourceLocation(file: """ + foo.\ + swift + """, line: 0) + """###, "foo.swift") + } + + func test(_ source: String, _ expectedValue: String, file: StaticString = #filePath, line: UInt = #line) { + let parsed = Parser.parse(source: source) + guard let literalExpr: SimpleStringLiteralExprSyntax = parsed.statements.first?.item.as(PoundSourceLocationSyntax.self)?.arguments?.fileName else { + return XCTFail("target literal expression not found", file: file, line: line) + } + XCTAssertEqual(literalExpr.representedLiteralValue, expectedValue, file: file, line: line) + } +}