Skip to content

Commit 3f2abd2

Browse files
committed
[SwiftParser] Improve '#if' recovery
1 parent bae4b34 commit 3f2abd2

File tree

9 files changed

+219
-255
lines changed

9 files changed

+219
-255
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,8 @@ extension Parser {
3333
//
3434
// In such cases, the second `#if` is not `consumeIfConfigOfAttributes()`.
3535
return .ifConfigDecl(
36-
self.parsePoundIfDirective { (parser, _) -> RawAttributeListSyntax.Element in
37-
return parser.parseAttributeListElement()
38-
} syntax: { parser, attributes in
39-
return .attributes(RawAttributeListSyntax(elements: attributes, arena: parser.arena))
36+
self.parsePoundIfDirective { parser in
37+
return .attributes(parser.parseAttributeList())
4038
}
4139
)
4240
} else {
@@ -896,7 +894,26 @@ extension Parser {
896894
return makeMissingProviderArguments(unexpectedBefore: [])
897895
}
898896

899-
let decl = parseDeclaration(in: .argumentList)
897+
let decl: RawDeclSyntax
898+
if self.at(.poundIf) {
899+
let ifConfig = self.parsePoundIfDirective({ parser in
900+
let decl = parser.parseDeclaration(in: .argumentList)
901+
let member = RawMemberBlockItemSyntax(decl: decl, semicolon: nil, arena: parser.arena)
902+
return .decls(RawMemberBlockItemListSyntax(elements: [member], arena: parser.arena))
903+
})
904+
decl = ifConfig.makeUnexpectedKeepingFirstNode(
905+
of: RawDeclSyntax.self, arena: self.arena,
906+
where: { !$0.is(RawIfConfigDeclSyntax.self) },
907+
makeMissing: {
908+
RawDeclSyntax(RawMissingDeclSyntax(
909+
attributes: self.emptyCollection(RawAttributeListSyntax.self),
910+
modifiers: self.emptyCollection(RawDeclModifierListSyntax.self),
911+
arena: self.arena)
912+
)
913+
})
914+
} else {
915+
decl = self.parseDeclaration(in: .argumentList)
916+
}
900917

901918
guard let provider = RawABIAttributeArgumentsSyntax.Provider(decl) else {
902919
return makeMissingProviderArguments(unexpectedBefore: [decl.raw])

Sources/SwiftParser/CompilerFiles.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ extension Parser {
8989

9090
/// Parse a declaration macro expansions in type contexts.
9191
mutating func parseMemberBlockItemListFile() -> RawMemberBlockItemListFileSyntax {
92-
let members = self.parseMemberDeclList()
92+
let members = self.parseMemberDeclList(until: { _ in false })
9393
let (unexpectedBeforeEndOfFileToken, endOfFile) = self.expectEndOfFile()
9494

9595
return RawMemberBlockItemListFileSyntax(

Sources/SwiftParser/Declarations.swift

Lines changed: 61 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,9 @@ extension TokenConsumer {
5353
}
5454

5555
mutating func atStartOfDeclaration(
56-
isAtTopLevel: Bool = false,
5756
allowInitDecl: Bool = true,
58-
allowRecovery: Bool = false,
59-
requiresDecl: Bool = false,
57+
requiresDecl: Bool = false
6058
) -> Bool {
61-
if self.at(.poundIf) {
62-
return true
63-
}
64-
6559
var subparser = self.lookahead()
6660

6761
var hasAttribute = false
@@ -71,7 +65,6 @@ extension TokenConsumer {
7165
_ = subparser.consumeAttributeList()
7266
hasAttribute = true
7367
} else if subparser.at(.poundIf) && subparser.consumeIfConfigOfAttributes() {
74-
subparser.skipSingle()
7568
hasAttribute = true
7669
} else {
7770
break
@@ -106,17 +99,7 @@ extension TokenConsumer {
10699
}
107100
}
108101

109-
let declStartKeyword: DeclarationKeyword?
110-
if allowRecovery {
111-
declStartKeyword =
112-
subparser.canRecoverTo(
113-
anyIn: DeclarationKeyword.self,
114-
overrideRecoveryPrecedence: isAtTopLevel ? nil : .closingBrace
115-
)?.0
116-
} else {
117-
declStartKeyword = subparser.at(anyIn: DeclarationKeyword.self)?.0
118-
}
119-
switch declStartKeyword {
102+
switch subparser.at(anyIn: DeclarationKeyword.self)?.0 {
120103
case .lhs(.actor):
121104
// actor Foo {}
122105
if subparser.peek().rawTokenKind == .identifier {
@@ -128,7 +111,7 @@ extension TokenConsumer {
128111
var lookahead = subparser.lookahead()
129112
repeat {
130113
lookahead.consumeAnyToken()
131-
} while lookahead.atStartOfDeclaration(isAtTopLevel: isAtTopLevel, allowInitDecl: allowInitDecl)
114+
} while lookahead.atStartOfDeclaration(allowInitDecl: allowInitDecl)
132115
return lookahead.at(.identifier)
133116
case .lhs(.case):
134117
// When 'case' appears inside a function, it's probably a switch
@@ -177,9 +160,8 @@ extension TokenConsumer {
177160
if subparser.at(anyIn: ContextualDeclKeyword.self)?.0 != nil {
178161
subparser.consumeAnyToken()
179162
return subparser.atStartOfDeclaration(
180-
isAtTopLevel: isAtTopLevel,
181163
allowInitDecl: allowInitDecl,
182-
allowRecovery: allowRecovery
164+
requiresDecl: requiresDecl
183165
)
184166
}
185167
if requiresDecl {
@@ -278,50 +260,6 @@ extension Parser {
278260
/// - Parameter context: Describes the code around the declaration being parsed. This affects how the parser tries
279261
/// to recover from malformed syntax in the declaration.
280262
mutating func parseDeclaration(in context: DeclarationParseContext = .topLevelOrCodeBlock) -> RawDeclSyntax {
281-
// If we are at a `#if` of attributes, the `#if` directive should be
282-
// parsed when we're parsing the attributes.
283-
if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) {
284-
let directive = self.parsePoundIfDirective { (parser, _) in
285-
let parsedDecl = parser.parseDeclaration()
286-
let semicolon = parser.consume(if: .semicolon)
287-
return RawMemberBlockItemSyntax(
288-
decl: parsedDecl,
289-
semicolon: semicolon,
290-
arena: parser.arena
291-
)
292-
} addSemicolonIfNeeded: { lastElement, newItemAtStartOfLine, newItem, parser in
293-
if lastElement.semicolon == nil && !newItemAtStartOfLine && !newItem.decl.is(RawUnexpectedCodeDeclSyntax.self) {
294-
return RawMemberBlockItemSyntax(
295-
lastElement.unexpectedBeforeDecl,
296-
decl: lastElement.decl,
297-
lastElement.unexpectedBetweenDeclAndSemicolon,
298-
semicolon: parser.missingToken(.semicolon),
299-
lastElement.unexpectedAfterSemicolon,
300-
arena: parser.arena
301-
)
302-
} else {
303-
return nil
304-
}
305-
} syntax: { parser, elements in
306-
return .decls(RawMemberBlockItemListSyntax(elements: elements, arena: parser.arena))
307-
}
308-
if !context.allowsIfConfigDecl {
309-
// Convert the IfConfig to unexpected syntax around the first decl inside it, if any.
310-
return directive.makeUnexpectedKeepingFirstNode(of: RawDeclSyntax.self, arena: self.arena) { node in
311-
return !node.is(RawIfConfigDeclSyntax.self)
312-
} makeMissing: {
313-
return RawDeclSyntax(
314-
RawMissingDeclSyntax(
315-
attributes: self.emptyCollection(RawAttributeListSyntax.self),
316-
modifiers: self.emptyCollection(RawDeclModifierListSyntax.self),
317-
arena: self.arena
318-
)
319-
)
320-
}
321-
}
322-
return RawDeclSyntax(directive)
323-
}
324-
325263
let attrs = DeclAttributes(
326264
attributes: self.parseAttributeList(),
327265
modifiers: self.parseDeclModifierList()
@@ -992,7 +930,7 @@ extension Parser {
992930
}
993931

994932
extension Parser {
995-
mutating func parseMemberBlockItem() -> RawMemberBlockItemSyntax? {
933+
mutating func parseMemberBlockItem(until stopCondition: (inout Parser) -> Bool) -> RawMemberBlockItemSyntax? {
996934
let startToken = self.currentToken
997935
if let syntax = self.loadCurrentSyntaxNodeFromCache(for: .memberBlockItem) {
998936
self.registerNodeForIncrementalParse(node: syntax.raw, startToken: startToken)
@@ -1013,35 +951,47 @@ extension Parser {
1013951
}
1014952

1015953
let decl: RawDeclSyntax
954+
let attachSemi: Bool
1016955
if self.at(.poundSourceLocation) {
1017956
decl = RawDeclSyntax(self.parsePoundSourceLocationDirective())
1018-
} else if self.atStartOfDeclaration(isAtTopLevel: false, allowInitDecl: true, requiresDecl: true) {
957+
attachSemi = false
958+
} else if self.at(.poundIf) && !self.withLookahead({ $0.consumeIfConfigOfAttributes() }) {
959+
decl = RawDeclSyntax(self.parsePoundIfDirective { parser in
960+
return .decls(parser.parseMemberDeclList(until: {$0.atEndOfIfConfigClauseBody()}))
961+
})
962+
attachSemi = false
963+
} else if self.atStartOfDeclaration(allowInitDecl: true, requiresDecl: true) {
1019964
decl = self.parseDeclaration(in: .memberDeclList)
965+
attachSemi = true
1020966
} else {
1021967
decl = RawDeclSyntax(
1022968
self.parseUnexpectedCodeDeclaration(
1023-
isAtTopLevel: false,
1024969
allowInitDecl: true,
1025970
requiresDecl: true,
1026-
skipToDeclOnly: true
971+
skipToDeclOnly: true,
972+
until: stopCondition
1027973
)
1028974
)
1029-
}
1030-
1031-
if decl.isEmpty && !self.at(.semicolon) {
1032-
return nil
975+
attachSemi = true
1033976
}
1034977

1035978
let semi: RawTokenSyntax?
1036-
if !decl.isEmpty {
1037-
semi = self.consume(if: .semicolon)
979+
var trailingSemis: [RawTokenSyntax] = []
980+
if attachSemi {
981+
if !decl.isEmpty {
982+
semi = self.consume(if: .semicolon)
983+
} else {
984+
semi = nil
985+
}
986+
while let trailingSemi = self.consume(if: .semicolon) {
987+
trailingSemis.append(trailingSemi)
988+
}
1038989
} else {
1039-
// orphan ';' case. Put it to "unexpected" nodes.
1040990
semi = nil
1041991
}
1042-
var trailingSemis: [RawTokenSyntax] = []
1043-
while let trailingSemi = self.consume(if: .semicolon) {
1044-
trailingSemis.append(trailingSemi)
992+
993+
if decl.isEmpty && semi == nil && trailingSemis.isEmpty {
994+
return nil
1045995
}
1046996

1047997
let result = RawMemberBlockItemSyntax(
@@ -1056,13 +1006,13 @@ extension Parser {
10561006
return result
10571007
}
10581008

1059-
mutating func parseMemberDeclList() -> RawMemberBlockItemListSyntax {
1009+
mutating func parseMemberDeclList(until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) }) -> RawMemberBlockItemListSyntax {
10601010
var elements = [RawMemberBlockItemSyntax]()
10611011
do {
10621012
var loopProgress = LoopProgressCondition()
1063-
while !self.at(.endOfFile, .rightBrace, .poundEndif) && self.hasProgressed(&loopProgress) {
1013+
while !self.at(.endOfFile) && !stopCondition(&self) && self.hasProgressed(&loopProgress) {
10641014
let newItemAtStartOfLine = self.atStartOfLine
1065-
guard let newElement = self.parseMemberBlockItem() else {
1015+
guard let newElement = self.parseMemberBlockItem(until: stopCondition) else {
10661016
break
10671017
}
10681018
if let lastItem = elements.last,
@@ -2380,52 +2330,42 @@ extension Parser {
23802330
)
23812331
}
23822332

2333+
/// Eat tokens until a start of decl, or if `skipToDeclOnly` is not set until
2334+
/// a start of statement or expression.
2335+
/// Returns consumed tokens as a `RawUnexpectedCodeDeclSyntax` declaration.
23832336
mutating func parseUnexpectedCodeDeclaration(
2384-
isAtTopLevel: Bool,
23852337
allowInitDecl: Bool,
23862338
requiresDecl: Bool,
23872339
skipToDeclOnly: Bool,
2340+
until stopCondition: (inout Parser) -> Bool
23882341
) -> RawUnexpectedCodeDeclSyntax {
2389-
let numTokensToSkip = withLookahead { (lookahead) -> Int in
2390-
while !lookahead.at(.endOfFile, .semicolon) {
2391-
if lookahead.at(.poundElse, .poundElseif, .poundEndif) {
2392-
break;
2393-
}
2394-
if !isAtTopLevel && lookahead.at(.rightBrace) {
2395-
break;
2396-
}
2397-
lookahead.skipSingle()
2342+
var unexpectedTokens = [RawSyntax]()
2343+
while !self.at(.endOfFile, .semicolon) && !stopCondition(&self) {
2344+
let numTokensToSkip = self.withLookahead({ $0.skipSingle(); return $0.tokensConsumed })
2345+
for _ in 0..<numTokensToSkip {
2346+
unexpectedTokens.append(RawSyntax(self.consumeAnyTokenWithoutAdjustingNestingLevel()))
2347+
}
23982348

2399-
if lookahead.at(.poundIf) {
2400-
break
2401-
}
2402-
if lookahead.at(.poundSourceLocation) {
2403-
break
2404-
}
2405-
if lookahead.atStartOfDeclaration(
2406-
isAtTopLevel: isAtTopLevel,
2407-
allowInitDecl: allowInitDecl,
2408-
requiresDecl: requiresDecl
2409-
) {
2410-
break
2411-
}
2349+
if self.at(.poundIf) {
2350+
break
2351+
}
2352+
if self.at(.poundSourceLocation) {
2353+
break
2354+
}
2355+
if self.atStartOfDeclaration(allowInitDecl: allowInitDecl, requiresDecl: requiresDecl) {
2356+
break
2357+
}
24122358

2413-
if skipToDeclOnly {
2414-
continue
2415-
}
2416-
if lookahead.atStartOfStatement(preferExpr: false) {
2417-
break
2418-
}
2419-
// Recover to an expression only if it's on the next line.
2420-
if lookahead.currentToken.isAtStartOfLine && lookahead.atStartOfExpression() {
2421-
break
2422-
}
2359+
if skipToDeclOnly {
2360+
continue
2361+
}
2362+
if self.atStartOfStatement(preferExpr: false) {
2363+
break
2364+
}
2365+
// Recover to an expression only if it's on the next line.
2366+
if self.currentToken.isAtStartOfLine && self.atStartOfExpression() {
2367+
break
24232368
}
2424-
return lookahead.tokensConsumed
2425-
}
2426-
var unexpectedTokens = [RawSyntax]()
2427-
for _ in 0..<numTokensToSkip {
2428-
unexpectedTokens.append(RawSyntax(self.consumeAnyTokenWithoutAdjustingNestingLevel()))
24292369
}
24302370
return RawUnexpectedCodeDeclSyntax(
24312371
unexpectedCode: RawUnexpectedNodesSyntax(elements: unexpectedTokens, arena: self.arena),

Sources/SwiftParser/Directives.swift

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
#endif
1818

1919
extension Parser {
20+
mutating func atEndOfIfConfigClauseBody() -> Bool {
21+
return self.at(.poundElseif, .poundElse, .poundEndif) || self.atElifTypo()
22+
}
23+
2024
private enum IfConfigContinuationClauseStartKeyword: TokenSpecSet {
2125
case poundElseif
2226
case poundElse
@@ -56,16 +60,8 @@ extension Parser {
5660
/// previous element.
5761
/// - syntax: A function that aggregates the parsed conditional elements
5862
/// into a syntax collection.
59-
mutating func parsePoundIfDirective<Element: RawSyntaxNodeProtocol>(
60-
_ parseElement: (_ parser: inout Parser, _ isFirstElement: Bool) -> Element?,
61-
addSemicolonIfNeeded:
62-
(_ lastElement: Element, _ newItemAtStartOfLine: Bool, _ newItem: Element, _ parser: inout Parser) -> Element? = {
63-
_,
64-
_,
65-
_,
66-
_ in nil
67-
},
68-
syntax: (inout Parser, [Element]) -> RawIfConfigClauseSyntax.Elements?
63+
mutating func parsePoundIfDirective(
64+
_ parseBody: (_ parser: inout Parser) -> RawIfConfigClauseSyntax.Elements?
6965
) -> RawIfConfigDeclSyntax {
7066
if let remainingTokens = remainingTokensIfMaximumNestingLevelReached() {
7167
return RawIfConfigDeclSyntax(
@@ -89,7 +85,7 @@ extension Parser {
8985
poundKeyword: poundIf,
9086
condition: condition,
9187
unexpectedBetweenConditionAndElements,
92-
elements: syntax(&self, parseIfConfigClauseElements(parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded)),
88+
elements: parseBody(&self),
9389
arena: self.arena
9490
)
9591
)
@@ -150,10 +146,7 @@ extension Parser {
150146
poundKeyword: pound,
151147
condition: condition,
152148
unexpectedBetweenConditionAndElements,
153-
elements: syntax(
154-
&self,
155-
parseIfConfigClauseElements(parseElement, addSemicolonIfNeeded: addSemicolonIfNeeded)
156-
),
149+
elements: parseBody(&self),
157150
arena: self.arena
158151
)
159152
)

0 commit comments

Comments
 (0)