Skip to content

Commit 032ebda

Browse files
committed
Significantly restrict nodes expressible by string interpolation
With this change the following nodes will be the only ones that are expressible by string interpolation: - All base nodes - `SourceFileSyntax` - `AccessorDeclSyntax` - `SwitchCase` Most importantly, this removes the implicit expressibility by string literals for all decl/stmt/... nodes, which parsed the node as a decl/stmt/... and then cast it to the expected type. New types now need to explicitly opt in to be expressible by string literals. rdar://104090218
1 parent 3ca3c0b commit 032ebda

33 files changed

+246
-655
lines changed

CodeGeneration/Sources/SyntaxSupport/Node.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class Node {
2424
public let description: String?
2525
public let baseKind: String
2626
public let parserFunction: String?
27+
public let expressibleByStringLiteral: Bool
2728
public let traits: [String]
2829
public let children: [Child]
2930
public let nonUnexpectedChildren: [Child]
@@ -83,6 +84,7 @@ public class Node {
8384
kind: String,
8485
traits: [String] = [],
8586
parserFunction: String? = nil,
87+
expressibleByStringLiteral: Bool = false,
8688
children: [Child] = [],
8789
element: String = "",
8890
elementName: String? = nil,
@@ -95,6 +97,7 @@ public class Node {
9597
self.nameForDiagnostics = nameForDiagnostics
9698
self.description = description
9799
self.parserFunction = parserFunction
100+
self.expressibleByStringLiteral = expressibleByStringLiteral
98101
self.traits = traits
99102
self.baseKind = kind
100103

CodeGeneration/Sources/SyntaxSupport/gyb_generated/AttributeNodes.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public let ATTRIBUTE_NODES: [Node] = [
1818
description: "An `@` attribute.",
1919
kind: "Syntax",
2020
parserFunction: "parseAttribute",
21+
expressibleByStringLiteral: true,
2122
children: [
2223
Child(name: "AtSignToken",
2324
kind: "AtSignToken",

CodeGeneration/Sources/SyntaxSupport/gyb_generated/CommonNodes.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,32 @@ public let COMMON_NODES: [Node] = [
1616
Node(name: "Decl",
1717
nameForDiagnostics: "declaration",
1818
kind: "Syntax",
19-
parserFunction: "parseDeclaration"),
19+
parserFunction: "parseDeclaration",
20+
expressibleByStringLiteral: true),
2021

2122
Node(name: "Expr",
2223
nameForDiagnostics: "expression",
2324
kind: "Syntax",
24-
parserFunction: "parseExpression"),
25+
parserFunction: "parseExpression",
26+
expressibleByStringLiteral: true),
2527

2628
Node(name: "Stmt",
2729
nameForDiagnostics: "statement",
2830
kind: "Syntax",
29-
parserFunction: "parseStatement"),
31+
parserFunction: "parseStatement",
32+
expressibleByStringLiteral: true),
3033

3134
Node(name: "Type",
3235
nameForDiagnostics: "type",
3336
kind: "Syntax",
34-
parserFunction: "parseType"),
37+
parserFunction: "parseType",
38+
expressibleByStringLiteral: true),
3539

3640
Node(name: "Pattern",
3741
nameForDiagnostics: "pattern",
3842
kind: "Syntax",
39-
parserFunction: "parsePattern"),
43+
parserFunction: "parsePattern",
44+
expressibleByStringLiteral: true),
4045

4146
Node(name: "Missing",
4247
nameForDiagnostics: nil,

CodeGeneration/Sources/SyntaxSupport/gyb_generated/DeclNodes.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ public let DECL_NODES: [Node] = [
721721
"WithStatements"
722722
],
723723
parserFunction: "parseSourceFile",
724+
expressibleByStringLiteral: true,
724725
children: [
725726
Child(name: "Statements",
726727
kind: "CodeBlockItemList",
@@ -1063,6 +1064,7 @@ public let DECL_NODES: [Node] = [
10631064
"Attributed"
10641065
],
10651066
parserFunction: "parseAccessorDecl",
1067+
expressibleByStringLiteral: true,
10661068
children: [
10671069
Child(name: "Attributes",
10681070
kind: "AttributeList",

CodeGeneration/Sources/SyntaxSupport/gyb_generated/StmtNodes.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ public let STMT_NODES: [Node] = [
571571
"WithStatements"
572572
],
573573
parserFunction: "parseSwitchCase",
574+
expressibleByStringLiteral: true,
574575
children: [
575576
Child(name: "UnknownAttr",
576577
kind: "Attribute",
@@ -678,6 +679,7 @@ public let STMT_NODES: [Node] = [
678679
"WithCodeBlock"
679680
],
680681
parserFunction: "parseCatchClause",
682+
expressibleByStringLiteral: true,
681683
children: [
682684
Child(name: "CatchKeyword",
683685
kind: "KeywordToken",

CodeGeneration/Sources/SyntaxSupport/gyb_helpers/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def make_swift_node(node):
2222

2323
if node.parser_function:
2424
parameters += ['parserFunction: "%s"' % node.parser_function]
25+
if node.expressible_by_string_literal:
26+
parameters += ['expressibleByStringLiteral: true']
2527

2628
if node.non_unexpected_children:
2729
children = []

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,38 +35,8 @@ let syntaxExpressibleByStringInterpolationConformancesFile = SourceFileSyntax {
3535
""")
3636
}
3737

38-
for node in SYNTAX_NODES {
39-
if node.isBase {
40-
ExtensionDeclSyntax("extension \(raw: node.name)Protocol") {
41-
InitializerDeclSyntax(
42-
"""
43-
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-
}
52-
""")
53-
}
54-
}
55-
56-
if node.parserFunction != nil {
57-
ExtensionDeclSyntax("extension \(raw: node.name): SyntaxExpressibleByStringInterpolation") {
58-
InitializerDeclSyntax(
59-
"""
60-
public init(stringInterpolationOrThrow stringInterpolation: SyntaxStringInterpolation) throws {
61-
self = try performParse(source: stringInterpolation.sourceText, parse: { parser in
62-
return Self.parse(from: &parser)
63-
})
64-
}
65-
""")
66-
}
67-
} else if !node.isMissing && node.baseType.baseName != "Syntax" && node.baseType.baseName != "SyntaxCollection" {
68-
ExtensionDeclSyntax("extension \(raw: node.name): SyntaxExpressibleByStringInterpolation { }")
69-
}
38+
for node in SYNTAX_NODES where node.expressibleByStringLiteral {
39+
ExtensionDeclSyntax("extension \(raw: node.name): SyntaxExpressibleByStringInterpolation {}")
7040
}
7141

7242
FunctionDeclSyntax(

Examples/CodeGenerationUsingSwiftSyntaxBuilder.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ struct Main {
4848
let source = SourceFileSyntax {
4949
StructDeclSyntax(identifier: "Person") {
5050
for (propertyName, propertyType) in properties {
51-
VariableDeclSyntax("var \(raw: propertyName): \(raw: propertyType)")
51+
DeclSyntax("var \(raw: propertyName): \(raw: propertyType)")
5252

53-
FunctionDeclSyntax("""
53+
DeclSyntax("""
5454
func with\(raw: propertyName.withFirstLetterUppercased())(_ \(raw: propertyName): \(raw: propertyType)) -> Person {
5555
var result = self
5656
result.\(raw: propertyName) = \(raw: propertyName)

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ extension VariableDeclSyntax {
408408
attributes: AttributeListSyntax? = nil,
409409
modifiers: ModifierListSyntax? = nil,
410410
_ letOrVarKeyword: Keyword,
411-
name: IdentifierPatternSyntax,
411+
name: PatternSyntax,
412412
type: TypeAnnotationSyntax? = nil,
413413
initializer: InitializerClauseSyntax? = nil
414414
) {
@@ -431,7 +431,7 @@ extension VariableDeclSyntax {
431431
leadingTrivia: Trivia = [],
432432
attributes: AttributeListSyntax? = nil,
433433
modifiers: ModifierListSyntax? = nil,
434-
name: IdentifierPatternSyntax,
434+
name: PatternSyntax,
435435
type: TypeAnnotationSyntax,
436436
@CodeBlockItemListBuilder accessor: () -> CodeBlockItemListSyntax
437437
) {

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ enum SyntaxStringInterpolationError: Error, CustomStringConvertible {
150150
case producedInvalidNodeType(expectedType: SyntaxProtocol.Type, actualType: SyntaxProtocol.Type)
151151
case diagnostics([Diagnostic], tree: Syntax)
152152

153+
static func producedInvalidNodeType<S: SyntaxProtocol>(expectedType: SyntaxProtocol.Type, actualNode: S) -> Self {
154+
return .producedInvalidNodeType(expectedType: expectedType, actualType: type(of: actualNode))
155+
}
156+
153157
var description: String {
154158
switch self {
155159
case .producedInvalidNodeType(expectedType: let expectedType, actualType: let actualType):

Sources/SwiftSyntaxBuilder/SyntaxNodeWithBody.swift

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,31 @@ extension SyntaxStringInterpolation {
3939

4040
public protocol HasTrailingCodeBlock {
4141
var body: CodeBlockSyntax { get set }
42+
43+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) throws
4244
}
4345

44-
public extension HasTrailingCodeBlock where Self: SyntaxExpressibleByStringInterpolation {
45-
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) {
46-
self = "\(signature) {}"
46+
public extension HasTrailingCodeBlock where Self: StmtSyntaxProtocol {
47+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) throws {
48+
let stmt = StmtSyntax("\(signature) {}")
49+
guard let castedStmt = stmt.as(Self.self) else {
50+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: stmt)
51+
}
52+
self = castedStmt
4753
self.body = CodeBlockSyntax(statements: bodyBuilder())
4854
}
4955
}
5056

51-
extension CatchClauseSyntax: HasTrailingCodeBlock {}
57+
extension CatchClauseSyntax: HasTrailingCodeBlock {
58+
public init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) throws {
59+
let stmt = CatchClauseSyntax("\(signature) {}")
60+
guard let castedStmt = stmt.as(Self.self) else {
61+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: stmt)
62+
}
63+
self = castedStmt
64+
self.body = CodeBlockSyntax(statements: bodyBuilder())
65+
}
66+
}
5267
extension DeferStmtSyntax: HasTrailingCodeBlock {}
5368
extension DoStmtSyntax: HasTrailingCodeBlock {}
5469
extension ForInStmtSyntax: HasTrailingCodeBlock {}
@@ -59,11 +74,17 @@ extension WhileStmtSyntax: HasTrailingCodeBlock {}
5974

6075
public protocol HasTrailingOptionalCodeBlock {
6176
var body: CodeBlockSyntax? { get set }
77+
78+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) throws
6279
}
6380

64-
public extension HasTrailingOptionalCodeBlock where Self: SyntaxExpressibleByStringInterpolation {
65-
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) {
66-
self = "\(signature) {}"
81+
public extension HasTrailingOptionalCodeBlock where Self: DeclSyntaxProtocol {
82+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax) throws {
83+
let decl = DeclSyntax("\(signature) {}")
84+
guard let castedDecl = decl.as(Self.self) else {
85+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: decl)
86+
}
87+
self = castedDecl
6788
self.body = CodeBlockSyntax(statements: bodyBuilder())
6889
}
6990
}
@@ -77,11 +98,17 @@ extension InitializerDeclSyntax: HasTrailingOptionalCodeBlock {}
7798

7899
public protocol HasTrailingMemberDeclBlock {
79100
var members: MemberDeclBlockSyntax { get set }
101+
102+
init(_ signature: PartialSyntaxNodeString, @MemberDeclListBuilder membersBuilder: () -> MemberDeclListSyntax) throws
80103
}
81104

82-
public extension HasTrailingMemberDeclBlock where Self: SyntaxExpressibleByStringInterpolation {
83-
init(_ signature: PartialSyntaxNodeString, @MemberDeclListBuilder membersBuilder: () -> MemberDeclListSyntax) {
84-
self = "\(signature) {}"
105+
public extension HasTrailingMemberDeclBlock where Self: DeclSyntaxProtocol {
106+
init(_ signature: PartialSyntaxNodeString, @MemberDeclListBuilder membersBuilder: () -> MemberDeclListSyntax) throws {
107+
let decl = DeclSyntax("\(signature) {}")
108+
guard let castedDecl = decl.as(Self.self) else {
109+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: decl)
110+
}
111+
self = castedDecl
85112
self.members = MemberDeclBlockSyntax(members: membersBuilder())
86113
}
87114
}
@@ -98,15 +125,23 @@ extension StructDeclSyntax: HasTrailingMemberDeclBlock {}
98125
// So we cannot conform to `HasTrailingCodeBlock`
99126

100127
public extension IfStmtSyntax {
101-
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, @CodeBlockItemListBuilder `else` elseBuilder: () -> CodeBlockItemListSyntax? = { nil }) {
102-
self = "\(signature) {}"
128+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, @CodeBlockItemListBuilder `else` elseBuilder: () -> CodeBlockItemListSyntax? = { nil }) throws {
129+
let stmt = StmtSyntax("\(signature) {}")
130+
guard let ifStmt = stmt.as(IfStmtSyntax.self) else {
131+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: stmt)
132+
}
133+
self = ifStmt
103134
self.body = CodeBlockSyntax(statements: bodyBuilder())
104135
self.elseBody = elseBuilder().map { .codeBlock(CodeBlockSyntax(statements: $0)) }
105136
self.elseKeyword = elseBody != nil ? .keyword(.else) : nil
106137
}
107138

108-
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, elseIf: IfStmtSyntax) {
109-
self = "\(signature) {}"
139+
init(_ signature: PartialSyntaxNodeString, @CodeBlockItemListBuilder bodyBuilder: () -> CodeBlockItemListSyntax, elseIf: IfStmtSyntax) throws {
140+
let stmt = StmtSyntax("\(signature) {}")
141+
guard let ifStmt = stmt.as(IfStmtSyntax.self) else {
142+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: stmt)
143+
}
144+
self = ifStmt
110145
self.body = CodeBlockSyntax(statements: bodyBuilder())
111146
self.elseBody = .ifStmt(elseIf)
112147
self.elseKeyword = elseBody != nil ? .keyword(.else) : nil
@@ -118,8 +153,12 @@ public extension IfStmtSyntax {
118153
// So we cannot conform to `HasTrailingCodeBlock` or `HasTrailingMemberDeclBlock`
119154

120155
public extension SwitchStmtSyntax {
121-
init(_ signature: PartialSyntaxNodeString, @SwitchCaseListBuilder casesBuilder: () -> SwitchCaseListSyntax = { SwitchCaseListSyntax([]) }) {
122-
self = "\(signature) {}"
156+
init(_ signature: PartialSyntaxNodeString, @SwitchCaseListBuilder casesBuilder: () -> SwitchCaseListSyntax = { SwitchCaseListSyntax([]) }) throws {
157+
let stmt = StmtSyntax("\(signature) {}")
158+
guard let castedStmt = stmt.as(Self.self) else {
159+
throw SyntaxStringInterpolationError.producedInvalidNodeType(expectedType: IfStmtSyntax.self, actualNode: stmt)
160+
}
161+
self = castedStmt
123162
self.cases = casesBuilder()
124163
}
125164
}

0 commit comments

Comments
 (0)