@@ -16,16 +16,22 @@ extension TokenConsumer {
16
16
/// Returns `true` if the current token represents the start of a statement
17
17
/// item.
18
18
///
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
+ ///
19
25
/// - Note: This function must be kept in sync with `parseStatement()`.
20
26
/// - Seealso: ``Parser/parseStatement()``
21
- func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
27
+ func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool ) -> Bool {
22
28
var lookahead = self . lookahead ( )
23
29
if allowRecovery {
24
30
// Attributes are not allowed on statements. But for recovery, skip over
25
31
// misplaced attributes.
26
32
_ = lookahead. consumeAttributeList ( )
27
33
}
28
- return lookahead. atStartOfStatement ( allowRecovery: allowRecovery)
34
+ return lookahead. atStartOfStatement ( allowRecovery: allowRecovery, preferExpr : preferExpr )
29
35
}
30
36
}
31
37
@@ -105,7 +111,9 @@ extension Parser {
105
111
return label ( self . parseDoStatement ( doHandle: handle) , with: optLabel)
106
112
case ( . yield, let handle) ? :
107
113
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, _) ? :
109
117
let missingStmt = RawStmtSyntax ( RawMissingStmtSyntax ( arena: self . arena) )
110
118
return label ( missingStmt, with: optLabel)
111
119
}
@@ -630,7 +638,7 @@ extension Parser {
630
638
if self . at ( anyIn: IfOrSwitch . self) != nil {
631
639
return true
632
640
}
633
- if self . atStartOfStatement ( ) || self . atStartOfDeclaration ( ) {
641
+ if self . atStartOfStatement ( preferExpr : true ) || self . atStartOfDeclaration ( ) {
634
642
return false
635
643
}
636
644
return true
@@ -723,6 +731,36 @@ extension Parser {
723
731
}
724
732
}
725
733
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
+
726
764
extension Parser {
727
765
struct StatementLabel {
728
766
var label : RawTokenSyntax
@@ -791,7 +829,7 @@ extension Parser {
791
829
}
792
830
793
831
guard
794
- self . at ( . identifier) && !self . atStartOfStatement ( ) && !self . atStartOfDeclaration ( )
832
+ self . at ( . identifier) && !self . atStartOfStatement ( preferExpr : true ) && !self . atStartOfDeclaration ( )
795
833
else {
796
834
return nil
797
835
}
@@ -806,9 +844,15 @@ extension Parser.Lookahead {
806
844
/// Returns `true` if the current token represents the start of a statement
807
845
/// item.
808
846
///
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
+ ///
809
853
/// - Note: This function must be kept in sync with `parseStatement()`.
810
854
/// - Seealso: ``Parser/parseStatement()``
811
- mutating func atStartOfStatement( allowRecovery: Bool = false ) -> Bool {
855
+ mutating func atStartOfStatement( allowRecovery: Bool = false , preferExpr : Bool ) -> Bool {
812
856
if ( self . at ( anyIn: SwitchCaseStart . self) != nil || self . at ( . atSign) ) && withLookahead ( { $0. atStartOfSwitchCaseItem ( ) } ) {
813
857
// We consider SwitchCaseItems statements so we don't parse the start of a new case item as trailing parts of an expression.
814
858
return true
@@ -877,11 +921,57 @@ extension Parser.Lookahead {
877
921
// For example, could be the function call "discard()".
878
922
return false
879
923
}
880
- case nil :
924
+
925
+ case . then where experimentalFeatures. contains ( . thenStatements) :
926
+ return atStartOfThenStatement ( preferExpr: preferExpr)
927
+
928
+ case nil , . then:
881
929
return false
882
930
}
883
931
}
884
932
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
+
885
975
/// Returns whether the parser's current position is the start of a switch case,
886
976
/// given that we're in the middle of a switch already.
887
977
mutating func atStartOfSwitchCase( allowRecovery: Bool = false ) -> Bool {
0 commit comments