Skip to content

Commit d66cdc5

Browse files
committed
Add whitespace if needed when returning syntaxTextBytes
1 parent e99a573 commit d66cdc5

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,21 @@ internal struct RawSyntaxData {
4141
case parsedToken(ParsedToken)
4242
case materializedToken(MaterializedToken)
4343
case layout(Layout)
44+
45+
var tokenKind: RawTokenKind? {
46+
switch self {
47+
case .parsedToken(let dat):
48+
return dat.tokenKind
49+
case .materializedToken(let dat):
50+
return dat.tokenKind
51+
case .layout(let dat):
52+
guard dat.descendantCount == 1 else {
53+
return nil
54+
}
55+
let singleChild = dat.layout.compactMap({ $0 }).first
56+
return singleChild?.payload.tokenKind
57+
}
58+
}
4459
}
4560

4661
/// Token with lazy trivia parsing.
@@ -404,8 +419,16 @@ extension RawSyntax {
404419
}
405420
}
406421
case .layout(let dat):
422+
var previousChild: RawSyntax?
407423
for case let child? in dat.layout {
424+
if child.payload.tokenKind != .stringSegment,
425+
previousChild?.trailingTriviaPieces?.isEmpty == true,
426+
previousChild?.payload.tokenKind?.isPunctuation == false,
427+
previousChild?.payload.tokenKind == child.payload.tokenKind {
428+
try body(SyntaxText(" "))
429+
}
408430
try child.withEachSyntaxText(body: body)
431+
previousChild = child
409432
}
410433
}
411434
}

Tests/SwiftSyntaxBuilderTest/FunctionTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,4 +399,30 @@ final class FunctionTests: XCTestCase {
399399
"""
400400
)
401401
}
402+
403+
func testFunctionDeclCreatedByStringInterpolation() throws {
404+
let declModifierList = DeclModifierListSyntax {
405+
DeclModifierSyntax(name: .keyword(.public))
406+
DeclModifierSyntax(name: .keyword(.static))
407+
}
408+
let identifier = TokenSyntax.identifier("==")
409+
let functionParameterClause = FunctionParameterClauseSyntax {
410+
FunctionParameterSyntax(firstName: TokenSyntax.identifier("lhs"), colon: .colonToken(), type: TypeSyntax("String"))
411+
FunctionParameterSyntax(firstName: TokenSyntax.identifier("rhs"), colon: .colonToken(), type: TypeSyntax("String"))
412+
}
413+
let returnClause = ReturnClauseSyntax(
414+
type: IdentifierTypeSyntax(name: TokenSyntax.identifier("Bool"))
415+
)
416+
let functionDecl = try FunctionDeclSyntax("\(declModifierList) func \(identifier) \(functionParameterClause) \(returnClause)") {
417+
"return lhs < rhs"
418+
}
419+
assertBuildResult(
420+
functionDecl,
421+
"""
422+
public static func == (lhs: String, rhs: String) -> Bool {
423+
return lhs < rhs
424+
}
425+
"""
426+
)
427+
}
402428
}

Tests/SwiftSyntaxBuilderTest/InitializerDeclTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,28 @@ final class InitializerDeclTests: XCTestCase {
8787
"""
8888
)
8989
}
90+
91+
func testInitializerDeclCreatedByStringInterpolation() throws {
92+
let functionPrameterClause = FunctionParameterClauseSyntax {
93+
FunctionParameterSyntax(firstName: "_", secondName: "name", type: TypeSyntax("String"))
94+
FunctionParameterSyntax(firstName: "description", type: TypeSyntax("String"))
95+
}
96+
let functionSignature = FunctionSignatureSyntax(
97+
parameterClause: functionPrameterClause,
98+
effectSpecifiers: .init(asyncSpecifier: .keyword(.async), throwsSpecifier: .keyword(.throws))
99+
)
100+
let initializerDeclFromString = try InitializerDeclSyntax("init\(functionSignature)") {
101+
"self.name = name"
102+
"self.description = description"
103+
}
104+
assertBuildResult(
105+
initializerDeclFromString,
106+
"""
107+
init(_ name: String, description: String) async throws {
108+
self.name = name
109+
self.description = description
110+
}
111+
"""
112+
)
113+
}
90114
}

0 commit comments

Comments
 (0)