Skip to content

Commit 2a97e0e

Browse files
committed
tbs
1 parent c072938 commit 2a97e0e

File tree

8 files changed

+171
-63
lines changed

8 files changed

+171
-63
lines changed

Sources/SwiftParser/CollectionNodes+Parsable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ extension CodeBlockItemListSyntax: SyntaxParseable {
116116
extension MemberBlockItemListSyntax: SyntaxParseable {
117117
public static func parse(from parser: inout Parser) -> Self {
118118
return parse(from: &parser) { parser in
119-
return parser.parseMemberDeclList()
119+
return parser.parseMemberDeclList(until: { _ in false })
120120
} makeMissing: { remainingTokens, arena in
121121
let missingDecl = RawMissingDeclSyntax(
122122
attributes: RawAttributeListSyntax(elements: [], arena: arena),

Sources/SwiftParser/Declarations.swift

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -966,13 +966,9 @@ extension Parser {
966966
decl = self.parseDeclaration(in: .memberDeclList)
967967
attachSemi = true
968968
} else {
969+
// Otherwise, eat the unexpected tokens into an "decl".
969970
decl = RawDeclSyntax(
970-
self.parseUnexpectedCodeDeclaration(
971-
allowInitDecl: true,
972-
requiresDecl: true,
973-
skipToDeclOnly: true,
974-
until: stopCondition
975-
)
971+
self.parseUnexpectedCodeDeclaration(allowInitDecl: true, requiresDecl: true, until: stopCondition)
976972
)
977973
attachSemi = true
978974
}
@@ -1009,7 +1005,7 @@ extension Parser {
10091005
}
10101006

10111007
mutating func parseMemberDeclList(
1012-
until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) }
1008+
until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) || $0.atEndOfIfConfigClauseBody() }
10131009
) -> RawMemberBlockItemListSyntax {
10141010
var elements = [RawMemberBlockItemSyntax]()
10151011
do {
@@ -1765,7 +1761,7 @@ extension Parser {
17651761
// There can only be an implicit getter if no other accessors were
17661762
// seen before this one.
17671763
guard let accessorList else {
1768-
let body = parseCodeBlockItemList(until: { $0.at(.rightBrace) })
1764+
let body = parseCodeBlockItemList()
17691765

17701766
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
17711767
return RawAccessorBlockSyntax(
@@ -2326,20 +2322,20 @@ extension Parser {
23262322
)
23272323
}
23282324

2329-
/// Eat tokens until a start of decl, or if `skipToDeclOnly` is not set until
2330-
/// a start of statement or expression.
2325+
/// Eats tokens until a start of decl, statement, or expression.
23312326
/// Returns consumed tokens as a `RawUnexpectedCodeDeclSyntax` declaration.
23322327
mutating func parseUnexpectedCodeDeclaration(
23332328
allowInitDecl: Bool,
23342329
requiresDecl: Bool,
2335-
skipToDeclOnly: Bool,
23362330
until stopCondition: (inout Parser) -> Bool
23372331
) -> RawUnexpectedCodeDeclSyntax {
23382332
var unexpectedTokens = [RawSyntax]()
2339-
while !self.at(.endOfFile, .semicolon) && !stopCondition(&self) {
2340-
let numTokensToSkip = self.withLookahead({
2341-
$0.skipSingle(); return $0.tokensConsumed
2342-
})
2333+
var loopProgress = LoopProgressCondition()
2334+
while !self.at(.endOfFile, .semicolon), !stopCondition(&self), self.hasProgressed(&loopProgress) {
2335+
let numTokensToSkip = self.withLookahead {
2336+
$0.skipSingle()
2337+
return $0.tokensConsumed
2338+
}
23432339
for _ in 0..<numTokensToSkip {
23442340
unexpectedTokens.append(RawSyntax(self.consumeAnyTokenWithoutAdjustingNestingLevel()))
23452341
}
@@ -2354,13 +2350,14 @@ extension Parser {
23542350
break
23552351
}
23562352

2357-
if skipToDeclOnly {
2353+
// If a declaration is expected, ignore statements and expressions.
2354+
if requiresDecl {
23582355
continue
23592356
}
23602357
if self.atStartOfStatement(preferExpr: false) {
23612358
break
23622359
}
2363-
// Recover to an expression only if it's on the next line.
2360+
// Recover to an expression only if it's on a new line.
23642361
if self.currentToken.isAtStartOfLine && self.atStartOfExpression() {
23652362
break
23662363
}

Sources/SwiftParser/Expressions.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,7 @@ extension Parser {
17731773
let signature = self.parseClosureSignatureIfPresent()
17741774

17751775
// Parse the body.
1776-
let elements = parseCodeBlockItemList(until: { $0.at(.rightBrace) })
1776+
let elements = parseCodeBlockItemList()
17771777

17781778
// Parse the closing '}'.
17791779
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
@@ -2331,9 +2331,7 @@ extension Parser {
23312331
mutating func parseSwitchCases(allowStandaloneStmtRecovery: Bool) -> RawSwitchCaseListSyntax {
23322332
var elements = [RawSwitchCaseListSyntax.Element]()
23332333
var elementsProgress = LoopProgressCondition()
2334-
while !self.at(.endOfFile, .rightBrace) && !self.at(.poundEndif, .poundElseif, .poundElse)
2335-
&& self.hasProgressed(&elementsProgress)
2336-
{
2334+
while !self.at(.endOfFile, .rightBrace), !self.atEndOfIfConfigClauseBody(), self.hasProgressed(&elementsProgress) {
23372335
if self.withLookahead({ $0.atStartOfSwitchCase(allowRecovery: false) }) {
23382336
elements.append(.switchCase(self.parseSwitchCase()))
23392337
} else if self.canRecoverTo(.poundIf) != nil {
@@ -2346,10 +2344,7 @@ extension Parser {
23462344
})
23472345
)
23482346
)
2349-
} else if allowStandaloneStmtRecovery
2350-
&& (self.atStartOfExpression() || self.atStartOfStatement(preferExpr: false)
2351-
|| self.atStartOfDeclaration())
2352-
{
2347+
} else if allowStandaloneStmtRecovery {
23532348
// Synthesize a label for the statement or declaration that isn't covered by a case right now.
23542349
let statements = parseSwitchCaseBody()
23552350
if statements.isEmpty {
@@ -2385,8 +2380,6 @@ extension Parser {
23852380
)
23862381
)
23872382
)
2388-
} else if self.withLookahead({ $0.atStartOfSwitchCase(allowRecovery: true) }) {
2389-
elements.append(.switchCase(self.parseSwitchCase()))
23902383
} else {
23912384
break
23922385
}
@@ -2396,14 +2389,11 @@ extension Parser {
23962389

23972390
mutating func parseSwitchCaseBody() -> RawCodeBlockItemListSyntax {
23982391
parseCodeBlockItemList(until: {
2399-
if $0.at(.rightBrace) || $0.at(.poundEndif, .poundElseif, .poundElse) {
2400-
return true
2401-
}
2402-
if $0.at(.keyword(.case), .keyword(.default)) {
2392+
if $0.at(.rightBrace, .keyword(.case), .keyword(.default)) || $0.atEndOfIfConfigClauseBody() {
24032393
return true
24042394
}
2405-
if $0.at(.atSign)
2406-
&& $0.withLookahead({
2395+
if $0.at(.atSign),
2396+
$0.withLookahead({
24072397
$0.consumeAnyAttribute(); return $0.at(.keyword(.case), .keyword(.default))
24082398
})
24092399
{

Sources/SwiftParser/Statements.swift

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,18 +1095,8 @@ extension Parser.Lookahead {
10951095
// Check for and consume attributes. The only valid attribute is `@unknown`
10961096
// but that's a semantic restriction.
10971097
var lookahead = self.lookahead()
1098-
var loopProgress = LoopProgressCondition()
1099-
var hasAttribute = false
1100-
while lookahead.at(.atSign) && lookahead.hasProgressed(&loopProgress) {
1101-
guard lookahead.peek().rawTokenKind == .identifier else {
1102-
return false
1103-
}
1104-
1105-
lookahead.eat(.atSign)
1106-
lookahead.eat(.identifier)
1107-
hasAttribute = true
1108-
}
1109-
1098+
1099+
let hasAttribute = lookahead.consumeAttributeList()
11101100
if hasAttribute && lookahead.at(.rightBrace) {
11111101
// If we are at an attribute that's the last token in the SwitchCase, parse
11121102
// that as an attribute to a missing 'case'. That way, if the developer writes

Sources/SwiftParser/TopLevel.swift

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ extension Parser {
6868
extension Parser {
6969
mutating func parseCodeBlockItemList(
7070
allowInitDecl: Bool = true,
71-
until stopCondition: (inout Parser) -> Bool
71+
until stopCondition: (inout Parser) -> Bool = { $0.at(.rightBrace) || $0.atEndOfIfConfigClauseBody() }
7272
) -> RawCodeBlockItemListSyntax {
7373
var elements = [RawCodeBlockItemSyntax]()
7474
var loopProgress = LoopProgressCondition()
75-
while !stopCondition(&self), self.hasProgressed(&loopProgress) {
75+
while !stopCondition(&self), !self.at(.endOfFile), self.hasProgressed(&loopProgress) {
7676
let newItemAtStartOfLine = self.atStartOfLine
7777
guard let newElement = self.parseCodeBlockItem(allowInitDecl: allowInitDecl, until: stopCondition) else {
7878
break
@@ -118,7 +118,7 @@ extension Parser {
118118
/// indented to close this code block or a surrounding context. See `expectRightBrace`.
119119
mutating func parseCodeBlock(introducer: RawTokenSyntax? = nil, allowInitDecl: Bool = true) -> RawCodeBlockSyntax {
120120
let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace)
121-
let itemList = parseCodeBlockItemList(allowInitDecl: allowInitDecl, until: { $0.at(.rightBrace) })
121+
let itemList = parseCodeBlockItemList(allowInitDecl: allowInitDecl)
122122
let (unexpectedBeforeRBrace, rbrace) = self.expectRightBrace(leftBrace: lbrace, introducer: introducer)
123123

124124
return .init(
@@ -223,14 +223,10 @@ extension Parser {
223223
attachSemi = true
224224

225225
} else {
226+
// Otherwise, eat the unexpected tokens into an "decl".
226227
item = .decl(
227228
RawDeclSyntax(
228-
self.parseUnexpectedCodeDeclaration(
229-
allowInitDecl: allowInitDecl,
230-
requiresDecl: false,
231-
skipToDeclOnly: false,
232-
until: stopCondition
233-
)
229+
self.parseUnexpectedCodeDeclaration(allowInitDecl: allowInitDecl, requiresDecl: false, until: stopCondition)
234230
)
235231
)
236232
attachSemi = true

Tests/SwiftParserTest/DirectiveTests.swift

Lines changed: 123 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -462,13 +462,104 @@ final class DirectiveTests: ParserTestCase {
462462
func testOrphanEndifInMember() {
463463
assertParse(
464464
"""
465-
struct S {
466-
1️⃣#endif
465+
struct S ℹ️{1️⃣
466+
2️⃣#endif
467467
}
468468
""",
469469
diagnostics: [
470-
DiagnosticSpec(message: "unexpected code '#endif' in struct")
471-
]
470+
DiagnosticSpec(
471+
locationMarker: "1️⃣",
472+
message: "expected '}' to end struct",
473+
notes: [NoteSpec(message: "to match this opening '{'")],
474+
fixIts: ["insert '}'"],
475+
),
476+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"),
477+
],
478+
fixedSource: """
479+
struct S {
480+
}
481+
#endif
482+
}
483+
"""
484+
)
485+
}
486+
487+
func testOrphanEndifInCodeBlock() {
488+
assertParse(
489+
"""
490+
func foo() ℹ️{1️⃣
491+
2️⃣#endif
492+
}
493+
""",
494+
diagnostics: [
495+
DiagnosticSpec(
496+
locationMarker: "1️⃣",
497+
message: "expected '}' to end function",
498+
notes: [NoteSpec(message: "to match this opening '{'")],
499+
fixIts: ["insert '}'"],
500+
),
501+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"),
502+
],
503+
fixedSource: """
504+
func foo() {
505+
}
506+
#endif
507+
}
508+
"""
509+
)
510+
}
511+
512+
func testOrphanEndifInSwitch() {
513+
assertParse(
514+
"""
515+
switch subject ℹ️{1️⃣
516+
2️⃣#endif
517+
}
518+
""",
519+
diagnostics: [
520+
DiagnosticSpec(
521+
locationMarker: "1️⃣",
522+
message: "expected '}' to end 'switch' statement",
523+
notes: [NoteSpec(message: "to match this opening '{'")],
524+
fixIts: ["insert '}'"],
525+
),
526+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"),
527+
],
528+
fixedSource: """
529+
switch subject {
530+
}
531+
#endif
532+
}
533+
"""
534+
)
535+
}
536+
537+
func testOrphanEndifInSwitchCase() {
538+
assertParse(
539+
"""
540+
switch subject ℹ️{
541+
case foo:
542+
print()1️⃣
543+
2️⃣#endif
544+
}
545+
""",
546+
diagnostics: [
547+
DiagnosticSpec(
548+
locationMarker: "1️⃣",
549+
message: "expected '}' to end 'switch' statement",
550+
notes: [NoteSpec(message: "to match this opening '{'")],
551+
fixIts: ["insert '}'"],
552+
),
553+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected code in source file"),
554+
],
555+
fixedSource: """
556+
switch subject {
557+
case foo:
558+
print()
559+
}
560+
#endif
561+
}
562+
"""
472563
)
473564
}
474565

@@ -492,4 +583,32 @@ final class DirectiveTests: ParserTestCase {
492583
]
493584
)
494585
}
586+
587+
func testMismatchedPoundIfAndCodeBlock() {
588+
assertParse(
589+
"""
590+
#if FOO
591+
func foo() ℹ️{1️⃣
592+
#endif
593+
2️⃣}
594+
""",
595+
diagnostics: [
596+
DiagnosticSpec(
597+
locationMarker: "1️⃣",
598+
message: "expected '}' to end function",
599+
notes: [NoteSpec(message: "to match this opening '{'")],
600+
fixIts: ["insert '}'"],
601+
),
602+
DiagnosticSpec(locationMarker: "2️⃣", message: "unexpected brace in source file"),
603+
],
604+
fixedSource: """
605+
#if FOO
606+
func foo() {
607+
}
608+
#endif
609+
}
610+
"""
611+
)
612+
}
613+
495614
}

Tests/SwiftParserTest/ExpressionTests.swift

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2662,10 +2662,26 @@ final class StatementExpressionTests: ParserTestCase {
26622662
assertParse(
26632663
"""
26642664
switch x {
2665-
1️⃣@case
2665+
@1️⃣case2️⃣
26662666
}
26672667
""",
2668-
diagnostics: [DiagnosticSpec(message: "unexpected code '@case' in 'switch' statement")]
2668+
diagnostics: [
2669+
DiagnosticSpec(
2670+
locationMarker: "1️⃣",
2671+
// FIXME: "expected attribute name after '@'".
2672+
message: "expected type in attribute", fixIts: ["insert type"]
2673+
),
2674+
DiagnosticSpec(
2675+
locationMarker: "2️⃣",
2676+
// FIXME: "expected pattern and ':' in switch case"
2677+
message: "expected expression and ':' in switch case", fixIts: ["insert expression and ':'"]
2678+
),
2679+
],
2680+
fixedSource: """
2681+
switch x {
2682+
@<#identifier#> case <#expression#>:
2683+
}
2684+
"""
26692685
)
26702686
}
26712687

Tests/SwiftParserTest/translated/RecoveryTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3429,8 +3429,8 @@ final class RecoveryTests: ParserTestCase {
34293429
func testTTT() {
34303430
assertParse(
34313431
"""
3432-
struct S {
3433-
:
3432+
switch s {
3433+
@
34343434
}
34353435
"""
34363436
)

0 commit comments

Comments
 (0)