Skip to content

Commit ae69dd5

Browse files
authored
Merge pull request #2950 from amritpan/method-keypaths
2 parents 5f7d382 + 8b5df29 commit ae69dd5

20 files changed

+942
-7
lines changed

CodeGeneration/Sources/SyntaxSupport/ExperimentalFeatures.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public enum ExperimentalFeature: String, CaseIterable {
2121
case coroutineAccessors
2222
case valueGenerics
2323
case abiAttribute
24+
case keypathWithMethodMembers
2425

2526
/// The name of the feature as it is written in the compiler's `Features.def` file.
2627
public var featureName: String {
@@ -41,6 +42,8 @@ public enum ExperimentalFeature: String, CaseIterable {
4142
return "ValueGenerics"
4243
case .abiAttribute:
4344
return "ABIAttribute"
45+
case .keypathWithMethodMembers:
46+
return "KeypathWithMethodMembers"
4447
}
4548
}
4649

@@ -63,6 +66,8 @@ public enum ExperimentalFeature: String, CaseIterable {
6366
return "value generics"
6467
case .abiAttribute:
6568
return "@abi attribute"
69+
case .keypathWithMethodMembers:
70+
return "keypaths with method members"
6671
}
6772
}
6873

CodeGeneration/Sources/SyntaxSupport/ExprNodes.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,11 @@ public let EXPR_NODES: [Node] = [
12081208
name: "property",
12091209
kind: .node(kind: .keyPathPropertyComponent)
12101210
),
1211+
Child(
1212+
name: "method",
1213+
kind: .node(kind: .keyPathMethodComponent),
1214+
experimentalFeature: .keypathWithMethodMembers
1215+
),
12111216
Child(
12121217
name: "subscript",
12131218
kind: .node(kind: .keyPathSubscriptComponent)
@@ -1312,6 +1317,33 @@ public let EXPR_NODES: [Node] = [
13121317
]
13131318
),
13141319

1320+
Node(
1321+
kind: .keyPathMethodComponent,
1322+
base: .syntax,
1323+
experimentalFeature: .keypathWithMethodMembers,
1324+
nameForDiagnostics: "key path method component",
1325+
documentation: "A key path component like `.method()`, `.method(10)`, or `.method(val: 10)`.",
1326+
children: [
1327+
Child(
1328+
name: "declName",
1329+
kind: .node(kind: .declReferenceExpr)
1330+
),
1331+
Child(
1332+
name: "leftParen",
1333+
kind: .token(choices: [.token(.leftParen)])
1334+
),
1335+
Child(
1336+
name: "arguments",
1337+
kind: .collection(kind: .labeledExprList, collectionElementName: "Argument"),
1338+
nameForDiagnostics: "arguments"
1339+
),
1340+
Child(
1341+
name: "rightParen",
1342+
kind: .token(choices: [.token(.rightParen)])
1343+
),
1344+
]
1345+
),
1346+
13151347
Node(
13161348
kind: .macroExpansionExpr,
13171349
base: .expr,

CodeGeneration/Sources/SyntaxSupport/SyntaxNodeKind.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public enum SyntaxNodeKind: String, CaseIterable, IdentifierConvertible, TypeCon
182182
case keyPathOptionalComponent
183183
case keyPathPropertyComponent
184184
case keyPathSubscriptComponent
185+
case keyPathMethodComponent
185186
case labeledExpr
186187
case labeledExprList
187188
case labeledSpecializeArgument

CodeGeneration/Tests/ValidateSyntaxNodes/ValidateSyntaxNodes.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,10 @@ class ValidateSyntaxNodes: XCTestCase {
570570
),
571571
ValidationFailure(node: .lifetimeTypeSpecifier, message: "could conform to trait 'Parenthesized' but does not"),
572572
ValidationFailure(node: .codeBlockFile, message: "could conform to trait 'WithCodeBlock' but does not"),
573+
ValidationFailure(
574+
node: .keyPathMethodComponent,
575+
message: "could conform to trait 'Parenthesized' but does not"
576+
),
573577
]
574578
)
575579
}

Sources/SwiftParser/Expressions.swift

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#if compiler(>=6)
14-
@_spi(RawSyntax) internal import SwiftSyntax
14+
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) internal import SwiftSyntax
1515
#else
16-
@_spi(RawSyntax) import SwiftSyntax
16+
@_spi(RawSyntax) @_spi(ExperimentalLanguageFeatures) import SwiftSyntax
1717
#endif
1818

