Skip to content

Commit 1036d2a

Browse files
committed
[SyntaxParse] Parse ObjC keypath expression syntax
1 parent 5e1f790 commit 1036d2a

File tree

6 files changed

+107
-73
lines changed

6 files changed

+107
-73
lines changed

include/swift/Parse/ASTGen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class ASTGen {
108108
const SourceLoc Loc);
109109
Expr *generate(const syntax::PoundDsohandleExprSyntax &Expr,
110110
const SourceLoc Loc);
111+
Expr *generate(const syntax::ObjcKeyPathExprSyntax &Expr,
112+
const SourceLoc Loc);
111113
Expr *generate(const syntax::ObjectLiteralExprSyntax &Expr,
112114
const SourceLoc Loc);
113115
Expr *generate(const syntax::CodeCompletionExprSyntax &Expr,

include/swift/Parse/Parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,7 @@ class Parser {
14521452
ParserResult<Expr> parseExprPrimary(Diag<> ID, bool isExprBasic);
14531453
ParserResult<Expr> parseExprUnary(Diag<> ID, bool isExprBasic);
14541454
ParserResult<Expr> parseExprKeyPathObjC();
1455+
ParsedSyntaxResult<ParsedExprSyntax> parseExprObjcKeyPathSyntax();
14551456
ParserResult<Expr> parseExprKeyPath();
14561457
ParserResult<Expr> parseExprSelector();
14571458
ParserResult<Expr> parseExprSuper();

lib/Parse/ASTGen.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ Expr *ASTGen::generate(const ExprSyntax &E, const SourceLoc Loc) {
272272
result = generate(*poundFunctionExpr, Loc);
273273
else if (auto poundDsohandleExpr = E.getAs<PoundDsohandleExprSyntax>())
274274
result = generate(*poundDsohandleExpr, Loc);
275+
else if (auto objcKeyPathExpr = E.getAs<ObjcKeyPathExprSyntax>())
276+
result = generate(*objcKeyPathExpr, Loc);
275277
else if (auto objectLiteralExpr = E.getAs<ObjectLiteralExprSyntax>())
276278
result = generate(*objectLiteralExpr, Loc);
277279
else if (auto completionExpr = E.getAs<CodeCompletionExprSyntax>())
@@ -716,6 +718,32 @@ Expr *ASTGen::generate(const PoundDsohandleExprSyntax &Expr,
716718
Loc);
717719
}
718720

721+
Expr *ASTGen::generate(const ObjcKeyPathExprSyntax &E, const SourceLoc Loc) {
722+
SmallVector<KeyPathExpr::Component, 4> components;
723+
if (E.getLeftParen().isMissing())
724+
return nullptr;
725+
726+
for (auto piece : E.getName()) {
727+
DeclName name;
728+
DeclNameLoc nameLoc;
729+
std::tie(name, nameLoc) =
730+
generateUnqualifiedDeclName(piece.getName(),
731+
piece.getDeclNameArguments(), Loc);
732+
auto component = KeyPathExpr::Component::forUnresolvedProperty(name,
733+
nameLoc.getBaseNameLoc());
734+
components.push_back(component);
735+
}
736+
auto keywordLoc = advanceLocBegin(Loc, E.getKeyPath());
737+
auto LParenLoc = advanceLocBegin(Loc, E.getLeftParen());
738+
auto RParenLoc = advanceLocEnd(Loc, E);
739+
740+
if (components.empty())
741+
return new (Context) ErrorExpr(SourceRange(keywordLoc, RParenLoc));
742+
743+
return new (Context) KeyPathExpr(
744+
Context, keywordLoc, LParenLoc, components, RParenLoc);
745+
}
746+
719747
Expr *ASTGen::generate(const ObjectLiteralExprSyntax &E, const SourceLoc Loc) {
720748
ObjectLiteralExpr::LiteralKind kind;
721749
switch (E.getIdentifier().getTokenKind()) {
@@ -768,6 +796,25 @@ Expr *ASTGen::generate(const CodeCompletionExprSyntax &E, const SourceLoc Loc) {
768796
llvm_unreachable("'(' <cc-token> is not suppported");
769797
}
770798
} else {
799+
if (auto objcKeyPathExpr = E.getBase()->getAs<ObjcKeyPathExprSyntax>()) {
800+
// #keyPath(<cc-token>
801+
// #keyPath(some <cc-token>
802+
// #keyPath(some.<cc-token>
803+
auto expr = generate(*objcKeyPathExpr, Loc);
804+
if (P.CodeCompletion) {
805+
SourceLoc dotLoc;
806+
if (!expr || isa<ErrorExpr>(expr)) {
807+
P.CodeCompletion->completeExprKeyPath(nullptr, SourceLoc());
808+
} else {
809+
auto namePieces = objcKeyPathExpr->getName();
810+
if (!namePieces.empty())
811+
if (auto dot = namePieces[namePieces.getNumChildren() - 1].getDot())
812+
dotLoc = advanceLocBegin(Loc, *dot);
813+
P.CodeCompletion->completeExprKeyPath(cast<KeyPathExpr>(expr), dotLoc);
814+
}
815+
}
816+
return expr;
817+
}
771818
// TODO: implement
772819
}
773820
llvm_unreachable("code completion expression not implemented");

lib/Parse/ParseExpr.cpp

Lines changed: 54 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -627,97 +627,79 @@ ParserResult<Expr> Parser::parseExprKeyPath() {
627627
/// expr-keypath-objc:
628628
/// '#keyPath' '(' unqualified-name ('.' unqualified-name) * ')'
629629
///
630-
ParserResult<Expr> Parser::parseExprKeyPathObjC() {
631-
SyntaxParsingContext ObjcKPCtx(SyntaxContext, SyntaxKind::ObjcKeyPathExpr);
630+
ParsedSyntaxResult<ParsedExprSyntax> Parser::parseExprObjcKeyPathSyntax() {
631+
ParsedObjcKeyPathExprSyntaxBuilder builder(*SyntaxContext);
632+
ParserStatus status;
633+
632634
// Consume '#keyPath'.
633-
SourceLoc keywordLoc = consumeToken(tok::pound_keyPath);
635+
builder.useKeyPath(consumeTokenSyntax(tok::pound_keyPath));
634636

635637
// Parse the leading '('.
636638
if (!Tok.is(tok::l_paren)) {
637639
diagnose(Tok, diag::expr_keypath_expected_lparen);
638-
return makeParserError();
640+
return makeParsedError(builder.build());
639641
}
640-
SourceLoc lParenLoc = consumeToken(tok::l_paren);
641-
642-
SmallVector<KeyPathExpr::Component, 4> components;
643-
/// Handler for code completion.
644-
auto handleCodeCompletion = [&](SourceLoc DotLoc) -> ParserResult<Expr> {
645-
KeyPathExpr *expr = nullptr;
646-
if (!components.empty()) {
647-
expr = new (Context)
648-
KeyPathExpr(Context, keywordLoc, lParenLoc, components, Tok.getLoc());
649-
}
650-
651-
if (CodeCompletion)
652-
CodeCompletion->completeExprKeyPath(expr, DotLoc);
653-
654-
// Eat the code completion token because we handled it.
655-
consumeToken(tok::code_complete);
656-
return makeParserCodeCompletionResult(expr);
657-
};
642+
auto LParenLoc = Tok.getLoc();
643+
builder.useLeftParen(consumeTokenSyntax(tok::l_paren));
658644

659645
// Parse the sequence of unqualified-names.
660-
ParserStatus status;
661-
SourceLoc LastDotLoc;
662-
while (true) {
663-
SyntaxParsingContext NamePieceCtx(SyntaxContext, SyntaxKind::ObjcNamePiece);
664-
// Handle code completion.
665-
if (Tok.is(tok::code_complete))
666-
return handleCodeCompletion(LastDotLoc);
667-
668-
// Parse the next name.
669-
DeclNameLoc nameLoc;
670-
bool afterDot = !components.empty();
671-
auto name = parseUnqualifiedDeclName(
672-
afterDot, nameLoc,
673-
diag::expr_keypath_expected_property_or_type);
674-
if (!name) {
675-
status.setIsParseError();
646+
bool isFirst = true;
647+
bool hasNext = true;
648+
do {
649+
// Parse the next name
650+
Optional<ParsedTokenSyntax> identTok;
651+
Optional<ParsedDeclNameArgumentsSyntax> declNameArgs;
652+
status |= parseUnqualifiedDeclNameSyntax(
653+
identTok, declNameArgs, /*afterDot=*/!isFirst,
654+
diag::expr_keypath_expected_property_or_type);
655+
isFirst = false;
656+
if (status.isError())
676657
break;
677-
}
678658

679-
// Record the name we parsed.
680-
auto component = KeyPathExpr::Component::forUnresolvedProperty(name,
681-
nameLoc.getBaseNameLoc());
682-
components.push_back(component);
659+
ParsedObjcNamePieceSyntaxBuilder elemBuilder(*SyntaxContext);
683660

684-
// Handle code completion.
685-
if (Tok.is(tok::code_complete))
686-
return handleCodeCompletion(SourceLoc());
661+
elemBuilder.useName(std::move(*identTok));
662+
if (declNameArgs)
663+
elemBuilder.useDeclNameArguments(std::move(*declNameArgs));
687664

688-
// Parse the next period to continue the path.
689-
if (consumeIf(tok::period, LastDotLoc))
690-
continue;
665+
hasNext = Tok.is(tok::period);
666+
if (hasNext)
667+
elemBuilder.useDot(consumeTokenSyntax(tok::period));
668+
builder.addNameMember(elemBuilder.build());
669+
} while (hasNext);
691670

692-
break;
671+
if (Tok.is(tok::code_complete)) {
672+
return makeParsedCodeCompletion(
673+
ParsedSyntaxRecorder::makeCodeCompletionExpr(
674+
builder.build(), None, consumeTokenSyntax(tok::code_complete),
675+
*SyntaxContext));
693676
}
694677

695-
// Collect all name pieces to an objc name.
696-
SyntaxContext->collectNodesInPlace(SyntaxKind::ObjcName);
697-
698-
// Parse the closing ')'.
699-
SourceLoc rParenLoc;
700678
if (status.isError()) {
701-
skipUntilDeclStmtRBrace(tok::r_paren);
702-
if (Tok.is(tok::r_paren))
703-
rParenLoc = consumeToken();
704-
else
705-
rParenLoc = PreviousLoc;
706-
} else {
707-
parseMatchingToken(tok::r_paren, rParenLoc,
708-
diag::expr_keypath_expected_rparen, lParenLoc);
679+
while (!Tok.isAny(tok::r_paren, tok::eof, tok::r_brace, tok::pound_endif,
680+
tok::pound_else, tok::pound_elseif) &&
681+
!isStartOfDecl() && !isStartOfStmt())
682+
ignoreSingle();
709683
}
710684

711-
// If we cannot build a useful expression, just return an error
712-
// expression.
713-
if (components.empty() || status.isError()) {
714-
return makeParserResult<Expr>(
715-
new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
716-
}
685+
// Parse the closing ')'.
686+
auto RParen =
687+
parseMatchingTokenSyntax(tok::r_paren, diag::expr_keypath_expected_rparen,
688+
LParenLoc, /*silenceDiag=*/status.isError());
689+
status |= RParen.getStatus();
690+
if (!RParen.isNull())
691+
builder.useRightParen(RParen.get());
692+
693+
return makeParsedResult(builder.build(), status);
694+
}
717695

718-
// We're done: create the key-path expression.
719-
return makeParserResult<Expr>(new (Context) KeyPathExpr(
720-
Context, keywordLoc, lParenLoc, components, rParenLoc));
696+
ParserResult<Expr> Parser::parseExprKeyPathObjC() {
697+
auto leadingLoc = leadingTriviaLoc();
698+
auto parsed = parseExprObjcKeyPathSyntax();
699+
SyntaxContext->addSyntax(parsed.get());
700+
auto syntax = SyntaxContext->topNode<ExprSyntax>();
701+
auto expr = Generator.generate(syntax, leadingLoc);
702+
return makeParserResult(parsed.getStatus(), expr);
721703
}
722704

723705
/// parseExprSelector

test/expr/primary/keypath/keypath-objc.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ func testSemanticErrors() {
134134
func testParseErrors() {
135135
let _: String = #keyPath; // expected-error{{expected '(' following '#keyPath'}}
136136
let _: String = #keyPath(123; // expected-error{{expected property or type name within '#keyPath(...)'}}
137-
let _: String = #keyPath(a.123; // expected-error{{expected property or type name within '#keyPath(...)'}}
137+
let _: String = #keyPath(a.123; // expected-error{{expected property or type name within '#keyPath(...)'}} expected-error {{use of unresolved identifier 'a'}}
138138
let _: String = #keyPath(A(b:c:d:).propSet); // expected-error{{an Objective-C key path cannot reference a declaration with a compound name}} expected-error{{unresolved identifier 'propSet'}}
139139
let _: String = #keyPath(A.propString; // expected-error{{expected ')' to complete '#keyPath' expression}}
140140
// expected-note@-1{{to match this opening '('}}

utils/gyb_syntax_support/ExprNodes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,8 @@
507507
Node('ObjcNamePiece', kind='Syntax',
508508
children=[
509509
Child('Name', kind='IdentifierToken'),
510+
Child('DeclNameArguments', kind='DeclNameArguments',
511+
is_optional=True),
510512
Child('Dot', kind='PeriodToken', is_optional=True),
511513
]),
512514

0 commit comments

Comments
 (0)