Skip to content

Commit 23a9650

Browse files
authored
Merge pull request #729 from CodaFi/concrete-structure
2 parents c919b5e + 43a1d29 commit 23a9650

File tree

4 files changed

+77
-19
lines changed

4 files changed

+77
-19
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ extension Parser {
7070
// sequenced.
7171
var lastElement: RawExprSyntax
7272

73-
lastElement = self.parseSequenceExpressionElement(flavor, forDirective: forDirective)
73+
lastElement = self.parseSequenceExpressionElement(flavor,
74+
forDirective: forDirective,
75+
inVarOrLet: inVarOrLet)
7476

7577
var loopCondition = LoopProgressCondition()
7678
while loopCondition.evaluate(currentToken) {
@@ -101,7 +103,9 @@ extension Parser {
101103
lastElement = RawExprSyntax(RawMissingExprSyntax(arena: self.arena))
102104
break
103105
} else {
104-
lastElement = self.parseSequenceExpressionElement(flavor, forDirective: forDirective)
106+
lastElement = self.parseSequenceExpressionElement(flavor,
107+
forDirective: forDirective,
108+
inVarOrLet: inVarOrLet)
105109
}
106110
}
107111

@@ -153,7 +157,7 @@ extension Parser {
153157
case .infixQuestionMark:
154158
// Save the '?'.
155159
let question = self.eat(.infixQuestionMark)
156-
let firstChoice = self.parseSequenceExpression(flavor)
160+
let firstChoice = self.parseSequenceExpression(flavor, inVarOrLet: inVarOrLet)
157161
// Make sure there's a matching ':' after the middle expr.
158162
let (unexpectedBeforeColon, colon) = self.expect(.colon)
159163

@@ -250,11 +254,13 @@ extension Parser {
250254
@_spi(RawSyntax)
251255
public mutating func parseSequenceExpressionElement(
252256
_ flavor: ExprFlavor,
253-
forDirective: Bool = false
257+
forDirective: Bool = false,
258+
inVarOrLet: Bool = false
254259
) -> RawExprSyntax {
255260
if self.currentToken.isContextualKeyword("await") {
256261
let awaitTok = self.consumeAnyToken()
257-
let sub = self.parseSequenceExpressionElement(flavor)
262+
let sub = self.parseSequenceExpressionElement(flavor,
263+
inVarOrLet: inVarOrLet)
258264
return RawExprSyntax(RawAwaitExprSyntax(
259265
awaitKeyword: awaitTok, expression: sub,
260266
arena: self.arena))
@@ -271,7 +277,9 @@ extension Parser {
271277
}
272278

273279
guard self.at(.tryKeyword) else {
274-
return self.parseUnaryExpression(flavor, forDirective: forDirective)
280+
return self.parseUnaryExpression(flavor,
281+
forDirective: forDirective,
282+
inVarOrLet: inVarOrLet)
275283
}
276284

277285
let tryKeyword = self.eat(.tryKeyword)
@@ -282,7 +290,8 @@ extension Parser {
282290
mark = nil
283291
}
284292

285-
let expression = self.parseSequenceExpressionElement(flavor)
293+
let expression = self.parseSequenceExpressionElement(flavor,
294+
inVarOrLet: inVarOrLet)
286295
return RawExprSyntax(RawTryExprSyntax(
287296
tryKeyword: tryKeyword,
288297
questionOrExclamationMark: mark,
@@ -302,31 +311,32 @@ extension Parser {
302311
@_spi(RawSyntax)
303312
public mutating func parseUnaryExpression(
304313
_ flavor: ExprFlavor,
305-
forDirective: Bool = false
314+
forDirective: Bool = false,
315+
inVarOrLet: Bool = false
306316
) -> RawExprSyntax {
307317
// First check to see if we have the start of a regex literal `/.../`.
308318
// tryLexRegexLiteral(/*forUnappliedOperator*/ false)
309319
switch self.currentToken.tokenKind {
310320
case .prefixAmpersand:
311321
let amp = self.eat(.prefixAmpersand)
312-
let expr = self.parseUnaryExpression(flavor)
322+
let expr = self.parseUnaryExpression(flavor, forDirective: forDirective, inVarOrLet: inVarOrLet)
313323
return RawExprSyntax(RawInOutExprSyntax(
314324
ampersand: amp, expression: RawExprSyntax(expr),
315325
arena: self.arena))
316326

317327
case .backslash:
318-
return RawExprSyntax(self.parseKeyPathExpression(forDirective: forDirective))
328+
return RawExprSyntax(self.parseKeyPathExpression(forDirective: forDirective, inVarOrLet: inVarOrLet))
319329

320330
case .prefixOperator:
321331
let op = self.eat(.prefixOperator)
322-
let postfix = self.parseUnaryExpression(flavor, forDirective: forDirective)
332+
let postfix = self.parseUnaryExpression(flavor, forDirective: forDirective, inVarOrLet: inVarOrLet)
323333
return RawExprSyntax(RawPrefixOperatorExprSyntax(
324334
operatorToken: op, postfixExpression: postfix,
325335
arena: self.arena))
326336

327337
default:
328338
// If the next token is not an operator, just parse this as expr-postfix.
329-
return self.parsePostfixExpression(flavor, forDirective: forDirective)
339+
return self.parsePostfixExpression(flavor, forDirective: forDirective, inVarOrLet: inVarOrLet)
330340
}
331341
}
332342

@@ -347,9 +357,10 @@ extension Parser {
347357
@_spi(RawSyntax)
348358
public mutating func parsePostfixExpression(
349359
_ flavor: ExprFlavor,
350-
forDirective: Bool
360+
forDirective: Bool,
361+
inVarOrLet: Bool
351362
) -> RawExprSyntax {
352-
let head = self.parsePrimaryExpression()
363+
let head = self.parsePrimaryExpression(inVarOrLet: inVarOrLet)
353364
guard !head.is(RawMissingExprSyntax.self) else {
354365
return head
355366
}
@@ -631,7 +642,7 @@ extension Parser {
631642
/// key-path-postfixes → key-path-postfix key-path-postfixes?
632643
/// key-path-postfix → '?' | '!' | 'self' | '[' function-call-argument-list ']'
633644
@_spi(RawSyntax)
634-
public mutating func parseKeyPathExpression(forDirective: Bool) -> RawKeyPathExprSyntax {
645+
public mutating func parseKeyPathExpression(forDirective: Bool, inVarOrLet: Bool) -> RawKeyPathExprSyntax {
635646
// Consume '\'.
636647
let backslash = self.eat(.backslash)
637648

@@ -642,7 +653,7 @@ extension Parser {
642653
// the token is a operator starts with '.', or the following token is '['.
643654
let root: RawExprSyntax?
644655
if !self.currentToken.starts(with: ".") {
645-
root = self.parsePostfixExpression(.basic, forDirective: forDirective)
656+
root = self.parsePostfixExpression(.basic, forDirective: forDirective, inVarOrLet: inVarOrLet)
646657
} else {
647658
root = nil
648659
}
@@ -689,7 +700,7 @@ extension Parser {
689700
/// primary-expression → selector-expression
690701
/// primary-expression → key-path-string-expression
691702
@_spi(RawSyntax)
692-
public mutating func parsePrimaryExpression() -> RawExprSyntax {
703+
public mutating func parsePrimaryExpression(inVarOrLet: Bool) -> RawExprSyntax {
693704
switch self.currentToken.tokenKind {
694705
case .integerLiteral:
695706
let digits = self.eat(.integerLiteral)
@@ -741,6 +752,16 @@ extension Parser {
741752
let tok = self.eat(.__dso_handle__Keyword)
742753
return RawExprSyntax(RawPoundDsohandleExprSyntax(poundDsohandle: tok, arena: self.arena))
743754
case .identifier, .selfKeyword:
755+
// If we have "case let x." or "case let x(", we parse x as a normal
756+
// name, not a binding, because it is the start of an enum pattern or
757+
// call pattern.
758+
if inVarOrLet && !self.lookahead().isNextTokenCallPattern() {
759+
let identifier = self.parseAnyIdentifier()
760+
let pattern = RawPatternSyntax(RawIdentifierPatternSyntax(
761+
identifier: identifier, arena: self.arena))
762+
return RawExprSyntax(RawUnresolvedPatternExprSyntax(pattern: pattern, arena: self.arena))
763+
}
764+
744765
// 'any' followed by another identifier is an existential type.
745766
if self.currentToken.isContextualKeyword("any"),
746767
self.peek().tokenKind == .identifier,
@@ -2114,4 +2135,15 @@ extension Parser.Lookahead {
21142135
}
21152136
return true
21162137
}
2138+
2139+
fileprivate func isNextTokenCallPattern() -> Bool {
2140+
switch self.peek().tokenKind {
2141+
case .period,
2142+
.prefixPeriod,
2143+
.leftParen:
2144+
return true
2145+
default:
2146+
return false
2147+
}
2148+
}
21172149
}

Sources/SwiftParser/Patterns.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,17 @@ extension Parser {
163163
// matching-pattern ::= expr
164164
// Fall back to expression parsing for ambiguous forms. Name lookup will
165165
// disambiguate.
166-
let expr = RawExprSyntax(self.parseSequenceExpression(.basic, inVarOrLet: true))
166+
let patternSyntax = self.parseSequenceExpression(.basic, inVarOrLet: true)
167+
if let pat = patternSyntax.as(RawUnresolvedPatternExprSyntax.self) {
168+
// The most common case here is to parse something that was a lexically
169+
// obvious pattern, which will come back wrapped in an immediate
170+
// RawUnresolvedPatternExprSyntax.
171+
//
172+
// FIXME: This is pretty gross. Let's find a way to disambiguate let
173+
// binding patterns much earlier.
174+
return RawPatternSyntax(pat.pattern)
175+
}
176+
let expr = RawExprSyntax(patternSyntax)
167177
return RawPatternSyntax(RawExpressionPatternSyntax(expression: expr, arena: self.arena))
168178
}
169179
}

Sources/SwiftParser/Statements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ extension Parser {
217217

218218
// Parse the basic expression case. If we have a leading let/var/case
219219
// keyword or an assignment, then we know this is a binding.
220-
if !self.at(.letKeyword) && !self.at(.varKeyword) && !self.at(.caseKeyword) {
220+
guard self.at(.letKeyword) || self.at(.varKeyword) || self.at(.caseKeyword) else {
221221
// If we lack it, then this is theoretically a boolean condition.
222222
// However, we also need to handle migrating from Swift 2 syntax, in
223223
// which a comma followed by an expression could actually be a pattern

Tests/SwiftParserTest/Statements.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@ import XCTest
44

55
final class StatementTests: XCTestCase {
66
func testIf() {
7+
AssertParse("""
8+
if let baz {}
9+
""",
10+
substructure: Syntax(IfStmtSyntax(ifKeyword: .ifKeyword(),
11+
conditions: ConditionElementListSyntax([
12+
ConditionElementSyntax(condition: Syntax(OptionalBindingConditionSyntax(
13+
letOrVarKeyword: .letKeyword(),
14+
pattern: PatternSyntax(IdentifierPatternSyntax(identifier: .identifier("baz"))),
15+
typeAnnotation: nil,
16+
initializer: nil)), trailingComma: nil)
17+
]),
18+
body: .init(leftBrace: .leftBraceToken(),
19+
statements: .init([]),
20+
rightBrace: .rightBraceToken()),
21+
elseKeyword: nil, elseBody: nil)))
22+
723
AssertParse("if let x { }")
824

925
AssertParse(

0 commit comments

Comments
 (0)