@@ -16,16 +16,21 @@ 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 an expression or statement could be parsed, returns
22+ /// `false` if this parameter is set to `true`.
23+ ///
1924 /// - Note: This function must be kept in sync with `parseStatement()`.
2025 /// - Seealso: ``Parser/parseStatement()``
21- func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
26+ func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool = false ) -> Bool {
2227 var lookahead = self . lookahead ( )
2328 if allowRecovery {
2429 // Attributes are not allowed on statements. But for recovery, skip over
2530 // misplaced attributes.
2631 _ = lookahead. consumeAttributeList ( )
2732 }
28- return lookahead. isStartOfStatement ( allowRecovery: allowRecovery)
33+ return lookahead. isStartOfStatement ( allowRecovery: allowRecovery, preferExpr : preferExpr )
2934 }
3035}
3136
@@ -105,6 +110,8 @@ extension Parser {
105110 return label ( self . parseDoStatement ( doHandle: handle) , with: optLabel)
106111 case ( . yield, let handle) ? :
107112 return label ( self . parseYieldStatement ( yieldHandle: handle) , with: optLabel)
113+ case ( . then, let handle) ? :
114+ return label ( self . parseThenStatement ( handle: handle) , with: optLabel)
108115 case nil :
109116 let missingStmt = RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
110117 return label ( missingStmt, with: optLabel)
@@ -630,7 +637,7 @@ extension Parser {
630637 if self . at ( anyIn: IfOrSwitch . self) != nil {
631638 return true
632639 }
633- if self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) {
640+ if self . atStartOfStatement ( preferExpr : true ) || self . atStartOfDeclaration ( ) {
634641 return false
635642 }
636643 return true
@@ -723,6 +730,35 @@ extension Parser {
723730 }
724731}
725732
733+ extension Parser {
734+ /// Parse a `then` statement.
735+ mutating func parseThenStatement( handle: RecoveryConsumptionHandle ) -> RawStmtSyntax {
736+ guard experimentalFeatures. contains ( . thenStatements) else {
737+ return RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
738+ }
739+ let ( unexpectedBeforeThen, then) = self . eat ( handle)
740+ let hasMisplacedTry = unexpectedBeforeThen? . containsToken ( where: { TokenSpec ( . try ) ~= $0 } ) ?? false
741+
742+ var expr = self . parseExpression ( )
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 ( RawThenStmtSyntax (
754+ unexpectedBeforeThen,
755+ thenKeyword: then,
756+ expression: expr,
757+ arena: self . arena
758+ ) )
759+ }
760+ }
761+
726762extension Parser {
727763 struct StatementLabel {
728764 var label : RawTokenSyntax
@@ -791,7 +827,7 @@ extension Parser {
791827 }
792828
793829 guard
794- self . at ( . identifier) && !self . atStartOfStatement ( ) && !self . atStartOfDeclaration ( )
830+ self . at ( . identifier) && !self . atStartOfStatement ( preferExpr : true ) && !self . atStartOfDeclaration ( )
795831 else {
796832 return nil
797833 }
@@ -806,9 +842,14 @@ extension Parser.Lookahead {
806842 /// Returns `true` if the current token represents the start of a statement
807843 /// item.
808844 ///
845+ /// - Parameters:
846+ /// - allowRecovery: Whether to attempt to perform recovery.
847+ /// - preferExpr: If `true`, if either an expression or statement could be
848+ /// parsed, returns `false` such that an expression can be parsed.
849+ ///
809850 /// - Note: This function must be kept in sync with `parseStatement()`.
810851 /// - Seealso: ``Parser/parseStatement()``
811- mutating func isStartOfStatement( allowRecovery: Bool = false ) -> Bool {
852+ mutating func isStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool = false ) -> Bool {
812853 if ( self . at ( anyIn: SwitchCaseStart . self) != nil || self . at ( . atSign) ) && withLookahead ( { $0. atStartOfSwitchCaseItem ( ) } ) {
813854 // We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression.
814855 return true
@@ -877,11 +918,66 @@ extension Parser.Lookahead {
877918 // For example, could be the function call "discard()".
878919 return false
879920 }
921+
922+ case . then:
923+ return isStartOfThenStatement ( preferExpr: preferExpr)
924+
880925 case nil :
881926 return false
882927 }
883928 }
884929
930+ /// Whether we're currently at a `then` token that should be parsed as a
931+ /// `then` statement.
932+ mutating func isStartOfThenStatement( preferExpr: Bool ) -> Bool {
933+ // If the feature is disabled, don't parse it.
934+ guard self . experimentalFeatures. contains ( . thenStatements) else {
935+ return false
936+ }
937+ guard self . at ( . keyword( . then) ) else {
938+ return false
939+ }
940+
941+ // If we want to prefer an expr, and aren't at the start of a newline, then
942+ // don't parse a ThenStmt.
943+ if preferExpr && !self . atStartOfLine {
944+ return false
945+ }
946+
947+ let next = peek ( )
948+
949+ // If 'then' is followed by a binary or postfix operator, prefer to parse as
950+ // an expr.
951+ if BinaryOperatorLike ( lexeme: next) != nil || PostfixOperatorLike ( lexeme: next) != nil {
952+ return false
953+ }
954+
955+ switch next. rawTokenKind {
956+ case . keyword:
957+ // Treat 'is' and 'as' like the binary operator case, and parse as an
958+ // expr.
959+ switch Keyword ( next. tokenText) {
960+ case . as, . is:
961+ return false
962+ default :
963+ break
964+ }
965+
966+ case . leftBrace:
967+ // This is a trailing closure.
968+ return false
969+
970+ case . leftParen, . leftSquare, . period:
971+ // These are handled based on whether there is trivia between the 'then'
972+ // and the token. If so, it's a 'then' statement. Otherwise it should
973+ // be treated as an expression, e.g `then(...)`, `then[...]`, `then.foo`.
974+ return !self . currentToken. trailingTriviaText. isEmpty || !next. leadingTriviaText. isEmpty
975+ default :
976+ break
977+ }
978+ return true
979+ }
980+
885981 /// Returns whether the parser's current position is the start of a switch case,
886982 /// given that we're in the middle of a switch already.
887983 mutating func isAtStartOfSwitchCase( allowRecovery: Bool = false ) -> Bool {
0 commit comments