Skip to content

Commit 0a74d2f

Browse files
committed
1 parent f4acb89 commit 0a74d2f

File tree

3 files changed

+71
-12
lines changed

3 files changed

+71
-12
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -997,18 +997,20 @@ extension Parser {
997997
// the token is an operator starts with '.', or the following token is '['.
998998
let rootType: RawTypeSyntax?
999999
if !self.at(prefix: ".") {
1000-
rootType = self.parseSimpleType(stopAtFirstPeriod: true)
1000+
rootType = self.parseSimpleType(allowMemberTypes: false)
10011001
} else {
10021002
rootType = nil
10031003
}
10041004

10051005
var components: [RawKeyPathComponentSyntax] = []
10061006
var loopProgress = LoopProgressCondition()
10071007
while self.hasProgressed(&loopProgress) {
1008-
// Check for a [] or .[] suffix. The latter is only permitted when there
1009-
// are no components.
1008+
// Check for a [] or .[] suffix. The latter is only permitted after the type name. Since we don't know what
1009+
// constitutes a nested type reference and what constitutes a type's member, we can only disallow it in the parser
1010+
// after seeing the first non-property component.
10101011
if self.at(TokenSpec(.leftSquare, allowAtStartOfLine: false))
1011-
|| (components.isEmpty && self.at(.period) && self.peek(isAt: .leftSquare))
1012+
|| (components.allSatisfy({ $0.component.is(RawKeyPathPropertyComponentSyntax.self) }) && self.at(.period)
1013+
&& self.peek(isAt: .leftSquare))
10121014
{
10131015
// Consume the '.', if it's allowed here.
10141016
let period: RawTokenSyntax?
@@ -1059,11 +1061,7 @@ extension Parser {
10591061
self.currentToken.tokenText
10601062
)
10611063
{
1062-
components.append(
1063-
contentsOf: self.consumeOptionalKeyPathPostfix(
1064-
numComponents: numComponents
1065-
)
1066-
)
1064+
components += self.consumeOptionalKeyPathPostfix(numComponents: numComponents)
10671065
continue
10681066
}
10691067

Sources/SwiftParser/Types.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,8 @@ extension Parser {
190190
return parseSimpleType(forAttributeName: true)
191191
}
192192

193-
/// Parse a "simple" type
194193
mutating func parseSimpleType(
195-
stopAtFirstPeriod: Bool = false,
194+
allowMemberTypes: Bool = true,
196195
forAttributeName: Bool = false
197196
) -> RawTypeSyntax {
198197
enum TypeBaseStart: TokenSpecSet {
@@ -260,7 +259,9 @@ extension Parser {
260259

261260
var loopProgress = LoopProgressCondition()
262261
while self.hasProgressed(&loopProgress) {
263-
if !stopAtFirstPeriod, self.at(.period) {
262+
if allowMemberTypes || (self.at(.period) && self.peek(isAt: .keyword(.Type), .keyword(.Protocol))),
263+
self.at(.period)
264+
{
264265
let (unexpectedPeriod, period, skipMemberName) = self.consumeMemberPeriod(previousNode: base)
265266
if skipMemberName {
266267
let missingIdentifier = missingToken(.identifier)

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,66 @@ final class ExpressionTests: ParserTestCase {
254254
)
255255
}
256256

257+
func testKeyPathSubscriptOnType() {
258+
assertParse(
259+
#"\Foo.Type.[2]"#,
260+
substructure: KeyPathExprSyntax(
261+
root: TypeSyntax(
262+
MetatypeTypeSyntax(baseType: TypeSyntax("Foo"), metatypeSpecifier: .keyword(.Type))
263+
),
264+
components: KeyPathComponentListSyntax([
265+
KeyPathComponentSyntax(
266+
period: .periodToken(),
267+
component: KeyPathComponentSyntax.Component(
268+
KeyPathSubscriptComponentSyntax(
269+
arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("2"))])
270+
)
271+
)
272+
)
273+
])
274+
)
275+
)
276+
277+
assertParse(
278+
#"\Foo.Bar.[2]"#,
279+
substructure: KeyPathExprSyntax(
280+
root: TypeSyntax("Foo"),
281+
components: KeyPathComponentListSyntax([
282+
KeyPathComponentSyntax(
283+
period: .periodToken(),
284+
component: KeyPathComponentSyntax.Component(
285+
KeyPathPropertyComponentSyntax(declName: DeclReferenceExprSyntax(baseName: .identifier("Bar")))
286+
)
287+
),
288+
KeyPathComponentSyntax(
289+
period: .periodToken(),
290+
component: KeyPathComponentSyntax.Component(
291+
KeyPathSubscriptComponentSyntax(
292+
arguments: LabeledExprListSyntax([LabeledExprSyntax(expression: ExprSyntax("2"))])
293+
)
294+
)
295+
),
296+
])
297+
)
298+
)
299+
300+
assertParse(
301+
#"\Foo.Bar.[2].1️⃣[1]"#,
302+
diagnostics: [
303+
DiagnosticSpec(message: "expected identifier in key path property component", fixIts: ["insert identifier"])
304+
],
305+
fixedSource: #"\Foo.Bar.[2].<#identifier#>[1]"#
306+
)
307+
308+
assertParse(
309+
#"\Foo.Bar.?.1️⃣[1]"#,
310+
diagnostics: [
311+
DiagnosticSpec(message: "expected identifier in key path property component", fixIts: ["insert identifier"])
312+
],
313+
fixedSource: #"\Foo.Bar.?.<#identifier#>[1]"#
314+
)
315+
}
316+
257317
func testKeypathExpressionWithSugaredRoot() {
258318
let cases: [UInt: String] = [
259319
// Identifiers

0 commit comments

Comments
 (0)