@@ -137,7 +137,7 @@ extension Parser {
137137 /// Parse a guard statement.
138138 mutating func parseGuardStatement( guardHandle: RecoveryConsumptionHandle ) -> RawGuardStmtSyntax {
139139 let ( unexpectedBeforeGuardKeyword, guardKeyword) = self . eat ( guardHandle)
140- let conditions = self . parseConditionList ( )
140+ let conditions = self . parseConditionList ( isGuardStatement : true )
141141 let ( unexpectedBeforeElseKeyword, elseKeyword) = self . expect ( . keyword( . else) )
142142 let body = self . parseCodeBlock ( introducer: guardKeyword)
143143 return RawGuardStmtSyntax (
@@ -154,7 +154,7 @@ extension Parser {
154154
155155extension Parser {
156156 /// Parse a list of condition elements.
157- mutating func parseConditionList( ) -> RawConditionElementListSyntax {
157+ mutating func parseConditionList( isGuardStatement : Bool ) -> RawConditionElementListSyntax {
158158 // We have a simple comma separated list of clauses, but also need to handle
159159 // a variety of common errors situations (including migrating from Swift 2
160160 // syntax).
@@ -183,11 +183,25 @@ extension Parser {
183183 arena: self . arena
184184 )
185185 )
186- } while keepGoing != nil && self . hasProgressed ( & loopProgress)
186+ } while keepGoing != nil && !atConditionListTerminator( isGuardStatement: isGuardStatement)
187+ && self . hasProgressed ( & loopProgress)
187188
188189 return RawConditionElementListSyntax ( elements: elements, arena: self . arena)
189190 }
190191
192+ mutating func atConditionListTerminator( isGuardStatement: Bool ) -> Bool {
193+ guard experimentalFeatures. contains ( . trailingComma) else {
194+ return false
195+ }
196+ // Condition terminator is `else` for `guard` statements.
197+ if isGuardStatement, self . at ( . keyword( . else) ) {
198+ return true
199+ }
200+ // Condition terminator is start of statement body for `if` or `while` statements.
201+ // Missing `else` is a common mistake for `guard` statements so we fall back to lookahead for a body.
202+ return self . at ( . leftBrace) && withLookahead ( { $0. atStartOfConditionalStatementBody ( ) } )
203+ }
204+
191205 /// Parse a condition element.
192206 ///
193207 /// `lastBindingKind` will be used to get a correct fall back, when there is missing `var` or `let` in a `if` statement etc.
@@ -498,7 +512,6 @@ extension Parser {
498512 mutating func parseWhileStatement( whileHandle: RecoveryConsumptionHandle ) -> RawWhileStmtSyntax {
499513 let ( unexpectedBeforeWhileKeyword, whileKeyword) = self . eat ( whileHandle)
500514 let conditions : RawConditionElementListSyntax
501-
502515 if self . at ( . leftBrace) {
503516 conditions = RawConditionElementListSyntax (
504517 elements: [
@@ -511,9 +524,11 @@ extension Parser {
511524 arena: self . arena
512525 )
513526 } else {
514- conditions = self . parseConditionList ( )
527+ conditions = self . parseConditionList ( isGuardStatement : false )
515528 }
529+
516530 let body = self . parseCodeBlock ( introducer: whileKeyword)
531+
517532 return RawWhileStmtSyntax (
518533 unexpectedBeforeWhileKeyword,
519534 whileKeyword: whileKeyword,
@@ -1053,4 +1068,54 @@ extension Parser.Lookahead {
10531068 } while lookahead. at ( . poundIf, . poundElseif, . poundElse) && lookahead. hasProgressed ( & loopProgress)
10541069 return lookahead. atStartOfSwitchCase ( )
10551070 }
1071+
1072+ /// Returns `true` if the current token represents the start of an `if` or `while` statement body.
1073+ mutating func atStartOfConditionalStatementBody( ) -> Bool {
1074+ guard at ( . leftBrace) else {
1075+ // Statement bodies always start with a '{'. If there is no '{', we can't be at the statement body.
1076+ return false
1077+ }
1078+ skipSingle ( )
1079+ if self . at ( . endOfFile) {
1080+ // There's nothing else in the source file that could be the statement body, so this must be it.
1081+ return true
1082+ }
1083+ if self . at ( . semicolon) {
1084+ // We can't have a semicolon between the condition and the statement body, so this must be the statement body.
1085+ return true
1086+ }
1087+ if self . at ( . keyword( . else) ) {
1088+ // If the current token is an `else` keyword, this must be the statement body of an `if` statement since conditions can't be followed by `else`.
1089+ return true
1090+ }
1091+ if self . at ( . rightBrace, . rightParen) {
1092+ // A right brace or parenthesis cannot start a statement body, nor can the condition list continue afterwards. So, this must be the statement body.
1093+ // This covers cases like `if true, { if true, { } }` or `( if true, { print(0) } )`. While the latter is not valid code, it improves diagnostics.
1094+ return true
1095+ }
1096+ if self . atStartOfLine {
1097+ // If the current token is at the start of a line, it is most likely a statement body. The only exceptions are:
1098+ if self . at ( . comma) {
1099+ // If newline begins with ',' it must be a condition trailing comma, so this can't be the statement body, e.g.
1100+ // if true, { true }
1101+ // , true { print("body") }
1102+ return false
1103+ }
1104+ if self . at ( . binaryOperator) {
1105+ // If current token is a binary operator this can't be the statement body since an `if` expression can't be the left-hand side of an operator, e.g.
1106+ // if true, { true }
1107+ // != nil
1108+ // {
1109+ // print("body")
1110+ // }
1111+ return false
1112+ }
1113+ // Excluded the above exceptions, this must be the statement body.
1114+ return true
1115+ } else {
1116+ // If the current token isn't at the start of a line and isn't `EOF`, `;`, `else`, `)` or `}` this can't be the statement body.
1117+ return false
1118+ }
1119+ }
1120+
10561121}
0 commit comments