Skip to content

Commit ff75751

Browse files
authored
Merge pull request #1826 from hamishknight/getting-out-of-a-bind-5.9
2 parents c89ea14 + 048d780 commit ff75751

File tree

2 files changed

+188
-9
lines changed

2 files changed

+188
-9
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,10 +1210,10 @@ extension Parser {
12101210
)
12111211
)
12121212
case (.identifier, let handle)?, (.selfKeyword, let handle)?, (.initKeyword, let handle)?:
1213-
// If we have "case let x." or "case let x(", we parse x as a normal
1214-
// name, not a binding, because it is the start of an enum pattern or
1215-
// call pattern.
1216-
if pattern.admitsBinding && !self.lookahead().isNextTokenCallPattern() {
1213+
// If we have "case let x" followed by ".", "(", "[", or a generic
1214+
// argument list, we parse x as a normal name, not a binding, because it
1215+
// is the start of an enum or expr pattern.
1216+
if pattern.admitsBinding && self.lookahead().isInBindingPatternPosition() {
12171217
let identifier = self.eat(handle)
12181218
let pattern = RawPatternSyntax(
12191219
RawIdentifierPatternSyntax(
@@ -2658,13 +2658,21 @@ extension Parser.Lookahead {
26582658
return self.peek().isLexerClassifiedKeyword || TokenSpec(.identifier) ~= self.peek()
26592659
}
26602660

2661-
fileprivate func isNextTokenCallPattern() -> Bool {
2661+
fileprivate func isInBindingPatternPosition() -> Bool {
2662+
// Cannot form a binding pattern if a generic argument list follows, this
2663+
// is something like 'case let E<Int>.foo(x)'.
2664+
if self.peek().isContextualPunctuator("<") {
2665+
var lookahead = self.lookahead()
2666+
lookahead.consumeAnyToken()
2667+
return !lookahead.canParseAsGenericArgumentList()
2668+
}
26622669
switch self.peek().rawTokenKind {
2663-
case .period,
2664-
.leftParen:
2665-
return true
2666-
default:
2670+
// A '.' indicates a member access, '(' and '[' indicate a function call or
2671+
// subscript. We can't form a binding pattern as the base of these.
2672+
case .period, .leftParen, .leftSquareBracket:
26672673
return false
2674+
default:
2675+
return true
26682676
}
26692677
}
26702678
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_spi(RawSyntax) import SwiftSyntax
14+
@_spi(RawSyntax) import SwiftParser
15+
import XCTest
16+
17+
final class PatternTests: XCTestCase {
18+
private var genericArgEnumPattern: Syntax {
19+
// let E<Int>.e(y)
20+
Syntax(
21+
ValueBindingPatternSyntax(
22+
bindingKeyword: .keyword(.let),
23+
valuePattern: ExpressionPatternSyntax(
24+
expression: FunctionCallExprSyntax(
25+
calledExpression: MemberAccessExprSyntax(
26+
base: SpecializeExprSyntax(
27+
expression: IdentifierExprSyntax(identifier: .identifier("E")),
28+
genericArgumentClause: GenericArgumentClauseSyntax(
29+
arguments: .init([
30+
.init(argumentType: SimpleTypeIdentifierSyntax(name: .identifier("Int")))
31+
])
32+
)
33+
),
34+
name: .identifier("e")
35+
),
36+
leftParen: .leftParenToken(),
37+
argumentList: TupleExprElementListSyntax([
38+
.init(
39+
expression: UnresolvedPatternExprSyntax(
40+
pattern: IdentifierPatternSyntax(identifier: .identifier("y"))
41+
)
42+
)
43+
]),
44+
rightParen: .rightParenToken()
45+
)
46+
)
47+
)
48+
)
49+
}
50+
51+
func testNonBinding1() {
52+
assertParse(
53+
"""
54+
if case let E<Int>.e(y) = x {}
55+
""",
56+
substructure: genericArgEnumPattern
57+
)
58+
}
59+
60+
func testNonBinding2() {
61+
assertParse(
62+
"""
63+
switch e {
64+
case let E<Int>.e(y):
65+
y
66+
}
67+
""",
68+
substructure: genericArgEnumPattern
69+
)
70+
}
71+
72+
private var tupleWithSubscriptAndBindingPattern: Syntax {
73+
// let (y[0], z)
74+
Syntax(
75+
ValueBindingPatternSyntax(
76+
bindingKeyword: .keyword(.let),
77+
valuePattern: ExpressionPatternSyntax(
78+
expression: TupleExprSyntax(
79+
elementList: .init([
80+
.init(
81+
expression: SubscriptExprSyntax(
82+
calledExpression: IdentifierExprSyntax(identifier: .identifier("y")),
83+
leftBracket: .leftSquareBracketToken(),
84+
argumentList: TupleExprElementListSyntax([
85+
.init(expression: IntegerLiteralExprSyntax(digits: .integerLiteral("0")))
86+
]),
87+
rightBracket: .rightSquareBracketToken()
88+
),
89+
trailingComma: .commaToken()
90+
),
91+
.init(
92+
expression: UnresolvedPatternExprSyntax(
93+
pattern: IdentifierPatternSyntax(identifier: .identifier("z"))
94+
)
95+
),
96+
])
97+
)
98+
)
99+
)
100+
)
101+
}
102+
103+
func testNonBinding3() {
104+
assertParse(
105+
"""
106+
if case let (y[0], z) = x {}
107+
""",
108+
substructure: tupleWithSubscriptAndBindingPattern
109+
)
110+
}
111+
112+
func testNonBinding4() {
113+
assertParse(
114+
"""
115+
switch x {
116+
case let (y[0], z):
117+
z
118+
}
119+
""",
120+
substructure: tupleWithSubscriptAndBindingPattern
121+
)
122+
}
123+
124+
private var subscriptWithBindingPattern: Syntax {
125+
// let y[z]
126+
Syntax(
127+
ValueBindingPatternSyntax(
128+
bindingKeyword: .keyword(.let),
129+
valuePattern: ExpressionPatternSyntax(
130+
expression: SubscriptExprSyntax(
131+
calledExpression: IdentifierExprSyntax(identifier: .identifier("y")),
132+
leftBracket: .leftSquareBracketToken(),
133+
argumentList: TupleExprElementListSyntax([
134+
.init(
135+
expression: UnresolvedPatternExprSyntax(
136+
pattern: IdentifierPatternSyntax(identifier: .identifier("z"))
137+
)
138+
)
139+
]),
140+
rightBracket: .rightSquareBracketToken()
141+
)
142+
)
143+
)
144+
)
145+
}
146+
147+
func testNonBinding5() {
148+
assertParse(
149+
"""
150+
if case let y[z] = x {}
151+
""",
152+
substructure: subscriptWithBindingPattern
153+
)
154+
}
155+
156+
func testNonBinding6() {
157+
assertParse(
158+
"""
159+
switch 0 {
160+
case let y[z]:
161+
z
162+
case y[z]:
163+
0
164+
default:
165+
0
166+
}
167+
""",
168+
substructure: subscriptWithBindingPattern
169+
)
170+
}
171+
}

0 commit comments

Comments
 (0)