Skip to content

Commit e5ff0fd

Browse files
committed
Fix parsing bug when a postfix expression is followed by an empty #if clause
1 parent 96439c4 commit e5ff0fd

File tree

6 files changed

+29
-13
lines changed

6 files changed

+29
-13
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ extension Parser {
212212
mutating func parseAttribute() -> RawAttributeListSyntax.Element {
213213
if self.at(.poundIfKeyword) {
214214
return .ifConfigDecl(
215-
self.parsePoundIfDirective { parser -> RawAttributeListSyntax.Element in
215+
self.parsePoundIfDirective { (parser, _) -> RawAttributeListSyntax.Element in
216216
return parser.parseAttribute()
217217
} syntax: { parser, attributes in
218218
return .attributes(RawAttributeListSyntax(elements: attributes, arena: parser.arena))

Sources/SwiftParser/Declarations.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ extension Parser {
176176
// parsed when we're parsing the attributes.
177177
break
178178
}
179-
let directive = self.parsePoundIfDirective { parser in
179+
let directive = self.parsePoundIfDirective { (parser, _) in
180180
let parsedDecl = parser.parseDeclaration()
181181
let semicolon = parser.consume(if: .semicolon)
182182
return RawMemberDeclListItemSyntax(

Sources/SwiftParser/Directives.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,19 @@
1313
@_spi(RawSyntax) import SwiftSyntax
1414

1515
extension Parser {
16-
private enum IfConfigClauseStartKeyword: TokenSpecSet {
17-
case poundIfKeyword
16+
private enum IfConfigContinuationClauseStartKeyword: TokenSpecSet {
1817
case poundElseifKeyword
1918
case poundElseKeyword
2019

2120
var spec: TokenSpec {
2221
switch self {
23-
case .poundIfKeyword: return .poundIfKeyword
2422
case .poundElseifKeyword: return .poundElseifKeyword
2523
case .poundElseKeyword: return .poundElseKeyword
2624
}
2725
}
2826

2927
init?(lexeme: Lexer.Lexeme) {
3028
switch PrepareForKeywordMatch(lexeme) {
31-
case TokenSpec(.poundIfKeyword): self = .poundIfKeyword
3229
case TokenSpec(.poundElseifKeyword): self = .poundElseifKeyword
3330
case TokenSpec(.poundElseKeyword): self = .poundElseKeyword
3431
default: return nil
@@ -89,7 +86,7 @@ extension Parser {
8986
/// into a syntax collection.
9087
@_spi(RawSyntax)
9188
public mutating func parsePoundIfDirective<Element: RawSyntaxNodeProtocol>(
92-
_ parseElement: (inout Parser) -> Element?,
89+
_ parseElement: (_ parser: inout Parser, _ isFirstElement: Bool) -> Element?,
9390
addSemicolonIfNeeded: (_ lastElement: Element, _ newItemAtStartOfLine: Bool, _ parser: inout Parser) -> Element? = { _, _, _ in nil },
9491
syntax: (inout Parser, [Element]) -> RawIfConfigClauseSyntax.Elements?
9592
) -> RawIfConfigDeclSyntax {
@@ -106,7 +103,7 @@ extension Parser {
106103
do {
107104
var firstIteration = true
108105
var loopProgress = LoopProgressCondition()
109-
while let poundIfHandle = firstIteration ? self.canRecoverTo(.poundIfKeyword) : self.canRecoverTo(anyIn: IfConfigClauseStartKeyword.self)?.handle,
106+
while let poundIfHandle = firstIteration ? self.canRecoverTo(.poundIfKeyword) : self.canRecoverTo(anyIn: IfConfigContinuationClauseStartKeyword.self)?.handle,
110107
loopProgress.evaluate(self.currentToken)
111108
{
112109
let (unexpectedBeforePoundIf, poundIf) = self.eat(poundIfHandle)
@@ -127,7 +124,7 @@ extension Parser {
127124
var elementsProgress = LoopProgressCondition()
128125
while !self.at(.eof) && !self.at(.poundElseKeyword, .poundElseifKeyword, .poundEndifKeyword) && elementsProgress.evaluate(currentToken) {
129126
let newItemAtStartOfLine = self.currentToken.isAtStartOfLine
130-
guard let element = parseElement(&self), !element.isEmpty else {
127+
guard let element = parseElement(&self, elements.isEmpty), !element.isEmpty else {
131128
break
132129
}
133130
if let lastElement = elements.last, let fixedUpLastItem = addSemicolonIfNeeded(lastElement, newItemAtStartOfLine, &self) {

Sources/SwiftParser/Expressions.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,10 @@ extension Parser {
659659
) -> RawExprSyntax {
660660
assert(self.at(.poundIfKeyword))
661661

662-
let config = self.parsePoundIfDirective { parser -> RawExprSyntax? in
662+
let config = self.parsePoundIfDirective { (parser, isFirstElement) -> RawExprSyntax? in
663+
if !isFirstElement {
664+
return nil
665+
}
663666
let head: RawExprSyntax
664667
if parser.at(.period) {
665668
head = parser.parseDottedExpressionSuffix(nil)
@@ -2236,7 +2239,7 @@ extension Parser {
22362239
elements.append(
22372240
.ifConfigDecl(
22382241
self.parsePoundIfDirective(
2239-
{ $0.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) },
2242+
{ (parser, _) in parser.parseSwitchCases(allowStandaloneStmtRecovery: allowStandaloneStmtRecovery) },
22402243
syntax: { parser, cases in
22412244
guard cases.count == 1, let firstCase = cases.first else {
22422245
assert(cases.isEmpty)

Sources/SwiftParser/TopLevel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,8 @@ extension Parser {
228228
if self.at(.poundIfKeyword) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) {
229229
// If config of attributes is parsed as part of declaration parsing as it
230230
// doesn't constitute its own code block item.
231-
let directive = self.parsePoundIfDirective {
232-
$0.parseCodeBlockItem()
231+
let directive = self.parsePoundIfDirective { (parser, _) in
232+
parser.parseCodeBlockItem()
233233
} addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, parser in
234234
if lastElement.semicolon == nil && !newItemAtStartOfLine {
235235
return RawCodeBlockItemSyntax(

Tests/SwiftParserTest/DirectiveTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ final class DirectiveTests: XCTestCase {
6969
)
7070
}
7171

72+
func testPostfixIfConfigExpressionContainsPoundIf() {
73+
AssertParse(
74+
"""
75+
b
76+
#if true
77+
.a
78+
1️⃣#if true
79+
#endif
80+
#endif
81+
""",
82+
diagnostics: [
83+
DiagnosticSpec(message: "unexpected code in conditional compilation block")
84+
]
85+
)
86+
}
87+
7288
func testSourceLocation() {
7389
AssertParse(
7490
"""

0 commit comments

Comments
 (0)