Skip to content

Commit 378dd9c

Browse files
authored
Merge pull request #1092 from bnbarham/add-syntax-init-defaults
Refactor `Syntax` initializer
2 parents fd2f742 + e71b64a commit 378dd9c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+3058
-5917
lines changed

CodeGeneration/Sources/generate-swiftsyntaxbuilder/BuildableNodesFile.swift

Lines changed: 21 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,36 @@ let buildableNodesFile = SourceFile {
2525
let type = node.type
2626
let hasTrailingComma = node.traits.contains("WithTrailingComma")
2727

28-
let docComment: SwiftSyntax.Trivia = node.documentation.isEmpty ? [] : .docLineComment("/// \(node.documentation)") + .newline
29-
ExtensionDecl(
30-
leadingTrivia: docComment,
31-
extendedType: SimpleTypeIdentifier(name: .identifier(type.shorthandName)),
32-
inheritanceClause: hasTrailingComma ? TypeInheritanceClause { InheritedType(typeName: Type("HasTrailingComma")) } : nil
33-
) {
34-
// Generate initializers
35-
createDefaultInitializer(node: node)
36-
if let convenienceInit = createConvenienceInitializer(node: node) {
37-
convenienceInit
38-
}
28+
let convenienceInit = createConvenienceInitializer(node: node)
29+
if convenienceInit != nil || hasTrailingComma {
30+
let docComment: SwiftSyntax.Trivia = node.documentation.isEmpty ? [] : .docLineComment("/// \(node.documentation)") + .newline
31+
ExtensionDecl(
32+
leadingTrivia: docComment,
33+
extendedType: SimpleTypeIdentifier(name: .identifier(type.shorthandName)),
34+
inheritanceClause: hasTrailingComma ? TypeInheritanceClause { InheritedType(typeName: Type("HasTrailingComma")) } : nil
35+
) {
36+
if let convenienceInit = convenienceInit {
37+
convenienceInit
38+
}
3939

40-
if hasTrailingComma {
41-
VariableDecl(
40+
if hasTrailingComma {
41+
VariableDecl(
4242
"""
4343
var hasTrailingComma: Bool {
4444
return trailingComma != nil
4545
}
4646
"""
47-
)
47+
)
4848

49-
FunctionDecl(
49+
FunctionDecl(
5050
"""
5151
/// Conformance to `HasTrailingComma`.
5252
public func withTrailingComma(_ withComma: Bool) -> Self {
5353
return withTrailingComma(withComma ? .commaToken() : nil)
5454
}
5555
"""
56-
)
56+
)
57+
}
5758
}
5859
}
5960
}
@@ -67,57 +68,6 @@ private func convertFromSyntaxProtocolToSyntaxType(child: Child) -> Expr {
6768
}
6869
}
6970

70-
/// Create the default initializer for the given node.
71-
private func createDefaultInitializer(node: Node) -> InitializerDecl {
72-
let type = node.type
73-
return InitializerDecl(
74-
leadingTrivia: ([
75-
"/// Creates a `\(type.shorthandName)` using the provided parameters.",
76-
"/// - Parameters:",
77-
] + node.children.map { child in
78-
"/// - \(child.swiftName): \(child.documentation)"
79-
}).map { .docLineComment($0) + .newline }.reduce([], +),
80-
// FIXME: If all parameters are specified, the SwiftSyntaxBuilder initializer is ambigious
81-
// with the memberwise initializer in SwiftSyntax.
82-
// Hot-fix this by preferring the overload in SwiftSyntax. In the long term, consider sinking
83-
// this initializer to SwiftSyntax.
84-
attributes: AttributeList { CustomAttribute("_disfavoredOverload").withTrailingTrivia(.space) },
85-
modifiers: [DeclModifier(name: .public)],
86-
signature: FunctionSignature(
87-
input: ParameterClause {
88-
for trivia in ["leadingTrivia", "trailingTrivia"] {
89-
FunctionParameter("\(trivia): Trivia = []", for: .functionParameters)
90-
}
91-
for child in node.children {
92-
FunctionParameter(
93-
firstName: .identifier(child.swiftName),
94-
colon: .colon,
95-
type: child.parameterType,
96-
defaultArgument: child.type.defaultInitialization.map { InitializerClause(value: $0) }
97-
)
98-
}
99-
}
100-
)
101-
) {
102-
for child in node.children {
103-
if let assertStmt = child.generateAssertStmtTextChoices(varName: child.swiftName) {
104-
assertStmt
105-
}
106-
}
107-
let nodeConstructorCall = FunctionCallExpr(callee: Expr(IdentifierExpr(identifier: .identifier(type.syntaxBaseName)))) {
108-
for child in node.children {
109-
TupleExprElement(
110-
label: child.isUnexpectedNodes ? nil : child.swiftName,
111-
expression: convertFromSyntaxProtocolToSyntaxType(child: child)
112-
)
113-
}
114-
}
115-
SequenceExpr("self = \(nodeConstructorCall)")
116-
SequenceExpr("self.leadingTrivia = leadingTrivia + (self.leadingTrivia ?? [])")
117-
SequenceExpr("self.trailingTrivia = trailingTrivia + (self.trailingTrivia ?? [])")
118-
}
119-
}
120-
12171
/// Create a builder-based convenience initializer, if needed.
12272
private func createConvenienceInitializer(node: Node) -> InitializerDecl? {
12373
// Only create the convenience initializer if at least one parameter
@@ -192,28 +142,24 @@ private func createConvenienceInitializer(node: Node) -> InitializerDecl? {
192142
"/// - Initializing syntax collections using result builders",
193143
"/// - Initializing tokens without default text using strings",
194144
].map { .docLineComment($0) + .newline }.reduce([], +),
195-
// FIXME: If all parameters are specified, the SwiftSyntaxBuilder initializer is ambigious
196-
// with the memberwise initializer in SwiftSyntax.
197-
// Hot-fix this by preferring the overload in SwiftSyntax. In the long term, consider sinking
198-
// this initializer to SwiftSyntax.
199-
attributes: AttributeList { CustomAttribute("_disfavoredOverload").withTrailingTrivia(.space) },
200145
modifiers: [DeclModifier(name: .public)],
201146
signature: FunctionSignature(
202147
input: ParameterClause {
203-
FunctionParameter("leadingTrivia: Trivia = []", for: .functionParameters)
148+
FunctionParameter("leadingTrivia: Trivia? = nil", for: .functionParameters)
204149
for param in normalParameters + builderParameters {
205150
param
206151
}
152+
FunctionParameter("trailingTrivia: Trivia? = nil", for: .functionParameters)
207153
}
208154
)
209155
) {
210156
FunctionCallExpr(callee: "self.init") {
157+
TupleExprElement(label: "leadingTrivia", expression: "leadingTrivia")
211158
for arg in delegatedInitArgs {
212159
arg
213160
}
161+
TupleExprElement(label: "trailingTrivia", expression: "trailingTrivia")
214162
}
215-
216-
SequenceExpr("self.leadingTrivia = leadingTrivia + (self.leadingTrivia ?? [])")
217163
}
218164
}
219165

