Skip to content

Commit f6943b6

Browse files
committed
Add a check whether a code block item appears to be an expression
If the item is not an expression, we can try skipping more tokens to parse a declaration.
1 parent 56e377c commit f6943b6

File tree

4 files changed

+223
-130
lines changed

4 files changed

+223
-130
lines changed

Sources/SwiftParser/Expressions.swift

Lines changed: 40 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@
1212

1313
@_spi(RawSyntax) import SwiftSyntax
1414

15+
extension TokenConsumer {
16+
func atStartOfExpression() -> Bool {
17+
if self.at(anyIn: ExpressionStart.self) != nil {
18+
return true
19+
}
20+
if self.at(.atSign) || self.at(.inoutKeyword) {
21+
var backtrack = self.lookahead()
22+
if backtrack.canParseType() {
23+
return true
24+
}
25+
}
26+
return false
27+
}
28+
}
29+
1530
extension Parser {
1631
public enum ExprFlavor {
1732
case basic
@@ -33,7 +48,7 @@ extension Parser {
3348
//
3449
// Only do this if we're parsing a pattern, to improve QoI on malformed
3550
// expressions followed by (e.g.) let/var decls.
36-
if self.at(any: [.varKeyword, .letKeyword, .isKeyword]) {
51+
if self.at(anyIn: MatchingPatternStart.self) != nil {
3752
let pattern = self.parseMatchingPattern()
3853
return RawExprSyntax(RawUnresolvedPatternExprSyntax(pattern: pattern, arena: self.arena))
3954
}
@@ -292,14 +307,6 @@ extension Parser {
292307
forDirective: Bool = false,
293308
inVarOrLet: Bool = false
294309
) -> RawExprSyntax {
295-
if let awaitTok = self.consumeIfContextualKeyword("await") {
296-
let sub = self.parseSequenceExpressionElement(flavor,
297-
inVarOrLet: inVarOrLet)
298-
return RawExprSyntax(RawAwaitExprSyntax(
299-
awaitKeyword: awaitTok, expression: sub,
300-
arena: self.arena))
301-
}
302-
303310
// Try to parse '@' sign or 'inout' as a attributed typerepr.
304311
if self.at(any: [.atSign, .inoutKeyword]) {
305312
var backtrack = self.lookahead()
@@ -310,21 +317,29 @@ extension Parser {
310317
}
311318
}
312319

313-
guard let tryKeyword = self.consume(if: .tryKeyword) else {
314-
return self.parseUnaryExpression(flavor,
315-
forDirective: forDirective,
316-
inVarOrLet: inVarOrLet)
320+
switch self.at(anyIn: AwaitTry.self) {
321+
case (.awaitContextualKeyword, let handle)?:
322+
let awaitTok = self.eat(handle)
323+
let sub = self.parseSequenceExpressionElement(flavor, inVarOrLet: inVarOrLet)
324+
return RawExprSyntax(RawAwaitExprSyntax(
325+
awaitKeyword: awaitTok,
326+
expression: sub,
327+
arena: self.arena
328+
))
329+
case (.tryKeyword, let handle)?:
330+
let tryKeyword = self.eat(handle)
331+
let mark = self.consume(ifAny: [.exclamationMark, .postfixQuestionMark])
332+
333+
let expression = self.parseSequenceExpressionElement(flavor, inVarOrLet: inVarOrLet)
334+
return RawExprSyntax(RawTryExprSyntax(
335+
tryKeyword: tryKeyword,
336+
questionOrExclamationMark: mark,
337+
expression: expression,
338+
arena: self.arena
339+
))
340+
case nil:
341+
return self.parseUnaryExpression(flavor, forDirective: forDirective, inVarOrLet: inVarOrLet)
317342
}
318-
319-
let mark = self.consume(ifAny: [.exclamationMark, .postfixQuestionMark])
320-
321-
let expression = self.parseSequenceExpressionElement(flavor,
322-
inVarOrLet: inVarOrLet)
323-
return RawExprSyntax(RawTryExprSyntax(
324-
tryKeyword: tryKeyword,
325-
questionOrExclamationMark: mark,
326-
expression: expression,
327-
arena: self.arena))
328343
}
329344

330345
/// Parse an optional prefix operator followed by an expression.
@@ -342,23 +357,9 @@ extension Parser {
342357
forDirective: Bool = false,
343358
inVarOrLet: Bool = false
344359
) -> RawExprSyntax {
345-
enum ExpectedTokenKind: RawTokenKindSubset {
346-
case prefixAmpersand
347-
case backslash
348-
case prefixOperator
349-
350-
var rawTokenKind: RawTokenKind {
351-
switch self {
352-
case .prefixAmpersand: return .prefixAmpersand
353-
case .backslash: return .backslash
354-
case .prefixOperator: return .prefixOperator
355-
}
356-
}
357-
}
358-
359360
// First check to see if we have the start of a regex literal `/.../`.
360361
// tryLexRegexLiteral(/*forUnappliedOperator*/ false)
361-
switch self.at(anyIn: ExpectedTokenKind.self) {
362+
switch self.at(anyIn: ExpressionPrefixOperator.self) {
362363
case (.prefixAmpersand, let handle)?:
363364
let amp = self.eat(handle)
364365
let expr = self.parseUnaryExpression(flavor, forDirective: forDirective, inVarOrLet: inVarOrLet)
@@ -748,94 +749,7 @@ extension Parser {
748749
/// primary-expression → key-path-string-expression
749750
@_spi(RawSyntax)
750751
public mutating func parsePrimaryExpression(inVarOrLet: Bool) -> RawExprSyntax {
751-
enum ExpectedTokenKind: RawTokenKindSubset {
752-
case integerLiteral
753-
case floatingLiteral
754-
case stringLiteral
755-
case regexLiteral
756-
case nilKeyword
757-
case trueKeyword
758-
case falseKeyword
759-
case __file__Keyword
760-
case poundFileIDKeyword
761-
case poundFileKeyword
762-
case poundFilePathKeyword
763-
case poundFunctionKeyword
764-
case __function__Keyword
765-
case poundLineKeyword
766-
case __line__Keyword
767-
case poundColumnKeyword
768-
case __column__Keyword
769-
case poundDsohandleKeyword
770-
case __dso_handle__Keyword
771-
case identifier
772-
case selfKeyword
773-
case capitalSelfKeyword
774-
case anyKeyword
775-
case dollarIdentifier
776-
case wildcardKeyword
777-
case poundSelectorKeyword
778-
case poundKeyPathKeyword
779-
case poundColorLiteralKeyword
780-
case poundImageLiteralKeyword
781-
case poundFileLiteralKeyword
782-
case leftBrace
783-
case period
784-
case prefixPeriod
785-
case superKeyword
786-
case leftParen
787-
case leftSquareBracket
788-
789-
var rawTokenKind: SwiftSyntax.RawTokenKind {
790-
switch self {
791-
case .integerLiteral: return .integerLiteral
792-
case .floatingLiteral: return .floatingLiteral
793-
case .stringLiteral: return .stringLiteral
794-
case .regexLiteral: return .regexLiteral
795-
case .nilKeyword: return .nilKeyword
796-
case .trueKeyword: return .trueKeyword
797-
case .falseKeyword: return .falseKeyword
798-
case .__file__Keyword: return .__file__Keyword
799-
case .poundFileIDKeyword: return .poundFileIDKeyword
800-
case .poundFileKeyword: return .poundFileKeyword
801-
case .poundFilePathKeyword: return .poundFilePathKeyword
802-
case .poundFunctionKeyword: return .poundFunctionKeyword
803-
case .__function__Keyword: return .__function__Keyword
804-
case .poundLineKeyword: return .poundLineKeyword
805-
case .__line__Keyword: return .__line__Keyword
806-
case .poundColumnKeyword: return .poundColumnKeyword
807-
case .__column__Keyword: return .__column__Keyword
808-
case .poundDsohandleKeyword: return .poundDsohandleKeyword
809-
case .__dso_handle__Keyword: return .__dso_handle__Keyword
810-
case .identifier: return .identifier
811-
case .selfKeyword: return .selfKeyword
812-
case .capitalSelfKeyword: return .capitalSelfKeyword
813-
case .anyKeyword: return .anyKeyword
814-
case .dollarIdentifier: return .dollarIdentifier
815-
case .wildcardKeyword: return .wildcardKeyword
816-
case .poundSelectorKeyword: return .poundSelectorKeyword
817-
case .poundKeyPathKeyword: return .poundKeyPathKeyword
818-
case .poundColorLiteralKeyword: return .poundColorLiteralKeyword
819-
case .poundImageLiteralKeyword: return .poundImageLiteralKeyword
820-
case .poundFileLiteralKeyword: return .poundFileLiteralKeyword
821-
case .leftBrace: return .leftBrace
822-
case .period: return .period
823-
case .prefixPeriod: return .prefixPeriod
824-
case .superKeyword: return .superKeyword
825-
case .leftParen: return .leftParen
826-
case .leftSquareBracket: return .leftSquareBracket
827-
}
828-
}
829-
830-
var remappedKind: RawTokenKind? {
831-
switch self {
832-
case .period: return .prefixPeriod
833-
default: return nil
834-
}
835-
}
836-
}
837-
838-
switch self.at(anyIn: ExpectedTokenKind.self) {
752+
switch self.at(anyIn: PrimaryExpressionStart.self) {
839753
case (.integerLiteral, let handle)?:
840754
let digits = self.eat(handle)
841755
return RawExprSyntax(RawIntegerLiteralExprSyntax(

Sources/SwiftParser/Patterns.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,18 +162,22 @@ extension Parser {
162162
/// for-in loops and guard clauses.
163163
mutating func parseMatchingPattern() -> RawPatternSyntax {
164164
// Parse productions that can only be patterns.
165-
if let letOrVar = self.consume(ifAny: [.varKeyword, .letKeyword]) {
165+
switch self.at(anyIn: MatchingPatternStart.self) {
166+
case (.varKeyword, let handle)?,
167+
(.letKeyword, let handle)?:
168+
let letOrVar = self.eat(handle)
166169
let value = self.parseMatchingPattern()
167170
return RawPatternSyntax(RawValueBindingPatternSyntax(
168171
letOrVarKeyword: letOrVar, valuePattern: value, arena: self.arena))
169-
} else if let isKeyword = self.consume(if: .isKeyword) {
172+
case (.isKeyword, let handle)?:
173+
let isKeyword = self.eat(handle)
170174
let type = self.parseType()
171175
return RawPatternSyntax(RawIsTypePatternSyntax(
172176
isKeyword: isKeyword,
173177
type: type,
174178
arena: self.arena
175179
))
176-
} else {
180+
case nil:
177181
// matching-pattern ::= expr
178182
// Fall back to expression parsing for ambiguous forms. Name lookup will
179183
// disambiguate.

0 commit comments

Comments
 (0)