@@ -16,16 +16,22 @@ extension TokenConsumer {
1616 /// Returns `true` if the current token represents the start of a statement
1717 /// item.
1818 ///
19+ /// - Parameters:
20+ /// - allowRecovery: Whether to attempt to perform recovery.
21+ /// - preferExpr: If either an expression or statement could be
22+ /// parsed and this parameter is `true`, the function returns `false`
23+ /// such that an expression can be parsed.
24+ ///
1925 /// - Note: This function must be kept in sync with `parseStatement()`.
2026 /// - Seealso: ``Parser/parseStatement()``
21- func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
27+ func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool ) -> Bool {
2228 var lookahead = self . lookahead ( )
2329 if allowRecovery {
2430 // Attributes are not allowed on statements. But for recovery, skip over
2531 // misplaced attributes.
2632 _ = lookahead. consumeAttributeList ( )
2733 }
28- return lookahead. atStartOfStatement ( allowRecovery: allowRecovery)
34+ return lookahead. atStartOfStatement ( allowRecovery: allowRecovery, preferExpr : preferExpr )
2935 }
3036}
3137
@@ -105,6 +111,8 @@ extension Parser {
105111 return label ( self . parseDoStatement ( doHandle: handle) , with: optLabel)
106112 case ( . yield, let handle) ? :
107113 return label ( self . parseYieldStatement ( yieldHandle: handle) , with: optLabel)
114+ case ( . then, let handle) ? :
115+ return label ( self . parseThenStatement ( handle: handle) , with: optLabel)
108116 case nil :
109117 let missingStmt = RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
110118 return label ( missingStmt, with: optLabel)
@@ -630,7 +638,7 @@ extension Parser {
630638 if self . at ( anyIn: IfOrSwitch . self) != nil {
631639 return true
632640 }
633- if self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) {
641+ if self . atStartOfStatement ( preferExpr : true ) || self . atStartOfDeclaration ( ) {
634642 return false
635643 }
636644 return true
@@ -723,6 +731,37 @@ extension Parser {
723731 }
724732}
725733
734+ extension Parser {
735+ /// Parse a `then` statement.
736+ mutating func parseThenStatement( handle: RecoveryConsumptionHandle ) -> RawStmtSyntax {
737+ guard experimentalFeatures. contains ( . thenStatements) else {
738+ return RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
739+ }
740+ let ( unexpectedBeforeThen, then) = self . eat ( handle)
741+ let hasMisplacedTry = unexpectedBeforeThen? . containsToken ( where: { TokenSpec ( . try ) ~= $0 } ) ?? false
742+
743+ var expr = self . parseExpression ( )
744+ if hasMisplacedTry && !expr. is ( RawTryExprSyntax . self) {
745+ expr = RawExprSyntax (
746+ RawTryExprSyntax (
747+ tryKeyword: missingToken ( . try ) ,
748+ questionOrExclamationMark: nil ,
749+ expression: expr,
750+ arena: self . arena
751+ )
752+ )
753+ }
754+ return RawStmtSyntax (
755+ RawThenStmtSyntax (
756+ unexpectedBeforeThen,
757+ thenKeyword: then,
758+ expression: expr,
759+ arena: self . arena
760+ )
761+ )
762+ }
763+ }
764+
726765extension Parser {
727766 struct StatementLabel {
728767 var label : RawTokenSyntax
@@ -791,7 +830,7 @@ extension Parser {
791830 }
792831
793832 guard
794- self . at ( . identifier) && !self . atStartOfStatement ( ) && !self . atStartOfDeclaration ( )
833+ self . at ( . identifier) && !self . atStartOfStatement ( preferExpr : true ) && !self . atStartOfDeclaration ( )
795834 else {
796835 return nil
797836 }
@@ -806,9 +845,15 @@ extension Parser.Lookahead {
806845 /// Returns `true` if the current token represents the start of a statement
807846 /// item.
808847 ///
848+ /// - Parameters:
849+ /// - allowRecovery: Whether to attempt to perform recovery.
850+ /// - preferExpr: If either an expression or statement could be
851+ /// parsed and this parameter is `true`, the function returns `false`
852+ /// such that an expression can be parsed.
853+ ///
809854 /// - Note: This function must be kept in sync with `parseStatement()`.
810855 /// - Seealso: ``Parser/parseStatement()``
811- mutating func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
856+ mutating func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool ) -> Bool {
812857 if ( self . at ( anyIn: SwitchCaseStart . self) != nil || self . at ( . atSign) ) && withLookahead ( { $0. atStartOfSwitchCaseItem ( ) } ) {
813858 // We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression.
814859 return true
@@ -877,11 +922,61 @@ extension Parser.Lookahead {
877922 // For example, could be the function call "discard()".
878923 return false
879924 }
925+
926+ case . then:
927+ return atStartOfThenStatement ( preferExpr: preferExpr)
928+
880929 case nil :
881930 return false
882931 }
883932 }
884933
934+ /// Whether we're currently at a `then` token that should be parsed as a
935+ /// `then` statement.
936+ mutating func atStartOfThenStatement( preferExpr: Bool ) -> Bool {
937+ // If the feature is disabled, don't parse it.
938+ guard self . experimentalFeatures. contains ( . thenStatements) else {
939+ return false
940+ }
941+ guard self . at ( . keyword( . then) ) else {
942+ return false
943+ }
944+
945+ // If we prefer an expr and aren't at the start of a newline, then don't
946+ // parse a ThenStmt.
947+ if preferExpr && !self . atStartOfLine {
948+ return false
949+ }
950+
951+ let next = peek ( )
952+
953+ // If 'then' is followed by a binary or postfix operator, prefer to parse as
954+ // an expr.
955+ if BinaryOperatorLike ( lexeme: next) != nil || PostfixOperatorLike ( lexeme: next) != nil {
956+ return false
957+ }
958+
959+ switch PrepareForKeywordMatch ( next) {
960+ case TokenSpec ( . is) , TokenSpec ( . as) :
961+ // Treat 'is' and 'as' like the binary operator case, and parse as an
962+ // expr.
963+ return false
964+
965+ case . leftBrace:
966+ // This is a trailing closure.
967+ return false
968+
969+ case . leftParen, . leftSquare, . period:
970+ // These are handled based on whether there is trivia between the 'then'
971+ // and the token. If so, it's a 'then' statement. Otherwise it should
972+ // be treated as an expression, e.g `then(...)`, `then[...]`, `then.foo`.
973+ return !self . currentToken. trailingTriviaText. isEmpty || !next. leadingTriviaText. isEmpty
974+ default :
975+ break
976+ }
977+ return true
978+ }
979+
885980 /// Returns whether the parser's current position is the start of a switch case,
886981 /// given that we're in the middle of a switch already.
887982 mutating func atStartOfSwitchCase( allowRecovery: Bool = false ) -> Bool {
0 commit comments