CodeGeneration/Sources/generate-swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,24 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFile {
3636
}
3737

3838
for node in SYNTAX_NODES {
39-
if node.parserFunction != nil && node.isBase {
39+
if node.isBase {
4040
ExtensionDecl("extension \(node.name)Protocol") {
4141
InitializerDecl(
4242
"""
4343
public init(stringInterpolationOrThrow stringInterpolation: SyntaxStringInterpolation) throws {
44-
self = try performParse(source: stringInterpolation.sourceText, parse: { parser in
45-
let node = \(raw: node.name).parse(from: &parser)
46-
guard let result = node.as(Self.self) else {
47-
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualType: node.kind.syntaxNodeType)
48-
}
49-
return result
50-
})
51-
}
44+
self = try performParse(source: stringInterpolation.sourceText, parse: { parser in
45+
let node = \(raw: node.name).parse(from: &parser)
46+
guard let result = node.as(Self.self) else {
47+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: Self.self, actualType: node.kind.syntaxNodeType)
48+
}
49+
return result
50+
})
51+
}
5252
""")
53-
5453
}
55-
54+
}
55+
56+
if node.parserFunction != nil {
5657
ExtensionDecl("extension \(node.name): SyntaxExpressibleByStringInterpolation") {
5758
InitializerDecl(
5859
"""
@@ -63,7 +64,7 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFile {
6364
}
6465
""")
6566
}
66-
} else if node.parserFunction != nil || (node.baseType.baseName != "Syntax" && node.baseType.baseName != "SyntaxCollection") {
67+
} else if !node.isMissing && node.baseType.baseName != "Syntax" && node.baseType.baseName != "SyntaxCollection" {
6768
ExtensionDecl("extension \(raw: node.name): SyntaxExpressibleByStringInterpolation { }")
6869
}
6970
}

Sources/SwiftOperators/OperatorTable+Defaults.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,7 @@ extension OperatorTable {
411411

412412
Operator(
413413
kind: .infix,
414-
name: "~>",
415-
precedenceGroup: nil
414+
name: "~>"
416415
)
417416
]
418417

Sources/SwiftOperators/SyntaxSynthesis.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ extension Operator {
1717
/// semantic definition.
1818
public func synthesizedSyntax() -> OperatorDeclSyntax {
1919
let modifiers = ModifierListSyntax(
20-
[DeclModifierSyntax(name: .identifier("\(kind)"), detail: nil)]
20+
[DeclModifierSyntax(name: .identifier("\(kind)"))]
2121
)
2222
let operatorKeyword = TokenSyntax.operatorKeyword(leadingTrivia: .space)
2323
let identifierSyntax =
@@ -31,7 +31,7 @@ extension Operator {
3131
}
3232

3333
return OperatorDeclSyntax(
34-
attributes: nil, modifiers: modifiers, operatorKeyword: operatorKeyword,
34+
modifiers: modifiers, operatorKeyword: operatorKeyword,
3535
identifier: identifierSyntax,
3636
operatorPrecedenceAndTypes: precedenceGroupSyntax
3737
)
@@ -55,8 +55,7 @@ extension PrecedenceRelation {
5555
otherNames: PrecedenceGroupNameListSyntax(
5656
[
5757
PrecedenceGroupNameElementSyntax(
58-
name: .identifier(groupName, leadingTrivia: .space),
59-
trailingComma: nil)
58+
name: .identifier(groupName, leadingTrivia: .space))
6059
]
6160
)
6261
)
@@ -126,7 +125,6 @@ extension PrecedenceGroup {
126125
)
127126

128127
return PrecedenceGroupDeclSyntax(
129-
attributes: nil, modifiers: nil,
130128
precedencegroupKeyword: precedencegroupKeyword,
131129
identifier: identifierSyntax, leftBrace: leftBrace,
132130
groupAttributes: PrecedenceGroupAttributeListSyntax(groupAttributes),

Sources/SwiftParserDiagnostics/PresenceUtils.swift

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ class PresentMaker: SyntaxRewriter {
7777
modifiers: node.modifiers,
7878
structKeyword: .structKeyword(presence: .missing),
7979
identifier: .identifier("<#declaration#>", leadingTrivia: leadingTriviaBeforePlaceholder),
80-
genericParameterClause: nil,
81-
inheritanceClause: nil,
82-
genericWhereClause: nil,
8380
members: MemberDeclBlockSyntax(
8481
leftBrace: .leftBraceToken(presence: .missing),
8582
members: MemberDeclListSyntax([]),
@@ -89,39 +86,24 @@ class PresentMaker: SyntaxRewriter {
8986
}
9087

9188
override func visit(_ node: MissingExprSyntax) -> ExprSyntax {
92-
return ExprSyntax(IdentifierExprSyntax(
93-
identifier: .identifier("<#expression#>"),
94-
declNameArguments: nil
95-
))
89+
return ExprSyntax(IdentifierExprSyntax(identifier: .identifier("<#expression#>")))
9690
}
9791

9892
override func visit(_ node: MissingPatternSyntax) -> PatternSyntax {
99-
return PatternSyntax(IdentifierPatternSyntax(
100-
identifier: .identifier("<#pattern#>")
101-
))
93+
return PatternSyntax(IdentifierPatternSyntax(identifier: .identifier("<#pattern#>")))
10294
}
10395

10496
override func visit(_ node: MissingStmtSyntax) -> StmtSyntax {
10597
return StmtSyntax(ExpressionStmtSyntax(
106-
expression: ExprSyntax(IdentifierExprSyntax(
107-
identifier: .identifier("<#statement#>"),
108-
declNameArguments: nil
109-
))
110-
))
98+
expression: IdentifierExprSyntax(identifier: .identifier("<#statement#>"))))
11199
}
112100

113101
override func visit(_ node: MissingTypeSyntax) -> TypeSyntax {
114-
return TypeSyntax(SimpleTypeIdentifierSyntax(
115-
name: .identifier("<#type#>"),
116-
genericArgumentClause: nil
117-
))
102+
return TypeSyntax(SimpleTypeIdentifierSyntax(name: .identifier("<#type#>")))
118103
}
119104

120105
override func visit(_ node: MissingSyntax) -> Syntax {
121-
return Syntax(IdentifierExprSyntax(
122-
identifier: .identifier("<#syntax#>"),
123-
declNameArguments: nil
124-
))
106+
return Syntax(IdentifierExprSyntax(identifier: .identifier("<#syntax#>")))
125107
}
126108
}
127109

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,24 @@ extension RawSyntax {
692692
static func makeLayout<C: Collection>(
693693
kind: SyntaxKind,
694694
from collection: C,
695-
arena: SyntaxArena
695+
arena: SyntaxArena,
696+
leadingTrivia: Trivia? = nil,
697+
trailingTrivia: Trivia? = nil
696698
) -> RawSyntax where C.Element == RawSyntax? {
697-
.makeLayout(kind: kind, uninitializedCount: collection.count, arena: arena) {
699+
if leadingTrivia != nil || trailingTrivia != nil {
700+
var layout = Array(collection)
701+
if let leadingTrivia = leadingTrivia,
702+
let idx = layout.firstIndex(where: {$0 != nil}) {
703+
layout[idx] = layout[idx]!.withLeadingTrivia(leadingTrivia, arena: arena)
704+
}
705+
if let trailingTrivia = trailingTrivia,
706+
let idx = layout.lastIndex(where: {$0 != nil}) {
707+
layout[idx] = layout[idx]!.withTrailingTrivia(trailingTrivia, arena: arena)
708+
}
709+
return .makeLayout(kind: kind, from: layout, arena: arena)
710+
}
711+
712+
return .makeLayout(kind: kind, uninitializedCount: collection.count, arena: arena) {
698713
_ = $0.initialize(from: collection)
699714
}
700715
}

Sources/SwiftSyntax/SyntaxNodes.swift.gyb.template

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -113,35 +113,60 @@ public struct ${node.name}: ${base_type}Protocol, SyntaxHashable {
113113
self._syntaxNode = Syntax(data)
114114
}
115115

116-
public init(
117-
% for (index, child) in enumerate(node.children):
118-
% comma = ',' if index != len(node.children) - 1 else ''
119-
% param_type = child.name if child.node_choices else child.type_name
120-
% if child.is_optional:
121-
% param_type = param_type + "?"
122-
% if child.is_unexpected_nodes():
123-
_ ${child.swift_name}: ${param_type} = nil${comma}
124-
% else:
125-
${child.swift_name}: ${param_type}${comma}
126-
% end
127-
% end
128-
) {
116+
${node.generate_initializer_decl(optional_base_as_missing=False, current_indentation=" ")} {
117+
% if node.children:
129118
let layout: [RawSyntax?] = [
130-
% for child in node.children:
131-
% if child.is_optional:
119+
% for child in node.children:
120+
% if child.is_optional:
132121
${child.swift_name}?.raw,
133-
% else:
122+
% else:
134123
${child.swift_name}.raw,
124+
% end
135125
% end
136-
% end
137126
]
127+
% end
138128
let data: SyntaxData = withExtendedLifetime(SyntaxArena()) { arena in
139-
let raw = RawSyntax.makeLayout(kind: SyntaxKind.${node.swift_syntax_kind},
140-
from: layout, arena: arena)
129+
% if node.children:
130+
let raw = RawSyntax.makeLayout(
131+
kind: SyntaxKind.${node.swift_syntax_kind}, from: layout, arena: arena,
132+
leadingTrivia: leadingTrivia, trailingTrivia: trailingTrivia)
133+
% else:
134+
let raw = RawSyntax.makeEmptyLayout(kind: SyntaxKind.${node.swift_syntax_kind}, arena: arena)
135+
% end
141136
return SyntaxData.forRoot(raw)
142137
}
143138
self.init(data)
144139
}
140+
% if node.has_optional_base_type_child():
141+
142+
% # TODO: Remove when we no longer support compiling in Swift 5.6. Change the
143+
% # above constructor to use `Optional<BaseType.none` instead.
144+
/// This initializer exists solely because Swift 5.6 does not support
145+
/// `Optional<ConcreteType>.none` as a default value of a generic parameter.
146+
/// The above initializer thus defaults to `nil` instead, but that means it
147+
/// is not actually callable when either not passing the defaulted parameter,
148+
/// or passing `nil`.
149+
///
150+
/// Hack around that limitation using this initializer, which takes a
151+
/// `Missing*` syntax node instead. `Missing*` is used over the base type as
152+
/// the base type would allow implicit conversion from a string literal,
153+
/// which the above initializer doesn't support.
154+
${node.generate_initializer_decl(optional_base_as_missing=True, current_indentation=" ")} {
155+
self.init(
156+
leadingTrivia: leadingTrivia,
157+
% for child in node.children:
158+
% if child.has_optional_base_type():
159+
${child.swift_name}: Optional<${child.type_name}>.none,
160+
% elif child.is_unexpected_nodes():
161+
${child.swift_name},
162+
% else:
163+
${child.swift_name}: ${child.swift_name},
164+
% end
165+
% end
166+
trailingTrivia: trailingTrivia
167+
)
168+
}
169+
% end
145170
% for (idx, child) in enumerate(node.children):
146171
% child_node = NODE_MAP.get(child.syntax_kind)
147172
%

0 commit comments

Comments
 (0)