@@ -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,7 +111,9 @@ 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)
108- case nil :
114+ case ( . then, let handle) ? where experimentalFeatures. contains ( . thenStatements) :
115+ return label ( self . parseThenStatement ( handle: handle) , with: optLabel)
116+ case nil , ( . then, _) ? :
109117 let missingStmt = RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
110118 return label ( missingStmt, with: optLabel)
111119 }
@@ -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,36 @@ extension Parser {
723731 }
724732}
725733
734+ extension Parser {
735+ /// Parse a `then` statement.
736+ mutating func parseThenStatement( handle: RecoveryConsumptionHandle ) -> RawStmtSyntax {
737+ assert ( experimentalFeatures. contains ( . thenStatements) )
738+
739+ let ( unexpectedBeforeThen, then) = self . eat ( handle)
740+ let hasMisplacedTry = unexpectedBeforeThen? . containsToken ( where: { TokenSpec ( . try ) ~= $0 } ) ?? false
741+
742+ var expr = self . parseExpression ( flavor: . basic, pattern: . none)
743+ if hasMisplacedTry && !expr. is ( RawTryExprSyntax . self) {
744+ expr = RawExprSyntax (
745+ RawTryExprSyntax (
746+ tryKeyword: missingToken ( . try ) ,
747+ questionOrExclamationMark: nil ,
748+ expression: expr,
749+ arena: self . arena
750+ )
751+ )
752+ }
753+ return RawStmtSyntax (
754+ RawThenStmtSyntax (
755+ unexpectedBeforeThen,
756+ thenKeyword: then,
757+ expression: expr,
758+ arena: self . arena
759+ )
760+ )
761+ }
762+ }
763+
726764extension Parser {
727765 struct StatementLabel {
728766 var label : RawTokenSyntax
@@ -791,7 +829,7 @@ extension Parser {
791829 }
792830
793831 guard
794- self . at ( . identifier) && !self . atStartOfStatement ( ) && !self . atStartOfDeclaration ( )
832+ self . at ( . identifier) && !self . atStartOfStatement ( preferExpr : true ) && !self . atStartOfDeclaration ( )
795833 else {
796834 return nil
797835 }
@@ -806,9 +844,15 @@ extension Parser.Lookahead {
806844 /// Returns `true` if the current token represents the start of a statement
807845 /// item.
808846 ///
847+ /// - Parameters:
848+ /// - allowRecovery: Whether to attempt to perform recovery.
849+ /// - preferExpr: If either an expression or statement could be
850+ /// parsed and this parameter is `true`, the function returns `false`
851+ /// such that an expression can be parsed.
852+ ///
809853 /// - Note: This function must be kept in sync with `parseStatement()`.
810854 /// - Seealso: ``Parser/parseStatement()``
811- mutating func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
855+ mutating func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool ) -> Bool {
812856 if ( self . at ( anyIn: SwitchCaseStart . self) != nil || self . at ( . atSign) ) && withLookahead ( { $0. atStartOfSwitchCaseItem ( ) } ) {
813857 // We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression.
814858 return true
@@ -877,11 +921,57 @@ extension Parser.Lookahead {
877921 // For example, could be the function call "discard()".
878922 return false
879923 }
880- case nil :
924+
925+ case . then where experimentalFeatures. contains ( . thenStatements) :
926+ return atStartOfThenStatement ( preferExpr: preferExpr)
927+
928+ case nil , . then:
881929 return false
882930 }
883931 }
884932
933+ /// Whether we're currently at a `then` token that should be parsed as a
934+ /// `then` statement.
935+ mutating func atStartOfThenStatement( preferExpr: Bool ) -> Bool {
936+ guard self . at ( . keyword( . then) ) else {
937+ return false
938+ }
939+
940+ // If we prefer an expr and aren't at the start of a newline, then don't
941+ // parse a ThenStmt.
942+ if preferExpr && !self . atStartOfLine {
943+ return false
944+ }
945+
946+ let next = peek ( )
947+
948+ // If 'then' is followed by a binary or postfix operator, prefer to parse as
949+ // an expr.
950+ if BinaryOperatorLike ( lexeme: next) != nil || PostfixOperatorLike ( lexeme: next) != nil {
951+ return false
952+ }
953+
954+ switch PrepareForKeywordMatch ( next) {
955+ case TokenSpec ( . is) , TokenSpec ( . as) :
956+ // Treat 'is' and 'as' like the binary operator case, and parse as an
957+ // expr.
958+ return false
959+
960+ case . leftBrace:
961+ // This is a trailing closure.
962+ return false
963+
964+ case . leftParen, . leftSquare, . period:
965+ // These are handled based on whether there is trivia between the 'then'
966+ // and the token. If so, it's a 'then' statement. Otherwise it should
967+ // be treated as an expression, e.g `then(...)`, `then[...]`, `then.foo`.
968+ return !self . currentToken. trailingTriviaText. isEmpty || !next. leadingTriviaText. isEmpty
969+ default :
970+ break
971+ }
972+ return true
973+ }
974+
885975 /// Returns whether the parser's current position is the start of a switch case,
886976 /// given that we're in the middle of a switch already.
887977 mutating func atStartOfSwitchCase( allowRecovery: Bool = false ) -> Bool {
0 commit comments