Skip to content

Commit 95bf56b

Browse files
committed
Fix error for trailing newline before end-of-file
1 parent 184930a commit 95bf56b

File tree

4 files changed

+47
-5
lines changed

4 files changed

+47
-5
lines changed

Sources/SwiftParser/ParseSourceFile.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,9 @@ extension Parser {
152152
}
153153

154154
let remainingTokens = self.consumeRemainingTokens()
155+
155156
if remainingTokens.isEmpty {
156-
return into
157+
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: into.raw))!
157158
}
158159

159160
let existingUnexpected: [RawSyntax]
@@ -166,6 +167,33 @@ extension Parser {
166167
let unexpected = RawUnexpectedNodesSyntax(elements: existingUnexpected + remainingTokens, arena: self.arena)
167168

168169
let withUnexpected = layout.replacingChild(at: layout.children.count - 1, with: unexpected.raw, arena: self.arena)
169-
return R.init(withUnexpected)!
170+
171+
return R.init(transferTrailingTrivaFromEndOfFileIfPresent(raw: withUnexpected))!
172+
}
173+
174+
/// Parses the end-of-file token and appends its leading trivia to the provided `RawSyntax`.
175+
/// - Parameter raw: The raw syntax node to which the leading trivia of the end-of-file token will be appended.
176+
/// - Returns: A new `RawSyntax` instance with trailing trivia transferred from the end-of-file token if present, otherwise it will return the raw parameter..
177+
private mutating func transferTrailingTrivaFromEndOfFileIfPresent(raw: RawSyntax) -> RawSyntax {
178+
guard let endOfFileToken = self.consume(if: .endOfFile),
179+
!endOfFileToken.leadingTriviaPieces.isEmpty,
180+
let raw = raw.withTrailingTrivia(
181+
Trivia(
182+
rawPieces: (raw.trailingTriviaPieces ?? []) + endOfFileToken.leadingTriviaPieces
183+
),
184+
arena: self.arena
185+
)
186+
else {
187+
return raw
188+
}
189+
190+
return raw
191+
}
192+
}
193+
194+
private extension Trivia {
195+
init(rawPieces: [RawTriviaPiece]) {
196+
let pieces = rawPieces.map(TriviaPiece.init(raw:))
197+
self.init(pieces: pieces)
170198
}
171199
}

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,8 @@ extension RawSyntax {
302302
/// - Parameters:
303303
/// - leadingTrivia: The trivia to attach.
304304
/// - arena: SyntaxArena to the result node data resides.
305-
func withLeadingTrivia(_ leadingTrivia: Trivia, arena: SyntaxArena) -> RawSyntax? {
305+
@_spi(RawSyntax)
306+
public func withLeadingTrivia(_ leadingTrivia: Trivia, arena: SyntaxArena) -> RawSyntax? {
306307
switch view {
307308
case .token(let tokenView):
308309
return .makeMaterializedToken(
@@ -328,7 +329,8 @@ extension RawSyntax {
328329
/// - Parameters:
329330
/// - trailingTrivia: The trivia to attach.
330331
/// - arena: SyntaxArena to the result node data resides.
331-
func withTrailingTrivia(_ trailingTrivia: Trivia, arena: SyntaxArena) -> RawSyntax? {
332+
@_spi(RawSyntax)
333+
public func withTrailingTrivia(_ trailingTrivia: Trivia, arena: SyntaxArena) -> RawSyntax? {
332334
switch view {
333335
case .token(let tokenView):
334336
return .makeMaterializedToken(

Tests/SwiftParserTest/DeclarationTests.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3141,4 +3141,17 @@ final class DeclarationTests: ParserTestCase {
31413141
experimentalFeatures: .nonescapableTypes
31423142
)
31433143
}
3144+
3145+
func testDeclarationEndingWithNewline() {
3146+
let inputs: [UInt: String] = [
3147+
#line: "var x = 0\n",
3148+
#line: "var x = 0 garbage\n",
3149+
#line: "var x = 0 \n",
3150+
]
3151+
3152+
for (line, input) in inputs {
3153+
let decl = DeclSyntax(stringLiteral: input)
3154+
XCTAssertEqual(decl.description, input, line: line)
3155+
}
3156+
}
31443157
}

Tests/SwiftSyntaxMacroExpansionTest/PeerMacroTests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ final class PeerMacroTests: XCTestCase {
115115
Task {
116116
completionHandler(await \(call))
117117
}
118-
119118
"""
120119

121120
// Drop the @addCompletionHandler attribute from the new declaration.

0 commit comments

Comments
 (0)