1919
extension TokenConsumer {
@@ -1127,11 +1127,54 @@ extension Parser {
11271127
continue
11281128
}
11291129

1130-
// Check for a .name or .1 suffix.
1130+
// Check for a .name, .1, .name(), .name("Kiwi"), .name(fruit:),
1131+
// .name(_:), .name(fruit: "Kiwi) suffix.
11311132
if self.at(.period) {
11321133
let (unexpectedPeriod, period, declName, generics) = parseDottedExpressionSuffix(
11331134
previousNode: components.last?.raw ?? rootType?.raw ?? backslash.raw
11341135
)
1136+
1137+
// If fully applied method component, parse as a keypath method.
1138+
if self.experimentalFeatures.contains(.keypathWithMethodMembers)
1139+
&& self.at(.leftParen)
1140+
{
1141+
var (unexpectedBeforeLParen, leftParen) = self.expect(.leftParen)
1142+
if let generics = generics {
1143+
unexpectedBeforeLParen = RawUnexpectedNodesSyntax(
1144+
(unexpectedBeforeLParen?.elements ?? []) + [RawSyntax(generics)],
1145+
arena: self.arena
1146+
)
1147+
}
1148+
let args = self.parseArgumentListElements(
1149+
pattern: pattern,
1150+
allowTrailingComma: true
1151+
)
1152+
let (unexpectedBeforeRParen, rightParen) = self.expect(.rightParen)
1153+
1154+
components.append(
1155+
RawKeyPathComponentSyntax(
1156+
unexpectedPeriod,
1157+
period: period,
1158+
component: .method(
1159+
RawKeyPathMethodComponentSyntax(
1160+
declName: declName,
1161+
unexpectedBeforeLParen,
1162+
leftParen: leftParen,
1163+
arguments: RawLabeledExprListSyntax(
1164+
elements: args,
1165+
arena: self.arena
1166+
),
1167+
unexpectedBeforeRParen,
1168+
rightParen: rightParen,
1169+
arena: self.arena
1170+
)
1171+
),
1172+
arena: self.arena
1173+
)
1174+
)
1175+
continue
1176+
}
1177+
// Else, parse as a property.
11351178
components.append(
11361179
RawKeyPathComponentSyntax(
11371180
unexpectedPeriod,
@@ -1152,7 +1195,6 @@ extension Parser {
11521195
// No more postfix expressions.
11531196
break
11541197
}
1155-
11561198
return RawKeyPathExprSyntax(
11571199
unexpectedBeforeBackslash,
11581200
backslash: backslash,

Sources/SwiftParser/generated/ExperimentalFeatures.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ extension Parser.ExperimentalFeatures {
4949
/// Whether to enable the parsing of @abi attribute.
5050
public static let abiAttribute = Self (rawValue: 1 << 7)
5151

52+
/// Whether to enable the parsing of keypaths with method members.
53+
public static let keypathWithMethodMembers = Self (rawValue: 1 << 8)
54+
5255
/// Creates a new value representing the experimental feature with the
5356
/// given name, or returns nil if the name is not recognized.
5457
public init?(name: String) {
@@ -69,6 +72,8 @@ extension Parser.ExperimentalFeatures {
6972
self = .valueGenerics
7073
case "ABIAttribute":
7174
self = .abiAttribute
75+
case "KeypathWithMethodMembers":
76+
self = .keypathWithMethodMembers
7277
default:
7378
return nil
7479
}

Sources/SwiftParserDiagnostics/generated/ChildNameForDiagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ private func childNameForDiagnostics(_ keyPath: AnyKeyPath) -> String? {
207207
return "generic where clause"
208208
case \KeyPathExprSyntax.root:
209209
return "root"
210+
case \KeyPathMethodComponentSyntax.arguments:
211+
return "arguments"
210212
case \KeyPathSubscriptComponentSyntax.arguments:
211213
return "arguments"
212214
case \LabeledExprSyntax.label:

Sources/SwiftParserDiagnostics/generated/SyntaxKindNameForDiagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ extension SyntaxKind {
250250
return "key path component"
251251
case .keyPathExpr:
252252
return "key path"
253+
case .keyPathMethodComponent:
254+
return "key path method component"
253255
case .keyPathOptionalComponent:
254256
return "key path optional component"
255257
case .keyPathPropertyComponent:

Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,6 +1952,24 @@ public func childName(_ keyPath: AnyKeyPath) -> String? {
19521952
return "components"
19531953
case \KeyPathExprSyntax.unexpectedAfterComponents:
19541954
return "unexpectedAfterComponents"
1955+
case \KeyPathMethodComponentSyntax.unexpectedBeforeDeclName:
1956+
return "unexpectedBeforeDeclName"
1957+
case \KeyPathMethodComponentSyntax.declName:
1958+
return "declName"
1959+
case \KeyPathMethodComponentSyntax.unexpectedBetweenDeclNameAndLeftParen:
1960+
return "unexpectedBetweenDeclNameAndLeftParen"
1961+
case \KeyPathMethodComponentSyntax.leftParen:
1962+
return "leftParen"
1963+
case \KeyPathMethodComponentSyntax.unexpectedBetweenLeftParenAndArguments:
1964+
return "unexpectedBetweenLeftParenAndArguments"
1965+
case \KeyPathMethodComponentSyntax.arguments:
1966+
return "arguments"
1967+
case \KeyPathMethodComponentSyntax.unexpectedBetweenArgumentsAndRightParen:
1968+
return "unexpectedBetweenArgumentsAndRightParen"
1969+
case \KeyPathMethodComponentSyntax.rightParen:
1970+
return "rightParen"
1971+
case \KeyPathMethodComponentSyntax.unexpectedAfterRightParen:
1972+
return "unexpectedAfterRightParen"
19551973
case \KeyPathOptionalComponentSyntax.unexpectedBeforeQuestionOrExclamationMark:
19561974
return "unexpectedBeforeQuestionOrExclamationMark"
19571975
case \KeyPathOptionalComponentSyntax.questionOrExclamationMark:

Sources/SwiftSyntax/generated/SyntaxAnyVisitor.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,16 @@ open class SyntaxAnyVisitor: SyntaxVisitor {
13251325
visitAnyPost(node._syntaxNode)
13261326
}
13271327

1328+
@_spi(ExperimentalLanguageFeatures)
1329+
override open func visit(_ node: KeyPathMethodComponentSyntax) -> SyntaxVisitorContinueKind {
1330+
return visitAny(node._syntaxNode)
1331+
}
1332+
1333+
@_spi(ExperimentalLanguageFeatures)
1334+
override open func visitPost(_ node: KeyPathMethodComponentSyntax) {
1335+
visitAnyPost(node._syntaxNode)
1336+
}
1337+
13281338
override open func visit(_ node: KeyPathOptionalComponentSyntax) -> SyntaxVisitorContinueKind {
13291339
return visitAny(node._syntaxNode)
13301340
}

0 commit comments

Comments
 (0)