Skip to content

Commit 07456f4

Browse files
committed
Fix missing newline in member macro
1 parent a1ecf4a commit 07456f4

File tree

5 files changed

+107
-4
lines changed

5 files changed

+107
-4
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: An instance of the generic type `R` if the end-of-file token is present, or `nil` otherwise.
177+
private mutating func transferTrailingTrivaFromEndOfFileIfPresent(raw: RawSyntax) -> RawSyntax {
178+
if 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+
{
187+
return raw
188+
} else {
189+
return raw
190+
}
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ extension RawSyntax {
302302
/// - Parameters:
303303
/// - leadingTrivia: The trivia to attach.
304304
/// - arena: SyntaxArena to the result node data resides.
305+
@_spi(RawSyntax)
305306
func withLeadingTrivia(_ leadingTrivia: Trivia, arena: SyntaxArena) -> RawSyntax? {
306307
switch view {
307308
case .token(let tokenView):
@@ -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
@@ -3120,4 +3120,17 @@ final class DeclarationTests: ParserTestCase {
31203120
experimentalFeatures: .nonEscapableTypes
31213121
)
31223122
}
3123+
3124+
func testDeclarationEndingWithNewline() {
3125+
let inputs: [UInt: String] = [
3126+
#line: "var x = 0\n",
3127+
#line: "var x = 0 garbage\n",
3128+
#line: "var x = 0 \n",
3129+
]
3130+
3131+
for (line, input) in inputs {
3132+
let decl = DeclSyntax(stringLiteral: input)
3133+
XCTAssertEqual(decl.description, input, line: line)
3134+
}
3135+
}
31233136
}

Tests/SwiftSyntaxMacroExpansionTest/MemberMacroTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,64 @@ final class MemberMacroTests: XCTestCase {
299299
]
300300
)
301301
}
302+
303+
func testAddMemberToEmptyDeclaration() {
304+
struct TestMacro: MemberMacro {
305+
static func expansion(
306+
of node: AttributeSyntax,
307+
providingMembersOf declaration: some DeclGroupSyntax,
308+
conformingTo protocols: [TypeSyntax],
309+
in context: some MacroExpansionContext
310+
) throws -> [DeclSyntax] {
311+
return [DeclSyntax("var x = 0")]
312+
}
313+
}
314+
315+
assertMacroExpansion(
316+
"""
317+
@Test
318+
struct Foo {}
319+
""",
320+
expandedSource: """
321+
struct Foo {
322+
323+
var x = 0
324+
}
325+
""",
326+
macros: [
327+
"Test": TestMacro.self
328+
],
329+
indentationWidth: indentationWidth
330+
)
331+
}
332+
333+
func testAddMemberToEmptyDeclarationWithEndingNewline() {
334+
struct TestMacro: MemberMacro {
335+
static func expansion(
336+
of node: AttributeSyntax,
337+
providingMembersOf declaration: some DeclGroupSyntax,
338+
conformingTo protocols: [TypeSyntax],
339+
in context: some MacroExpansionContext
340+
) throws -> [DeclSyntax] {
341+
return [DeclSyntax("var x = 0\n")]
342+
}
343+
}
344+
345+
assertMacroExpansion(
346+
"""
347+
@Test
348+
struct Foo {}
349+
""",
350+
expandedSource: """
351+
struct Foo {
352+
353+
var x = 0
354+
}
355+
""",
356+
macros: [
357+
"Test": TestMacro.self
358+
],
359+
indentationWidth: indentationWidth
360+
)
361+
}
302362
}

Tests/SwiftSyntaxMacroExpansionTest/PeerMacroTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ final class PeerMacroTests: XCTestCase {
151151
Task {
152152
completionHandler(await f(a: a, for: b, value))
153153
}
154-
}
154+
}
155155
""",
156156
macros: ["addCompletionHandler": AddCompletionHandler.self],
157157
indentationWidth: indentationWidth

0 commit comments

Comments
 (0)