mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
2532 lines
86 KiB
C++
2532 lines
86 KiB
C++
//===--- ParseExpr.cpp - Swift Language Parser for Expressions ------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See http://swift.org/LICENSE.txt for license information
|
|
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Expression Parsing and AST Building
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Parse/Parser.h"
|
|
#include "swift/AST/DiagnosticsParse.h"
|
|
#include "swift/Basic/EditorPlaceholder.h"
|
|
#include "swift/Parse/CodeCompletionCallbacks.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "swift/Basic/Fallthrough.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// \brief Create an argument with a trailing closure, with (optionally)
|
|
/// the elements, names, and parentheses locations from an existing argument.
|
|
static Expr *createArgWithTrailingClosure(ASTContext &context,
|
|
SourceLoc leftParen,
|
|
ArrayRef<Expr *> elementsIn,
|
|
ArrayRef<Identifier> namesIn,
|
|
ArrayRef<SourceLoc> nameLocsIn,
|
|
SourceLoc rightParen,
|
|
Expr *closure) {
|
|
// If there are no elements, just build a parenthesized expression around
|
|
// the closure.
|
|
if (elementsIn.empty()) {
|
|
return new (context) ParenExpr(leftParen, closure, rightParen,
|
|
/*hasTrailingClosure=*/true);
|
|
}
|
|
|
|
// Create the list of elements, and add the trailing closure to the end.
|
|
SmallVector<Expr *, 4> elements(elementsIn.begin(), elementsIn.end());
|
|
elements.push_back(closure);
|
|
|
|
SmallVector<Identifier, 4> names;
|
|
SmallVector<SourceLoc, 4> nameLocs;
|
|
if (!namesIn.empty()) {
|
|
names.append(namesIn.begin(), namesIn.end());
|
|
names.push_back(Identifier());
|
|
|
|
nameLocs.append(nameLocsIn.begin(), nameLocsIn.end());
|
|
nameLocs.push_back(SourceLoc());
|
|
}
|
|
|
|
// Form a full tuple expression.
|
|
return TupleExpr::create(context, leftParen, elements, names, nameLocs,
|
|
rightParen, /*hasTrailingClosure=*/true,
|
|
/*Implicit=*/false);
|
|
}
|
|
|
|
/// \brief Add the given trailing closure argument to the call argument.
|
|
static Expr *addTrailingClosureToArgument(ASTContext &context,
|
|
Expr *arg, Expr *closure) {
|
|
// Deconstruct the call argument to find its elements, element names,
|
|
// and the locations of the left and right parentheses.
|
|
if (auto tuple = dyn_cast<TupleExpr>(arg)) {
|
|
// Deconstruct a tuple expression.
|
|
return createArgWithTrailingClosure(context,
|
|
tuple->getLParenLoc(),
|
|
tuple->getElements(),
|
|
tuple->getElementNames(),
|
|
tuple->getElementNameLocs(),
|
|
tuple->getRParenLoc(),
|
|
closure);
|
|
}
|
|
|
|
// Deconstruct a parenthesized expression.
|
|
auto paren = dyn_cast<ParenExpr>(arg);
|
|
return createArgWithTrailingClosure(context,
|
|
paren->getLParenLoc(),
|
|
paren->getSubExpr(),
|
|
{ },
|
|
{ },
|
|
paren->getRParenLoc(), closure);
|
|
}
|
|
|
|
/// parseExpr
|
|
///
|
|
/// expr:
|
|
/// expr-sequence(basic | trailing-closure)
|
|
///
|
|
/// \param isExprBasic Whether we're only parsing an expr-basic.
|
|
ParserResult<Expr> Parser::parseExprImpl(Diag<> Message, bool isExprBasic) {
|
|
// If we are parsing a refutable pattern, check to see if this is the start
|
|
// of a let/var/is pattern. If so, parse it to an UnresolvedPatternExpr and
|
|
// name binding will perform final validation.
|
|
//
|
|
// Only do this if we're parsing a pattern, to improve QoI on malformed
|
|
// expressions followed by (e.g.) let/var decls.
|
|
//
|
|
if (InVarOrLetPattern && isOnlyStartOfMatchingPattern()) {
|
|
ParserResult<Pattern> pattern = parseMatchingPattern(/*isExprBasic*/false);
|
|
if (pattern.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (pattern.isNull())
|
|
return nullptr;
|
|
return makeParserResult(new (Context) UnresolvedPatternExpr(pattern.get()));
|
|
}
|
|
|
|
ParserResult<Expr> expr = parseExprSequence(Message, isExprBasic);
|
|
if (expr.hasCodeCompletion())
|
|
return expr;
|
|
if (expr.isNull())
|
|
return nullptr;
|
|
|
|
return makeParserResult(expr.get());
|
|
}
|
|
|
|
/// parseExprIs
|
|
/// expr-is:
|
|
/// 'is' type
|
|
ParserResult<Expr> Parser::parseExprIs() {
|
|
SourceLoc isLoc = consumeToken(tok::kw_is);
|
|
|
|
ParserResult<TypeRepr> type = parseType(diag::expected_type_after_is);
|
|
if (type.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (type.isNull())
|
|
return nullptr;
|
|
|
|
return makeParserResult(new (Context) IsExpr(isLoc, type.get()));
|
|
}
|
|
|
|
/// parseExprAs
|
|
/// expr-as:
|
|
/// 'as' type
|
|
/// 'as?' type
|
|
/// 'as!' type
|
|
ParserResult<Expr> Parser::parseExprAs() {
|
|
// Parse the 'as'.
|
|
SourceLoc asLoc = consumeToken(tok::kw_as);
|
|
|
|
// Parse the postfix '?'.
|
|
SourceLoc questionLoc;
|
|
SourceLoc exclaimLoc;
|
|
if (Tok.is(tok::question_postfix)) {
|
|
questionLoc = consumeToken(tok::question_postfix);
|
|
} else if (Tok.is(tok::exclaim_postfix)) {
|
|
exclaimLoc = consumeToken(tok::exclaim_postfix);
|
|
}
|
|
|
|
ParserResult<TypeRepr> type = parseType(diag::expected_type_after_as);
|
|
|
|
if (type.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (type.isNull())
|
|
return nullptr;
|
|
|
|
Expr *parsed;
|
|
if (questionLoc.isValid()) {
|
|
parsed = new (Context) ConditionalCheckedCastExpr(asLoc, questionLoc,
|
|
type.get());
|
|
} else if (exclaimLoc.isValid()) {
|
|
parsed = new (Context) ForcedCheckedCastExpr(asLoc, exclaimLoc, type.get());
|
|
} else {
|
|
parsed = new (Context) CoerceExpr(asLoc, type.get());
|
|
}
|
|
return makeParserResult(parsed);
|
|
}
|
|
|
|
/// parseExprSequence
|
|
///
|
|
/// expr-sequence(Mode):
|
|
/// expr-sequence-element(Mode) expr-binary(Mode)*
|
|
/// expr-binary(Mode):
|
|
/// operator-binary expr-sequence-element(Mode)
|
|
/// '?' expr-sequence(Mode) ':' expr-sequence-element(Mode)
|
|
/// '=' expr-unary
|
|
/// expr-is
|
|
/// expr-as
|
|
///
|
|
/// The sequencing for binary exprs is not structural, i.e., binary operators
|
|
/// are not inherently right-associative. If present, '?' and ':' tokens must
|
|
/// match.
|
|
///
|
|
/// Similarly, the parsing of 'try' as part of expr-sequence-element
|
|
/// is not structural. 'try' is not permitted at arbitrary points in
|
|
/// a sequence; in the places it's permitted, it's hoisted out to
|
|
/// apply to everything to its right.
|
|
ParserResult<Expr> Parser::parseExprSequence(Diag<> Message,
|
|
bool isExprBasic,
|
|
bool isConfigCondition) {
|
|
SmallVector<Expr*, 8> SequencedExprs;
|
|
SourceLoc startLoc = Tok.getLoc();
|
|
bool HasCodeCompletion = false;
|
|
|
|
while (true) {
|
|
if (isConfigCondition && Tok.isAtStartOfLine())
|
|
break;
|
|
|
|
// Parse a unary expression.
|
|
ParserResult<Expr> Primary =
|
|
parseExprSequenceElement(Message, isExprBasic);
|
|
HasCodeCompletion |= Primary.hasCodeCompletion();
|
|
if (Primary.isNull()) {
|
|
if (Primary.hasCodeCompletion()) {
|
|
if (CodeCompletion) {
|
|
CodeCompletion->setLeadingSequenceExprs(SequencedExprs);
|
|
}
|
|
return Primary;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
SequencedExprs.push_back(Primary.get());
|
|
|
|
parse_operator:
|
|
switch (Tok.getKind()) {
|
|
case tok::oper_binary_spaced:
|
|
case tok::oper_binary_unspaced: {
|
|
// If '>' is not an operator and this token starts with a '>', we're done.
|
|
if (!GreaterThanIsOperator && startsWithGreater(Tok))
|
|
goto done;
|
|
|
|
// Parse the operator.
|
|
Expr *Operator = parseExprOperator();
|
|
SequencedExprs.push_back(Operator);
|
|
|
|
// The message is only valid for the first subexpr.
|
|
Message = diag::expected_expr_after_operator;
|
|
break;
|
|
}
|
|
|
|
case tok::question_infix: {
|
|
// Save the '?'.
|
|
SourceLoc questionLoc = consumeToken();
|
|
|
|
// Parse the middle expression of the ternary.
|
|
ParserResult<Expr> middle =
|
|
parseExprSequence(diag::expected_expr_after_if_question, isExprBasic);
|
|
if (middle.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (middle.isNull())
|
|
return nullptr;
|
|
|
|
// Make sure there's a matching ':' after the middle expr.
|
|
if (!Tok.is(tok::colon)) {
|
|
diagnose(questionLoc, diag::expected_colon_after_if_question);
|
|
|
|
return makeParserErrorResult(new (Context) ErrorExpr(
|
|
{startLoc, middle.get()->getSourceRange().End}));
|
|
}
|
|
|
|
SourceLoc colonLoc = consumeToken();
|
|
|
|
auto *unresolvedIf
|
|
= new (Context) IfExpr(questionLoc,
|
|
middle.get(),
|
|
colonLoc);
|
|
SequencedExprs.push_back(unresolvedIf);
|
|
Message = diag::expected_expr_after_if_colon;
|
|
break;
|
|
}
|
|
|
|
case tok::equal: {
|
|
// If we're parsing an expression as the body of a refutable var/let
|
|
// pattern, then an assignment doesn't make sense. In a "if let"
|
|
// statement the equals is the start of the condition, so don't parse it
|
|
// as a binary operator.
|
|
if (InVarOrLetPattern)
|
|
goto done;
|
|
|
|
SourceLoc equalsLoc = consumeToken();
|
|
auto *assign = new (Context) AssignExpr(equalsLoc);
|
|
SequencedExprs.push_back(assign);
|
|
Message = diag::expected_expr_assignment;
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (CodeCompletion) {
|
|
auto RHS = new (Context) ErrorExpr(
|
|
SourceRange(Tok.getRange().getStart(), Tok.getRange().getEnd()));
|
|
assign->setSrc(RHS);
|
|
SequencedExprs.pop_back();
|
|
assign->setDest(SequencedExprs.back());
|
|
SequencedExprs.pop_back();
|
|
SequencedExprs.push_back(assign);
|
|
CodeCompletion->completeAssignmentRHS(assign);
|
|
}
|
|
consumeToken();
|
|
if (SequencedExprs.size() > 0 && (SequencedExprs.size() & 1) == 0) {
|
|
// Make sure we have odd number of sequence exprs.
|
|
SequencedExprs.pop_back();
|
|
}
|
|
auto Result = SequencedExprs.size() == 1 ?
|
|
makeParserResult(SequencedExprs[0]):
|
|
makeParserResult(SequenceExpr::create(Context, SequencedExprs));
|
|
Result.setHasCodeCompletion();
|
|
return Result;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case tok::kw_is: {
|
|
// Parse a type after the 'is' token instead of an expression.
|
|
ParserResult<Expr> is = parseExprIs();
|
|
if (is.isNull() || is.hasCodeCompletion())
|
|
return nullptr;
|
|
|
|
// Store the expr itself as a placeholder RHS. The real RHS is the
|
|
// type parameter stored in the node itself.
|
|
SequencedExprs.push_back(is.get());
|
|
SequencedExprs.push_back(is.get());
|
|
|
|
// We already parsed the right operand as part of the 'is' production.
|
|
// Jump directly to parsing another operator.
|
|
goto parse_operator;
|
|
}
|
|
|
|
case tok::kw_as: {
|
|
ParserResult<Expr> as = parseExprAs();
|
|
if (as.isNull() || as.hasCodeCompletion())
|
|
return nullptr;
|
|
|
|
// Store the expr itself as a placeholder RHS. The real RHS is the
|
|
// type parameter stored in the node itself.
|
|
SequencedExprs.push_back(as.get());
|
|
SequencedExprs.push_back(as.get());
|
|
|
|
// We already parsed the right operand as part of the 'is' production.
|
|
// Jump directly to parsing another operator.
|
|
goto parse_operator;
|
|
}
|
|
|
|
default:
|
|
// If the next token is not a binary operator, we're done.
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
|
|
if (SequencedExprs.empty()) {
|
|
if (isConfigCondition) {
|
|
diagnose(startLoc, diag::expected_close_to_config_stmt);
|
|
return makeParserError();
|
|
} else {
|
|
// If we had semantic errors, just fail here.
|
|
assert(!SequencedExprs.empty());
|
|
}
|
|
}
|
|
|
|
// If we saw no operators, don't build a sequence.
|
|
if (SequencedExprs.size() == 1) {
|
|
auto Result = makeParserResult(SequencedExprs[0]);
|
|
if (HasCodeCompletion)
|
|
Result.setHasCodeCompletion();
|
|
return Result;
|
|
}
|
|
|
|
auto Result = makeParserResult(SequenceExpr::create(Context, SequencedExprs));
|
|
if (HasCodeCompletion)
|
|
Result.setHasCodeCompletion();
|
|
return Result;
|
|
}
|
|
|
|
/// parseExprSequenceElement
|
|
///
|
|
/// expr-sequence-element(Mode):
|
|
/// 'try' expr-unary(Mode)
|
|
/// 'try' '?' expr-unary(Mode)
|
|
/// 'try' '!' expr-unary(Mode)
|
|
/// expr-unary(Mode)
|
|
///
|
|
/// 'try' is not actually allowed at an arbitrary position of a
|
|
/// sequence, but this isn't enforced until sequence-folding.
|
|
ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
|
|
bool isExprBasic) {
|
|
SourceLoc tryLoc;
|
|
bool hadTry = consumeIf(tok::kw_try, tryLoc);
|
|
Optional<Token> trySuffix;
|
|
if (hadTry && Tok.isAny(tok::exclaim_postfix, tok::question_postfix)) {
|
|
trySuffix = Tok;
|
|
consumeToken();
|
|
}
|
|
|
|
ParserResult<Expr> sub = parseExprUnary(message, isExprBasic);
|
|
|
|
if (hadTry && !sub.hasCodeCompletion() && !sub.isNull()) {
|
|
switch (trySuffix ? trySuffix->getKind() : tok::NUM_TOKENS) {
|
|
case tok::exclaim_postfix:
|
|
sub = makeParserResult(
|
|
new (Context) ForceTryExpr(tryLoc, sub.get(), trySuffix->getLoc()));
|
|
break;
|
|
case tok::question_postfix:
|
|
sub = makeParserResult(
|
|
new (Context) OptionalTryExpr(tryLoc, sub.get(),
|
|
trySuffix->getLoc()));
|
|
break;
|
|
default:
|
|
// If this is a simple "try expr" situation, where the expr is a closure
|
|
// literal, and the next token is a 'catch', then the user wrote
|
|
// try/catch instead of do/catch. Emit a fixit hint to rewrite to the
|
|
// correct do/catch construct.
|
|
if (Tok.is(tok::kw_catch) && isa<ClosureExpr>(sub.get())) {
|
|
diagnose(tryLoc, diag::docatch_not_trycatch)
|
|
.fixItReplace(tryLoc, "do");
|
|
|
|
// Eat all of the catch clauses, so we don't trip over them in error
|
|
// recovery.
|
|
while (Tok.is(tok::kw_catch)) {
|
|
ParserResult<CatchStmt> clause = parseStmtCatch();
|
|
if (clause.hasCodeCompletion() && clause.isNull())
|
|
break;
|
|
}
|
|
|
|
return makeParserResult(new (Context) ErrorExpr(tryLoc));
|
|
}
|
|
|
|
sub = makeParserResult(new (Context) TryExpr(tryLoc, sub.get()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return sub;
|
|
}
|
|
|
|
/// parseExprUnary
|
|
///
|
|
/// expr-unary(Mode):
|
|
/// expr-postfix(Mode)
|
|
/// operator-prefix expr-unary(Mode)
|
|
/// '&' expr-unary(Mode)
|
|
/// expr-selector
|
|
///
|
|
ParserResult<Expr> Parser::parseExprUnary(Diag<> Message, bool isExprBasic) {
|
|
UnresolvedDeclRefExpr *Operator;
|
|
switch (Tok.getKind()) {
|
|
default:
|
|
// If the next token is not an operator, just parse this as expr-postfix.
|
|
return parseExprPostfix(Message, isExprBasic);
|
|
|
|
case tok::amp_prefix: {
|
|
SourceLoc Loc = consumeToken(tok::amp_prefix);
|
|
|
|
ParserResult<Expr> SubExpr = parseExprUnary(Message, isExprBasic);
|
|
if (SubExpr.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (SubExpr.isNull())
|
|
return nullptr;
|
|
return makeParserResult(
|
|
new (Context) InOutExpr(Loc, SubExpr.get(), Type()));
|
|
}
|
|
|
|
case tok::pound_selector:
|
|
return parseExprSelector();
|
|
|
|
case tok::oper_postfix:
|
|
// Postfix operators cannot start a subexpression, but can happen
|
|
// syntactically because the operator may just follow whatever precedes this
|
|
// expression (and that may not always be an expression).
|
|
diagnose(Tok, diag::invalid_postfix_operator);
|
|
Tok.setKind(tok::oper_prefix);
|
|
SWIFT_FALLTHROUGH;
|
|
case tok::oper_prefix:
|
|
Operator = parseExprOperator();
|
|
break;
|
|
case tok::oper_binary_spaced:
|
|
case tok::oper_binary_unspaced: {
|
|
// For recovery purposes, accept an oper_binary here.
|
|
SourceLoc OperEndLoc = Tok.getLoc().getAdvancedLoc(Tok.getLength());
|
|
Tok.setKind(tok::oper_prefix);
|
|
Operator = parseExprOperator();
|
|
|
|
if (OperEndLoc == Tok.getLoc())
|
|
diagnose(PreviousLoc, diag::expected_expr_after_unary_operator);
|
|
else
|
|
diagnose(PreviousLoc, diag::expected_prefix_operator)
|
|
.fixItRemoveChars(OperEndLoc, Tok.getLoc());
|
|
break;
|
|
}
|
|
}
|
|
|
|
ParserResult<Expr> SubExpr = parseExprUnary(Message, isExprBasic);
|
|
if (SubExpr.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (SubExpr.isNull())
|
|
return nullptr;
|
|
|
|
// Check if we have a unary '-' with number literal sub-expression, for
|
|
// example, "-42" or "-1.25".
|
|
if (auto *LE = dyn_cast<NumberLiteralExpr>(SubExpr.get())) {
|
|
if (Operator->hasName() && Operator->getName().getBaseName().str() == "-") {
|
|
LE->setNegative(Operator->getLoc());
|
|
return makeParserResult(LE);
|
|
}
|
|
}
|
|
|
|
return makeParserResult(
|
|
new (Context) PrefixUnaryExpr(Operator, SubExpr.get()));
|
|
}
|
|
|
|
/// parseExprSelector
|
|
///
|
|
/// expr-selector:
|
|
/// '#selector' '(' expr ')'
|
|
///
|
|
ParserResult<Expr> Parser::parseExprSelector() {
|
|
// Consume '#selector'.
|
|
SourceLoc keywordLoc = consumeToken(tok::pound_selector);
|
|
|
|
// Parse the leading '('.
|
|
if (!Tok.is(tok::l_paren)) {
|
|
diagnose(Tok, diag::expr_selector_expected_lparen);
|
|
return makeParserError();
|
|
}
|
|
SourceLoc lParenLoc = consumeToken(tok::l_paren);
|
|
|
|
// Parse the subexpression.
|
|
ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
|
|
if (subExpr.hasCodeCompletion())
|
|
return subExpr;
|
|
|
|
// Parse the closing ')'
|
|
SourceLoc rParenLoc;
|
|
if (subExpr.isParseError()) {
|
|
skipUntilDeclStmtRBrace(tok::r_paren);
|
|
if (Tok.is(tok::r_paren))
|
|
rParenLoc = consumeToken();
|
|
else
|
|
rParenLoc = Tok.getLoc();
|
|
} else {
|
|
parseMatchingToken(tok::r_paren, rParenLoc,
|
|
diag::expr_selector_expected_rparen, lParenLoc);
|
|
}
|
|
|
|
// If the subexpression was in error, just propagate the error.
|
|
if (subExpr.isParseError())
|
|
return makeParserResult<Expr>(
|
|
new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc)));
|
|
|
|
return makeParserResult<Expr>(
|
|
new (Context) ObjCSelectorExpr(keywordLoc, lParenLoc, subExpr.get(),
|
|
rParenLoc));
|
|
}
|
|
|
|
static DeclRefKind getDeclRefKindForOperator(tok kind) {
|
|
switch (kind) {
|
|
case tok::oper_binary_spaced:
|
|
case tok::oper_binary_unspaced: return DeclRefKind::BinaryOperator;
|
|
case tok::oper_postfix: return DeclRefKind::PostfixOperator;
|
|
case tok::oper_prefix: return DeclRefKind::PrefixOperator;
|
|
default: llvm_unreachable("bad operator token kind");
|
|
}
|
|
}
|
|
|
|
/// parseExprOperator - Parse an operator reference expression. These
|
|
/// are not "proper" expressions; they can only appear in binary/unary
|
|
/// operators.
|
|
UnresolvedDeclRefExpr *Parser::parseExprOperator() {
|
|
assert(Tok.isAnyOperator());
|
|
DeclRefKind refKind = getDeclRefKindForOperator(Tok.getKind());
|
|
SourceLoc loc = Tok.getLoc();
|
|
Identifier name = Context.getIdentifier(Tok.getText());
|
|
consumeToken();
|
|
|
|
// Bypass local lookup.
|
|
return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc));
|
|
}
|
|
|
|
static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P,
|
|
DeclContext *DC,
|
|
SourceLoc Loc) {
|
|
auto *methodContext = DC->getInnermostMethodContext();
|
|
if (!methodContext) {
|
|
P.diagnose(Loc, diag::super_not_in_class_method);
|
|
return nullptr;
|
|
}
|
|
|
|
// Do an actual lookup for 'self' in case it shows up in a capture list.
|
|
auto *methodSelf = methodContext->getImplicitSelfDecl();
|
|
auto *lookupSelf = P.lookupInScope(P.Context.Id_self);
|
|
if (lookupSelf && lookupSelf != methodSelf) {
|
|
// FIXME: This is the wrong diagnostic for if someone manually declares a
|
|
// variable named 'self' using backticks.
|
|
P.diagnose(Loc, diag::super_in_closure_with_capture);
|
|
P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here);
|
|
return nullptr;
|
|
}
|
|
|
|
return methodSelf;
|
|
}
|
|
|
|
/// parseExprSuper
|
|
///
|
|
/// expr-super:
|
|
/// expr-super-member
|
|
/// expr-super-init
|
|
/// expr-super-subscript
|
|
/// expr-super-member:
|
|
/// 'super' '.' identifier
|
|
/// expr-super-init:
|
|
/// 'super' '.' 'init'
|
|
/// expr-super-subscript:
|
|
/// 'super' '[' expr ']'
|
|
ParserResult<Expr> Parser::parseExprSuper() {
|
|
// Parse the 'super' reference.
|
|
SourceLoc superLoc = consumeToken(tok::kw_super);
|
|
|
|
VarDecl *selfDecl = getImplicitSelfDeclForSuperContext(*this,
|
|
CurDeclContext,
|
|
superLoc);
|
|
bool ErrorOccurred = selfDecl == nullptr;
|
|
|
|
Expr *superRef = !ErrorOccurred
|
|
? cast<Expr>(new (Context) SuperRefExpr(selfDecl, superLoc,
|
|
/*Implicit=*/false))
|
|
: cast<Expr>(new (Context) ErrorExpr(superLoc));
|
|
|
|
if (Tok.is(tok::period)) {
|
|
// 'super.' must be followed by a member or initializer ref.
|
|
|
|
SourceLoc dotLoc = consumeToken(tok::period);
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (CodeCompletion) {
|
|
if (auto *SRE = dyn_cast<SuperRefExpr>(superRef))
|
|
CodeCompletion->completeExprSuperDot(SRE);
|
|
}
|
|
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
return makeParserCodeCompletionResult(superRef);
|
|
}
|
|
|
|
DeclNameLoc nameLoc;
|
|
DeclName name = parseUnqualifiedDeclName(
|
|
/*allowInit=*/true,
|
|
nameLoc,
|
|
diag::expected_identifier_after_super_dot_expr);
|
|
if (!name)
|
|
return nullptr;
|
|
|
|
return makeParserResult(
|
|
new (Context) UnresolvedDotExpr(superRef, dotLoc, name, nameLoc,
|
|
/*Implicit=*/false));
|
|
}
|
|
|
|
if (Tok.isFollowingLSquare()) {
|
|
// super[expr]
|
|
ParserResult<Expr> idx = parseExprList(tok::l_square, tok::r_square);
|
|
if (idx.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (idx.isNull())
|
|
return nullptr;
|
|
return makeParserResult(new (Context) SubscriptExpr(superRef, idx.get()));
|
|
}
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (CodeCompletion) {
|
|
if (auto *SRE = dyn_cast<SuperRefExpr>(superRef))
|
|
CodeCompletion->completeExprSuper(SRE);
|
|
}
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
return makeParserCodeCompletionResult(superRef);
|
|
}
|
|
|
|
if (consumeIf(tok::unknown))
|
|
return nullptr;
|
|
|
|
diagnose(Tok, diag::expected_dot_or_subscript_after_super);
|
|
return nullptr;
|
|
}
|
|
|
|
/// Copy a numeric literal value into AST-owned memory, stripping underscores
|
|
/// so the semantic part of the value can be parsed by APInt/APFloat parsers.
|
|
static StringRef copyAndStripUnderscores(ASTContext &C, StringRef orig) {
|
|
char *start = static_cast<char*>(C.Allocate(orig.size(), 1));
|
|
char *p = start;
|
|
|
|
for (char c : orig)
|
|
if (c != '_')
|
|
*p++ = c;
|
|
|
|
return StringRef(start, p - start);
|
|
}
|
|
|
|
/// Disambiguate the parse after '{' token that is in a place that might be
|
|
/// the start of a trailing closure, or start the variable accessor block.
|
|
///
|
|
/// Check to see if the '{' is followed by a 'didSet' or a 'willSet' label,
|
|
/// possibly preceded by attributes. If so, we disambiguate the parse as the
|
|
/// start of a get-set block in a variable definition (not as a trailing
|
|
/// closure).
|
|
static bool isStartOfGetSetAccessor(Parser &P) {
|
|
assert(P.Tok.is(tok::l_brace) && "not checking a brace?");
|
|
|
|
// The only case this can happen is if the accessor label is immediately after
|
|
// a brace (possibly preceded by attributes). "get" is implicit, so it can't
|
|
// be checked for. Conveniently however, get/set properties are not allowed
|
|
// to have initializers, so we don't have an ambiguity, we just have to check
|
|
// for observing accessors.
|
|
//
|
|
// If we have a 'didSet' or a 'willSet' label, disambiguate immediately as
|
|
// an accessor block.
|
|
Token NextToken = P.peekToken();
|
|
if (NextToken.isContextualKeyword("didSet") ||
|
|
NextToken.isContextualKeyword("willSet"))
|
|
return true;
|
|
|
|
// If we don't have attributes, then it cannot be an accessor block.
|
|
if (NextToken.isNot(tok::at_sign))
|
|
return false;
|
|
|
|
Parser::BacktrackingScope Backtrack(P);
|
|
|
|
// Eat the "{".
|
|
P.consumeToken(tok::l_brace);
|
|
|
|
// Eat attributes, if present.
|
|
if (!P.canParseAttributes())
|
|
return false;
|
|
|
|
// Check if we have 'didSet'/'willSet' after attributes.
|
|
return P.Tok.isContextualKeyword("didSet") ||
|
|
P.Tok.isContextualKeyword("willSet");
|
|
}
|
|
|
|
/// Map magic literal tokens such as __FILE__ to their
|
|
/// MagicIdentifierLiteralExpr kind.
|
|
MagicIdentifierLiteralExpr::Kind getMagicIdentifierLiteralKind(tok Kind) {
|
|
switch (Kind) {
|
|
case tok::kw___COLUMN__:
|
|
return MagicIdentifierLiteralExpr::Kind::Column;
|
|
case tok::kw___FILE__:
|
|
return MagicIdentifierLiteralExpr::Kind::File;
|
|
case tok::kw___FUNCTION__:
|
|
return MagicIdentifierLiteralExpr::Kind::Function;
|
|
case tok::kw___LINE__:
|
|
return MagicIdentifierLiteralExpr::Kind::Line;
|
|
case tok::kw___DSO_HANDLE__:
|
|
return MagicIdentifierLiteralExpr::Kind::DSOHandle;
|
|
|
|
default:
|
|
llvm_unreachable("not a magic literal");
|
|
}
|
|
}
|
|
|
|
/// parseExprPostfix
|
|
///
|
|
/// expr-literal:
|
|
/// integer_literal
|
|
/// floating_literal
|
|
/// string_literal
|
|
/// nil
|
|
/// true
|
|
/// false
|
|
/// '__FILE__'
|
|
/// '__LINE__'
|
|
/// '__COLUMN__'
|
|
/// '__FUNCTION__'
|
|
/// '__DSO_HANDLE__'
|
|
///
|
|
/// expr-primary:
|
|
/// expr-literal
|
|
/// expr-identifier expr-call-suffix?
|
|
/// expr-closure
|
|
/// expr-anon-closure-argument
|
|
/// expr-delayed-identifier
|
|
/// expr-paren
|
|
/// expr-super
|
|
/// expr-discard
|
|
///
|
|
/// expr-delayed-identifier:
|
|
/// '.' identifier
|
|
///
|
|
/// expr-discard:
|
|
/// '_'
|
|
///
|
|
/// expr-dot:
|
|
/// expr-postfix '.' 'type'
|
|
/// expr-postfix '.' identifier generic-args? expr-call-suffix?
|
|
/// expr-postfix '.' integer_literal
|
|
///
|
|
/// expr-subscript:
|
|
/// expr-postfix '[' expr ']'
|
|
///
|
|
/// expr-call:
|
|
/// expr-postfix expr-paren
|
|
///
|
|
/// expr-force-value:
|
|
/// expr-postfix '!'
|
|
///
|
|
/// expr-trailing-closure:
|
|
/// expr-postfix(trailing-closure) expr-closure
|
|
///
|
|
/// expr-postfix(Mode):
|
|
/// expr-postfix(Mode) operator-postfix
|
|
///
|
|
/// expr-postfix(basic):
|
|
/// expr-primary
|
|
/// expr-dot
|
|
/// expr-metatype
|
|
/// expr-init
|
|
/// expr-subscript
|
|
/// expr-call
|
|
/// expr-force-value
|
|
///
|
|
/// expr-postfix(trailing-closure):
|
|
/// expr-postfix(basic)
|
|
/// expr-trailing-closure
|
|
///
|
|
ParserResult<Expr> Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) {
|
|
ParserResult<Expr> Result;
|
|
switch (Tok.getKind()) {
|
|
case tok::integer_literal: {
|
|
StringRef Text = copyAndStripUnderscores(Context, Tok.getText());
|
|
SourceLoc Loc = consumeToken(tok::integer_literal);
|
|
Result = makeParserResult(new (Context) IntegerLiteralExpr(Text, Loc,
|
|
/*Implicit=*/false));
|
|
break;
|
|
}
|
|
case tok::floating_literal: {
|
|
StringRef Text = copyAndStripUnderscores(Context, Tok.getText());
|
|
SourceLoc Loc = consumeToken(tok::floating_literal);
|
|
Result = makeParserResult(new (Context) FloatLiteralExpr(Text, Loc,
|
|
/*Implicit=*/false));
|
|
break;
|
|
}
|
|
case tok::at_sign:
|
|
// Objective-C programmers habitually type @"foo", so recover gracefully
|
|
// with a fixit. If this isn't @"foo", just handle it like an unknown
|
|
// input.
|
|
if (peekToken().isNot(tok::string_literal))
|
|
goto UnknownCharacter;
|
|
|
|
diagnose(Tok.getLoc(), diag::string_literal_no_atsign)
|
|
.fixItRemove(Tok.getLoc());
|
|
consumeToken(tok::at_sign);
|
|
SWIFT_FALLTHROUGH;
|
|
|
|
case tok::string_literal: // "foo"
|
|
Result = makeParserResult(parseExprStringLiteral());
|
|
break;
|
|
|
|
case tok::kw_nil:
|
|
Result = makeParserResult(
|
|
new (Context) NilLiteralExpr(consumeToken(tok::kw_nil)));
|
|
break;
|
|
|
|
case tok::kw_true:
|
|
case tok::kw_false: {
|
|
bool isTrue = Tok.is(tok::kw_true);
|
|
Result = makeParserResult(
|
|
new (Context) BooleanLiteralExpr(isTrue, consumeToken()));
|
|
break;
|
|
}
|
|
|
|
case tok::kw___FILE__:
|
|
case tok::kw___LINE__:
|
|
case tok::kw___COLUMN__:
|
|
case tok::kw___FUNCTION__:
|
|
case tok::kw___DSO_HANDLE__: {
|
|
auto Kind = getMagicIdentifierLiteralKind(Tok.getKind());
|
|
SourceLoc Loc = consumeToken();
|
|
Result = makeParserResult(
|
|
new (Context) MagicIdentifierLiteralExpr(Kind, Loc, /*Implicit=*/false));
|
|
break;
|
|
}
|
|
|
|
case tok::identifier: // foo
|
|
// If we are parsing a refutable pattern and are inside a let/var pattern,
|
|
// the identifiers change to be value bindings instead of decl references.
|
|
// Parse and return this as an UnresolvedPatternExpr around a binding. This
|
|
// will be resolved (or rejected) by sema when the overall refutable pattern
|
|
// it transformed from an expression into a pattern.
|
|
if ((InVarOrLetPattern == IVOLP_ImplicitlyImmutable ||
|
|
InVarOrLetPattern == IVOLP_InVar ||
|
|
InVarOrLetPattern == IVOLP_InLet) &&
|
|
// If we have "case let x." or "case let x(", we parse x as a normal
|
|
// name, not a binding, because it is the start of an enum pattern or
|
|
// call pattern.
|
|
peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) {
|
|
Identifier name;
|
|
SourceLoc loc = consumeIdentifier(&name);
|
|
auto pattern = createBindingFromPattern(loc, name,
|
|
InVarOrLetPattern != IVOLP_InVar);
|
|
Result = makeParserResult(new (Context) UnresolvedPatternExpr(pattern));
|
|
break;
|
|
}
|
|
|
|
SWIFT_FALLTHROUGH;
|
|
case tok::kw_self: // self
|
|
case tok::kw_Self: // Self
|
|
Result = makeParserResult(parseExprIdentifier());
|
|
|
|
// If there is an expr-call-suffix, parse it and form a call.
|
|
if (Tok.isFollowingLParen()) {
|
|
Result = parseExprCallSuffix(Result);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
case tok::dollarident: // $1
|
|
Result = makeParserResult(parseExprAnonClosureArg());
|
|
break;
|
|
|
|
// If the next token is '_', parse a discard expression.
|
|
case tok::kw__:
|
|
Result = makeParserResult(
|
|
new (Context) DiscardAssignmentExpr(consumeToken(), /*Implicit=*/false));
|
|
break;
|
|
|
|
case tok::l_brace: // expr-closure
|
|
Result = parseExprClosure();
|
|
break;
|
|
|
|
case tok::period: //=.foo
|
|
case tok::period_prefix: { // .foo
|
|
SourceLoc DotLoc = consumeToken();
|
|
|
|
// Special case ".<integer_literal>" like ".4". This isn't valid, but the
|
|
// developer almost certainly meant to use "0.4". Diagnose this, and
|
|
// recover as if they wrote that.
|
|
if (Tok.is(tok::integer_literal) && !Tok.isAtStartOfLine()) {
|
|
diagnose(DotLoc, diag::invalid_float_literal_missing_leading_zero,
|
|
Tok.getText())
|
|
.fixItInsert(DotLoc, "0")
|
|
.highlight({DotLoc, Tok.getLoc()});
|
|
char *Ptr = (char*)Context.Allocate(Tok.getLength()+2, 1);
|
|
memcpy(Ptr, "0.", 2);
|
|
memcpy(Ptr+2, Tok.getText().data(), Tok.getLength());
|
|
auto FltText = StringRef(Ptr, Tok.getLength()+2);
|
|
FltText = copyAndStripUnderscores(Context, FltText);
|
|
|
|
consumeToken(tok::integer_literal);
|
|
Result = makeParserResult(new (Context)
|
|
FloatLiteralExpr(FltText, DotLoc,
|
|
/*Implicit=*/false));
|
|
break;
|
|
}
|
|
|
|
DeclName Name;
|
|
DeclNameLoc NameLoc;
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
auto Expr = new (Context) UnresolvedMemberExpr(
|
|
DotLoc,
|
|
DeclNameLoc(DotLoc.getAdvancedLoc(1)),
|
|
Context.getIdentifier("_"), nullptr);
|
|
Result = makeParserResult(Expr);
|
|
if (CodeCompletion) {
|
|
std::vector<StringRef> Identifiers;
|
|
|
|
// Move lexer to the start of the current line.
|
|
L->backtrackToState(L->getStateForBeginningOfTokenLoc(
|
|
L->getLocForStartOfLine(SourceMgr, Tok.getLoc())));
|
|
|
|
bool HasReturn = false;
|
|
|
|
// Until we see the code completion token, collect identifiers.
|
|
for (L->lex(Tok); !Tok.is(tok::code_complete); consumeToken()) {
|
|
if (!HasReturn)
|
|
HasReturn = Tok.is(tok::kw_return);
|
|
if (Tok.is(tok::identifier)) {
|
|
Identifiers.push_back(Tok.getText());
|
|
}
|
|
}
|
|
CodeCompletion->completeUnresolvedMember(Expr, Identifiers, HasReturn);
|
|
} else {
|
|
Result.setHasCodeCompletion();
|
|
}
|
|
consumeToken();
|
|
return Result;
|
|
}
|
|
|
|
Name = parseUnqualifiedDeclName(/*allowInit=*/true, NameLoc,
|
|
diag::expected_identifier_after_dot_expr);
|
|
if (!Name) return nullptr;
|
|
|
|
ParserResult<Expr> Arg;
|
|
|
|
// Check for a () suffix, which indicates a call when constructing
|
|
// this member. Note that this cannot be the start of a new line.
|
|
if (Tok.isFollowingLParen()) {
|
|
Arg = parseExprList(tok::l_paren, tok::r_paren);
|
|
if (Arg.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (Arg.isNull())
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle .foo by just making an AST node.
|
|
Result = makeParserResult(
|
|
new (Context) UnresolvedMemberExpr(DotLoc, NameLoc, Name,
|
|
Arg.getPtrOrNull()));
|
|
break;
|
|
}
|
|
|
|
case tok::kw_super: { // super.foo or super[foo]
|
|
Result = parseExprSuper();
|
|
break;
|
|
}
|
|
|
|
case tok::l_paren:
|
|
Result = parseExprList(tok::l_paren, tok::r_paren);
|
|
break;
|
|
|
|
case tok::l_square:
|
|
Result = parseExprCollection();
|
|
break;
|
|
|
|
case tok::l_square_lit: // [#Color(...)#], [#Image(...)#]
|
|
Result = parseExprObjectLiteral();
|
|
break;
|
|
|
|
case tok::pound_available: {
|
|
// For better error recovery, parse but reject #available in an expr
|
|
// context.
|
|
diagnose(Tok.getLoc(), diag::availability_query_outside_if_stmt_guard);
|
|
auto res = parseStmtConditionPoundAvailable();
|
|
if (res.hasCodeCompletion())
|
|
return makeParserCodeCompletionStatus();
|
|
if (res.isParseError() || res.isNull())
|
|
return nullptr;
|
|
Result = makeParserResult(
|
|
new (Context) ErrorExpr(res.get()->getSourceRange()));
|
|
break;
|
|
}
|
|
|
|
case tok::code_complete:
|
|
Result = makeParserResult(new (Context) CodeCompletionExpr(Tok.getRange()));
|
|
Result.setHasCodeCompletion();
|
|
if (CodeCompletion)
|
|
CodeCompletion->completePostfixExprBeginning(dyn_cast<CodeCompletionExpr>(
|
|
Result.get()));
|
|
consumeToken(tok::code_complete);
|
|
break;
|
|
|
|
// Eat an invalid token in an expression context. Error tokens are diagnosed
|
|
// by the lexer, so there is no reason to emit another diagnostic.
|
|
case tok::unknown:
|
|
consumeToken(tok::unknown);
|
|
return nullptr;
|
|
|
|
default:
|
|
UnknownCharacter:
|
|
checkForInputIncomplete();
|
|
// FIXME: offer a fixit: 'Self' -> 'self'
|
|
diagnose(Tok, ID);
|
|
return nullptr;
|
|
}
|
|
|
|
// If we had a parse error, don't attempt to parse suffixes.
|
|
if (Result.isParseError())
|
|
return Result;
|
|
|
|
bool hasBindOptional = false;
|
|
|
|
// Handle suffix expressions.
|
|
while (1) {
|
|
// FIXME: Better recovery.
|
|
if (Result.isNull())
|
|
return Result;
|
|
|
|
// Check for a .foo suffix.
|
|
SourceLoc TokLoc = Tok.getLoc();
|
|
if (consumeIf(tok::period) || consumeIf(tok::period_prefix)) {
|
|
// Non-identifier cases.
|
|
if (Tok.isNot(tok::identifier) && Tok.isNot(tok::integer_literal) &&
|
|
Tok.isNot(tok::kw_init)) {
|
|
// A metatype expr.
|
|
if (Tok.is(tok::kw_dynamicType)) {
|
|
Result = makeParserResult(
|
|
new (Context) DynamicTypeExpr(Result.get(), consumeToken(),
|
|
Type()));
|
|
continue;
|
|
}
|
|
|
|
// A '.self' expr.
|
|
if (Tok.is(tok::kw_self)) {
|
|
Result = makeParserResult(
|
|
new (Context) DotSelfExpr(Result.get(), TokLoc, consumeToken()));
|
|
continue;
|
|
}
|
|
|
|
// If we have '.<keyword><code_complete>', try to recover by creating
|
|
// an identifier with the same spelling as the keyword.
|
|
if (Tok.isKeyword() && peekToken().is(tok::code_complete)) {
|
|
Identifier Name = Context.getIdentifier(Tok.getText());
|
|
Result = makeParserResult(
|
|
new (Context) UnresolvedDotExpr(Result.get(), TokLoc,
|
|
Name, DeclNameLoc(Tok.getLoc()),
|
|
/*Implicit=*/false));
|
|
consumeToken();
|
|
}
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (CodeCompletion && Result.isNonNull())
|
|
CodeCompletion->completeDotExpr(Result.get(), /*DotLoc=*/TokLoc);
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
Result.setHasCodeCompletion();
|
|
return Result;
|
|
}
|
|
checkForInputIncomplete();
|
|
diagnose(Tok, diag::expected_member_name);
|
|
return nullptr;
|
|
}
|
|
|
|
// Don't allow '.<integer literal>' following a numeric literal
|
|
// expression.
|
|
if (Tok.is(tok::integer_literal) && Result.isNonNull() &&
|
|
(isa<FloatLiteralExpr>(Result.get()) ||
|
|
isa<IntegerLiteralExpr>(Result.get()))) {
|
|
diagnose(Tok, diag::numeric_literal_numeric_member)
|
|
.highlight(Result.get()->getSourceRange());
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
|
|
if (Result.isParseError())
|
|
continue;
|
|
|
|
if (Tok.isAny(tok::identifier, tok::kw_init)) {
|
|
DeclNameLoc NameLoc;
|
|
DeclName Name = parseUnqualifiedDeclName(/*allowInit=*/true,
|
|
NameLoc,
|
|
diag::expected_member_name);
|
|
if (!Name) return nullptr;
|
|
|
|
Result = makeParserResult(
|
|
new (Context) UnresolvedDotExpr(Result.get(), TokLoc, Name,
|
|
NameLoc,
|
|
/*Implicit=*/false));
|
|
|
|
if (canParseAsGenericArgumentList()) {
|
|
SmallVector<TypeRepr*, 8> args;
|
|
SourceLoc LAngleLoc, RAngleLoc;
|
|
if (parseGenericArguments(args, LAngleLoc, RAngleLoc)) {
|
|
diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket);
|
|
}
|
|
|
|
SmallVector<TypeLoc, 8> locArgs;
|
|
for (auto ty : args)
|
|
locArgs.push_back(ty);
|
|
Result = makeParserResult(new (Context) UnresolvedSpecializeExpr(
|
|
Result.get(), LAngleLoc, Context.AllocateCopy(locArgs),
|
|
RAngleLoc));
|
|
}
|
|
} else {
|
|
DeclName name = Context.getIdentifier(Tok.getText());
|
|
SourceLoc nameLoc = consumeToken(tok::integer_literal);
|
|
Result = makeParserResult(
|
|
new (Context) UnresolvedDotExpr(Result.get(), TokLoc, name,
|
|
DeclNameLoc(nameLoc),
|
|
/*Implicit=*/false));
|
|
}
|
|
|
|
// If there is an expr-call-suffix, parse it and form a call.
|
|
if (Tok.isFollowingLParen())
|
|
Result = parseExprCallSuffix(Result);
|
|
|
|
continue;
|
|
}
|
|
|
|
// Check for a () suffix, which indicates a call.
|
|
// Note that this cannot be the start of a new line.
|
|
if (Tok.isFollowingLParen()) {
|
|
if (peekToken().is(tok::code_complete)) {
|
|
consumeToken(tok::l_paren);
|
|
auto SubResult = makeParserResult(new (Context) CodeCompletionExpr(Tok.
|
|
getRange()));
|
|
SubResult.setHasCodeCompletion();
|
|
if (CodeCompletion && Result.isNonNull())
|
|
CodeCompletion->completePostfixExprParen(Result.get(), SubResult.get());
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
return SubResult;
|
|
}
|
|
ParserResult<Expr> Arg = parseExprList(tok::l_paren, tok::r_paren);
|
|
Result = makeParserResult(new (Context) CallExpr(Result.get(), Arg.get(),
|
|
/*Implicit=*/false));
|
|
if (Arg.hasCodeCompletion())
|
|
Result.setHasCodeCompletion();
|
|
|
|
if (Arg.isParseError())
|
|
Result.setIsParseError();
|
|
|
|
continue;
|
|
}
|
|
|
|
// Check for a [expr] suffix.
|
|
// Note that this cannot be the start of a new line.
|
|
if (Tok.isFollowingLSquare()) {
|
|
ParserResult<Expr> Idx = parseExprList(tok::l_square, tok::r_square);
|
|
if (Idx.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
if (Idx.isNull() || Result.isNull())
|
|
return nullptr;
|
|
Result = makeParserResult(
|
|
new (Context) SubscriptExpr(Result.get(), Idx.get()));
|
|
continue;
|
|
}
|
|
|
|
// Check for a trailing closure, if allowed.
|
|
if (!isExprBasic && Tok.is(tok::l_brace) &&
|
|
!isStartOfGetSetAccessor(*this)) {
|
|
SourceLoc braceLoc = Tok.getLoc();
|
|
// Parse the closure.
|
|
ParserResult<Expr> closure = parseExprClosure();
|
|
if (closure.isNull())
|
|
return nullptr;
|
|
|
|
// Track the original end location of the expression we're trailing so
|
|
// we can warn about excess newlines.
|
|
auto origEndLoc = Result.get()->getEndLoc();
|
|
auto origLineCol = SourceMgr.getLineAndColumn(origEndLoc);
|
|
auto braceLineCol = SourceMgr.getLineAndColumn(braceLoc);
|
|
if (((int)braceLineCol.first - (int)origLineCol.first) > 1) {
|
|
diagnose(braceLoc, diag::trailing_closure_excess_newlines);
|
|
diagnose(Result.get()->getLoc(), diag::trailing_closure_call_here);
|
|
}
|
|
|
|
// Introduce the trailing closure into the call, or form a call, as
|
|
// necessary.
|
|
if (auto call = dyn_cast<CallExpr>(Result.get())) {
|
|
// When a closure follows a call, it becomes the last argument of
|
|
// that call.
|
|
Expr *arg = addTrailingClosureToArgument(Context, call->getArg(),
|
|
closure.get());
|
|
call->setArg(arg);
|
|
|
|
if (closure.hasCodeCompletion())
|
|
Result.setHasCodeCompletion();
|
|
} else {
|
|
// Otherwise, the closure implicitly forms a call.
|
|
Expr *arg = createArgWithTrailingClosure(Context, SourceLoc(), { },
|
|
{ }, { }, SourceLoc(),
|
|
closure.get());
|
|
Result = makeParserResult(
|
|
ParserStatus(closure),
|
|
new (Context) CallExpr(Result.get(), arg, /*Implicit=*/true));
|
|
}
|
|
|
|
if (Result.hasCodeCompletion())
|
|
return Result;
|
|
|
|
// We only allow a single trailing closure on a call. This could be
|
|
// generalized in the future, but needs further design.
|
|
if (Tok.is(tok::l_brace)) break;
|
|
continue;
|
|
}
|
|
|
|
// Check for a ? suffix.
|
|
if (consumeIf(tok::question_postfix)) {
|
|
Result = makeParserResult(
|
|
new (Context) BindOptionalExpr(Result.get(), TokLoc, /*depth*/ 0));
|
|
hasBindOptional = true;
|
|
continue;
|
|
}
|
|
|
|
// Check for a ! suffix.
|
|
if (consumeIf(tok::exclaim_postfix)) {
|
|
Result = makeParserResult(new (Context) ForceValueExpr(Result.get(),
|
|
TokLoc));
|
|
continue;
|
|
}
|
|
|
|
// Check for a postfix-operator suffix.
|
|
if (Tok.is(tok::oper_postfix)) {
|
|
// If '>' is not an operator and this token starts with a '>', we're done.
|
|
if (!GreaterThanIsOperator && startsWithGreater(Tok))
|
|
return Result;
|
|
|
|
Expr *oper = parseExprOperator();
|
|
Result = makeParserResult(
|
|
new (Context) PostfixUnaryExpr(oper, Result.get()));
|
|
continue;
|
|
}
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (Tok.isAtStartOfLine()) {
|
|
// Postfix expression is located on a different line than the code
|
|
// completion token, and thus they are not related.
|
|
return Result;
|
|
}
|
|
|
|
if (CodeCompletion && Result.isNonNull()) {
|
|
bool hasSpace = Tok.getLoc() != getEndOfPreviousLoc();
|
|
CodeCompletion->completePostfixExpr(Result.get(), hasSpace);
|
|
}
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If we had a ? suffix expression, bind the entire postfix chain
|
|
// within an OptionalEvaluationExpr.
|
|
if (hasBindOptional) {
|
|
Result = makeParserResult(
|
|
new (Context) OptionalEvaluationExpr(Result.get()));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static StringLiteralExpr *
|
|
createStringLiteralExprFromSegment(ASTContext &Ctx,
|
|
const Lexer *L,
|
|
Lexer::StringSegment &Segment,
|
|
SourceLoc TokenLoc) {
|
|
assert(Segment.Kind == Lexer::StringSegment::Literal);
|
|
// FIXME: Consider lazily encoding the string when needed.
|
|
llvm::SmallString<256> Buf;
|
|
StringRef EncodedStr = L->getEncodedStringSegment(Segment, Buf);
|
|
if (!Buf.empty()) {
|
|
assert(EncodedStr.begin() == Buf.begin() &&
|
|
"Returned string is not from buffer?");
|
|
EncodedStr = Ctx.AllocateCopy(EncodedStr);
|
|
}
|
|
return new (Ctx) StringLiteralExpr(EncodedStr, TokenLoc);
|
|
}
|
|
|
|
/// expr-literal:
|
|
/// string_literal
|
|
Expr *Parser::parseExprStringLiteral() {
|
|
SmallVector<Lexer::StringSegment, 1> Segments;
|
|
L->getStringLiteralSegments(Tok, Segments);
|
|
SourceLoc Loc = consumeToken();
|
|
|
|
// The simple case: just a single literal segment.
|
|
if (Segments.size() == 1 &&
|
|
Segments.front().Kind == Lexer::StringSegment::Literal) {
|
|
return createStringLiteralExprFromSegment(Context, L, Segments.front(),
|
|
Loc);
|
|
}
|
|
|
|
SmallVector<Expr*, 4> Exprs;
|
|
bool First = true;
|
|
for (auto Segment : Segments) {
|
|
switch (Segment.Kind) {
|
|
case Lexer::StringSegment::Literal: {
|
|
auto TokenLoc = First ? Loc : Segment.Loc;
|
|
Exprs.push_back(
|
|
createStringLiteralExprFromSegment(Context, L, Segment, TokenLoc));
|
|
|
|
// Since the string is already parsed, Tok already points to the first
|
|
// token after the whole string, but PreviousLoc is not exactly correct.
|
|
PreviousLoc = TokenLoc;
|
|
break;
|
|
}
|
|
|
|
case Lexer::StringSegment::Expr: {
|
|
// We are going to mess with Tok to do reparsing for interpolated literals,
|
|
// don't lose our 'next' token.
|
|
llvm::SaveAndRestore<Token> SavedTok(Tok);
|
|
|
|
// Create a temporary lexer that lexes from the body of the string.
|
|
Lexer::State BeginState =
|
|
L->getStateForBeginningOfTokenLoc(Segment.Loc);
|
|
// We need to set the EOF at r_paren, to prevent the Lexer from eagerly
|
|
// trying to lex the token beyond it. Parser::parseList() does a special
|
|
// check for a tok::EOF that is spelled with a ')'.
|
|
// FIXME: This seems like a hack, there must be a better way..
|
|
Lexer::State EndState = BeginState.advance(Segment.Length-1);
|
|
Lexer LocalLex(*L, BeginState, EndState);
|
|
|
|
// Temporarily swap out the parser's current lexer with our new one.
|
|
llvm::SaveAndRestore<Lexer *> T(L, &LocalLex);
|
|
|
|
// Prime the new lexer with a '(' as the first token.
|
|
// We might be at tok::eof now, so ensure that consumeToken() does not
|
|
// assert about lexing past eof.
|
|
Tok.setKind(tok::unknown);
|
|
consumeToken();
|
|
assert(Tok.is(tok::l_paren));
|
|
|
|
ParserResult<Expr> E = parseExprList(tok::l_paren, tok::r_paren);
|
|
if (E.isNonNull()) {
|
|
Exprs.push_back(E.get());
|
|
|
|
if (!Tok.is(tok::eof)) {
|
|
diagnose(Tok, diag::string_interpolation_extra);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
First = false;
|
|
}
|
|
|
|
if (Exprs.empty())
|
|
return new (Context) ErrorExpr(Loc);
|
|
|
|
return new (Context) InterpolatedStringLiteralExpr(Loc,
|
|
Context.AllocateCopy(Exprs));
|
|
}
|
|
|
|
void Parser::diagnoseEscapedArgumentLabel(const Token &tok) {
|
|
assert(tok.isEscapedIdentifier() && "Only for escaped identifiers");
|
|
if (!canBeArgumentLabel(tok.getText())) return;
|
|
|
|
SourceLoc start = tok.getLoc();
|
|
SourceLoc end = start.getAdvancedLoc(tok.getLength());
|
|
diagnose(tok, diag::escaped_parameter_name, tok.getText())
|
|
.fixItRemoveChars(start, start.getAdvancedLoc(1))
|
|
.fixItRemoveChars(end.getAdvancedLoc(-1), end);
|
|
}
|
|
|
|
DeclName Parser::parseUnqualifiedDeclName(bool allowInit,
|
|
DeclNameLoc &loc,
|
|
const Diagnostic &diag) {
|
|
// Consume the base name.
|
|
Identifier baseName;
|
|
SourceLoc baseNameLoc;
|
|
if (Tok.is(tok::kw_init) && allowInit) {
|
|
baseName = Context.Id_init;
|
|
baseNameLoc = consumeToken(tok::kw_init);
|
|
} else if (Tok.is(tok::identifier) || Tok.is(tok::kw_Self) ||
|
|
Tok.is(tok::kw_self)) {
|
|
baseNameLoc = consumeIdentifier(&baseName);
|
|
} else {
|
|
checkForInputIncomplete();
|
|
diagnose(Tok, diag);
|
|
return DeclName();
|
|
}
|
|
|
|
// If the next token isn't a following '(', we don't have a compound name.
|
|
if (!Tok.isFollowingLParen()) {
|
|
loc = DeclNameLoc(baseNameLoc);
|
|
return baseName;
|
|
}
|
|
|
|
// If the token after that isn't an argument label or ':', we don't have a
|
|
// compound name.
|
|
if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) ||
|
|
Identifier::isEditorPlaceholder(peekToken().getText())) {
|
|
loc = DeclNameLoc(baseNameLoc);
|
|
return baseName;
|
|
}
|
|
|
|
// Try to parse a compound name.
|
|
BacktrackingScope backtrack(*this);
|
|
|
|
SmallVector<Identifier, 2> argumentLabels;
|
|
SmallVector<SourceLoc, 2> argumentLabelLocs;
|
|
SourceLoc lparenLoc = consumeToken(tok::l_paren);
|
|
SourceLoc rparenLoc;
|
|
while (true) {
|
|
// Terminate at ')'.
|
|
if (Tok.is(tok::r_paren)) {
|
|
rparenLoc = consumeToken(tok::r_paren);
|
|
break;
|
|
}
|
|
|
|
// If we see a ':', the user forgot the '_';
|
|
if (Tok.is(tok::colon)) {
|
|
diagnose(Tok, diag::empty_arg_label_underscore)
|
|
.fixItInsert(Tok.getLoc(), "_");
|
|
argumentLabels.push_back(Identifier());
|
|
argumentLabelLocs.push_back(consumeToken(tok::colon));
|
|
}
|
|
|
|
// If we see a potential argument label followed by a ':', consume
|
|
// it.
|
|
if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
|
|
// If this was an escaped identifier that need not have been escaped,
|
|
// say so.
|
|
if (Tok.isEscapedIdentifier())
|
|
diagnoseEscapedArgumentLabel(Tok);
|
|
|
|
if (Tok.is(tok::kw__))
|
|
argumentLabels.push_back(Identifier());
|
|
else
|
|
argumentLabels.push_back(Context.getIdentifier(Tok.getText()));
|
|
argumentLabelLocs.push_back(consumeToken());
|
|
(void)consumeToken(tok::colon);
|
|
continue;
|
|
}
|
|
|
|
// This is not a compound name.
|
|
// FIXME: Could recover better if we "know" it's a compound name.
|
|
loc = DeclNameLoc(baseNameLoc);
|
|
return baseName;
|
|
}
|
|
|
|
assert(!argumentLabels.empty() && "Logic above should prevent this");
|
|
assert(argumentLabels.size() == argumentLabelLocs.size());
|
|
|
|
// We have a compound name. Cancel backtracking and build that name.
|
|
backtrack.cancelBacktrack();
|
|
loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs,
|
|
rparenLoc);
|
|
return DeclName(Context, baseName, argumentLabels);
|
|
}
|
|
|
|
/// expr-identifier:
|
|
/// unqualified-decl-name generic-args?
|
|
Expr *Parser::parseExprIdentifier() {
|
|
assert(Tok.is(tok::identifier) || Tok.is(tok::kw_self) ||
|
|
Tok.is(tok::kw_Self));
|
|
|
|
Token IdentTok = Tok;
|
|
|
|
// Parse the unqualified-decl-name.
|
|
DeclNameLoc loc;
|
|
DeclName name = parseUnqualifiedDeclName(/*allowInit=*/false, loc,
|
|
diag::expected_expr);
|
|
|
|
SmallVector<TypeRepr*, 8> args;
|
|
SourceLoc LAngleLoc, RAngleLoc;
|
|
bool hasGenericArgumentList = false;
|
|
|
|
/// The generic-args case is ambiguous with an expression involving '<'
|
|
/// and '>' operators. The operator expression is favored unless a generic
|
|
/// argument list can be successfully parsed, and the closing bracket is
|
|
/// followed by one of these tokens:
|
|
/// lparen_following rparen lsquare_following rsquare lbrace rbrace
|
|
/// period_following comma semicolon
|
|
///
|
|
if (canParseAsGenericArgumentList()) {
|
|
if (parseGenericArguments(args, LAngleLoc, RAngleLoc)) {
|
|
diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket);
|
|
}
|
|
|
|
// The result can be empty in error cases.
|
|
hasGenericArgumentList = !args.empty();
|
|
}
|
|
|
|
ValueDecl *D = lookupInScope(name.getBaseName());
|
|
// FIXME: We want this to work: "var x = { x() }", but for now it's better
|
|
// to disallow it than to crash.
|
|
if (D) {
|
|
for (auto activeVar : DisabledVars) {
|
|
if (activeVar == D) {
|
|
diagnose(loc.getBaseNameLoc(), DisabledVarReason);
|
|
return new (Context) ErrorExpr(loc.getSourceRange());
|
|
}
|
|
}
|
|
} else {
|
|
for (auto activeVar : DisabledVars) {
|
|
if (activeVar->getFullName() == name) {
|
|
diagnose(loc.getBaseNameLoc(), DisabledVarReason);
|
|
return new (Context) ErrorExpr(loc.getSourceRange());
|
|
}
|
|
}
|
|
}
|
|
|
|
Expr *E;
|
|
if (D == 0) {
|
|
if (name.getBaseName().isEditorPlaceholder())
|
|
return parseExprEditorPlaceholder(IdentTok, name.getBaseName());
|
|
|
|
auto refKind = DeclRefKind::Ordinary;
|
|
auto unresolved = new (Context) UnresolvedDeclRefExpr(name, refKind, loc);
|
|
unresolved->setSpecialized(hasGenericArgumentList);
|
|
E = unresolved;
|
|
} else if (auto TD = dyn_cast<TypeDecl>(D)) {
|
|
if (!hasGenericArgumentList)
|
|
E = TypeExpr::createForDecl(loc.getBaseNameLoc(), TD, /*implicit*/false);
|
|
else
|
|
E = TypeExpr::createForSpecializedDecl(loc.getBaseNameLoc(), TD,
|
|
Context.AllocateCopy(args),
|
|
SourceRange(LAngleLoc,
|
|
RAngleLoc));
|
|
} else {
|
|
auto declRef = new (Context) DeclRefExpr(D, loc, /*Implicit=*/false);
|
|
declRef->setGenericArgs(args);
|
|
E = declRef;
|
|
}
|
|
|
|
if (hasGenericArgumentList) {
|
|
SmallVector<TypeLoc, 8> locArgs;
|
|
for (auto ty : args)
|
|
locArgs.push_back(ty);
|
|
E = new (Context) UnresolvedSpecializeExpr(E, LAngleLoc,
|
|
Context.AllocateCopy(locArgs),
|
|
RAngleLoc);
|
|
}
|
|
return E;
|
|
}
|
|
|
|
Expr *Parser::parseExprEditorPlaceholder(Token PlaceholderTok,
|
|
Identifier PlaceholderId) {
|
|
assert(PlaceholderTok.is(tok::identifier));
|
|
assert(PlaceholderId.isEditorPlaceholder());
|
|
|
|
auto parseTypeForPlaceholder = [&](TypeLoc &TyLoc, TypeRepr *&ExpansionTyR) {
|
|
Optional<EditorPlaceholderData> DataOpt =
|
|
swift::parseEditorPlaceholder(PlaceholderTok.getText());
|
|
if (!DataOpt)
|
|
return;
|
|
StringRef TypeStr = DataOpt->Type;
|
|
if (TypeStr.empty())
|
|
return;
|
|
|
|
// Ensure that we restore the parser state at exit.
|
|
ParserPositionRAII PPR(*this);
|
|
|
|
auto parseTypeString = [&](StringRef TyStr) -> TypeRepr* {
|
|
unsigned Offset = TyStr.data() - PlaceholderTok.getText().data();
|
|
SourceLoc TypeStartLoc = PlaceholderTok.getLoc().getAdvancedLoc(Offset);
|
|
SourceLoc TypeEndLoc = TypeStartLoc.getAdvancedLoc(TyStr.size());
|
|
|
|
Lexer::State StartState = L->getStateForBeginningOfTokenLoc(TypeStartLoc);
|
|
Lexer::State EndState = L->getStateForBeginningOfTokenLoc(TypeEndLoc);
|
|
|
|
// Create a lexer for the type sub-string.
|
|
Lexer LocalLex(*L, StartState, EndState);
|
|
|
|
// Temporarily swap out the parser's current lexer with our new one.
|
|
llvm::SaveAndRestore<Lexer *> T(L, &LocalLex);
|
|
Tok.setKind(tok::unknown); // we might be at tok::eof now.
|
|
consumeToken();
|
|
return parseType().getPtrOrNull();
|
|
};
|
|
|
|
TypeRepr *TyR = parseTypeString(TypeStr);
|
|
TyLoc = TyR;
|
|
if (DataOpt->TypeForExpansion == TypeStr) {
|
|
ExpansionTyR = TyR;
|
|
} else {
|
|
ExpansionTyR = parseTypeString(DataOpt->TypeForExpansion);
|
|
}
|
|
};
|
|
|
|
TypeLoc TyLoc;
|
|
TypeRepr *ExpansionTyR = nullptr;
|
|
parseTypeForPlaceholder(TyLoc, ExpansionTyR);
|
|
return new (Context) EditorPlaceholderExpr(PlaceholderId,
|
|
PlaceholderTok.getLoc(),
|
|
TyLoc, ExpansionTyR);
|
|
}
|
|
|
|
bool Parser::
|
|
parseClosureSignatureIfPresent(SmallVectorImpl<CaptureListEntry> &captureList,
|
|
ParameterList *¶ms, SourceLoc &throwsLoc,
|
|
SourceLoc &arrowLoc,
|
|
TypeRepr *&explicitResultType, SourceLoc &inLoc){
|
|
// Clear out result parameters.
|
|
params = nullptr;
|
|
throwsLoc = SourceLoc();
|
|
arrowLoc = SourceLoc();
|
|
explicitResultType = nullptr;
|
|
inLoc = SourceLoc();
|
|
|
|
// If we have a leading token that may be part of the closure signature, do a
|
|
// speculative parse to validate it and look for 'in'.
|
|
if (Tok.isAny(tok::l_paren, tok::l_square, tok::identifier, tok::kw__)) {
|
|
BacktrackingScope backtrack(*this);
|
|
|
|
// Skip by a closure capture list if present.
|
|
if (consumeIf(tok::l_square)) {
|
|
skipUntil(tok::r_square);
|
|
if (!consumeIf(tok::r_square))
|
|
return false;
|
|
}
|
|
|
|
// Parse pattern-tuple func-signature-result? 'in'.
|
|
if (consumeIf(tok::l_paren)) { // Consume the ')'.
|
|
|
|
// While we don't have '->' or ')', eat balanced tokens.
|
|
while (!Tok.is(tok::r_paren) && !Tok.is(tok::eof))
|
|
skipSingle();
|
|
|
|
// Consume the ')', if it's there.
|
|
if (consumeIf(tok::r_paren)) {
|
|
consumeIf(tok::kw_throws) || consumeIf(tok::kw_rethrows);
|
|
// Parse the func-signature-result, if present.
|
|
if (consumeIf(tok::arrow)) {
|
|
if (!canParseType())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Okay, we have a closure signature.
|
|
} else if (Tok.isIdentifierOrUnderscore()) {
|
|
// Parse identifier (',' identifier)*
|
|
consumeToken();
|
|
while (consumeIf(tok::comma)) {
|
|
if (Tok.isIdentifierOrUnderscore()) {
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
consumeIf(tok::kw_throws) || consumeIf(tok::kw_rethrows);
|
|
|
|
// Parse the func-signature-result, if present.
|
|
if (consumeIf(tok::arrow)) {
|
|
if (!canParseType())
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Parse the 'in' at the end.
|
|
if (Tok.isNot(tok::kw_in))
|
|
return false;
|
|
|
|
// Okay, we have a closure signature.
|
|
} else {
|
|
// No closure signature.
|
|
return false;
|
|
}
|
|
|
|
// At this point, we know we have a closure signature. Parse the capture list
|
|
// and parameters.
|
|
if (consumeIf(tok::l_square) &&
|
|
!consumeIf(tok::r_square)) {
|
|
do {
|
|
// Check for the strength specifier: "weak", "unowned", or
|
|
// "unowned(safe/unsafe)".
|
|
SourceLoc loc;
|
|
Ownership ownershipKind = Ownership::Strong;
|
|
if (Tok.isContextualKeyword("weak")){
|
|
loc = consumeToken(tok::identifier);
|
|
ownershipKind = Ownership::Weak;
|
|
} else if (Tok.isContextualKeyword("unowned")) {
|
|
loc = consumeToken(tok::identifier);
|
|
ownershipKind = Ownership::Unowned;
|
|
|
|
// Skip over "safe" and "unsafe" if present.
|
|
if (consumeIf(tok::l_paren)) {
|
|
if (Tok.getText() == "safe")
|
|
ownershipKind = Ownership::Unowned; // FIXME: No "safe" variant.
|
|
else if (Tok.getText() == "unsafe")
|
|
ownershipKind = Ownership::Unmanaged;
|
|
else
|
|
diagnose(Tok, diag::attr_unowned_invalid_specifier);
|
|
consumeIf(tok::identifier);
|
|
if (!consumeIf(tok::r_paren))
|
|
diagnose(Tok, diag::attr_unowned_expected_rparen);
|
|
}
|
|
} else if (Tok.is(tok::identifier) &&
|
|
peekToken().isAny(tok::equal, tok::comma, tok::r_square)) {
|
|
// "x = 42", "x," and "x]" are all strong captures of x.
|
|
loc = Tok.getLoc();
|
|
} else {
|
|
diagnose(Tok, diag::expected_capture_specifier);
|
|
skipUntil(tok::comma, tok::r_square);
|
|
continue;
|
|
}
|
|
|
|
if (Tok.isNot(tok::identifier, tok::kw_self)) {
|
|
diagnose(Tok, diag::expected_capture_specifier_name);
|
|
skipUntil(tok::comma, tok::r_square);
|
|
continue;
|
|
}
|
|
|
|
// The thing being capture specified is an identifier, or as an identifier
|
|
// followed by an expression.
|
|
Expr *initializer;
|
|
Identifier name;
|
|
SourceLoc nameLoc = Tok.getLoc();
|
|
if (peekToken().isNot(tok::equal)) {
|
|
// If this is the simple case, then the identifier is both the name and
|
|
// the expression to capture.
|
|
name = Context.getIdentifier(Tok.getText());
|
|
initializer = parseExprIdentifier();
|
|
|
|
// It is a common error to try to capture a nested field instead of just
|
|
// a local name, reject it with a specific error message.
|
|
if (Tok.isAny(tok::period, tok::exclaim_postfix,tok::question_postfix)){
|
|
diagnose(Tok, diag::cannot_capture_fields);
|
|
skipUntil(tok::comma, tok::r_square);
|
|
continue;
|
|
}
|
|
|
|
} else {
|
|
// Otherwise, the name is a new declaration.
|
|
consumeIdentifier(&name);
|
|
consumeToken(tok::equal);
|
|
|
|
auto ExprResult = parseExpr(diag::expected_init_capture_specifier);
|
|
if (ExprResult.isNull())
|
|
continue;
|
|
initializer = ExprResult.get();
|
|
}
|
|
|
|
// Create the VarDecl and the PatternBindingDecl for the captured
|
|
// expression. This uses the parent declcontext (not the closure) since
|
|
// the initializer expression is evaluated before the closure is formed.
|
|
auto *VD = new (Context) VarDecl(/*isStatic*/false,
|
|
/*isLet*/ownershipKind !=Ownership::Weak,
|
|
nameLoc, name, Type(), CurDeclContext);
|
|
// Attributes.
|
|
if (ownershipKind != Ownership::Strong)
|
|
VD->getAttrs().add(new (Context) OwnershipAttr(ownershipKind));
|
|
|
|
auto pattern = new (Context) NamedPattern(VD, /*implicit*/true);
|
|
|
|
auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
nameLoc, pattern, initializer,
|
|
CurDeclContext);
|
|
|
|
|
|
|
|
captureList.push_back(CaptureListEntry(VD, PBD));
|
|
} while (consumeIf(tok::comma));
|
|
|
|
// The capture list needs to be closed off with a ']'.
|
|
if (!consumeIf(tok::r_square)) {
|
|
diagnose(Tok, diag::expected_capture_list_end_rsquare);
|
|
skipUntil(tok::r_square);
|
|
if (Tok.is(tok::r_square))
|
|
consumeToken(tok::r_square);
|
|
}
|
|
}
|
|
|
|
bool invalid = false;
|
|
if (Tok.isNot(tok::kw_in)) {
|
|
if (Tok.is(tok::l_paren)) {
|
|
// Parse the closure arguments.
|
|
auto pattern = parseSingleParameterClause(ParameterContextKind::Closure);
|
|
if (pattern.isNonNull())
|
|
params = pattern.get();
|
|
else
|
|
invalid = true;
|
|
} else {
|
|
// Parse identifier (',' identifier)*
|
|
SmallVector<ParamDecl*, 4> elements;
|
|
do {
|
|
if (Tok.isNot(tok::identifier, tok::kw__)) {
|
|
diagnose(Tok, diag::expected_closure_parameter_name);
|
|
invalid = true;
|
|
break;
|
|
}
|
|
|
|
Identifier name = Tok.is(tok::identifier) ?
|
|
Context.getIdentifier(Tok.getText()) : Identifier();
|
|
auto var = new (Context) ParamDecl(/*IsLet*/ true, SourceLoc(),
|
|
Identifier(), Tok.getLoc(), name,
|
|
Type(), nullptr);
|
|
elements.push_back(var);
|
|
consumeToken();
|
|
|
|
// Consume a comma to continue.
|
|
} while (consumeIf(tok::comma));
|
|
|
|
params = ParameterList::create(Context, elements);
|
|
}
|
|
|
|
if (Tok.is(tok::kw_throws)) {
|
|
throwsLoc = consumeToken();
|
|
} else if (Tok.is(tok::kw_rethrows)) {
|
|
throwsLoc = consumeToken();
|
|
diagnose(throwsLoc, diag::rethrowing_function_type);
|
|
}
|
|
|
|
// Parse the optional explicit return type.
|
|
if (Tok.is(tok::arrow)) {
|
|
// Consume the '->'.
|
|
arrowLoc = consumeToken();
|
|
|
|
// Parse the type.
|
|
explicitResultType =
|
|
parseType(diag::expected_closure_result_type).getPtrOrNull();
|
|
if (!explicitResultType) {
|
|
// If we couldn't parse the result type, clear out the arrow location.
|
|
arrowLoc = SourceLoc();
|
|
invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse the 'in'.
|
|
if (Tok.is(tok::kw_in)) {
|
|
inLoc = consumeToken();
|
|
} else {
|
|
// Scan forward to see if we can find the 'in'. This re-synchronizes the
|
|
// parser so we can at least parse the body correctly.
|
|
SourceLoc startLoc = Tok.getLoc();
|
|
ParserPosition pos = getParserPosition();
|
|
while (Tok.isNot(tok::eof) && !Tok.is(tok::kw_in) &&
|
|
Tok.isNot(tok::r_brace)) {
|
|
skipSingle();
|
|
}
|
|
|
|
if (Tok.is(tok::kw_in)) {
|
|
// We found the 'in'. If this is the first error, complain about the
|
|
// junk tokens in-between but re-sync at the 'in'.
|
|
if (!invalid) {
|
|
diagnose(startLoc, diag::unexpected_tokens_before_closure_in);
|
|
}
|
|
inLoc = consumeToken();
|
|
} else {
|
|
// We didn't find an 'in', backtrack to where we started. If this is the
|
|
// first error, complain about the missing 'in'.
|
|
backtrackToPosition(pos);
|
|
if (!invalid) {
|
|
diagnose(Tok, diag::expected_closure_in)
|
|
.fixItInsert(Tok.getLoc(), "in ");
|
|
}
|
|
inLoc = Tok.getLoc();
|
|
}
|
|
}
|
|
|
|
return invalid;
|
|
}
|
|
|
|
ParserResult<Expr> Parser::parseExprClosure() {
|
|
assert(Tok.is(tok::l_brace) && "Not at a left brace?");
|
|
|
|
// We may be parsing this closure expr in a matching pattern context. If so,
|
|
// reset our state to not be in a pattern for any recursive pattern parses.
|
|
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
|
|
T(InVarOrLetPattern, IVOLP_NotInVarOrLet);
|
|
|
|
// Parse the opening left brace.
|
|
SourceLoc leftBrace = consumeToken();
|
|
|
|
// Parse the closure-signature, if present.
|
|
ParameterList *params = nullptr;
|
|
SourceLoc throwsLoc;
|
|
SourceLoc arrowLoc;
|
|
TypeRepr *explicitResultType;
|
|
SourceLoc inLoc;
|
|
SmallVector<CaptureListEntry, 2> captureList;
|
|
parseClosureSignatureIfPresent(captureList, params, throwsLoc, arrowLoc,
|
|
explicitResultType, inLoc);
|
|
|
|
// If the closure was created in the context of an array type signature's
|
|
// size expression, there will not be a local context. A parse error will
|
|
// be reported at the signature's declaration site.
|
|
if (!CurLocalContext) {
|
|
skipUntil(tok::r_brace);
|
|
if (Tok.is(tok::r_brace))
|
|
consumeToken();
|
|
return makeParserError();
|
|
}
|
|
|
|
unsigned discriminator = CurLocalContext->claimNextClosureDiscriminator();
|
|
|
|
// Create the closure expression and enter its context.
|
|
auto *closure = new (Context) ClosureExpr(params, throwsLoc, arrowLoc, inLoc,
|
|
explicitResultType,
|
|
discriminator, CurDeclContext);
|
|
// The arguments to the func are defined in their own scope.
|
|
Scope S(this, ScopeKind::ClosureParams);
|
|
ParseFunctionBody cc(*this, closure);
|
|
|
|
// Handle parameters.
|
|
if (params) {
|
|
// Add the parameters into scope.
|
|
addParametersToScope(params);
|
|
} else {
|
|
// There are no parameters; allow anonymous closure variables.
|
|
// FIXME: We could do this all the time, and then provide Fix-Its
|
|
// to map $i -> the appropriately-named argument. This might help
|
|
// users who are refactoring code by adding names.
|
|
AnonClosureVars.push_back({ leftBrace, {}});
|
|
}
|
|
|
|
// Add capture list variables to scope.
|
|
for (auto c : captureList)
|
|
addToScope(c.Var);
|
|
|
|
// Parse the body.
|
|
SmallVector<ASTNode, 4> bodyElements;
|
|
ParserStatus Status;
|
|
Status |= parseBraceItems(bodyElements, BraceItemListKind::Brace);
|
|
|
|
// Parse the closing '}'.
|
|
SourceLoc rightBrace;
|
|
parseMatchingToken(tok::r_brace, rightBrace, diag::expected_closure_rbrace,
|
|
leftBrace);
|
|
|
|
// If we didn't have any parameters, create a parameter list from the
|
|
// anonymous closure arguments.
|
|
if (!params) {
|
|
// Create a parameter pattern containing the anonymous variables.
|
|
auto &anonVars = AnonClosureVars.back().second;
|
|
SmallVector<ParamDecl*, 4> elements;
|
|
for (auto anonVar : anonVars)
|
|
elements.push_back(anonVar);
|
|
|
|
params = ParameterList::create(Context, leftBrace, elements, leftBrace);
|
|
|
|
// Pop out of the anonymous closure variables scope.
|
|
AnonClosureVars.pop_back();
|
|
|
|
// Attach the parameters to the closure.
|
|
closure->setParameterList(params);
|
|
closure->setHasAnonymousClosureVars();
|
|
}
|
|
|
|
// If the body consists of a single expression, turn it into a return
|
|
// statement.
|
|
//
|
|
// But don't do this transformation during code completion, as the source
|
|
// may be incomplete and the type mismatch in return statement will just
|
|
// confuse the type checker.
|
|
bool hasSingleExpressionBody = false;
|
|
if (!Status.hasCodeCompletion() && bodyElements.size() == 1) {
|
|
// If the closure's only body element is a single return statement,
|
|
// use that instead of creating a new wrapping return expression.
|
|
Expr *returnExpr = nullptr;
|
|
|
|
if (bodyElements[0].is<Stmt *>()) {
|
|
if (auto returnStmt =
|
|
dyn_cast<ReturnStmt>(bodyElements[0].get<Stmt*>())) {
|
|
|
|
if (!returnStmt->hasResult()) {
|
|
|
|
returnExpr = TupleExpr::createEmpty(Context,
|
|
SourceLoc(),
|
|
SourceLoc(),
|
|
/*implicit*/true);
|
|
|
|
returnStmt->setResult(returnExpr);
|
|
}
|
|
|
|
hasSingleExpressionBody = true;
|
|
}
|
|
}
|
|
|
|
// Otherwise, create the wrapping return.
|
|
if (bodyElements[0].is<Expr *>()) {
|
|
hasSingleExpressionBody = true;
|
|
returnExpr = bodyElements[0].get<Expr*>();
|
|
bodyElements[0] = new (Context) ReturnStmt(SourceLoc(),
|
|
returnExpr);
|
|
}
|
|
}
|
|
|
|
// Set the body of the closure.
|
|
closure->setBody(BraceStmt::create(Context, leftBrace, bodyElements,
|
|
rightBrace),
|
|
hasSingleExpressionBody);
|
|
|
|
// If the closure includes a capture list, create an AST node for it as well.
|
|
Expr *result = closure;
|
|
if (!captureList.empty())
|
|
result = new (Context) CaptureListExpr(Context.AllocateCopy(captureList),
|
|
closure);
|
|
|
|
return makeParserResult(Status, result);
|
|
}
|
|
|
|
/// expr-anon-closure-argument:
|
|
/// dollarident
|
|
Expr *Parser::parseExprAnonClosureArg() {
|
|
StringRef Name = Tok.getText();
|
|
SourceLoc Loc = consumeToken(tok::dollarident);
|
|
assert(Name[0] == '$' && "Not a dollarident");
|
|
|
|
// We know from the lexer that this is all-numeric.
|
|
|
|
unsigned ArgNo = 0;
|
|
if (Name.substr(1).getAsInteger(10, ArgNo)) {
|
|
diagnose(Loc.getAdvancedLoc(1), diag::dollar_numeric_too_large);
|
|
return new (Context) ErrorExpr(Loc);
|
|
}
|
|
|
|
// If this is a closure expression that did not have any named parameters,
|
|
// generate the anonymous variables we need.
|
|
auto closure = dyn_cast_or_null<ClosureExpr>(
|
|
dyn_cast<AbstractClosureExpr>(CurDeclContext));
|
|
if (!closure || closure->getParameters()) {
|
|
// FIXME: specialize diagnostic when there were closure parameters.
|
|
// We can be fairly smart here.
|
|
diagnose(Loc, closure ? diag::anon_closure_arg_in_closure_with_args
|
|
: diag::anon_closure_arg_not_in_closure);
|
|
return new (Context) ErrorExpr(Loc);
|
|
}
|
|
|
|
auto leftBraceLoc = AnonClosureVars.back().first;
|
|
auto &decls = AnonClosureVars.back().second;
|
|
while (ArgNo >= decls.size()) {
|
|
unsigned nextIdx = decls.size();
|
|
SmallVector<char, 4> StrBuf;
|
|
StringRef varName = ("$" + Twine(nextIdx)).toStringRef(StrBuf);
|
|
Identifier ident = Context.getIdentifier(varName);
|
|
SourceLoc varLoc = leftBraceLoc;
|
|
auto *var = new (Context) ParamDecl(/*IsLet*/ true, SourceLoc(),
|
|
Identifier(), varLoc, ident, Type(),
|
|
closure);
|
|
decls.push_back(var);
|
|
}
|
|
|
|
return new (Context) DeclRefExpr(decls[ArgNo], DeclNameLoc(Loc),
|
|
/*Implicit=*/false);
|
|
}
|
|
|
|
|
|
/// parseExprList - Parse a list of expressions.
|
|
///
|
|
/// expr-paren:
|
|
/// lparen-any ')'
|
|
/// lparen-any binary-op ')'
|
|
/// lparen-any expr-paren-element (',' expr-paren-element)* ')'
|
|
///
|
|
/// expr-paren-element:
|
|
/// (identifier ':')? expr
|
|
///
|
|
ParserResult<Expr> Parser::parseExprList(tok LeftTok, tok RightTok) {
|
|
StructureMarkerRAII ParsingExprList(*this, Tok);
|
|
|
|
SourceLoc LLoc = consumeToken(LeftTok);
|
|
SourceLoc RLoc;
|
|
|
|
SmallVector<Expr*, 8> SubExprs;
|
|
SmallVector<Identifier, 8> SubExprNames;
|
|
SmallVector<SourceLoc, 8> SubExprNameLocs;
|
|
|
|
ParserStatus Status = parseList(RightTok, LLoc, RLoc,
|
|
tok::comma, /*OptionalSep=*/false,
|
|
/*AllowSepAfterLast=*/false,
|
|
RightTok == tok::r_paren ?
|
|
diag::expected_rparen_expr_list :
|
|
diag::expected_rsquare_expr_list,
|
|
[&] () -> ParserStatus {
|
|
Identifier FieldName;
|
|
SourceLoc FieldNameLoc;
|
|
|
|
// Check to see if there is an argument label.
|
|
if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
|
|
// If this was an escaped identifier that need not have been escaped,
|
|
// say so.
|
|
if (Tok.isEscapedIdentifier())
|
|
diagnoseEscapedArgumentLabel(Tok);
|
|
|
|
if (!Tok.is(tok::kw__))
|
|
FieldName = Context.getIdentifier(Tok.getText());
|
|
FieldNameLoc = consumeToken();
|
|
consumeToken(tok::colon);
|
|
}
|
|
|
|
// See if we have an operator decl ref '(<op>)'. The operator token in
|
|
// this case lexes as a binary operator because it neither leads nor
|
|
// follows a proper subexpression.
|
|
ParserStatus Status;
|
|
Expr *SubExpr = nullptr;
|
|
if (Tok.isBinaryOperator() && peekToken().isAny(RightTok, tok::comma)) {
|
|
SourceLoc Loc;
|
|
Identifier OperName;
|
|
if (parseAnyIdentifier(OperName, Loc, diag::expected_operator_ref)) {
|
|
return makeParserError();
|
|
}
|
|
// Bypass local lookup. Use an 'Ordinary' reference kind so that the
|
|
// reference may resolve to any unary or binary operator based on
|
|
// context.
|
|
SubExpr = new(Context) UnresolvedDeclRefExpr(OperName,
|
|
DeclRefKind::Ordinary,
|
|
DeclNameLoc(Loc));
|
|
} else {
|
|
ParserResult<Expr> ParsedSubExpr
|
|
= parseExpr(diag::expected_expr_in_expr_list);
|
|
SubExpr = ParsedSubExpr.getPtrOrNull();
|
|
Status = ParsedSubExpr;
|
|
}
|
|
|
|
// If we got a subexpression, add it.
|
|
if (SubExpr) {
|
|
// Update names and locations.
|
|
if (!SubExprNames.empty()) {
|
|
SubExprNames.push_back(FieldName);
|
|
SubExprNameLocs.push_back(FieldNameLoc);
|
|
} else if (FieldName.get()) {
|
|
SubExprNames.resize(SubExprs.size());
|
|
SubExprNames.push_back(FieldName);
|
|
|
|
SubExprNameLocs.resize(SubExprs.size());
|
|
SubExprNameLocs.push_back(FieldNameLoc);
|
|
}
|
|
|
|
// Add the subexpression.
|
|
SubExprs.push_back(SubExpr);
|
|
}
|
|
|
|
return Status;
|
|
});
|
|
|
|
// A tuple with a single, unlabelled element is just parentheses.
|
|
if (SubExprs.size() == 1 &&
|
|
(SubExprNames.empty() || SubExprNames[0].empty())) {
|
|
return makeParserResult(
|
|
Status, new (Context) ParenExpr(LLoc, SubExprs[0], RLoc,
|
|
/*hasTrailingClosure=*/false));
|
|
}
|
|
|
|
return makeParserResult(
|
|
Status,
|
|
TupleExpr::create(Context, LLoc, SubExprs, SubExprNames, SubExprNameLocs,
|
|
RLoc, /*hasTrailingClosure=*/false,
|
|
/*Implicit=*/false));
|
|
}
|
|
|
|
/// \brief Parse an object literal expression.
|
|
///
|
|
/// expr-literal:
|
|
/// '[#' identifier expr-paren '#]'
|
|
ParserResult<Expr>
|
|
Parser::parseExprObjectLiteral() {
|
|
SourceLoc LLitLoc = consumeToken(tok::l_square_lit);
|
|
Identifier Name;
|
|
SourceLoc NameLoc;
|
|
if (parseIdentifier(Name, NameLoc,
|
|
diag::expected_identifier_after_l_square_lit)) {
|
|
return makeParserError();
|
|
}
|
|
// Parse a tuple of args
|
|
if (!Tok.is(tok::l_paren)) {
|
|
diagnose(Tok, diag::expected_arg_list_in_object_literal);
|
|
return makeParserError();
|
|
}
|
|
ParserResult<Expr> Arg;
|
|
Arg = parseExprList(tok::l_paren, tok::r_paren);
|
|
if (Arg.hasCodeCompletion()) {
|
|
return Arg;
|
|
}
|
|
if (Arg.isParseError()) {
|
|
return makeParserError();
|
|
}
|
|
if (!Tok.is(tok::r_square_lit)) {
|
|
diagnose(Tok, diag::expected_r_square_lit_after_object_literal);
|
|
return makeParserError();
|
|
}
|
|
SourceLoc RLitLoc = consumeToken(tok::r_square_lit);
|
|
return makeParserResult(
|
|
new (Context) ObjectLiteralExpr(LLitLoc, Name, NameLoc, Arg.get(), RLitLoc,
|
|
/*implicit=*/false));
|
|
}
|
|
|
|
/// \brief Parse an expression call suffix.
|
|
///
|
|
/// expr-call-suffix:
|
|
/// expr-paren selector-arg*
|
|
/// expr-closure selector-arg* (except in expr-basic)
|
|
///
|
|
/// selector-arg:
|
|
/// identifier expr-paren
|
|
ParserResult<Expr>
|
|
Parser::parseExprCallSuffix(ParserResult<Expr> fn,
|
|
Identifier firstSelectorPiece,
|
|
SourceLoc firstSelectorPieceLoc) {
|
|
assert(Tok.isFollowingLParen() && "Not a call suffix?");
|
|
|
|
// Parse the first argument.
|
|
|
|
// If there is a code completion token right after the '(', do a special case
|
|
// callback.
|
|
if (peekToken().is(tok::code_complete) && CodeCompletion) {
|
|
consumeToken(tok::l_paren);
|
|
auto CCE = new (Context) CodeCompletionExpr(Tok.getRange());
|
|
auto Result = makeParserResult(new (Context) CallExpr(fn.get(),
|
|
new (Context) ParenExpr(SourceLoc(), CCE, SourceLoc(),
|
|
/*hasTrailingClosure=*/false),
|
|
/*Implicit=*/false));
|
|
CodeCompletion->completePostfixExprParen(fn.get(), CCE);
|
|
// Eat the code completion token because we handled it.
|
|
consumeToken(tok::code_complete);
|
|
Result.setHasCodeCompletion();
|
|
return Result;
|
|
}
|
|
|
|
ParserResult<Expr> firstArg = parseExprList(Tok.getKind(), tok::r_paren);
|
|
|
|
// Form the call.
|
|
auto Result = makeParserResult(new (Context) CallExpr(fn.get(), firstArg.get(),
|
|
/*Implicit=*/false));
|
|
if (fn.isParseError() || firstArg.isParseError())
|
|
Result.setIsParseError();
|
|
|
|
if (fn.hasCodeCompletion() || firstArg.hasCodeCompletion()) {
|
|
if (CodeCompletion) {
|
|
CodeCompletion->completeCallArg(Result.get());
|
|
}
|
|
Result.setHasCodeCompletion();
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
/// parseExprCollection - Parse a collection literal expression.
|
|
///
|
|
/// expr-collection:
|
|
/// expr-array
|
|
/// expr-dictionary
|
|
// lsquare-starting ']'
|
|
ParserResult<Expr> Parser::parseExprCollection() {
|
|
Parser::StructureMarkerRAII ParsingCollection(*this, Tok);
|
|
SourceLoc LSquareLoc = consumeToken(tok::l_square);
|
|
|
|
// [] is always an array.
|
|
if (Tok.is(tok::r_square)) {
|
|
SourceLoc RSquareLoc = consumeToken(tok::r_square);
|
|
return makeParserResult(
|
|
ArrayExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc));
|
|
}
|
|
|
|
// [:] is always an empty dictionary.
|
|
if (Tok.is(tok::colon) && peekToken().is(tok::r_square)) {
|
|
consumeToken(tok::colon);
|
|
SourceLoc RSquareLoc = consumeToken(tok::r_square);
|
|
return makeParserResult(
|
|
DictionaryExpr::create(Context, LSquareLoc, {}, RSquareLoc));
|
|
}
|
|
|
|
// Parse the first expression.
|
|
ParserResult<Expr> FirstExpr
|
|
= parseExpr(diag::expected_expr_in_collection_literal);
|
|
if (FirstExpr.isNull() || FirstExpr.hasCodeCompletion()) {
|
|
skipUntil(tok::r_square);
|
|
if (Tok.is(tok::r_square))
|
|
consumeToken();
|
|
if (FirstExpr.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
return nullptr;
|
|
}
|
|
|
|
// If we have a ':', this is a dictionary literal.
|
|
if (Tok.is(tok::colon)) {
|
|
return parseExprDictionary(LSquareLoc, FirstExpr.get());
|
|
}
|
|
|
|
// Otherwise, we have an array literal.
|
|
return parseExprArray(LSquareLoc, FirstExpr.get());
|
|
}
|
|
|
|
/// parseExprArray - Parse an array literal expression.
|
|
///
|
|
/// The lsquare-starting and first expression have already been
|
|
/// parsed, and are passed in as parameters.
|
|
///
|
|
/// expr-array:
|
|
/// '[' expr (',' expr)* ','? ']'
|
|
/// '[' ']'
|
|
ParserResult<Expr> Parser::parseExprArray(SourceLoc LSquareLoc,
|
|
Expr *FirstExpr) {
|
|
SmallVector<Expr *, 8> SubExprs;
|
|
SmallVector<SourceLoc, 8> CommaLocs;
|
|
SubExprs.push_back(FirstExpr);
|
|
|
|
SourceLoc CommaLoc, RSquareLoc;
|
|
ParserStatus Status;
|
|
|
|
if (Tok.isNot(tok::r_square) && !consumeIf(tok::comma, CommaLoc)) {
|
|
diagnose(Tok, diag::expected_separator, ",")
|
|
.fixItInsertAfter(PreviousLoc, ",");
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
CommaLocs.push_back(CommaLoc);
|
|
|
|
Status |= parseList(tok::r_square, LSquareLoc, RSquareLoc,
|
|
tok::comma, /*OptionalSep=*/false,
|
|
/*AllowSepAfterLast=*/true,
|
|
diag::expected_rsquare_array_expr,
|
|
[&] () -> ParserStatus
|
|
{
|
|
ParserResult<Expr> Element
|
|
= parseExpr(diag::expected_expr_in_collection_literal);
|
|
if (Element.isNonNull())
|
|
SubExprs.push_back(Element.get());
|
|
|
|
if (Tok.is(tok::comma))
|
|
CommaLocs.push_back(Tok.getLoc());
|
|
|
|
return Element;
|
|
});
|
|
|
|
if (Status.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
|
|
assert(SubExprs.size() >= 1);
|
|
return makeParserResult(Status,
|
|
ArrayExpr::create(Context, LSquareLoc, SubExprs, CommaLocs,
|
|
RSquareLoc));
|
|
}
|
|
|
|
/// parseExprDictionary - Parse a dictionary literal expression.
|
|
///
|
|
/// The lsquare-starting and first key have already been parsed, and
|
|
/// are passed in as parameters.
|
|
///
|
|
/// expr-dictionary:
|
|
/// '[' expr ':' expr (',' expr ':' expr)* ','? ']'
|
|
/// '[' ':' ']'
|
|
ParserResult<Expr> Parser::parseExprDictionary(SourceLoc LSquareLoc,
|
|
Expr *FirstKey) {
|
|
assert(Tok.is(tok::colon));
|
|
|
|
// Each subexpression is a (key, value) tuple.
|
|
// FIXME: We're not tracking the colon locations in the AST.
|
|
SmallVector<Expr *, 8> SubExprs;
|
|
SourceLoc RSquareLoc;
|
|
|
|
// Function that adds a new key/value pair.
|
|
auto addKeyValuePair = [&](Expr *Key, Expr *Value) -> void {
|
|
Expr *Exprs[] = {Key, Value};
|
|
SubExprs.push_back(TupleExpr::createImplicit(Context, Exprs, { }));
|
|
};
|
|
|
|
bool FirstPair = true;
|
|
|
|
ParserStatus Status =
|
|
parseList(tok::r_square, LSquareLoc, RSquareLoc, tok::comma,
|
|
/*OptionalSep=*/false, /*AllowSepAfterLast=*/true,
|
|
diag::expected_rsquare_array_expr, [&]() -> ParserStatus {
|
|
// Parse the next key.
|
|
ParserResult<Expr> Key;
|
|
if (FirstPair) {
|
|
Key = makeParserResult(FirstKey);
|
|
FirstPair = false;
|
|
} else {
|
|
Key = parseExpr(diag::expected_key_in_dictionary_literal);
|
|
if (Key.isNull() || Key.hasCodeCompletion())
|
|
return Key;
|
|
}
|
|
|
|
// Parse the ':'.
|
|
if (Tok.isNot(tok::colon)) {
|
|
diagnose(Tok, diag::expected_colon_in_dictionary_literal);
|
|
return makeParserError();
|
|
}
|
|
consumeToken();
|
|
|
|
// Parse the next value.
|
|
ParserResult<Expr> Value =
|
|
parseExpr(diag::expected_value_in_dictionary_literal);
|
|
if (Value.hasCodeCompletion())
|
|
return Value;
|
|
|
|
if (Value.isNull())
|
|
Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc));
|
|
|
|
// Add this key/value pair.
|
|
addKeyValuePair(Key.get(), Value.get());
|
|
return Value;
|
|
});
|
|
|
|
if (Status.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Expr>();
|
|
|
|
assert(SubExprs.size() >= 1);
|
|
return makeParserResult(DictionaryExpr::create(Context, LSquareLoc, SubExprs,
|
|
RSquareLoc));
|
|
}
|
|
|
|
void Parser::addPatternVariablesToScope(ArrayRef<Pattern *> Patterns) {
|
|
for (Pattern *Pat : Patterns) {
|
|
Pat->forEachVariable([&](VarDecl *VD) {
|
|
if (VD->hasName()) {
|
|
// Add any variable declarations to the current scope.
|
|
addToScope(VD);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void Parser::addParametersToScope(ParameterList *PL) {
|
|
for (auto param : *PL)
|
|
if (param->hasName())
|
|
addToScope(param);
|
|
}
|
|
|
|
|
|
|
|
/// Parse availability query specification.
|
|
///
|
|
/// availability-spec:
|
|
/// '*'
|
|
/// version-constraint-spec
|
|
ParserResult<AvailabilitySpec> Parser::parseAvailabilitySpec() {
|
|
if (Tok.isBinaryOperator() && Tok.getText() == "*") {
|
|
SourceLoc StarLoc = Tok.getLoc();
|
|
consumeToken();
|
|
|
|
return makeParserResult(new (Context) OtherPlatformAvailabilitySpec(StarLoc));
|
|
}
|
|
return parseVersionConstraintSpec();
|
|
}
|
|
|
|
/// Parse version constraint specification.
|
|
///
|
|
/// version-constraint-spec:
|
|
/// identifier version-comparison version-tuple
|
|
ParserResult<VersionConstraintAvailabilitySpec>
|
|
Parser::parseVersionConstraintSpec() {
|
|
Identifier PlatformIdentifier;
|
|
SourceLoc PlatformLoc;
|
|
if (Tok.is(tok::code_complete)) {
|
|
consumeToken();
|
|
if (CodeCompletion) {
|
|
CodeCompletion->completePoundAvailablePlatform();
|
|
}
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
if (parseIdentifier(PlatformIdentifier, PlatformLoc,
|
|
diag::avail_query_expected_platform_name)) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (Tok.isBinaryOperator() && Tok.getText() == ">=") {
|
|
diagnose(Tok, diag::avail_query_version_comparison_not_needed)
|
|
.fixItRemove(Tok.getLoc());
|
|
consumeToken();
|
|
}
|
|
|
|
clang::VersionTuple Version;
|
|
SourceRange VersionRange;
|
|
|
|
if (parseVersionTuple(Version, VersionRange,
|
|
diag::avail_query_expected_version_number)) {
|
|
return nullptr;
|
|
}
|
|
|
|
Optional<PlatformKind> Platform =
|
|
platformFromString(PlatformIdentifier.str());
|
|
|
|
if (!Platform.hasValue() || Platform.getValue() == PlatformKind::none) {
|
|
diagnose(Tok, diag::avail_query_unrecognized_platform_name,
|
|
PlatformIdentifier);
|
|
return nullptr;
|
|
}
|
|
|
|
return makeParserResult(new (Context) VersionConstraintAvailabilitySpec(
|
|
Platform.getValue(), PlatformLoc, Version, VersionRange));
|
|
}
|