Skip to content

Commit a1a6c19

Browse files
committed
libSyntax: add a mechanism to synthesize syntax nodes in SyntaxParsingContext.
To enhance the error-recovery of syntax parsing, this patch allows the parser to synthesize missing nodes to satisfy the requirement of a syntax node under parsing. As proof-of-concept, we synthesize r-braces for function body to avoid regressing a function decl to an unknown decl.
1 parent be79673 commit a1a6c19

File tree

5 files changed

+55
-2
lines changed

5 files changed

+55
-2
lines changed

include/swift/Parse/SyntaxParsingContext.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,14 @@ class alignas(1 << SyntaxAlignInBits) SyntaxParsingContext {
255255
/// the syntax tree before closing the root context.
256256
void finalizeRoot();
257257

258+
/// Make a missing node corresponding to the given token kind and text, and
259+
/// push this node into the context. The synthesized node can help
260+
/// the creation of valid syntax nodes.
261+
void synthesize(tok Kind, StringRef Text = "");
262+
263+
/// Make a missing node corresponding to the given node kind, and
264+
/// push this node into the context.
265+
void synthesize(SyntaxKind Kind);
258266
};
259267

260268
} // namespace swift

lib/Parse/ParseStmt.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,8 +637,11 @@ ParserResult<BraceStmt> Parser::parseBraceItemList(Diag<> ID) {
637637

638638
ParserStatus Status = parseBraceItems(Entries, BraceItemListKind::Brace,
639639
BraceItemListKind::Brace);
640-
parseMatchingToken(tok::r_brace, RBLoc,
641-
diag::expected_rbrace_in_brace_stmt, LBLoc);
640+
if (parseMatchingToken(tok::r_brace, RBLoc,
641+
diag::expected_rbrace_in_brace_stmt, LBLoc)) {
642+
// Synthesize a r-brace if the source doesn't have any.
643+
LocalContext.synthesize(tok::r_brace);
644+
}
642645

643646
return makeParserResult(Status,
644647
BraceStmt::create(Context, LBLoc, Entries, RBLoc));

lib/Parse/SyntaxParsingContext.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,20 @@ void SyntaxParsingContext::finalizeRoot() {
317317
getRootData().Storage.clear();
318318
}
319319

320+
void SyntaxParsingContext::synthesize(tok Kind, StringRef Text) {
321+
if (!Enabled)
322+
return;
323+
if (Text.empty())
324+
Text = getTokenText(Kind);
325+
Storage.push_back(RawSyntax::missing(Kind, Text));
326+
}
327+
328+
void SyntaxParsingContext::synthesize(SyntaxKind Kind) {
329+
if (!Enabled)
330+
return;
331+
Storage.push_back(RawSyntax::missing(Kind));
332+
}
333+
320334
SyntaxParsingContext::~SyntaxParsingContext() {
321335
assert(isTopOfContextStack() && "destructed in wrong order");
322336

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<FunctionDecl>// RUN: rm -rf %t
2+
// RUN: %swift-syntax-test -input-source-filename %s -parse-gen > %t
3+
// RUN: diff -u %s %t
4+
// RUN: %swift-syntax-test -input-source-filename %s -parse-gen -print-node-kind > %t.withkinds
5+
// RUN: diff -u %S/Outputs/round_trip_invalid.swift.withkinds %t.withkinds
6+
// RUN: %swift-syntax-test -input-source-filename %s -eof > %t
7+
// RUN: diff -u %s %t
8+
// RUN: %swift-syntax-test -serialize-raw-tree -input-source-filename %s > %t.dump
9+
// RUN: %swift-syntax-test -deserialize-raw-tree -input-source-filename %t.dump -output-filename %t
10+
// RUN: diff -u %s %t
11+
12+
// Function body without closing brace token.
13+
func foo<FunctionSignature><ParameterClause>() </ParameterClause></FunctionSignature><CodeBlock>{<VariableDecl>
14+
var <PatternBinding><IdentifierPattern>a </IdentifierPattern><InitializerClause>= <IntegerLiteralExpr>2</IntegerLiteralExpr></InitializerClause></PatternBinding></VariableDecl></CodeBlock></FunctionDecl>

test/Syntax/round_trip_invalid.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: rm -rf %t
2+
// RUN: %swift-syntax-test -input-source-filename %s -parse-gen > %t
3+
// RUN: diff -u %s %t
4+
// RUN: %swift-syntax-test -input-source-filename %s -parse-gen -print-node-kind > %t.withkinds
5+
// RUN: diff -u %S/Outputs/round_trip_invalid.swift.withkinds %t.withkinds
6+
// RUN: %swift-syntax-test -input-source-filename %s -eof > %t
7+
// RUN: diff -u %s %t
8+
// RUN: %swift-syntax-test -serialize-raw-tree -input-source-filename %s > %t.dump
9+
// RUN: %swift-syntax-test -deserialize-raw-tree -input-source-filename %t.dump -output-filename %t
10+
// RUN: diff -u %s %t
11+
12+
// Function body without closing brace token.
13+
func foo() {
14+
var a = 2

0 commit comments

Comments
 (0)