Skip to content

Commit e0c6ccb

Browse files
committed
Parse keypath methods and initializers.
1 parent caa685e commit e0c6ccb

File tree

2 files changed

+227
-1
lines changed

2 files changed

+227
-1
lines changed

Sources/SwiftParser/Expressions.swift

+66-1
Original file line numberDiff line numberDiff line change
@@ -1103,11 +1103,76 @@ extension Parser {
11031103
continue
11041104
}
11051105

1106-
// Check for a .name or .1 suffix.
1106+
// Check for a .name, .1, .name(), .name("Kiwi"), .name(fruit: "Kiwi) suffix
11071107
if self.at(.period) {
11081108
let (unexpectedPeriod, period, declName, generics) = parseDottedExpressionSuffix(
11091109
previousNode: components.last?.raw ?? rootType?.raw ?? backslash.raw
11101110
)
1111+
1112+
if self.experimentalFeatures.contains(.keypathWithMethodMembers) {
1113+
// If `()` exist, then parse as method
1114+
if self.at(.leftParen) {
1115+
let leftParen = self.consumeAnyToken()
1116+
let args: [RawLabeledExprSyntax]
1117+
if self.at(.rightParen) {
1118+
args = []
1119+
} else {
1120+
args = self.parseArgumentListElements(
1121+
pattern: pattern,
1122+
allowTrailingComma: true
1123+
)
1124+
}
1125+
let (unexpectedBeforeRParen, rightParen) = self.expect(.rightParen)
1126+
1127+
if (!args.isEmpty) {
1128+
components.append(
1129+
RawKeyPathComponentSyntax(
1130+
unexpectedPeriod,
1131+
period: period,
1132+
component: .method(
1133+
RawKeyPathMethodComponentSyntax(
1134+
declName: declName,
1135+
genericArgumentClause: generics,
1136+
leftParen: leftParen,
1137+
arguments: RawLabeledExprListSyntax(
1138+
elements: args,
1139+
arena: self.arena
1140+
),
1141+
unexpectedBeforeRParen,
1142+
rightParen: rightParen,
1143+
arena: self.arena
1144+
)
1145+
),
1146+
arena: self.arena
1147+
)
1148+
)
1149+
} else {
1150+
let list = RawLabeledExprListSyntax(elements: [], arena: self.arena)
1151+
components.append(
1152+
RawKeyPathComponentSyntax(
1153+
unexpectedPeriod,
1154+
period: period,
1155+
component: .method(
1156+
RawKeyPathMethodComponentSyntax(
1157+
declName: declName,
1158+
genericArgumentClause: generics,
1159+
leftParen: leftParen,
1160+
arguments: list,
1161+
unexpectedBeforeRParen,
1162+
rightParen: rightParen,
1163+
arena: self.arena
1164+
)
1165+
),
1166+
arena: self.arena
1167+
)
1168+
)
1169+
}
1170+
1171+
continue
1172+
}
1173+
}
1174+
1175+
// Otherwise, parse as property
11111176
components.append(
11121177
RawKeyPathComponentSyntax(
11131178
unexpectedPeriod,

Tests/SwiftParserTest/ExpressionTests.swift

+161
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,167 @@ final class ExpressionTests: ParserTestCase {
256256
)
257257
}
258258

259+
func testKeyPathMethodAndInitializers() {
260+
assertParse(
261+
#"\Foo.method()"#,
262+
substructure: KeyPathExprSyntax(
263+
root: TypeSyntax("Foo"),
264+
components: KeyPathComponentListSyntax([
265+
KeyPathComponentSyntax(
266+
period: .periodToken(),
267+
component: KeyPathComponentSyntax.Component(
268+
KeyPathMethodComponentSyntax(
269+
declName: DeclReferenceExprSyntax(baseName: .identifier("method")),
270+
leftParen: .leftParenToken(),
271+
arguments: LabeledExprListSyntax([]),
272+
rightParen: .rightParenToken()
273+
)
274+
)
275+
)
276+
])
277+
),
278+
experimentalFeatures: .keypathWithMethodMembers
279+
)
280+
281+
assertParse(
282+
#"\Foo.method(10)"#,
283+
substructure: KeyPathExprSyntax(
284+
root: TypeSyntax("Foo"),
285+
components: KeyPathComponentListSyntax([
286+
KeyPathComponentSyntax(
287+
period: .periodToken(),
288+
component: .init(
289+
KeyPathMethodComponentSyntax(
290+
declName: DeclReferenceExprSyntax(baseName: .identifier("method")),
291+
leftParen: .leftParenToken(),
292+
arguments: LabeledExprListSyntax([
293+
LabeledExprSyntax(
294+
label: nil,
295+
colon: nil,
296+
expression: ExprSyntax("10")
297+
)
298+
]),
299+
rightParen: .rightParenToken()
300+
)
301+
)
302+
)
303+
])
304+
),
305+
experimentalFeatures: .keypathWithMethodMembers
306+
)
307+
308+
assertParse(
309+
#"\Foo.method(arg: 10)"#,
310+
substructure: KeyPathExprSyntax(
311+
root: TypeSyntax("Foo"),
312+
components: KeyPathComponentListSyntax([
313+
KeyPathComponentSyntax(
314+
period: .periodToken(),
315+
component: .init(
316+
KeyPathMethodComponentSyntax(
317+
declName: DeclReferenceExprSyntax(baseName: .identifier("method")),
318+
leftParen: .leftParenToken(),
319+
arguments: LabeledExprListSyntax([
320+
LabeledExprSyntax(
321+
label: .identifier("arg"),
322+
colon: .colonToken(),
323+
expression: ExprSyntax("10")
324+
)
325+
]),
326+
rightParen: .rightParenToken()
327+
)
328+
)
329+
)
330+
])
331+
),
332+
experimentalFeatures: .keypathWithMethodMembers
333+
)
334+
335+
assertParse(
336+
#"\Foo.method().anotherMethod(arg: 10)"#,
337+
substructure: KeyPathExprSyntax(
338+
root: TypeSyntax("Foo"),
339+
components: KeyPathComponentListSyntax([
340+
KeyPathComponentSyntax(
341+
period: .periodToken(),
342+
component: .init(
343+
KeyPathMethodComponentSyntax(
344+
declName: DeclReferenceExprSyntax(baseName: .identifier("method")),
345+
leftParen: .leftParenToken(),
346+
arguments: LabeledExprListSyntax([]),
347+
rightParen: .rightParenToken()
348+
)
349+
)
350+
),
351+
KeyPathComponentSyntax(
352+
period: .periodToken(),
353+
component: .init(
354+
KeyPathMethodComponentSyntax(
355+
declName: DeclReferenceExprSyntax(baseName: .identifier("anotherMethod")),
356+
leftParen: .leftParenToken(),
357+
arguments: LabeledExprListSyntax([
358+
LabeledExprSyntax(
359+
label: .identifier("arg"),
360+
colon: .colonToken(),
361+
expression: ExprSyntax("10")
362+
)
363+
]),
364+
rightParen: .rightParenToken()
365+
)
366+
)
367+
),
368+
])
369+
),
370+
experimentalFeatures: .keypathWithMethodMembers
371+
)
372+
373+
assertParse(
374+
#"\Foo.Type.init()"#,
375+
substructure: KeyPathExprSyntax(
376+
root: TypeSyntax(
377+
MetatypeTypeSyntax(baseType: TypeSyntax("Foo"), metatypeSpecifier: .keyword(.Type))
378+
),
379+
components: KeyPathComponentListSyntax([
380+
KeyPathComponentSyntax(
381+
period: .periodToken(),
382+
component: KeyPathComponentSyntax.Component(
383+
KeyPathMethodComponentSyntax(
384+
declName: DeclReferenceExprSyntax(baseName: .keyword(.init("init")!)),
385+
leftParen: .leftParenToken(),
386+
arguments: LabeledExprListSyntax([]),
387+
rightParen: .rightParenToken()
388+
)
389+
)
390+
)
391+
])
392+
),
393+
experimentalFeatures: .keypathWithMethodMembers
394+
)
395+
396+
assertParse(
397+
#"""
398+
\Foo.method(1️⃣
399+
"""#,
400+
diagnostics: [
401+
DiagnosticSpec(
402+
message: "expected value and ')' to end key path method component",
403+
fixIts: ["insert value and ')'"]
404+
)
405+
],
406+
fixedSource: #"\Foo.method(<#expression#>)"#,
407+
experimentalFeatures: .keypathWithMethodMembers
408+
)
409+
410+
assertParse(
411+
#"\Foo.1️⃣()"#,
412+
diagnostics: [
413+
DiagnosticSpec(message: "expected identifier in key path method component", fixIts: ["insert identifier"])
414+
],
415+
fixedSource: #"\Foo.<#identifier#>()"#,
416+
experimentalFeatures: .keypathWithMethodMembers
417+
)
418+
}
419+
259420
func testKeyPathSubscript() {
260421
assertParse(
261422
#"\Foo.Type.[2]"#,

0 commit comments

Comments
 (0)