Skip to content

Commit d321b74

Browse files
authored
Merge pull request #655 from ahoppen/ahoppen/no-legacy-recovery
Remove `recover` methods from the parser
2 parents 8ef16b2 + 42c3af5 commit d321b74

15 files changed

+191
-342
lines changed

Sources/SwiftParser/Attributes.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ extension Parser {
340340
guard self.at(.leftParen) else {
341341
// If no opening '(' for parameter list, parse a single parameter.
342342
let param = self.parseDifferentiabilityParameter().map(RawSyntax.init(_:))
343-
?? RawSyntax(RawTokenListSyntax(elements: self.recover(), arena: self.arena))
343+
?? RawSyntax(RawMissingSyntax(arena: self.arena))
344344
return RawDifferentiabilityParamsClauseSyntax(
345345
wrtLabel: wrt,
346346
unexpectedBeforeColon,

Sources/SwiftParser/Declarations.swift

Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,6 @@ extension Parser {
5252
if self.at(.poundIfKeyword) {
5353
return RawDeclSyntax(self.parsePoundIfDirective { parser in
5454
var parsedDecl = parser.parseDeclaration()
55-
if parsedDecl.is(RawMissingDeclSyntax.self) {
56-
// Try to recover from a bogus decl.
57-
var tokenList = [RawTokenSyntax]()
58-
while !parser.at(.eof) && !parser.at(.poundElseKeyword) &&
59-
!parser.at(.poundElseifKeyword) && !parser.at(.poundEndifKeyword) {
60-
let tokens = parser.recover()
61-
guard !tokens.isEmpty else {
62-
break
63-
}
64-
tokenList.append(contentsOf: tokens)
65-
}
66-
let unexpected = RawUnexpectedNodesSyntax(elements: tokenList.map(RawSyntax.init), arena: parser.arena)
67-
parsedDecl = RawDeclSyntax(RawMissingDeclSyntax(unexpected, attributes: nil, modifiers: nil, arena: parser.arena))
68-
}
6955
let semicolon = parser.consume(if: .semicolon)
7056
return RawMemberDeclListItemSyntax(
7157
decl: parsedDecl,
@@ -1504,22 +1490,6 @@ extension Parser {
15041490
// There can only be an implicit getter if no other accessors were
15051491
// seen before this one.
15061492
guard elements.isEmpty else {
1507-
// Recover until the matching right brace. It's a little
1508-
// presumptuous of us to assume everything between here and there
1509-
// is an accessor, but we cannot stick unexpected anywhere for the
1510-
// moment...
1511-
while !self.at(.eof) && !self.at(.rightBrace) {
1512-
for token in self.recover() {
1513-
elements.append(RawAccessorDeclSyntax(
1514-
attributes: nil, modifier: nil,
1515-
accessorKind: token,
1516-
parameter: nil,
1517-
asyncKeyword: nil, throwsKeyword: nil,
1518-
body: nil,
1519-
arena: self.arena))
1520-
}
1521-
}
1522-
15231493
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
15241494
return RawSyntax(RawAccessorBlockSyntax(
15251495
leftBrace: lbrace,
@@ -1530,8 +1500,8 @@ extension Parser {
15301500
}
15311501

15321502
var body = [RawCodeBlockItemSyntax]()
1533-
while !self.at(.eof) && !self.at(.rightBrace) {
1534-
body.append(self.parseCodeBlockItem())
1503+
while !self.at(.rightBrace), let newItem = self.parseCodeBlockItem() {
1504+
body.append(newItem)
15351505
}
15361506
let (unexpectedBeforeRBrace, rbrace) = self.expect(.rightBrace)
15371507
return RawSyntax(RawCodeBlockSyntax(
@@ -1719,7 +1689,7 @@ extension Parser {
17191689
let (unexpectedBeforeLBrace, lbrace) = self.expect(.leftBrace)
17201690
var elements = [RawSyntax]()
17211691
do {
1722-
while !self.at(.eof) && !self.at(.rightBrace) {
1692+
LOOP: while !self.at(.eof) && !self.at(.rightBrace) {
17231693
switch self.currentToken.tokenText {
17241694
case "associativity":
17251695
let associativity = self.consumeIdentifier()
@@ -1772,15 +1742,7 @@ extension Parser {
17721742
otherNames: RawPrecedenceGroupNameListSyntax(elements: names, arena: self.arena),
17731743
arena: self.arena)))
17741744
default:
1775-
var tokenList = [RawTokenSyntax]()
1776-
while !self.at(.eof) && !self.at(.rightBrace) {
1777-
let tokens = self.recover()
1778-
guard !tokens.isEmpty else {
1779-
break
1780-
}
1781-
tokenList.append(contentsOf: tokens)
1782-
}
1783-
elements.append(RawSyntax(RawTokenListSyntax(elements: tokenList, arena: self.arena)))
1745+
break LOOP
17841746
}
17851747
}
17861748
}

Sources/SwiftParser/Diagnostics/ParseDiagnosticsGenerator.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
import SwiftDiagnostics
14-
import SwiftSyntax
14+
@_spi(RawSyntax) import SwiftSyntax
1515

1616
extension UnexpectedNodesSyntax {
1717
func tokens(satisfying isIncluded: (TokenSyntax) -> Bool) -> [TokenSyntax] {
@@ -58,6 +58,9 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
5858
) -> [Diagnostic] {
5959
let diagProducer = ParseDiagnosticsGenerator()
6060
diagProducer.walk(tree)
61+
diagProducer.diagnostics.sort {
62+
return $0.node.id.indexInTree < $1.node.id.indexInTree
63+
}
6164
return diagProducer.diagnostics
6265
}
6366

@@ -172,6 +175,17 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
172175
return .visitChildren
173176
}
174177

178+
public override func visit(_ node: SourceFileSyntax) -> SyntaxVisitorContinueKind {
179+
if shouldSkip(node) {
180+
return .skipChildren
181+
}
182+
if let extraneous = node.unexpectedBetweenStatementsAndEOFToken, !extraneous.isEmpty {
183+
addDiagnostic(extraneous, ExtaneousCodeAtTopLevel(extraneousCode: extraneous))
184+
markNodesAsHandled(extraneous.id)
185+
}
186+
return .visitChildren
187+
}
188+
175189
public override func visit(_ node: UnresolvedTernaryExprSyntax) -> SyntaxVisitorContinueKind {
176190
if shouldSkip(node) {
177191
return .skipChildren

Sources/SwiftParser/Diagnostics/ParserDiagnosticMessages.swift

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,28 @@ import SwiftSyntax
1515

1616
let diagnosticDomain: String = "SwiftParser"
1717

18-
extension Syntax {
18+
extension SyntaxProtocol {
1919
var nodeTypeNameForDiagnostics: String? {
20-
if let name = self.as(SyntaxEnum.self).nameForDiagnostics {
20+
if let name = Syntax(self).as(SyntaxEnum.self).nameForDiagnostics {
2121
return name
2222
}
2323
if let parent = self.parent {
2424
return parent.nodeTypeNameForDiagnostics
2525
}
2626
return nil
2727
}
28+
29+
/// If the syntax node (excluding leading and trailing trivia) only spans a
30+
/// single line and has less than 100 characters (and thus fits into a
31+
/// diagnostic message), return that. Otherwise, return `nil`.
32+
var contentForDiagnosticsIfShortSingleLine: String? {
33+
let contentWithoutTrivia = self.withoutLeadingTrivia().withoutTrailingTrivia().description
34+
if contentWithoutTrivia.contains("\n") || contentWithoutTrivia.count > 100 {
35+
return nil
36+
} else {
37+
return contentWithoutTrivia
38+
}
39+
}
2840
}
2941

3042
/// A error diagnostic whose ID is determined by the diagnostic's type.
@@ -90,6 +102,18 @@ public enum StaticParserFixIt: String, FixItMessage {
90102

91103
// MARK: - Diagnostics (please sort alphabetically)
92104

105+
public struct ExtaneousCodeAtTopLevel: ParserError {
106+
public let extraneousCode: UnexpectedNodesSyntax
107+
108+
public var message: String {
109+
if let shortContent = extraneousCode.contentForDiagnosticsIfShortSingleLine {
110+
return "Extraneous '\(shortContent)' at top level"
111+
} else {
112+
return "Extraneous code at top level"
113+
}
114+
}
115+
}
116+
93117
public struct MissingTokenError: ParserError {
94118
public let missingToken: TokenSyntax
95119

@@ -117,10 +141,17 @@ public struct UnexpectedNodesError: ParserError {
117141
public let unexpectedNodes: UnexpectedNodesSyntax
118142

119143
public var message: String {
120-
if let parentTypeName = unexpectedNodes.parent?.nodeTypeNameForDiagnostics {
121-
return "Unexpected text '\(unexpectedNodes.description)' found in \(parentTypeName)"
122-
} else {
123-
return "Unexpected text '\(unexpectedNodes.description)'"
144+
let parentTypeName = unexpectedNodes.parent?.nodeTypeNameForDiagnostics
145+
let shortContent = unexpectedNodes.contentForDiagnosticsIfShortSingleLine
146+
switch (parentTypeName, shortContent) {
147+
case (let parentTypeName?, let shortContent?):
148+
return "Unexpected text '\(shortContent)' found in \(parentTypeName)"
149+
case (let parentTypeName?, nil):
150+
return "Unexpected text found in \(parentTypeName)"
151+
case (nil, let shortContent?):
152+
return "Unexpected text '\(shortContent)'"
153+
case (nil, nil):
154+
return "Unexpected text"
124155
}
125156
}
126157
}

Sources/SwiftParser/Expressions.swift

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,8 +1547,8 @@ extension Parser {
15471547
// Parse the body.
15481548
var elements = [RawCodeBlockItemSyntax]()
15491549
do {
1550-
while !self.at(.eof) && !self.at(.rightBrace) {
1551-
elements.append(self.parseCodeBlockItem())
1550+
while !self.at(.rightBrace), let newItem = self.parseCodeBlockItem() {
1551+
elements.append(newItem)
15521552
}
15531553
}
15541554

@@ -1714,7 +1714,7 @@ extension Parser {
17141714
}
17151715

17161716
@_spi(RawSyntax)
1717-
public mutating func parseClosureCaptureSpecifiers() -> RawTokenListSyntax {
1717+
public mutating func parseClosureCaptureSpecifiers() -> RawTokenListSyntax? {
17181718
var specifiers = [RawTokenSyntax]()
17191719
do {
17201720
// Check for the strength specifier: "weak", "unowned", or
@@ -1734,26 +1734,14 @@ extension Parser {
17341734
guard next.tokenKind == .equal || next.tokenKind == .comma
17351735
|| next.tokenKind == .rightSquareBracket || next.tokenKind == .period
17361736
else {
1737-
// Recover from unexpected in the capture specifiers.
1738-
//
1739-
// FIXME: This is quite poor modeling in SwiftSyntax.
1740-
specifiers.append(contentsOf: self.recover())
1741-
return RawTokenListSyntax(elements: specifiers, arena: self.arena)
1737+
return nil
17421738
}
17431739
} else {
1744-
// Recover from unexpected in the capture specifiers.
1745-
//
1746-
// FIXME: This is quite poor modeling in SwiftSyntax.
1747-
specifiers.append(contentsOf: self.recover())
1748-
return RawTokenListSyntax(elements: specifiers, arena: self.arena)
1740+
return nil
17491741
}
17501742

17511743
guard self.currentToken.isIdentifier || self.at(.selfKeyword) else {
1752-
// Recover from unexpected in the capture specifiers.
1753-
//
1754-
// FIXME: This is quite poor modeling in SwiftSyntax.
1755-
specifiers.append(contentsOf: self.recover())
1756-
return RawTokenListSyntax(elements: specifiers, arena: self.arena)
1744+
return nil
17571745
}
17581746
}
17591747
// Squash all tokens, if any, as the specifier of the captured item.

0 commit comments

Comments
 (0)