mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
With the tests updated to account for not having the correct behaviour
for brace-stmt items from after the code-completion point. That part
turns out to be harder to fix.
This reverts commit a5325e6281.
2682 lines
95 KiB
C++
2682 lines
95 KiB
C++
//===--- ParseStmt.cpp - Swift Language Parser for Statements -------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Statement Parsing and AST Building
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Parse/Parser.h"
|
|
#include "swift/AST/Attr.h"
|
|
#include "swift/AST/Decl.h"
|
|
#include "swift/Basic/Version.h"
|
|
#include "swift/Parse/Lexer.h"
|
|
#include "swift/Parse/CodeCompletionCallbacks.h"
|
|
#include "llvm/ADT/PointerUnion.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/SaveAndRestore.h"
|
|
|
|
using namespace swift;
|
|
|
|
/// isStartOfStmt - Return true if the current token starts a statement.
|
|
///
|
|
bool Parser::isStartOfStmt() {
|
|
switch (Tok.getKind()) {
|
|
default: return false;
|
|
case tok::kw_return:
|
|
case tok::kw_throw:
|
|
case tok::kw_defer:
|
|
case tok::kw_if:
|
|
case tok::kw_guard:
|
|
case tok::kw_while:
|
|
case tok::kw_do:
|
|
case tok::kw_repeat:
|
|
case tok::kw_for:
|
|
case tok::kw_break:
|
|
case tok::kw_continue:
|
|
case tok::kw_fallthrough:
|
|
case tok::kw_switch:
|
|
case tok::kw_case:
|
|
case tok::kw_default:
|
|
case tok::pound_if:
|
|
case tok::pound_setline:
|
|
return true;
|
|
|
|
case tok::pound_line:
|
|
// #line at the start of a line is a directive, when within, it is an expr.
|
|
return Tok.isAtStartOfLine();
|
|
|
|
case tok::kw_try: {
|
|
// "try" cannot actually start any statements, but we parse it there for
|
|
// better recovery.
|
|
Parser::BacktrackingScope backtrack(*this);
|
|
consumeToken(tok::kw_try);
|
|
return isStartOfStmt();
|
|
}
|
|
|
|
case tok::identifier: {
|
|
// "identifier ':' for/while/do/switch" is a label on a loop/switch.
|
|
if (!peekToken().is(tok::colon)) return false;
|
|
|
|
// To disambiguate other cases of "identifier :", which might be part of a
|
|
// question colon expression or something else, we look ahead to the second
|
|
// token.
|
|
Parser::BacktrackingScope backtrack(*this);
|
|
consumeToken(tok::identifier);
|
|
consumeToken(tok::colon);
|
|
// For better recovery, we just accept a label on any statement. We reject
|
|
// putting a label on something inappropriate in parseStmt().
|
|
return isStartOfStmt();
|
|
}
|
|
}
|
|
}
|
|
|
|
ParserStatus Parser::parseExprOrStmt(ASTNode &Result) {
|
|
if (Tok.is(tok::semi)) {
|
|
diagnose(Tok, diag::illegal_semi_stmt)
|
|
.fixItRemove(SourceRange(Tok.getLoc()));
|
|
consumeToken();
|
|
return makeParserError();
|
|
}
|
|
|
|
if (isStartOfStmt()) {
|
|
ParserResult<Stmt> Res = parseStmt();
|
|
if (Res.isNonNull())
|
|
Result = Res.get();
|
|
return Res;
|
|
}
|
|
|
|
// Note that we're parsing a statement.
|
|
StructureMarkerRAII ParsingStmt(*this, Tok.getLoc(),
|
|
StructureMarkerKind::Statement);
|
|
|
|
if (CodeCompletion)
|
|
CodeCompletion->setExprBeginning(getParserPosition());
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
if (CodeCompletion)
|
|
CodeCompletion->completeStmtOrExpr();
|
|
consumeToken(tok::code_complete);
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
ParserResult<Expr> ResultExpr = parseExpr(diag::expected_expr);
|
|
if (ResultExpr.isNonNull()) {
|
|
Result = ResultExpr.get();
|
|
} else if (!ResultExpr.hasCodeCompletion()) {
|
|
// If we've consumed any tokens at all, build an error expression
|
|
// covering the consumed range.
|
|
SourceLoc startLoc = StructureMarkers.back().Loc;
|
|
if (startLoc != Tok.getLoc()) {
|
|
Result = new (Context) ErrorExpr(SourceRange(startLoc, PreviousLoc));
|
|
}
|
|
}
|
|
|
|
if (ResultExpr.hasCodeCompletion() && CodeCompletion) {
|
|
CodeCompletion->completeExpr();
|
|
}
|
|
|
|
return ResultExpr;
|
|
}
|
|
|
|
static bool isTerminatorForBraceItemListKind(const Token &Tok,
|
|
BraceItemListKind Kind,
|
|
ArrayRef<ASTNode> ParsedDecls) {
|
|
switch (Kind) {
|
|
case BraceItemListKind::Brace:
|
|
return false;
|
|
case BraceItemListKind::Case:
|
|
return Tok.is(tok::kw_case) || Tok.is(tok::kw_default);
|
|
case BraceItemListKind::TopLevelCode:
|
|
// When parsing the top level executable code for a module, if we parsed
|
|
// some executable code, then we're done. We want to process (name bind,
|
|
// type check, etc) decls one at a time to make sure that there are not
|
|
// forward type references, etc. There is an outer loop around the parser
|
|
// that will reinvoke the parser at the top level on each statement until
|
|
// EOF. In contrast, it is ok to have forward references between classes,
|
|
// functions, etc.
|
|
for (auto I : ParsedDecls) {
|
|
if (isa<TopLevelCodeDecl>(I.get<Decl*>()))
|
|
// Only bail out if the next token is at the start of a line. If we
|
|
// don't, then we may accidentally allow things like "a = 1 b = 4".
|
|
// FIXME: This is really dubious. This will reject some things, but
|
|
// allow other things we don't want.
|
|
if (Tok.isAtStartOfLine())
|
|
return true;
|
|
}
|
|
return false;
|
|
case BraceItemListKind::TopLevelLibrary:
|
|
return false;
|
|
case BraceItemListKind::ActiveConditionalBlock:
|
|
case BraceItemListKind::InactiveConditionalBlock:
|
|
return Tok.isNot(tok::pound_else) && Tok.isNot(tok::pound_endif) &&
|
|
Tok.isNot(tok::pound_elseif);
|
|
}
|
|
}
|
|
|
|
void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition,
|
|
TopLevelCodeDecl *TLCD) {
|
|
backtrackToPosition(BeginParserPosition);
|
|
SourceLoc BeginLoc = Tok.getLoc();
|
|
// Consume tokens up to code completion token.
|
|
while (Tok.isNot(tok::code_complete)) {
|
|
consumeToken();
|
|
}
|
|
// Consume the code completion token, if there is one.
|
|
consumeIf(tok::code_complete);
|
|
// Also perform the same recovery as the main parser to capture tokens from
|
|
// this decl that are past the code completion token.
|
|
skipUntilDeclStmtRBrace(tok::l_brace);
|
|
SourceLoc EndLoc = Tok.getLoc();
|
|
State->delayTopLevel(TLCD, { BeginLoc, EndLoc },
|
|
BeginParserPosition.PreviousLoc);
|
|
|
|
// Skip the rest of the file to prevent the parser from constructing the AST
|
|
// for it. Forward references are not allowed at the top level.
|
|
skipUntil(tok::eof);
|
|
}
|
|
|
|
static void diagnoseDiscardedClosure(Parser &P, ASTNode &Result) {
|
|
// If we parsed a bare closure as an expression, it will be a discarded value
|
|
// expression and the type checker will complain.
|
|
|
|
if (isa<AbstractClosureExpr>(P.CurDeclContext))
|
|
// Inside a closure expression, an expression which syntactically looks
|
|
// like a discarded value expression, can become the return value of the
|
|
// closure. Don't attempt recovery.
|
|
return;
|
|
|
|
if (auto *E = Result.dyn_cast<Expr *>()) {
|
|
if (auto *CE = dyn_cast<ClosureExpr>(E)) {
|
|
if (!CE->hasAnonymousClosureVars())
|
|
// Parameters are explicitly specified, and could be used in the body,
|
|
// don't attempt recovery.
|
|
return;
|
|
P.diagnose(CE->getBody()->getLBraceLoc(), diag::brace_stmt_invalid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// brace-item:
|
|
/// decl
|
|
/// expr
|
|
/// stmt
|
|
/// stmt:
|
|
/// ';'
|
|
/// stmt-assign
|
|
/// stmt-if
|
|
/// stmt-guard
|
|
/// stmt-for-c-style
|
|
/// stmt-for-each
|
|
/// stmt-switch
|
|
/// stmt-control-transfer
|
|
/// stmt-control-transfer:
|
|
/// stmt-return
|
|
/// stmt-break
|
|
/// stmt-continue
|
|
/// stmt-fallthrough
|
|
/// stmt-assign:
|
|
/// expr '=' expr
|
|
ParserStatus Parser::parseBraceItems(SmallVectorImpl<ASTNode> &Entries,
|
|
BraceItemListKind Kind,
|
|
BraceItemListKind ConditionalBlockKind) {
|
|
|
|
bool IsTopLevel = (Kind == BraceItemListKind::TopLevelCode) ||
|
|
(Kind == BraceItemListKind::TopLevelLibrary);
|
|
bool isActiveConditionalBlock =
|
|
ConditionalBlockKind == BraceItemListKind::ActiveConditionalBlock;
|
|
bool isConditionalBlock = isActiveConditionalBlock ||
|
|
ConditionalBlockKind == BraceItemListKind::InactiveConditionalBlock;
|
|
|
|
// If we're not parsing an active #if block, form a new lexical scope.
|
|
Optional<Scope> initScope;
|
|
if (!isActiveConditionalBlock) {
|
|
auto scopeKind = IsTopLevel ? ScopeKind::TopLevel : ScopeKind::Brace;
|
|
initScope.emplace(this, scopeKind,
|
|
ConditionalBlockKind ==
|
|
BraceItemListKind::InactiveConditionalBlock);
|
|
}
|
|
|
|
ParserStatus BraceItemsStatus;
|
|
SmallVector<Decl*, 8> TmpDecls;
|
|
|
|
bool PreviousHadSemi = true;
|
|
while ((Kind == BraceItemListKind::TopLevelLibrary ||
|
|
Tok.isNot(tok::r_brace)) &&
|
|
Tok.isNot(tok::pound_endif) &&
|
|
Tok.isNot(tok::pound_elseif) &&
|
|
Tok.isNot(tok::pound_else) &&
|
|
Tok.isNot(tok::eof) &&
|
|
Tok.isNot(tok::kw_sil) &&
|
|
Tok.isNot(tok::kw_sil_scope) &&
|
|
Tok.isNot(tok::kw_sil_stage) &&
|
|
Tok.isNot(tok::kw_sil_vtable) &&
|
|
Tok.isNot(tok::kw_sil_global) &&
|
|
Tok.isNot(tok::kw_sil_witness_table) &&
|
|
Tok.isNot(tok::kw_sil_default_witness_table) &&
|
|
(isConditionalBlock ||
|
|
!isTerminatorForBraceItemListKind(Tok, Kind, Entries))) {
|
|
if (Kind == BraceItemListKind::TopLevelLibrary &&
|
|
skipExtraTopLevelRBraces())
|
|
continue;
|
|
|
|
// Eat invalid tokens instead of allowing them to produce downstream errors.
|
|
if (consumeIf(tok::unknown))
|
|
continue;
|
|
|
|
bool NeedParseErrorRecovery = false;
|
|
ASTNode Result;
|
|
|
|
// If the previous statement didn't have a semicolon and this new
|
|
// statement doesn't start a line, complain.
|
|
if (!PreviousHadSemi && !Tok.isAtStartOfLine()) {
|
|
SourceLoc EndOfPreviousLoc = getEndOfPreviousLoc();
|
|
diagnose(EndOfPreviousLoc, diag::statement_same_line_without_semi)
|
|
.fixItInsert(EndOfPreviousLoc, ";");
|
|
// FIXME: Add semicolon to the AST?
|
|
}
|
|
|
|
ParserPosition BeginParserPosition;
|
|
if (isCodeCompletionFirstPass())
|
|
BeginParserPosition = getParserPosition();
|
|
|
|
// Parse the decl, stmt, or expression.
|
|
PreviousHadSemi = false;
|
|
if (isStartOfDecl()
|
|
&& Tok.isNot(tok::pound_if)
|
|
&& Tok.isNot(tok::pound_setline)) {
|
|
ParserStatus Status =
|
|
parseDecl(TmpDecls, IsTopLevel ? PD_AllowTopLevel : PD_Default);
|
|
if (Status.isError()) {
|
|
NeedParseErrorRecovery = true;
|
|
if (Status.hasCodeCompletion() && IsTopLevel &&
|
|
isCodeCompletionFirstPass()) {
|
|
consumeDecl(BeginParserPosition, None, IsTopLevel);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
for (Decl *D : TmpDecls)
|
|
Entries.push_back(D);
|
|
if (!TmpDecls.empty())
|
|
PreviousHadSemi = TmpDecls.back()->TrailingSemiLoc.isValid();
|
|
TmpDecls.clear();
|
|
} else if (Tok.is(tok::pound_if)) {
|
|
SourceLoc StartLoc = Tok.getLoc();
|
|
|
|
// We'll want to parse the #if block, but not wrap it in a top-level
|
|
// code declaration immediately.
|
|
auto IfConfigResult = parseStmtIfConfig(Kind);
|
|
|
|
if (IfConfigResult.isParseError()) {
|
|
NeedParseErrorRecovery = true;
|
|
continue;
|
|
}
|
|
|
|
Result = IfConfigResult.get();
|
|
|
|
if (!Result) {
|
|
NeedParseErrorRecovery = true;
|
|
continue;
|
|
}
|
|
|
|
// Add the #if block itself as a TLCD if necessary
|
|
if (Kind == BraceItemListKind::TopLevelCode) {
|
|
auto *TLCD = new (Context) TopLevelCodeDecl(CurDeclContext);
|
|
auto Brace = BraceStmt::create(Context, StartLoc,
|
|
{Result}, PreviousLoc);
|
|
TLCD->setBody(Brace);
|
|
Entries.push_back(TLCD);
|
|
} else {
|
|
Entries.push_back(Result);
|
|
}
|
|
|
|
IfConfigStmt *ICS = cast<IfConfigStmt>(Result.get<Stmt*>());
|
|
|
|
for (auto &Entry : ICS->getActiveClauseElements()) {
|
|
Entries.push_back(Entry);
|
|
}
|
|
|
|
} else if (Tok.is(tok::pound_line)) {
|
|
ParserStatus Status = parseLineDirective(true);
|
|
BraceItemsStatus |= Status;
|
|
NeedParseErrorRecovery = Status.isError();
|
|
} else if (Tok.is(tok::pound_setline)) {
|
|
ParserStatus Status = parseLineDirective(false);
|
|
BraceItemsStatus |= Status;
|
|
NeedParseErrorRecovery = Status.isError();
|
|
} else if (IsTopLevel) {
|
|
// If this is a statement or expression at the top level of the module,
|
|
// Parse it as a child of a TopLevelCodeDecl.
|
|
auto *TLCD = new (Context) TopLevelCodeDecl(CurDeclContext);
|
|
ContextChange CC(*this, TLCD, &State->getTopLevelContext());
|
|
SourceLoc StartLoc = Tok.getLoc();
|
|
|
|
// Expressions can't begin with a closure literal at statement position.
|
|
// This prevents potential ambiguities with trailing closure syntax.
|
|
if (Tok.is(tok::l_brace)) {
|
|
diagnose(Tok, diag::statement_begins_with_closure);
|
|
diagnose(Tok, diag::discard_result_of_closure)
|
|
.fixItInsert(Tok.getLoc(), "_ = ");
|
|
}
|
|
|
|
ParserStatus Status = parseExprOrStmt(Result);
|
|
if (Status.hasCodeCompletion() && isCodeCompletionFirstPass()) {
|
|
consumeTopLevelDecl(BeginParserPosition, TLCD);
|
|
auto Brace = BraceStmt::create(Context, StartLoc, {}, Tok.getLoc());
|
|
TLCD->setBody(Brace);
|
|
Entries.push_back(TLCD);
|
|
return Status;
|
|
}
|
|
if (Status.isError())
|
|
NeedParseErrorRecovery = true;
|
|
else if (!allowTopLevelCode()) {
|
|
diagnose(StartLoc,
|
|
Result.is<Stmt*>() ? diag::illegal_top_level_stmt
|
|
: diag::illegal_top_level_expr);
|
|
}
|
|
diagnoseDiscardedClosure(*this, Result);
|
|
if (!Result.isNull()) {
|
|
// NOTE: this is a 'virtual' brace statement which does not have
|
|
// explicit '{' or '}', so the start and end locations should be
|
|
// the same as those of the result node
|
|
auto Brace = BraceStmt::create(Context, Result.getStartLoc(),
|
|
Result, Result.getEndLoc());
|
|
TLCD->setBody(Brace);
|
|
Entries.push_back(TLCD);
|
|
|
|
// If the parsed stmt was a GuardStmt, push the VarDecls into the
|
|
// Entries list, so that they can be found by unqual name lookup later.
|
|
if (!IsTopLevel) {
|
|
auto resultStmt = Result.dyn_cast<Stmt*>();
|
|
if (auto guard = dyn_cast_or_null<GuardStmt>(resultStmt)) {
|
|
for (const auto &elt : guard->getCond()) {
|
|
if (!elt.getPatternOrNull()) continue;
|
|
|
|
elt.getPattern()->forEachVariable([&](VarDecl *VD) {
|
|
Entries.push_back(VD);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (Tok.is(tok::kw_init) && isa<ConstructorDecl>(CurDeclContext)) {
|
|
SourceLoc StartLoc = Tok.getLoc();
|
|
auto CD = cast<ConstructorDecl>(CurDeclContext);
|
|
// Hint at missing 'self.' or 'super.' then skip this statement.
|
|
bool isConvenient = CD->isConvenienceInit();
|
|
diagnose(StartLoc, diag::invalid_nested_init, isConvenient)
|
|
.fixItInsert(StartLoc, isConvenient ? "self." : "super.");
|
|
NeedParseErrorRecovery = true;
|
|
} else {
|
|
ParserStatus ExprOrStmtStatus = parseExprOrStmt(Result);
|
|
BraceItemsStatus |= ExprOrStmtStatus;
|
|
if (ExprOrStmtStatus.isError())
|
|
NeedParseErrorRecovery = true;
|
|
diagnoseDiscardedClosure(*this, Result);
|
|
if (ExprOrStmtStatus.isSuccess() && IsTopLevel) {
|
|
// If this is a normal library, you can't have expressions or
|
|
// statements outside at the top level.
|
|
diagnose(Tok.getLoc(),
|
|
Result.is<Stmt*>() ? diag::illegal_top_level_stmt
|
|
: diag::illegal_top_level_expr);
|
|
Result = ASTNode();
|
|
}
|
|
|
|
if (!Result.isNull())
|
|
Entries.push_back(Result);
|
|
}
|
|
|
|
if (!NeedParseErrorRecovery && !PreviousHadSemi && Tok.is(tok::semi)) {
|
|
if (Result) {
|
|
if (Result.is<Expr*>()) {
|
|
Result.get<Expr*>()->TrailingSemiLoc = consumeToken(tok::semi);
|
|
} else {
|
|
Result.get<Stmt*>()->TrailingSemiLoc = consumeToken(tok::semi);
|
|
}
|
|
}
|
|
PreviousHadSemi = true;
|
|
}
|
|
|
|
if (NeedParseErrorRecovery) {
|
|
// If we had a parse error, skip to the start of the next stmt, decl or
|
|
// '{'.
|
|
//
|
|
// It would be ideal to stop at the start of the next expression (e.g.
|
|
// "X = 4"), but distinguishing the start of an expression from the middle
|
|
// of one is "hard".
|
|
skipUntilDeclStmtRBrace(tok::l_brace);
|
|
|
|
// If we have to recover, pretend that we had a semicolon; it's less
|
|
// noisy that way.
|
|
PreviousHadSemi = true;
|
|
}
|
|
}
|
|
|
|
return BraceItemsStatus;
|
|
}
|
|
|
|
void Parser::parseTopLevelCodeDeclDelayed() {
|
|
auto DelayedState = State->takeDelayedDeclState();
|
|
assert(DelayedState.get() && "should have delayed state");
|
|
|
|
auto BeginParserPosition = getParserPosition(DelayedState->BodyPos);
|
|
auto EndLexerState = L->getStateForEndOfTokenLoc(DelayedState->BodyEnd);
|
|
|
|
// ParserPositionRAII needs a primed parser to restore to.
|
|
if (Tok.is(tok::NUM_TOKENS))
|
|
consumeToken();
|
|
|
|
// Ensure that we restore the parser state at exit.
|
|
ParserPositionRAII PPR(*this);
|
|
|
|
// Create a lexer that cannot go past the end state.
|
|
Lexer LocalLex(*L, BeginParserPosition.LS, EndLexerState);
|
|
|
|
// Temporarily swap out the parser's current lexer with our new one.
|
|
llvm::SaveAndRestore<Lexer *> T(L, &LocalLex);
|
|
|
|
// Rewind to the beginning of the top-level code.
|
|
restoreParserPosition(BeginParserPosition);
|
|
|
|
// Re-enter the lexical scope.
|
|
Scope S(this, DelayedState->takeScope());
|
|
|
|
// Re-enter the top-level decl context.
|
|
// FIXME: this can issue discriminators out-of-order?
|
|
auto *TLCD = cast<TopLevelCodeDecl>(DelayedState->ParentContext);
|
|
ContextChange CC(*this, TLCD, &State->getTopLevelContext());
|
|
|
|
SourceLoc StartLoc = Tok.getLoc();
|
|
ASTNode Result;
|
|
|
|
// Expressions can't begin with a closure literal at statement position. This
|
|
// prevents potential ambiguities with trailing closure syntax.
|
|
if (Tok.is(tok::l_brace)) {
|
|
diagnose(Tok, diag::statement_begins_with_closure);
|
|
diagnose(Tok, diag::discard_result_of_closure)
|
|
.fixItInsert(Tok.getLoc(), "_ = ");
|
|
}
|
|
|
|
parseExprOrStmt(Result);
|
|
if (!Result.isNull()) {
|
|
auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc());
|
|
TLCD->setBody(Brace);
|
|
}
|
|
}
|
|
|
|
/// Recover from a 'case' or 'default' outside of a 'switch' by consuming up to
|
|
/// the next ':'.
|
|
static ParserResult<Stmt> recoverFromInvalidCase(Parser &P) {
|
|
assert(P.Tok.is(tok::kw_case) || P.Tok.is(tok::kw_default)
|
|
&& "not case or default?!");
|
|
P.diagnose(P.Tok, diag::case_outside_of_switch, P.Tok.getText());
|
|
P.skipUntil(tok::colon);
|
|
// FIXME: Return an ErrorStmt?
|
|
return nullptr;
|
|
}
|
|
|
|
ParserResult<Stmt> Parser::parseStmt() {
|
|
|
|
// Note that we're parsing a statement.
|
|
StructureMarkerRAII ParsingStmt(*this, Tok.getLoc(),
|
|
StructureMarkerKind::Statement);
|
|
|
|
LabeledStmtInfo LabelInfo;
|
|
|
|
// If this is a label on a loop/switch statement, consume it and pass it into
|
|
// parsing logic below.
|
|
if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) {
|
|
LabelInfo.Loc = consumeIdentifier(&LabelInfo.Name);
|
|
consumeToken(tok::colon);
|
|
}
|
|
|
|
SourceLoc tryLoc;
|
|
(void)consumeIf(tok::kw_try, tryLoc);
|
|
|
|
switch (Tok.getKind()) {
|
|
default:
|
|
diagnose(Tok, tryLoc.isValid() ? diag::expected_expr : diag::expected_stmt);
|
|
return nullptr;
|
|
case tok::kw_return:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
return parseStmtReturn(tryLoc);
|
|
case tok::kw_throw:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
return parseStmtThrow(tryLoc);
|
|
case tok::kw_defer:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtDefer();
|
|
case tok::kw_if:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtIf(LabelInfo);
|
|
case tok::kw_guard:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtGuard();
|
|
case tok::pound_if:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtIfConfig();
|
|
case tok::pound_line:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseLineDirective(true);
|
|
case tok::pound_setline:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseLineDirective(false);
|
|
case tok::kw_while:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtWhile(LabelInfo);
|
|
case tok::kw_repeat:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtRepeat(LabelInfo);
|
|
case tok::kw_do:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtDo(LabelInfo);
|
|
case tok::kw_for:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtFor(LabelInfo);
|
|
case tok::kw_switch:
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtSwitch(LabelInfo);
|
|
/// 'case' and 'default' are only valid at the top level of a switch.
|
|
case tok::kw_case:
|
|
case tok::kw_default:
|
|
return recoverFromInvalidCase(*this);
|
|
case tok::kw_break:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtBreak();
|
|
case tok::kw_continue:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return parseStmtContinue();
|
|
case tok::kw_fallthrough:
|
|
if (LabelInfo) diagnose(LabelInfo.Loc, diag::invalid_label_on_stmt);
|
|
if (tryLoc.isValid()) diagnose(tryLoc, diag::try_on_stmt, Tok.getText());
|
|
return makeParserResult(
|
|
new (Context) FallthroughStmt(consumeToken(tok::kw_fallthrough)));
|
|
}
|
|
}
|
|
|
|
/// parseBraceItemList - A brace enclosed expression/statement/decl list. For
|
|
/// example { 1; 4+5; } or { 1; 2 }. Always occurs as part of some other stmt
|
|
/// or decl.
|
|
///
|
|
/// brace-item-list:
|
|
/// '{' brace-item* '}'
|
|
///
|
|
ParserResult<BraceStmt> Parser::parseBraceItemList(Diag<> ID) {
|
|
if (Tok.isNot(tok::l_brace)) {
|
|
diagnose(Tok, ID);
|
|
return nullptr;
|
|
}
|
|
SourceLoc LBLoc = consumeToken(tok::l_brace);
|
|
|
|
SmallVector<ASTNode, 16> Entries;
|
|
SourceLoc RBLoc;
|
|
|
|
ParserStatus Status = parseBraceItems(Entries);
|
|
parseMatchingToken(tok::r_brace, RBLoc,
|
|
diag::expected_rbrace_in_brace_stmt, LBLoc);
|
|
|
|
return makeParserResult(Status,
|
|
BraceStmt::create(Context, LBLoc, Entries, RBLoc));
|
|
}
|
|
|
|
/// \brief Parses the elements in active or inactive if config clauses.
|
|
void Parser::parseIfConfigClauseElements(bool isActive,
|
|
BraceItemListKind Kind,
|
|
SmallVectorImpl<ASTNode> &Elements) {
|
|
parseBraceItems(Elements,
|
|
Kind,
|
|
isActive
|
|
? BraceItemListKind::ActiveConditionalBlock
|
|
: BraceItemListKind::InactiveConditionalBlock);
|
|
}
|
|
|
|
/// parseStmtBreak
|
|
///
|
|
/// stmt-break:
|
|
/// 'break' identifier?
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtBreak() {
|
|
SourceLoc Loc = consumeToken(tok::kw_break);
|
|
SourceLoc TargetLoc;
|
|
Identifier Target;
|
|
|
|
// If we have an identifier after this, which is not the start of another
|
|
// stmt or decl, we assume it is the label to break to, unless there is a
|
|
// line break. There is ambiguity with expressions (e.g. "break x+y") but
|
|
// since the expression after the break is dead, we don't feel bad eagerly
|
|
// parsing this.
|
|
if (Tok.is(tok::identifier) && !Tok.isAtStartOfLine() &&
|
|
!isStartOfStmt() && !isStartOfDecl())
|
|
TargetLoc = consumeIdentifier(&Target);
|
|
|
|
return makeParserResult(new (Context) BreakStmt(Loc, Target, TargetLoc));
|
|
}
|
|
|
|
/// parseStmtContinue
|
|
///
|
|
/// stmt-continue:
|
|
/// 'continue' identifier?
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtContinue() {
|
|
SourceLoc Loc = consumeToken(tok::kw_continue);
|
|
SourceLoc TargetLoc;
|
|
Identifier Target;
|
|
|
|
// If we have an identifier after this, which is not the start of another
|
|
// stmt or decl, we assume it is the label to continue to, unless there is a
|
|
// line break. There is ambiguity with expressions (e.g. "continue x+y") but
|
|
// since the expression after the continue is dead, we don't feel bad eagerly
|
|
// parsing this.
|
|
if (Tok.is(tok::identifier) && !Tok.isAtStartOfLine() &&
|
|
!isStartOfStmt() && !isStartOfDecl())
|
|
TargetLoc = consumeIdentifier(&Target);
|
|
|
|
return makeParserResult(new (Context) ContinueStmt(Loc, Target, TargetLoc));
|
|
}
|
|
|
|
|
|
/// parseStmtReturn
|
|
///
|
|
/// stmt-return:
|
|
/// 'return' expr?
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtReturn(SourceLoc tryLoc) {
|
|
SourceLoc ReturnLoc = consumeToken(tok::kw_return);
|
|
|
|
if (Tok.is(tok::code_complete)) {
|
|
auto CCE = new (Context) CodeCompletionExpr(SourceRange(Tok.getLoc()));
|
|
auto Result = makeParserResult(new (Context) ReturnStmt(ReturnLoc, CCE));
|
|
if (CodeCompletion) {
|
|
CodeCompletion->completeReturnStmt(CCE);
|
|
}
|
|
Result.setHasCodeCompletion();
|
|
consumeToken();
|
|
return Result;
|
|
}
|
|
|
|
// Handle the ambiguity between consuming the expression and allowing the
|
|
// enclosing stmt-brace to get it by eagerly eating it unless the return is
|
|
// followed by a '}', ';', statement or decl start keyword sequence.
|
|
if (Tok.isNot(tok::r_brace, tok::semi, tok::eof, tok::pound_if,
|
|
tok::pound_endif, tok::pound_else, tok::pound_elseif) &&
|
|
!isStartOfStmt() && !isStartOfDecl()) {
|
|
SourceLoc ExprLoc = Tok.getLoc();
|
|
|
|
// Issue a warning when the returned expression is on a different line than
|
|
// the return keyword, but both have the same indentation.
|
|
if (SourceMgr.getLineAndColumn(ReturnLoc).second ==
|
|
SourceMgr.getLineAndColumn(ExprLoc).second) {
|
|
diagnose(ExprLoc, diag::unindented_code_after_return);
|
|
diagnose(ExprLoc, diag::indent_expression_to_silence);
|
|
}
|
|
|
|
ParserResult<Expr> Result = parseExpr(diag::expected_expr_return);
|
|
if (Result.isNull()) {
|
|
// Create an ErrorExpr to tell the type checker that this return
|
|
// statement had an expression argument in the source. This suppresses
|
|
// the error about missing return value in a non-void function.
|
|
Result = makeParserErrorResult(new (Context) ErrorExpr(ExprLoc));
|
|
}
|
|
|
|
if (tryLoc.isValid()) {
|
|
diagnose(tryLoc, diag::try_on_return_throw, /*isThrow=*/false)
|
|
.fixItInsert(ExprLoc, "try ")
|
|
.fixItRemoveChars(tryLoc, ReturnLoc);
|
|
|
|
// Note: We can't use tryLoc here because that's outside the ReturnStmt's
|
|
// source range.
|
|
if (Result.isNonNull() && !isa<ErrorExpr>(Result.get()))
|
|
Result = makeParserResult(new (Context) TryExpr(ExprLoc, Result.get()));
|
|
}
|
|
|
|
return makeParserResult(
|
|
Result, new (Context) ReturnStmt(ReturnLoc, Result.getPtrOrNull()));
|
|
}
|
|
|
|
if (tryLoc.isValid())
|
|
diagnose(tryLoc, diag::try_on_stmt, "return");
|
|
|
|
return makeParserResult(new (Context) ReturnStmt(ReturnLoc, nullptr));
|
|
}
|
|
|
|
/// parseStmtThrow
|
|
///
|
|
/// stmt-throw
|
|
/// 'throw' expr
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtThrow(SourceLoc tryLoc) {
|
|
SourceLoc throwLoc = consumeToken(tok::kw_throw);
|
|
SourceLoc exprLoc;
|
|
if (Tok.isNot(tok::eof))
|
|
exprLoc = Tok.getLoc();
|
|
|
|
ParserResult<Expr> Result = parseExpr(diag::expected_expr_throw);
|
|
|
|
if (Result.hasCodeCompletion())
|
|
return makeParserCodeCompletionResult<Stmt>();
|
|
|
|
if (Result.isNull())
|
|
Result = makeParserErrorResult(new (Context) ErrorExpr(throwLoc));
|
|
|
|
if (tryLoc.isValid() && exprLoc.isValid()) {
|
|
diagnose(tryLoc, diag::try_on_return_throw, /*isThrow=*/true)
|
|
.fixItInsert(exprLoc, "try ")
|
|
.fixItRemoveChars(tryLoc, throwLoc);
|
|
|
|
// Note: We can't use tryLoc here because that's outside the ThrowStmt's
|
|
// source range.
|
|
if (Result.isNonNull() && !isa<ErrorExpr>(Result.get()))
|
|
Result = makeParserResult(new (Context) TryExpr(exprLoc, Result.get()));
|
|
}
|
|
|
|
return makeParserResult(Result,
|
|
new (Context) ThrowStmt(throwLoc, Result.get()));
|
|
}
|
|
|
|
/// parseStmtDefer
|
|
///
|
|
/// stmt-defer:
|
|
/// 'defer' brace-stmt
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtDefer() {
|
|
SourceLoc DeferLoc = consumeToken(tok::kw_defer);
|
|
|
|
// Macro expand out the defer into a closure and call, which we can typecheck
|
|
// and emit where needed.
|
|
//
|
|
// The AST representation for a defer statement is a bit weird. We retain the
|
|
// brace statement that the user wrote, but actually model this as if they
|
|
// wrote:
|
|
//
|
|
// func tmpClosure() { body }
|
|
// tmpClosure() // This is emitted on each path that needs to run this.
|
|
//
|
|
// As such, the body of the 'defer' is actually type checked within the
|
|
// closure's DeclContext.
|
|
auto params = ParameterList::createEmpty(Context);
|
|
DeclName name(Context, Context.getIdentifier("$defer"), params);
|
|
auto tempDecl
|
|
= FuncDecl::create(Context,
|
|
/*static*/ SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
/*func*/ SourceLoc(),
|
|
name,
|
|
/*name*/ SourceLoc(),
|
|
/*throws*/ SourceLoc(), /*Accessor keyword*/SourceLoc(),
|
|
/*generic params*/ nullptr,
|
|
Type(),
|
|
params,
|
|
/*return type*/ TypeLoc(),
|
|
CurDeclContext);
|
|
tempDecl->setImplicit();
|
|
setLocalDiscriminator(tempDecl);
|
|
ParserStatus Status;
|
|
{
|
|
// Change the DeclContext for any variables declared in the defer to be within
|
|
// the defer closure.
|
|
ParseFunctionBody cc(*this, tempDecl);
|
|
|
|
ParserResult<BraceStmt> Body =
|
|
parseBraceItemList(diag::expected_lbrace_after_defer);
|
|
if (Body.isNull())
|
|
return nullptr;
|
|
Status |= Body;
|
|
tempDecl->setBody(Body.get());
|
|
}
|
|
|
|
SourceLoc loc = tempDecl->getBody()->getStartLoc();
|
|
|
|
// Form the call, which will be emitted on any path that needs to run the
|
|
// code.
|
|
auto DRE = new (Context) DeclRefExpr(tempDecl, DeclNameLoc(loc),
|
|
/*Implicit*/true,
|
|
AccessSemantics::DirectToStorage);
|
|
auto args = TupleExpr::createEmpty(Context, loc, loc, true);
|
|
auto call = new (Context) CallExpr(DRE, args, /*implicit*/true);
|
|
|
|
auto DS = new (Context) DeferStmt(DeferLoc, tempDecl, call);
|
|
return makeParserResult(Status, DS);
|
|
}
|
|
|
|
namespace {
|
|
struct GuardedPattern {
|
|
Pattern *ThePattern = nullptr;
|
|
SourceLoc WhereLoc;
|
|
Expr *Guard = nullptr;
|
|
};
|
|
|
|
/// Contexts in which a guarded pattern can appears.
|
|
enum class GuardedPatternContext {
|
|
Case,
|
|
Catch,
|
|
};
|
|
} // unnamed namespace
|
|
|
|
/// Parse a pattern-matching clause for a case or catch statement,
|
|
/// including the guard expression:
|
|
///
|
|
/// pattern 'where' expr
|
|
static void parseGuardedPattern(Parser &P, GuardedPattern &result,
|
|
ParserStatus &status,
|
|
SmallVectorImpl<VarDecl *> &boundDecls,
|
|
GuardedPatternContext parsingContext,
|
|
bool isFirstPattern) {
|
|
ParserResult<Pattern> patternResult;
|
|
auto setErrorResult = [&] () {
|
|
patternResult = makeParserErrorResult(new (P.Context)
|
|
AnyPattern(SourceLoc()));
|
|
};
|
|
bool isExprBasic = [&]() -> bool {
|
|
switch (parsingContext) {
|
|
// 'case' is terminated with a colon and so allows a trailing closure.
|
|
case GuardedPatternContext::Case:
|
|
return false;
|
|
// 'catch' is terminated with a brace and so cannot.
|
|
case GuardedPatternContext::Catch:
|
|
return true;
|
|
}
|
|
llvm_unreachable("bad pattern context");
|
|
}();
|
|
|
|
// Do some special-case code completion for the start of the pattern.
|
|
if (P.Tok.is(tok::code_complete)) {
|
|
setErrorResult();
|
|
if (P.CodeCompletion) {
|
|
switch (parsingContext) {
|
|
case GuardedPatternContext::Case:
|
|
P.CodeCompletion->completeCaseStmtBeginning();
|
|
break;
|
|
case GuardedPatternContext::Catch:
|
|
P.CodeCompletion->completePostfixExprBeginning(nullptr);
|
|
break;
|
|
}
|
|
P.consumeToken();
|
|
} else {
|
|
result.ThePattern = patternResult.get();
|
|
status.setHasCodeCompletion();
|
|
return;
|
|
}
|
|
}
|
|
if (parsingContext == GuardedPatternContext::Case &&
|
|
P.Tok.isAny(tok::period_prefix, tok::period) &&
|
|
P.peekToken().is(tok::code_complete)) {
|
|
setErrorResult();
|
|
if (P.CodeCompletion) {
|
|
P.consumeToken();
|
|
P.CodeCompletion->completeCaseStmtDotPrefix();
|
|
P.consumeToken();
|
|
} else {
|
|
result.ThePattern = patternResult.get();
|
|
status.setHasCodeCompletion();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// If this is a 'catch' clause and we have "catch {" or "catch where...",
|
|
// then we get an implicit "let error" pattern.
|
|
if (parsingContext == GuardedPatternContext::Catch &&
|
|
P.Tok.isAny(tok::l_brace, tok::kw_where)) {
|
|
auto loc = P.Tok.getLoc();
|
|
auto errorName = P.Context.Id_error;
|
|
auto var = new (P.Context) VarDecl(/*static*/ false, /*IsLet*/true,
|
|
loc, errorName, Type(),
|
|
P.CurDeclContext);
|
|
var->setImplicit();
|
|
auto namePattern = new (P.Context) NamedPattern(var);
|
|
auto varPattern = new (P.Context) VarPattern(loc, /*isLet*/true,
|
|
namePattern, /*implicit*/true);
|
|
patternResult = makeParserResult(varPattern);
|
|
}
|
|
|
|
|
|
// Okay, if the special code-completion didn't kick in, parse a
|
|
// matching pattern.
|
|
if (patternResult.isNull()) {
|
|
llvm::SaveAndRestore<decltype(P.InVarOrLetPattern)>
|
|
T(P.InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
|
|
patternResult = P.parseMatchingPattern(isExprBasic);
|
|
}
|
|
|
|
// If that didn't work, use a bogus pattern so that we can fill out
|
|
// the AST.
|
|
if (patternResult.isNull())
|
|
patternResult =
|
|
makeParserErrorResult(new (P.Context) AnyPattern(P.PreviousLoc));
|
|
|
|
// Fill in the pattern.
|
|
status |= patternResult;
|
|
result.ThePattern = patternResult.get();
|
|
|
|
if (isFirstPattern) {
|
|
// Add variable bindings from the pattern to the case scope. We have
|
|
// to do this with a full AST walk, because the freshly parsed pattern
|
|
// represents tuples and var patterns as tupleexprs and
|
|
// unresolved_pattern_expr nodes, instead of as proper pattern nodes.
|
|
patternResult.get()->forEachVariable([&](VarDecl *VD) {
|
|
if (VD->hasName()) P.addToScope(VD);
|
|
boundDecls.push_back(VD);
|
|
});
|
|
} else {
|
|
// If boundDecls already contains variables, then we must match the
|
|
// same number and same names in this pattern as were declared in a
|
|
// previous pattern (and later we will make sure they have the same
|
|
// types).
|
|
SmallVector<VarDecl*, 4> repeatedDecls;
|
|
patternResult.get()->forEachVariable([&](VarDecl *VD) {
|
|
if (!VD->hasName())
|
|
return;
|
|
|
|
for (auto repeat : repeatedDecls)
|
|
if (repeat->getName() == VD->getName())
|
|
P.addToScope(VD); // will diagnose a duplicate declaration
|
|
|
|
bool found = false;
|
|
for (auto previous : boundDecls) {
|
|
if (previous->hasName() && previous->getName() == VD->getName()) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
// Diagnose a declaration that doesn't match a previous pattern.
|
|
P.diagnose(VD->getLoc(), diag::extra_var_in_multiple_pattern_list, VD->getName());
|
|
status.setIsParseError();
|
|
}
|
|
repeatedDecls.push_back(VD);
|
|
});
|
|
|
|
for (auto previous : boundDecls) {
|
|
bool found = false;
|
|
for (auto repeat : repeatedDecls) {
|
|
if (previous->hasName() && previous->getName() == repeat->getName()) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
// Diagnose a previous declaration that is missing in this pattern.
|
|
P.diagnose(previous->getLoc(), diag::extra_var_in_multiple_pattern_list, previous->getName());
|
|
status.setIsParseError();
|
|
}
|
|
}
|
|
|
|
for (auto VD : repeatedDecls) {
|
|
VD->setHasNonPatternBindingInit();
|
|
VD->setImplicit();
|
|
}
|
|
}
|
|
|
|
// Now that we have them, mark them as being initialized without a PBD.
|
|
for (auto VD : boundDecls)
|
|
VD->setHasNonPatternBindingInit();
|
|
|
|
// Parse the optional 'where' guard.
|
|
if (P.consumeIf(tok::kw_where, result.WhereLoc)) {
|
|
SourceLoc startOfGuard = P.Tok.getLoc();
|
|
|
|
auto diagKind = [=]() -> Diag<> {
|
|
switch (parsingContext) {
|
|
case GuardedPatternContext::Case:
|
|
return diag::expected_case_where_expr;
|
|
case GuardedPatternContext::Catch:
|
|
return diag::expected_catch_where_expr;
|
|
}
|
|
llvm_unreachable("bad context");
|
|
}();
|
|
ParserResult<Expr> guardResult = P.parseExprImpl(diagKind, isExprBasic);
|
|
status |= guardResult;
|
|
|
|
// Use the parsed guard expression if possible.
|
|
if (guardResult.isNonNull()) {
|
|
result.Guard = guardResult.get();
|
|
|
|
// Otherwise, fake up an ErrorExpr.
|
|
} else {
|
|
// If we didn't consume any tokens failing to parse the
|
|
// expression, don't put in the source range of the ErrorExpr.
|
|
SourceRange errorRange;
|
|
if (startOfGuard == P.Tok.getLoc()) {
|
|
errorRange = result.WhereLoc;
|
|
} else {
|
|
errorRange = SourceRange(startOfGuard, P.PreviousLoc);
|
|
}
|
|
result.Guard = new (P.Context) ErrorExpr(errorRange);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Validate availability spec list, emitting diagnostics if necessary.
|
|
static void validateAvailabilitySpecList(Parser &P,
|
|
ArrayRef<AvailabilitySpec *> Specs) {
|
|
llvm::SmallSet<PlatformKind, 4> Platforms;
|
|
bool HasOtherPlatformSpec = false;
|
|
for (auto *Spec : Specs) {
|
|
if (isa<OtherPlatformAvailabilitySpec>(Spec)) {
|
|
HasOtherPlatformSpec = true;
|
|
continue;
|
|
}
|
|
|
|
auto *VersionSpec = cast<VersionConstraintAvailabilitySpec>(Spec);
|
|
bool Inserted = Platforms.insert(VersionSpec->getPlatform()).second;
|
|
if (!Inserted) {
|
|
// Rule out multiple version specs referring to the same platform.
|
|
// For example, we emit an error for
|
|
/// #available(OSX 10.10, OSX 10.11, *)
|
|
PlatformKind Platform = VersionSpec->getPlatform();
|
|
P.diagnose(VersionSpec->getPlatformLoc(),
|
|
diag::availability_query_repeated_platform,
|
|
platformString(Platform));
|
|
}
|
|
}
|
|
|
|
if (!HasOtherPlatformSpec) {
|
|
SourceLoc InsertWildcardLoc = Specs.back()->getSourceRange().End;
|
|
P.diagnose(InsertWildcardLoc, diag::availability_query_wildcard_required)
|
|
.fixItInsertAfter(InsertWildcardLoc, ", *");
|
|
}
|
|
}
|
|
|
|
// #available(...)
|
|
ParserResult<PoundAvailableInfo> Parser::parseStmtConditionPoundAvailable() {
|
|
SourceLoc PoundLoc = consumeToken(tok::pound_available);
|
|
|
|
if (!Tok.isFollowingLParen()) {
|
|
diagnose(Tok, diag::avail_query_expected_condition);
|
|
return makeParserError();
|
|
}
|
|
|
|
StructureMarkerRAII ParsingAvailabilitySpecList(*this, Tok);
|
|
SourceLoc LParenLoc = consumeToken(tok::l_paren);
|
|
|
|
SmallVector<AvailabilitySpec *, 5> Specs;
|
|
ParserStatus Status = parseAvailabilitySpecList(Specs);
|
|
|
|
SourceLoc RParenLoc;
|
|
if (parseMatchingToken(tok::r_paren, RParenLoc,
|
|
diag::avail_query_expected_rparen, LParenLoc))
|
|
Status.setIsParseError();
|
|
|
|
auto *result = PoundAvailableInfo::create(Context, PoundLoc, Specs,RParenLoc);
|
|
return makeParserResult(Status, result);
|
|
}
|
|
|
|
ParserStatus
|
|
Parser::parseAvailabilitySpecList(SmallVectorImpl<AvailabilitySpec *> &Specs) {
|
|
ParserStatus Status = makeParserSuccess();
|
|
|
|
// We don't use parseList() because we want to provide more specific
|
|
// diagnostics disallowing operators in version specs.
|
|
while (1) {
|
|
auto SpecResult = parseAvailabilitySpec();
|
|
if (auto *Spec = SpecResult.getPtrOrNull()) {
|
|
Specs.push_back(Spec);
|
|
} else {
|
|
if (SpecResult.hasCodeCompletion()) {
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
// We don't allow binary operators to combine specs.
|
|
if (Tok.isBinaryOperator()) {
|
|
diagnose(Tok, diag::avail_query_disallowed_operator, Tok.getText());
|
|
consumeToken();
|
|
Status.setIsParseError();
|
|
} else if (consumeIf(tok::comma)) {
|
|
// keep going.
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status.isSuccess())
|
|
validateAvailabilitySpecList(*this, Specs);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/// Return true if the specified token looks like the start of a clause in a
|
|
/// stmt-condition.
|
|
static bool isStartOfStmtConditionClause(const Token &Tok) {
|
|
return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_case,tok::pound_available);
|
|
}
|
|
|
|
|
|
/// Parse the condition of an 'if' or 'while'.
|
|
///
|
|
/// condition:
|
|
/// expr-basic
|
|
/// expr-basic ',' bind-or-available (',' bind-or-available)*
|
|
/// bind-or-available (',' bind-or-available)*
|
|
/// bind-or-available:
|
|
/// ('var' | 'let') condition-bind (',' condition-bind)* condition-where
|
|
/// 'case' condition-bind
|
|
/// '#available' '(' availability-spec (',' availability-spec)* ')'
|
|
/// condition-bind:
|
|
/// pattern '=' expr-basic
|
|
/// condition-where:
|
|
/// 'where' expr-basic
|
|
///
|
|
/// The use of expr-basic here disallows trailing closures, which are
|
|
/// problematic given the curly braces around the if/while body.
|
|
///
|
|
ParserStatus Parser::parseStmtCondition(StmtCondition &Condition,
|
|
Diag<> ID, StmtKind ParentKind) {
|
|
ParserStatus Status;
|
|
Condition = StmtCondition();
|
|
|
|
SmallVector<StmtConditionElement, 4> result;
|
|
|
|
|
|
// This little helper function is used to consume a separator comma if
|
|
// present, it returns false if it isn't there. It also gracefully handles
|
|
// the case when the user used && instead of comma, since that is a common
|
|
// error.
|
|
auto consumeSeparatorComma = [&]() -> bool {
|
|
// If we have an "&&" token followed by a continuation of the statement
|
|
// condition, then fixit the "&&" to "," and keep going.
|
|
if (Tok.isAny(tok::oper_binary_spaced, tok::oper_binary_unspaced) &&
|
|
Tok.getText() == "&&") {
|
|
diagnose(Tok, diag::expected_comma_stmtcondition)
|
|
.fixItReplace(Tok.getLoc(), ",");
|
|
consumeToken();
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, if a comma exists consume it and succeed.
|
|
return consumeIf(tok::comma);
|
|
};
|
|
|
|
|
|
if (Tok.is(tok::pound) && peekToken().is(tok::code_complete)) {
|
|
auto PoundPos = consumeToken();
|
|
auto CodeCompletionPos = consumeToken();
|
|
auto Expr = new (Context) CodeCompletionExpr(CharSourceRange(SourceMgr,
|
|
PoundPos, CodeCompletionPos));
|
|
if (CodeCompletion) {
|
|
CodeCompletion->completeAfterPound(Expr, ParentKind);
|
|
}
|
|
result.push_back(Expr);
|
|
Status.setHasCodeCompletion();
|
|
}
|
|
|
|
// Parse a leading #available condition if present.
|
|
if (Tok.is(tok::pound_available)) {
|
|
auto res = parseStmtConditionPoundAvailable();
|
|
if (res.isNull() || res.hasCodeCompletion()) {
|
|
Status |= res;
|
|
return Status;
|
|
}
|
|
|
|
result.push_back({res.get()});
|
|
|
|
if (!consumeSeparatorComma()) {
|
|
Condition = Context.AllocateCopy(result);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Parse the leading boolean condition if present.
|
|
if (!isStartOfStmtConditionClause(Tok)) {
|
|
ParserResult<Expr> CondExpr = parseExprBasic(ID);
|
|
Status |= CondExpr;
|
|
result.push_back(CondExpr.getPtrOrNull());
|
|
|
|
// If there is a comma after the expression, parse a list of let/var
|
|
// bindings.
|
|
SourceLoc CommaLoc = Tok.getLoc();
|
|
|
|
// If there is no comma then we're done.
|
|
if (!consumeSeparatorComma()) {
|
|
Condition = Context.AllocateCopy(result);
|
|
return Status;
|
|
}
|
|
|
|
// If a let-binding doesn't follow, diagnose the problem with a tailored
|
|
// error message.
|
|
if (!isStartOfStmtConditionClause(Tok)) {
|
|
// If an { exists after the comma, assume it is a stray comma and this is
|
|
// the start of the if/while body. If a non-expression thing exists after
|
|
// the comma, then we don't know what is going on.
|
|
if (Tok.is(tok::l_brace) || isStartOfDecl() || isStartOfStmt()) {
|
|
diagnose(Tok, diag::expected_expr_conditional_letbinding);
|
|
Condition = Context.AllocateCopy(result);
|
|
if (Tok.isNot(tok::l_brace)) Status.setIsParseError();
|
|
return Status;
|
|
}
|
|
|
|
// If an expression follows the comma, then it is a second boolean
|
|
// condition. Produce a fix-it hint to rewrite the comma to &&.
|
|
diagnose(CommaLoc,
|
|
diag::expected_expr_conditional_letbinding_bool_conditions)
|
|
.fixItReplace(CommaLoc, " &&");
|
|
do {
|
|
ParserResult<Expr> CondExpr = parseExprBasic(ID);
|
|
Status |= CondExpr;
|
|
result.push_back(CondExpr.getPtrOrNull());
|
|
} while (consumeIf(tok::comma) && !isStartOfStmtConditionClause(Tok));
|
|
|
|
if (!isStartOfStmtConditionClause(Tok)) {
|
|
Condition = Context.AllocateCopy(result);
|
|
return Status;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We're parsing a conditional binding.
|
|
assert(CurDeclContext->isLocalContext() &&
|
|
"conditional binding in non-local context?!");
|
|
|
|
// For error recovery purposes, keep track of the disposition of the last
|
|
// pattern binding we saw ('let' vs 'var') in multiple PBD cases.
|
|
enum BK_BindingKind {
|
|
BK_Let, BK_Var, BK_Case, BK_LetCase, BK_VarCase
|
|
} BindingKind = BK_Let;
|
|
StringRef BindingKindStr = "let";
|
|
|
|
// Parse the list of condition-bindings, each of which can have a 'where'.
|
|
do {
|
|
// Parse a #available condition if present.
|
|
if (Tok.is(tok::pound_available)) {
|
|
auto res = parseStmtConditionPoundAvailable();
|
|
if (res.isNull() || res.hasCodeCompletion()) {
|
|
Status |= res;
|
|
return Status;
|
|
}
|
|
|
|
result.push_back({res.get()});
|
|
continue;
|
|
}
|
|
|
|
// Otherwise it must be a pattern binding.
|
|
SourceLoc VarLoc;
|
|
|
|
if (Tok.isAny(tok::kw_let, tok::kw_var, tok::kw_case)) {
|
|
BindingKind =
|
|
Tok.is(tok::kw_let) ? BK_Let : Tok.is(tok::kw_var) ? BK_Var : BK_Case;
|
|
BindingKindStr = Tok.getText();
|
|
VarLoc = consumeToken();
|
|
|
|
// If will probably be a common typo to write "if let case" instead of
|
|
// "if case let" so detect this and produce a nice fixit.
|
|
if ((BindingKind == BK_Let || BindingKind == BK_Var) &&
|
|
Tok.is(tok::kw_case)) {
|
|
diagnose(VarLoc, diag::wrong_condition_case_location, BindingKindStr)
|
|
.fixItRemove(VarLoc)
|
|
.fixItInsertAfter(Tok.getLoc(), " " + BindingKindStr.str());
|
|
|
|
BindingKindStr = "case";
|
|
BindingKind = BindingKind == BK_Let ? BK_LetCase : BK_VarCase;
|
|
VarLoc = consumeToken(tok::kw_case);
|
|
}
|
|
|
|
} else {
|
|
// We get here with erroneous code like:
|
|
// if let x = foo() where cond(), y? = bar()
|
|
// which is a common typo for:
|
|
// if let x = foo() where cond(),
|
|
// LET y? = bar()
|
|
// diagnose this specifically and produce a nice fixit.
|
|
diagnose(Tok, diag::where_end_of_binding_use_letvar, BindingKindStr)
|
|
.fixItInsert(Tok.getLoc(), BindingKindStr.str() + " ");
|
|
VarLoc = Tok.getLoc();
|
|
}
|
|
|
|
// The first pattern entry we parse will record the location of the
|
|
// let/var/case into the StmtCondition.
|
|
SourceLoc IntroducerLoc = VarLoc;
|
|
bool hadIncorrectlyWrittenWhereClause = false;
|
|
|
|
// Parse the list of name bindings within a let/var clauses.
|
|
while (1) {
|
|
ParserResult<Pattern> ThePattern;
|
|
|
|
if (BindingKind == BK_Case) {
|
|
// In our recursive parse, remember that we're in a matching pattern.
|
|
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
|
|
T(InVarOrLetPattern, IVOLP_InMatchingPattern);
|
|
ThePattern = parseMatchingPattern(/*isExprBasic*/ true);
|
|
} else if (BindingKind == BK_LetCase || BindingKind == BK_VarCase) {
|
|
// Recover from the 'if let case' typo gracefully.
|
|
|
|
// In our recursive parse, remember that we're in a var/let pattern.
|
|
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
|
|
T(InVarOrLetPattern,
|
|
BindingKind == BK_LetCase ? IVOLP_InLet : IVOLP_InVar);
|
|
ThePattern = parseMatchingPattern(/*isExprBasic*/ true);
|
|
|
|
if (ThePattern.isNonNull()) {
|
|
auto *P = new (Context) VarPattern(VarLoc, BindingKind == BK_LetCase,
|
|
ThePattern.get(), /*impl*/false);
|
|
ThePattern = makeParserResult(P);
|
|
}
|
|
} else {
|
|
// Otherwise, this is an implicit optional binding "if let".
|
|
ThePattern =
|
|
parseMatchingPatternAsLetOrVar(BindingKind == BK_Let, VarLoc,
|
|
/*isExprBasic*/ true);
|
|
// The let/var pattern is part of the statement.
|
|
if (Pattern *P = ThePattern.getPtrOrNull())
|
|
P->setImplicit();
|
|
}
|
|
|
|
ThePattern = parseOptionalPatternTypeAnnotation(ThePattern,
|
|
BindingKind != BK_Case);
|
|
Status |= ThePattern;
|
|
|
|
if (ThePattern.isNull() || ThePattern.hasCodeCompletion())
|
|
return Status;
|
|
|
|
Expr *Init;
|
|
// Conditional bindings must have an initializer.
|
|
if (consumeIf(tok::equal)) {
|
|
ParserResult<Expr> InitExpr
|
|
= parseExprBasic(diag::expected_expr_conditional_var);
|
|
Status |= InitExpr;
|
|
if (InitExpr.isNull() || InitExpr.hasCodeCompletion())
|
|
return Status;
|
|
Init = InitExpr.get();
|
|
|
|
} else {
|
|
// Although we require an initializer, recover by parsing as if it were
|
|
// merely omitted.
|
|
diagnose(Tok, diag::conditional_var_initializer_required);
|
|
Init = new (Context) ErrorExpr(Tok.getLoc());
|
|
}
|
|
|
|
result.push_back({IntroducerLoc, ThePattern.get(), Init});
|
|
IntroducerLoc = SourceLoc();
|
|
|
|
// Add variable bindings from the pattern to our current scope and mark
|
|
// them as being having a non-pattern-binding initializer.
|
|
ThePattern.get()->forEachVariable([&](VarDecl *VD) {
|
|
if (VD->hasName())
|
|
addToScope(VD);
|
|
VD->setHasNonPatternBindingInit();
|
|
});
|
|
|
|
// We're done if there is a 'where' clause, 'else' or any other noncomma.
|
|
if (Tok.isNot(tok::comma)) break;
|
|
|
|
// If we have a comma, we could be continuing to another pattern as in:
|
|
// let x = foo(), y = bar()
|
|
// Alternatively, this could be start of another clause, as in:
|
|
// let x = foo(), let y = bar()
|
|
if (isStartOfStmtConditionClause(peekToken()))
|
|
break;
|
|
|
|
// At this point, we know that the next thing should be a pattern to
|
|
// follow in the series. However, it is fairly common for people to
|
|
// forget a 'where' clause and write something like:
|
|
//
|
|
// let x = foo(), x != 42
|
|
//
|
|
// instead of:
|
|
//
|
|
// let x = foo() where x != 42
|
|
//
|
|
// It is hard to tell whether the next clause is a pattern or an invalid
|
|
// expression, because 'case' patterns can have expressions embedded in
|
|
// them. As such, if we're continuing a non-case pattern, do a bit more
|
|
// lookahead to disambiguate this.
|
|
if (BindingKind == BK_Let || BindingKind == BK_Var) {
|
|
// Determine whether this was an invalid pattern or if the pattern has
|
|
// no trailing "=".
|
|
{
|
|
Parser::BacktrackingScope Backtrack(*this);
|
|
consumeToken(tok::comma);
|
|
hadIncorrectlyWrittenWhereClause =
|
|
!canParseTypedPattern() || Tok.isNot(tok::equal);
|
|
}
|
|
|
|
if (hadIncorrectlyWrittenWhereClause) {
|
|
diagnose(Tok, diag::comma_should_be_where)
|
|
.fixItReplace(Tok.getLoc(), " where");
|
|
consumeToken(tok::comma);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Otherwise, it really does look like this comma continues the pattern
|
|
// clause, so eat it and parse the next clause.
|
|
consumeToken(tok::comma);
|
|
}
|
|
|
|
// If there is a where clause on this let/var specification, parse and
|
|
// remember it.
|
|
if (hadIncorrectlyWrittenWhereClause || consumeIf(tok::kw_where)) {
|
|
ParserResult<Expr> WhereExpr
|
|
= parseExprBasic(diag::expected_expr_conditional_where);
|
|
Status |= WhereExpr;
|
|
if (WhereExpr.isNull() || WhereExpr.hasCodeCompletion())
|
|
return Status;
|
|
result.push_back(WhereExpr.get());
|
|
}
|
|
|
|
} while (consumeSeparatorComma());
|
|
|
|
Condition = Context.AllocateCopy(result);
|
|
return Status;
|
|
}
|
|
|
|
///
|
|
/// stmt-if:
|
|
/// 'if' condition stmt-brace stmt-if-else?
|
|
/// stmt-if-else:
|
|
/// 'else' stmt-brace
|
|
/// 'else' stmt-if
|
|
ParserResult<Stmt> Parser::parseStmtIf(LabeledStmtInfo LabelInfo) {
|
|
SourceLoc IfLoc = consumeToken(tok::kw_if);
|
|
|
|
ParserStatus Status;
|
|
StmtCondition Condition;
|
|
ParserResult<BraceStmt> NormalBody;
|
|
|
|
// A scope encloses the condition and true branch for any variables bound
|
|
// by a conditional binding. The else branch does *not* see these variables.
|
|
{
|
|
Scope S(this, ScopeKind::IfVars);
|
|
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc LBraceLoc = Tok.getLoc();
|
|
diagnose(IfLoc, diag::missing_condition_after_if)
|
|
.highlight(SourceRange(IfLoc, LBraceLoc));
|
|
SmallVector<StmtConditionElement, 1> ConditionElems;
|
|
ConditionElems.emplace_back(new (Context) ErrorExpr(LBraceLoc));
|
|
Condition = Context.AllocateCopy(ConditionElems);
|
|
} else {
|
|
Status |= parseStmtCondition(Condition, diag::expected_condition_if,
|
|
StmtKind::If);
|
|
if (Status.isError() || Status.hasCodeCompletion()) {
|
|
// FIXME: better recovery
|
|
return makeParserResult<Stmt>(Status, nullptr);
|
|
}
|
|
}
|
|
|
|
NormalBody = parseBraceItemList(diag::expected_lbrace_after_if);
|
|
if (NormalBody.isNull())
|
|
return nullptr; // FIXME: better recovery
|
|
|
|
Status |= NormalBody;
|
|
}
|
|
|
|
// The else branch, if any, is outside of the scope of the condition.
|
|
SourceLoc ElseLoc;
|
|
ParserResult<Stmt> ElseBody;
|
|
if (Tok.is(tok::kw_else)) {
|
|
ElseLoc = consumeToken(tok::kw_else);
|
|
if (Tok.is(tok::kw_if))
|
|
ElseBody = parseStmtIf(LabeledStmtInfo());
|
|
else
|
|
ElseBody = parseBraceItemList(diag::expected_lbrace_after_else);
|
|
Status |= ElseBody;
|
|
}
|
|
|
|
return makeParserResult(
|
|
Status, new (Context) IfStmt(LabelInfo,
|
|
IfLoc, Condition, NormalBody.get(),
|
|
ElseLoc, ElseBody.getPtrOrNull()));
|
|
}
|
|
|
|
/// stmt-guard:
|
|
/// 'guard' condition 'else' stmt-brace
|
|
///
|
|
ParserResult<Stmt> Parser::parseStmtGuard() {
|
|
SourceLoc GuardLoc = consumeToken(tok::kw_guard);
|
|
|
|
ParserStatus Status;
|
|
StmtCondition Condition;
|
|
ParserResult<BraceStmt> Body;
|
|
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc LBraceLoc = Tok.getLoc();
|
|
diagnose(GuardLoc, diag::missing_condition_after_guard)
|
|
.highlight(SourceRange(GuardLoc, LBraceLoc));
|
|
SmallVector<StmtConditionElement, 1> ConditionElems;
|
|
ConditionElems.emplace_back(new (Context) ErrorExpr(LBraceLoc));
|
|
Condition = Context.AllocateCopy(ConditionElems);
|
|
} else {
|
|
Status |= parseStmtCondition(Condition, diag::expected_condition_guard,
|
|
StmtKind::Guard);
|
|
if (Status.isError() || Status.hasCodeCompletion()) {
|
|
// FIXME: better recovery
|
|
return makeParserResult<Stmt>(Status, nullptr);
|
|
}
|
|
}
|
|
|
|
// Parse the 'else'. If it is missing, and if the following token isn't a {
|
|
// then the parser is hopelessly lost - just give up instead of spewing.
|
|
if (parseToken(tok::kw_else, diag::expected_else_after_guard) &&
|
|
Tok.isNot(tok::l_brace))
|
|
return makeParserError();
|
|
|
|
// Before parsing the body, disable all of the bound variables so that they
|
|
// cannot be used unbound.
|
|
SmallVector<VarDecl *, 4> Vars;
|
|
for (auto &elt : Condition)
|
|
if (auto pattern = elt.getPatternOrNull())
|
|
pattern->collectVariables(Vars);
|
|
|
|
llvm::SaveAndRestore<decltype(DisabledVars)>
|
|
RestoreCurVars(DisabledVars, Vars);
|
|
|
|
llvm::SaveAndRestore<decltype(DisabledVarReason)>
|
|
RestoreReason(DisabledVarReason, diag::bound_var_guard_body);
|
|
|
|
Body = parseBraceItemList(diag::expected_lbrace_after_guard);
|
|
if (Body.isNull())
|
|
return nullptr; // FIXME: better recovery
|
|
|
|
Status |= Body;
|
|
|
|
return makeParserResult(Status,
|
|
new (Context) GuardStmt(GuardLoc, Condition, Body.get()));
|
|
}
|
|
|
|
|
|
// Evaluate a subset of expression types suitable for build configuration
|
|
// conditional expressions. The accepted expression types are:
|
|
// - The magic constants "true" and "false".
|
|
// - Named decl ref expressions ("FOO")
|
|
// - Parenthesized expressions ("(FOO)")
|
|
// - Binary "&&" or "||" operations applied to other build configuration
|
|
// conditional expressions
|
|
// - Unary "!" expressions applied to other build configuration conditional
|
|
// expressions
|
|
// - Single-argument call expressions, where the function being invoked is a
|
|
// supported target configuration (currently "os", "arch", and
|
|
// "_compiler_version"), and whose argument is a named decl ref expression
|
|
ConditionalCompilationExprState
|
|
Parser::evaluateConditionalCompilationExpr(Expr *condition) {
|
|
// Evaluate a ParenExpr.
|
|
if (auto *PE = dyn_cast<ParenExpr>(condition))
|
|
return evaluateConditionalCompilationExpr(PE->getSubExpr());
|
|
|
|
// Evaluate a "&&" or "||" expression.
|
|
if (auto *SE = dyn_cast<SequenceExpr>(condition)) {
|
|
// Check for '&&' or '||' as the expression type.
|
|
if (SE->getNumElements() < 3) {
|
|
diagnose(SE->getLoc(),
|
|
diag::unsupported_conditional_compilation_binary_expression);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
// Before type checking, chains of binary expressions will not be fully
|
|
// parsed, so associativity has not yet been encoded in the subtree.
|
|
auto elements = SE->getElements();
|
|
auto numElements = SE->getNumElements();
|
|
size_t iOperator = 1;
|
|
size_t iOperand = 2;
|
|
|
|
auto result = evaluateConditionalCompilationExpr(elements[0]);
|
|
|
|
while (iOperand < numElements) {
|
|
|
|
if (auto *UDREOp = dyn_cast<UnresolvedDeclRefExpr>(elements[iOperator])) {
|
|
auto name = UDREOp->getName().getBaseName().str();
|
|
|
|
if (name.equals("||") || name.equals("&&")) {
|
|
auto rhs = evaluateConditionalCompilationExpr(elements[iOperand]);
|
|
|
|
if (name.equals("||")) {
|
|
result = result || rhs;
|
|
if (result.isConditionActive())
|
|
break;
|
|
}
|
|
|
|
if (name.equals("&&")) {
|
|
result = result && rhs;
|
|
if (!result.isConditionActive())
|
|
break;
|
|
}
|
|
} else {
|
|
diagnose(SE->getLoc(),
|
|
diag::unsupported_conditional_compilation_binary_expression);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
}
|
|
|
|
iOperator += 2;
|
|
iOperand += 2;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Evaluate a named reference expression.
|
|
if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(condition)) {
|
|
auto name = UDRE->getName().getBaseName().str();
|
|
return {Context.LangOpts.isCustomConditionalCompilationFlagSet(name),
|
|
ConditionalCompilationExprKind::DeclRef};
|
|
}
|
|
|
|
// Evaluate a Boolean literal.
|
|
if (auto *boolLit = dyn_cast<BooleanLiteralExpr>(condition)) {
|
|
return {boolLit->getValue(), ConditionalCompilationExprKind::Boolean};
|
|
}
|
|
|
|
// Evaluate a negation (unary "!") expression.
|
|
if (auto *PUE = dyn_cast<PrefixUnaryExpr>(condition)) {
|
|
// If the PUE is not a negation expression, return false
|
|
auto name =
|
|
cast<UnresolvedDeclRefExpr>(PUE->getFn())->getName().getBaseName().str();
|
|
if (name != "!") {
|
|
diagnose(PUE->getLoc(),
|
|
diag::unsupported_conditional_compilation_unary_expression);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
return !evaluateConditionalCompilationExpr(PUE->getArg());
|
|
}
|
|
|
|
// Evaluate a target config call expression.
|
|
if (auto *CE = dyn_cast<CallExpr>(condition)) {
|
|
// look up target config, and compare value
|
|
auto fnNameExpr = dyn_cast<UnresolvedDeclRefExpr>(CE->getFn());
|
|
|
|
// Get the arg, which should be in a paren expression.
|
|
if (!fnNameExpr) {
|
|
diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
auto fnName = fnNameExpr->getName().getBaseName().str();
|
|
|
|
auto *PE = dyn_cast<ParenExpr>(CE->getArg());
|
|
if (!PE) {
|
|
auto diag = diagnose(CE->getLoc(),
|
|
diag::platform_condition_expected_one_argument);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
if (!fnName.equals("arch") && !fnName.equals("os") &&
|
|
!fnName.equals("_runtime") &&
|
|
!fnName.equals("swift") &&
|
|
!fnName.equals("_compiler_version")) {
|
|
diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
if (fnName.equals("_compiler_version")) {
|
|
if (auto SLE = dyn_cast<StringLiteralExpr>(PE->getSubExpr())) {
|
|
if (SLE->getValue().empty()) {
|
|
diagnose(CE->getLoc(), diag::empty_version_string);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
auto versionRequirement =
|
|
version::Version::parseCompilerVersionString(SLE->getValue(),
|
|
SLE->getLoc(),
|
|
&Diags);
|
|
auto thisVersion = version::Version::getCurrentCompilerVersion();
|
|
auto VersionNewEnough = thisVersion >= versionRequirement;
|
|
return {VersionNewEnough,
|
|
ConditionalCompilationExprKind::CompilerVersion};
|
|
} else {
|
|
diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
|
|
"string literal");
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
} else if(fnName.equals("swift")) {
|
|
auto PUE = dyn_cast<PrefixUnaryExpr>(PE->getSubExpr());
|
|
if (!PUE) {
|
|
diagnose(PE->getSubExpr()->getLoc(),
|
|
diag::unsupported_platform_condition_argument,
|
|
"a unary comparison, such as '>=2.2'");
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
auto prefix = dyn_cast<UnresolvedDeclRefExpr>(PUE->getFn());
|
|
auto versionArg = PUE->getArg();
|
|
auto versionStartLoc = versionArg->getStartLoc();
|
|
auto endLoc = Lexer::getLocForEndOfToken(SourceMgr,
|
|
versionArg->getSourceRange().End);
|
|
CharSourceRange versionCharRange(SourceMgr, versionStartLoc,
|
|
endLoc);
|
|
auto versionString = SourceMgr.extractText(versionCharRange);
|
|
|
|
auto versionRequirement =
|
|
version::Version::parseVersionString(versionString,
|
|
versionStartLoc,
|
|
&Diags);
|
|
|
|
if (!versionRequirement.hasValue())
|
|
return ConditionalCompilationExprState::error();
|
|
|
|
auto thisVersion = version::Version::getCurrentLanguageVersion();
|
|
|
|
if (!prefix->getName().getBaseName().str().equals(">=")) {
|
|
diagnose(PUE->getFn()->getLoc(),
|
|
diag::unexpected_version_comparison_operator)
|
|
.fixItReplace(PUE->getFn()->getLoc(), ">=");
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
auto VersionNewEnough = thisVersion >= versionRequirement.getValue();
|
|
return {VersionNewEnough,
|
|
ConditionalCompilationExprKind::LanguageVersion};
|
|
} else {
|
|
if (auto UDRE = dyn_cast<UnresolvedDeclRefExpr>(PE->getSubExpr())) {
|
|
// The sub expression should be an UnresolvedDeclRefExpr (we won't
|
|
// tolerate extra parens).
|
|
auto argument = UDRE->getName().getBaseName().str();
|
|
|
|
// Error for values that don't make sense if there's a clear definition
|
|
// of the possible values (as there is for _runtime).
|
|
if (fnName.equals("_runtime") &&
|
|
!argument.equals("_ObjC") && !argument.equals("_Native")) {
|
|
diagnose(CE->getLoc(),
|
|
diag::unsupported_platform_runtime_condition_argument);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
if (fnName == "os") {
|
|
if (!LangOptions::isPlatformConditionOSSupported(argument)) {
|
|
diagnose(UDRE->getLoc(), diag::unknown_platform_condition_argument,
|
|
"operating system", fnName);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
} else if (fnName == "arch") {
|
|
if (!LangOptions::isPlatformConditionArchSupported(argument)) {
|
|
diagnose(UDRE->getLoc(), diag::unknown_platform_condition_argument,
|
|
"architecture", fnName);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
}
|
|
auto target = Context.LangOpts.getPlatformConditionValue(fnName);
|
|
return {target == argument, ConditionalCompilationExprKind::DeclRef};
|
|
} else {
|
|
diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
|
|
"identifier");
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
}
|
|
}
|
|
|
|
// "#if 0" isn't valid, but it is common, so recognize it and handle it
|
|
// with a fixit elegantly.
|
|
if (auto *IL = dyn_cast<IntegerLiteralExpr>(condition))
|
|
if (IL->getDigitsText() == "0" || IL->getDigitsText() == "1") {
|
|
StringRef replacement = IL->getDigitsText() == "0" ? "false" :"true";
|
|
diagnose(IL->getLoc(), diag::unsupported_conditional_compilation_integer,
|
|
IL->getDigitsText(), replacement)
|
|
.fixItReplace(IL->getLoc(), replacement);
|
|
return {IL->getDigitsText() == "1",
|
|
ConditionalCompilationExprKind::Integer};
|
|
}
|
|
|
|
|
|
// If we've gotten here, it's an unsupported expression type.
|
|
diagnose(condition->getLoc(),
|
|
diag::unsupported_conditional_compilation_expression_type);
|
|
return ConditionalCompilationExprState::error();
|
|
}
|
|
|
|
ParserResult<Stmt> Parser::parseStmtIfConfig(BraceItemListKind Kind) {
|
|
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
|
|
StructureMarkerKind::IfConfig);
|
|
|
|
ConditionalCompilationExprState ConfigState;
|
|
bool foundActive = false;
|
|
SmallVector<IfConfigStmtClause, 4> Clauses;
|
|
|
|
while (1) {
|
|
bool isElse = Tok.is(tok::pound_else);
|
|
SourceLoc ClauseLoc = consumeToken();
|
|
Expr *Condition = nullptr;
|
|
|
|
if (isElse) {
|
|
ConfigState.setConditionActive(!foundActive);
|
|
} else {
|
|
if (Tok.isAtStartOfLine()) {
|
|
diagnose(ClauseLoc, diag::expected_conditional_compilation_expression,
|
|
!Clauses.empty());
|
|
}
|
|
|
|
// Evaluate the condition.
|
|
ParserResult<Expr> Result = parseExprSequence(diag::expected_expr,
|
|
/*basic*/true,
|
|
/*isForDirective*/true);
|
|
if (Result.isNull())
|
|
return makeParserError();
|
|
|
|
Condition = Result.get();
|
|
|
|
// Evaluate the condition, to validate it.
|
|
ConfigState = evaluateConditionalCompilationExpr(Condition);
|
|
}
|
|
|
|
foundActive |= ConfigState.isConditionActive();
|
|
|
|
if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
|
|
diagnose(Tok.getLoc(),
|
|
diag::extra_tokens_conditional_compilation_directive);
|
|
}
|
|
|
|
SmallVector<ASTNode, 16> Elements;
|
|
if (ConfigState.shouldParse())
|
|
parseIfConfigClauseElements(ConfigState.isConditionActive(), Kind,
|
|
Elements);
|
|
else {
|
|
DiagnosticTransaction DT(Diags);
|
|
skipUntilConditionalBlockClose();
|
|
DT.abort();
|
|
}
|
|
|
|
Clauses.push_back(IfConfigStmtClause(ClauseLoc, Condition,
|
|
Context.AllocateCopy(Elements),
|
|
ConfigState.isConditionActive()));
|
|
|
|
if (Tok.isNot(tok::pound_elseif) && Tok.isNot(tok::pound_else))
|
|
break;
|
|
|
|
if (isElse)
|
|
diagnose(Tok, diag::expected_close_after_else_directive);
|
|
}
|
|
|
|
SourceLoc EndLoc;
|
|
bool HadMissingEnd = parseEndIfDirective(EndLoc);
|
|
|
|
auto *ICS = new (Context) IfConfigStmt(Context.AllocateCopy(Clauses),
|
|
EndLoc, HadMissingEnd);
|
|
return makeParserResult(ICS);
|
|
}
|
|
|
|
///
|
|
/// stmt-while:
|
|
/// (identifier ':')? 'while' expr-basic stmt-brace
|
|
ParserResult<Stmt> Parser::parseStmtWhile(LabeledStmtInfo LabelInfo) {
|
|
SourceLoc WhileLoc = consumeToken(tok::kw_while);
|
|
|
|
Scope S(this, ScopeKind::WhileVars);
|
|
|
|
ParserStatus Status;
|
|
StmtCondition Condition;
|
|
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc LBraceLoc = Tok.getLoc();
|
|
diagnose(WhileLoc, diag::missing_condition_after_while)
|
|
.highlight(SourceRange(WhileLoc, LBraceLoc));
|
|
SmallVector<StmtConditionElement, 1> ConditionElems;
|
|
ConditionElems.emplace_back(new (Context) ErrorExpr(LBraceLoc));
|
|
Condition = Context.AllocateCopy(ConditionElems);
|
|
} else {
|
|
Status |= parseStmtCondition(Condition, diag::expected_condition_while,
|
|
StmtKind::While);
|
|
if (Status.isError() || Status.hasCodeCompletion()) {
|
|
// FIXME: better recovery
|
|
return makeParserResult<Stmt>(Status, nullptr);
|
|
}
|
|
}
|
|
|
|
ParserResult<BraceStmt> Body =
|
|
parseBraceItemList(diag::expected_lbrace_after_while);
|
|
if (Body.isNull())
|
|
return nullptr; // FIXME: better recovery
|
|
|
|
Status |= Body;
|
|
|
|
return makeParserResult(
|
|
Status, new (Context) WhileStmt(LabelInfo, WhileLoc, Condition,
|
|
Body.get()));
|
|
}
|
|
|
|
///
|
|
/// stmt-repeat:
|
|
/// (identifier ':')? 'repeat' stmt-brace 'while' expr
|
|
ParserResult<Stmt> Parser::parseStmtRepeat(LabeledStmtInfo labelInfo) {
|
|
SourceLoc repeatLoc = consumeToken(tok::kw_repeat);
|
|
|
|
ParserStatus status;
|
|
|
|
ParserResult<BraceStmt> body =
|
|
parseBraceItemList(diag::expected_lbrace_after_repeat);
|
|
status |= body;
|
|
if (status.hasCodeCompletion())
|
|
return makeParserResult<Stmt>(status, nullptr);
|
|
if (body.isNull())
|
|
body = makeParserResult(
|
|
body, BraceStmt::create(Context, repeatLoc, {}, PreviousLoc, true));
|
|
|
|
SourceLoc whileLoc;
|
|
|
|
if (!consumeIf(tok::kw_while, whileLoc)) {
|
|
diagnose(body.getPtrOrNull()->getEndLoc(),
|
|
diag::expected_while_after_repeat_body);
|
|
return body;
|
|
}
|
|
|
|
ParserResult<Expr> condition;
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc lbraceLoc = Tok.getLoc();
|
|
diagnose(whileLoc, diag::missing_condition_after_while);
|
|
condition = makeParserErrorResult(new (Context) ErrorExpr(lbraceLoc));
|
|
} else {
|
|
condition = parseExpr(diag::expected_expr_repeat_while);
|
|
status |= condition;
|
|
if (condition.isNull()) {
|
|
return makeParserResult<Stmt>(status, nullptr); // FIXME: better recovery
|
|
}
|
|
}
|
|
|
|
return makeParserResult(
|
|
status,
|
|
new (Context) RepeatWhileStmt(labelInfo, repeatLoc, condition.get(),
|
|
whileLoc, body.get()));
|
|
}
|
|
|
|
///
|
|
/// stmt-do:
|
|
/// (identifier ':')? 'do' stmt-brace
|
|
/// (identifier ':')? 'do' stmt-brace stmt-catch+
|
|
ParserResult<Stmt> Parser::parseStmtDo(LabeledStmtInfo labelInfo) {
|
|
SourceLoc doLoc = consumeToken(tok::kw_do);
|
|
|
|
ParserStatus status;
|
|
|
|
ParserResult<BraceStmt> body =
|
|
parseBraceItemList(diag::expected_lbrace_after_do);
|
|
status |= body;
|
|
if (body.isNull())
|
|
body = makeParserResult(
|
|
body, BraceStmt::create(Context, doLoc, {}, PreviousLoc, true));
|
|
|
|
// If the next token is 'catch', this is a 'do'/'catch' statement.
|
|
if (Tok.is(tok::kw_catch)) {
|
|
// Parse 'catch' clauses
|
|
SmallVector<CatchStmt*, 4> allClauses;
|
|
do {
|
|
ParserResult<CatchStmt> clause = parseStmtCatch();
|
|
status |= clause;
|
|
if (status.hasCodeCompletion() && clause.isNull())
|
|
return makeParserResult<Stmt>(status, nullptr);
|
|
|
|
// parseStmtCatch promises to return non-null unless we are
|
|
// completing inside the catch's pattern.
|
|
allClauses.push_back(clause.get());
|
|
} while (Tok.is(tok::kw_catch) && !status.hasCodeCompletion());
|
|
|
|
// Recover from all of the clauses failing to parse by returning a
|
|
// normal do-statement.
|
|
if (allClauses.empty()) {
|
|
assert(status.isError());
|
|
return makeParserResult(status,
|
|
new (Context) DoStmt(labelInfo, doLoc, body.get()));
|
|
}
|
|
|
|
return makeParserResult(status,
|
|
DoCatchStmt::create(Context, labelInfo, doLoc, body.get(), allClauses));
|
|
}
|
|
|
|
SourceLoc whileLoc;
|
|
|
|
// If we don't see a 'while', this is just the bare 'do' scoping
|
|
// statement.
|
|
if (!consumeIf(tok::kw_while, whileLoc)) {
|
|
return makeParserResult(status,
|
|
new (Context) DoStmt(labelInfo, doLoc, body.get()));
|
|
}
|
|
|
|
// But if we do, advise the programmer that it's 'repeat' now.
|
|
diagnose(doLoc, diag::do_while_now_repeat_while)
|
|
.fixItReplace(doLoc, "repeat");
|
|
status.setIsParseError();
|
|
ParserResult<Expr> condition;
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc lbraceLoc = Tok.getLoc();
|
|
diagnose(whileLoc, diag::missing_condition_after_while);
|
|
condition = makeParserErrorResult(new (Context) ErrorExpr(lbraceLoc));
|
|
} else {
|
|
condition = parseExpr(diag::expected_expr_repeat_while);
|
|
status |= condition;
|
|
if (condition.isNull() || condition.hasCodeCompletion())
|
|
return makeParserResult<Stmt>(status, nullptr); // FIXME: better recovery
|
|
}
|
|
|
|
return makeParserResult(
|
|
status,
|
|
new (Context) RepeatWhileStmt(labelInfo, doLoc, condition.get(), whileLoc,
|
|
body.get()));
|
|
}
|
|
|
|
/// stmt-catch:
|
|
/// 'catch' pattern ('where' expr)? stmt-brace
|
|
///
|
|
/// Note that this is not a "first class" statement; it can only
|
|
/// appear following a 'do' statement.
|
|
///
|
|
/// This routine promises to return a non-null result unless there was
|
|
/// a code-completion token in the pattern.
|
|
ParserResult<CatchStmt> Parser::parseStmtCatch() {
|
|
// A catch block has its own scope for variables bound out of the pattern.
|
|
Scope S(this, ScopeKind::CatchVars);
|
|
|
|
SourceLoc catchLoc = consumeToken(tok::kw_catch);
|
|
|
|
SmallVector<VarDecl*, 4> boundDecls;
|
|
|
|
ParserStatus status;
|
|
GuardedPattern pattern;
|
|
parseGuardedPattern(*this, pattern, status, boundDecls,
|
|
GuardedPatternContext::Catch, /* isFirst */ true);
|
|
if (status.hasCodeCompletion()) {
|
|
return makeParserCodeCompletionResult<CatchStmt>();
|
|
}
|
|
|
|
SourceLoc startOfBody = Tok.getLoc();
|
|
auto bodyResult = parseBraceItemList(diag::expected_lbrace_after_catch);
|
|
status |= bodyResult;
|
|
if (bodyResult.isNull()) {
|
|
bodyResult = makeParserErrorResult(BraceStmt::create(Context, startOfBody,
|
|
{}, PreviousLoc,
|
|
/*implicit=*/ true));
|
|
}
|
|
|
|
auto result =
|
|
new (Context) CatchStmt(catchLoc, pattern.ThePattern, pattern.WhereLoc,
|
|
pattern.Guard, bodyResult.get());
|
|
return makeParserResult(status, result);
|
|
}
|
|
|
|
ParserResult<Stmt> Parser::parseStmtFor(LabeledStmtInfo LabelInfo) {
|
|
SourceLoc ForLoc = consumeToken(tok::kw_for);
|
|
|
|
// The c-style-for loop and foreach-style-for loop are conflated together into
|
|
// a single keyword, so we have to do some lookahead to resolve what is going
|
|
// on.
|
|
|
|
// If we have a leading identifier followed by a ':' or 'in', then this is
|
|
// obviously a for-each loop. For error recovery, also parse "for in ..." as
|
|
// foreach.
|
|
if ((Tok.isIdentifierOrUnderscore() &&
|
|
peekToken().isAny(tok::colon, tok::kw_in)) ||
|
|
Tok.is(tok::kw_in))
|
|
return parseStmtForEach(ForLoc, LabelInfo);
|
|
|
|
// If we have "for ;" then this is clearly a c-style for loop.
|
|
if (Tok.is(tok::semi))
|
|
return parseStmtForCStyle(ForLoc, LabelInfo);
|
|
|
|
// Otherwise, we have to do lookahead. An unparenthesized valid C-style
|
|
// for-each loop will start with "let/var <irrefutable pattern> =". Check for
|
|
// that.
|
|
bool isCStyleFor = false;
|
|
{
|
|
Parser::BacktrackingScope Backtrack(*this);
|
|
|
|
// The condition of a foreach loop can be parenthesized.
|
|
consumeIf(tok::l_paren);
|
|
|
|
// Skip until we see eof, "in" (in which case we have a for-in loop),
|
|
// ";" in which case we have a simple expression as the first part of a
|
|
// c-style for loop, or "{" in which case we have a malformed statement.
|
|
while (Tok.isNot(tok::eof, tok::kw_in, tok::semi, tok::l_brace))
|
|
skipSingle();
|
|
|
|
isCStyleFor = Tok.isAny(tok::semi, tok::l_brace, tok::eof);
|
|
}
|
|
|
|
// Otherwise, this is some sort of c-style for loop.
|
|
if (isCStyleFor)
|
|
return parseStmtForCStyle(ForLoc, LabelInfo);
|
|
|
|
return parseStmtForEach(ForLoc, LabelInfo);
|
|
}
|
|
|
|
|
|
/// Given an expression, check to see if it is a set of braces "{...}" parsed as
|
|
/// a ClosureExpr that is probably a body of a statement. If so, convert it
|
|
/// into a BraceStmt that can be used as the body of a control flow statement
|
|
/// to improve error recovery.
|
|
///
|
|
/// If this expression isn't a ClosureExpr or isn't convertible, this returns
|
|
/// null.
|
|
///
|
|
static BraceStmt *ConvertClosureToBraceStmt(Expr *E, ASTContext &Ctx) {
|
|
if (!E) return nullptr;
|
|
|
|
auto *CE = dyn_cast<ClosureExpr>(E);
|
|
if (!CE) return nullptr;
|
|
|
|
// If this had a signature or anon-closure parameters (like $0) used, then it
|
|
// doesn't "look" like the body of a control flow statement, it looks like a
|
|
// closure.
|
|
if (CE->getInLoc().isValid() || CE->hasExplicitResultType() ||
|
|
CE->getParameters()->size() != 0)
|
|
return nullptr;
|
|
|
|
// Silence downstream errors by giving it type ()->(), to match up with the
|
|
// call we will produce.
|
|
CE->setImplicit();
|
|
auto empty = TupleTypeRepr::create(Ctx, {}, CE->getStartLoc(), SourceLoc(),
|
|
0);
|
|
CE->setExplicitResultType(CE->getStartLoc(), empty);
|
|
|
|
// The trick here is that the ClosureExpr provides a DeclContext for stuff
|
|
// inside of it, so it isn't safe to just drop it and rip the BraceStmt
|
|
// from inside of it. While we could try to walk the body and update any
|
|
// Decls, ClosureExprs, etc within the body of the ClosureExpr, it is easier
|
|
// to just turn it into BraceStmt(CallExpr(TheClosure, VoidTuple)). This also
|
|
// more correctly handles the implicit ReturnStmt injected into single-expr
|
|
// closures.
|
|
auto voidArg = TupleExpr::createEmpty(Ctx, CE->getEndLoc(), CE->getEndLoc(),
|
|
/*implicit*/true);
|
|
ASTNode theCall = new (Ctx) CallExpr(CE, voidArg, /*implicit*/true);
|
|
return BraceStmt::create(Ctx, CE->getStartLoc(), theCall, CE->getEndLoc(),
|
|
/*implicit*/true);
|
|
}
|
|
|
|
|
|
/// stmt-for-c-style:
|
|
/// (identifier ':')? 'for' stmt-for-c-style-init? ';' expr-basic? ';'
|
|
/// (expr-basic (',' expr-basic)*)? stmt-brace
|
|
/// (identifier ':')? 'for' '(' stmt-for-c-style-init? ';' expr-basic? ';'
|
|
/// (expr-basic (',' expr-basic)*)? ')' stmt-brace
|
|
/// stmt-for-c-style-init:
|
|
/// decl-var
|
|
/// expr (',' expr)*
|
|
ParserResult<Stmt> Parser::parseStmtForCStyle(SourceLoc ForLoc,
|
|
LabeledStmtInfo LabelInfo) {
|
|
SourceLoc Semi1Loc, Semi2Loc;
|
|
SourceLoc LPLoc, RPLoc;
|
|
bool LPLocConsumed = false;
|
|
|
|
ParserStatus Status;
|
|
|
|
bool HaveFirst = false;
|
|
ParserResult<Expr> First;
|
|
SmallVector<Decl*, 2> FirstDecls;
|
|
ParserResult<Expr> Second;
|
|
ParserResult<Expr> Third;
|
|
ParserResult<BraceStmt> Body;
|
|
|
|
// Introduce a new scope to contain any var decls in the init value.
|
|
Scope S(this, ScopeKind::ForVars);
|
|
|
|
if (Tok.is(tok::l_paren)) {
|
|
LPLoc = consumeToken();
|
|
LPLocConsumed = true;
|
|
}
|
|
// Parse the first part, either a var, let, expr, or stmt-assign.
|
|
if (Tok.is(tok::kw_var) || Tok.is(tok::kw_let) || Tok.is(tok::at_sign)) {
|
|
DeclAttributes Attributes;
|
|
bool FoundCCToken;
|
|
parseDeclAttributeList(Attributes, FoundCCToken);
|
|
|
|
// After parsing optional attributes above we should be at 'var' or 'let'
|
|
if (!Tok.is(tok::kw_var) && !Tok.is(tok::kw_let)) {
|
|
diagnose(Tok.getLoc(), diag::expected_var_decl_for_stmt);
|
|
return makeParserError();
|
|
}
|
|
|
|
ParserStatus VarDeclStatus = parseDeclVar(PD_InLoop, Attributes, FirstDecls,
|
|
SourceLoc(),
|
|
StaticSpellingKind::None,
|
|
SourceLoc());
|
|
if (VarDeclStatus.isError())
|
|
return VarDeclStatus; // FIXME: better recovery
|
|
} else if (Tok.isNot(tok::semi)) {
|
|
SmallVector<Expr *, 1> FirstExprs;
|
|
|
|
// Parse the first expression.
|
|
HaveFirst = true;
|
|
First = parseExpr(diag::expected_init_for_stmt);
|
|
Status |= First;
|
|
if (First.isNull() || First.hasCodeCompletion())
|
|
return makeParserResult<Stmt>(Status, nullptr); // FIXME: better recovery
|
|
|
|
FirstExprs.push_back(First.get());
|
|
|
|
// Parse additional expressions.
|
|
while (Tok.is(tok::comma)) {
|
|
consumeToken(tok::comma);
|
|
|
|
First = parseExpr(diag::expected_expr);
|
|
Status |= First;
|
|
|
|
if (First.isNull() || First.hasCodeCompletion())
|
|
return makeParserResult<Stmt>(Status, nullptr); // FIXME: better recovery
|
|
|
|
if (First.isNonNull())
|
|
FirstExprs.push_back(First.get());
|
|
}
|
|
|
|
// If we had more than one expression, form a tuple.
|
|
if (FirstExprs.size() > 1) {
|
|
First = makeParserResult(
|
|
TupleExpr::createImplicit(Context, FirstExprs, { }));
|
|
}
|
|
}
|
|
|
|
ArrayRef<Decl *> FirstDeclsContext;
|
|
if (!FirstDecls.empty())
|
|
FirstDeclsContext = Context.AllocateCopy(FirstDecls);
|
|
VarDecl *IterationVariable = nullptr;
|
|
for (auto *D : FirstDeclsContext) {
|
|
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
|
IterationVariable = VD;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're missing a semicolon, try to recover.
|
|
if (Tok.isNot(tok::semi)) {
|
|
if (auto *BS = ConvertClosureToBraceStmt(First.getPtrOrNull(), Context)) {
|
|
// We have seen:
|
|
// for { ... }
|
|
// and there's no semicolon after that.
|
|
//
|
|
// We parsed the brace statement as a closure. Recover by using the
|
|
// brace statement as a 'for' body.
|
|
First = makeParserErrorResult(new (Context) ErrorExpr(BS->getStartLoc()));
|
|
Second = nullptr;
|
|
Third = nullptr;
|
|
Body = makeParserErrorResult(BS);
|
|
diagnose(ForLoc, diag::missing_init_for_stmt)
|
|
.highlight(SourceRange(ForLoc, BS->getStartLoc()));
|
|
Status.setIsParseError();
|
|
|
|
return makeParserResult(
|
|
Status, new (Context) ForStmt(LabelInfo, ForLoc, First.getPtrOrNull(),
|
|
FirstDeclsContext,
|
|
Semi1Loc, Second.getPtrOrNull(),
|
|
Semi2Loc, Third.getPtrOrNull(),
|
|
Body.get()));
|
|
}
|
|
}
|
|
|
|
// Consume the first semicolon.
|
|
if (parseToken(tok::semi, Semi1Loc, diag::expected_semi_for_stmt))
|
|
Status.setIsParseError();
|
|
|
|
CodeCompletionCallbacks::InCStyleForExprRAII InCStyleForExpr(
|
|
CodeCompletion, IterationVariable);
|
|
|
|
if (Tok.isNot(tok::semi)) {
|
|
Second = parseExprBasic(diag::expected_cond_for_stmt);
|
|
Status |= Second;
|
|
}
|
|
|
|
if (Tok.isNot(tok::semi) && Second.isNonNull()) {
|
|
Expr *RecoveredCondition = nullptr;
|
|
BraceStmt *RecoveredBody = ConvertClosureToBraceStmt(Second.get(), Context);
|
|
|
|
if (auto *CE = dyn_cast<CallExpr>(Second.get())) {
|
|
if (auto *PE = dyn_cast<ParenExpr>(CE->getArg())) {
|
|
if (PE->hasTrailingClosure() && !RecoveredBody) {
|
|
// We have seen:
|
|
// for ... ; ... { ... }
|
|
// and there's no semicolon after that.
|
|
//
|
|
// We parsed the condition as a CallExpr with a brace statement as a
|
|
// trailing closure. Recover by using the original expression as the
|
|
// condition and brace statement as a 'for' body.
|
|
RecoveredBody = ConvertClosureToBraceStmt(PE->getSubExpr(), Context);
|
|
RecoveredCondition = CE->getFn();
|
|
}
|
|
}
|
|
}
|
|
if (RecoveredBody) {
|
|
SourceLoc LBraceLoc = RecoveredBody->getStartLoc();
|
|
Second = makeParserErrorResult(RecoveredCondition);
|
|
Third = nullptr;
|
|
Body = makeParserErrorResult(RecoveredBody);
|
|
diagnose(LBraceLoc, diag::expected_semi_for_stmt)
|
|
.highlight(SourceRange(ForLoc, LBraceLoc));
|
|
Status.setIsParseError();
|
|
|
|
return makeParserResult(
|
|
Status, new (Context) ForStmt(LabelInfo, ForLoc, First.getPtrOrNull(),
|
|
FirstDeclsContext,
|
|
Semi1Loc, Second.getPtrOrNull(),
|
|
Semi2Loc, Third.getPtrOrNull(),
|
|
Body.get()));
|
|
}
|
|
}
|
|
|
|
// Consume the second semicolon.
|
|
if (parseToken(tok::semi, Semi2Loc, diag::expected_semi_for_stmt))
|
|
Status.setIsParseError();
|
|
|
|
if (Tok.isNot(tok::l_brace, tok::r_paren)) {
|
|
SmallVector<Expr *, 1> ThirdExprs;
|
|
|
|
// Parse the first expression.
|
|
Third = parseExprBasic(diag::expected_expr);
|
|
Status |= Third;
|
|
if (Third.isNonNull())
|
|
ThirdExprs.push_back(Third.get());
|
|
|
|
// Parse additional expressions.
|
|
while (Tok.is(tok::comma)) {
|
|
consumeToken(tok::comma);
|
|
|
|
Third = parseExprBasic(diag::expected_expr);
|
|
Status |= Third;
|
|
|
|
if (Third.isNonNull())
|
|
ThirdExprs.push_back(Third.get());
|
|
}
|
|
|
|
// If we had more than one expression, form a tuple.
|
|
if (ThirdExprs.size() > 1) {
|
|
Third = makeParserResult(
|
|
TupleExpr::createImplicit(Context, ThirdExprs, { }));
|
|
}
|
|
}
|
|
|
|
InCStyleForExpr.finished();
|
|
|
|
if (LPLocConsumed && parseMatchingToken(tok::r_paren, RPLoc,
|
|
diag::expected_rparen_for_stmt,LPLoc))
|
|
Status.setIsParseError();
|
|
|
|
Body = parseBraceItemList(diag::expected_lbrace_after_for);
|
|
Status |= Body;
|
|
if (Body.isNull())
|
|
Body = makeParserResult(
|
|
Body, BraceStmt::create(Context, ForLoc, {}, PreviousLoc, true));
|
|
|
|
return makeParserResult(
|
|
Status,
|
|
new (Context) ForStmt(LabelInfo, ForLoc, First.getPtrOrNull(),
|
|
FirstDeclsContext,
|
|
Semi1Loc, Second.getPtrOrNull(), Semi2Loc,
|
|
Third.getPtrOrNull(), Body.get()));
|
|
}
|
|
|
|
///
|
|
/// stmt-for-each:
|
|
/// (identifier ':')? 'for' pattern 'in' expr-basic \
|
|
/// ('where' expr-basic)? stmt-brace
|
|
ParserResult<Stmt> Parser::parseStmtForEach(SourceLoc ForLoc,
|
|
LabeledStmtInfo LabelInfo) {
|
|
ParserStatus Status;
|
|
|
|
ParserResult<Pattern> pattern;
|
|
|
|
// Parse the pattern. This is either 'case <refutable pattern>' or just a
|
|
// normal pattern.
|
|
if (consumeIf(tok::kw_case)) {
|
|
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
|
|
T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
|
|
pattern = parseMatchingPattern(/*isExprBasic*/true);
|
|
pattern = parseOptionalPatternTypeAnnotation(pattern, /*isOptional*/false);
|
|
} else {
|
|
// Change the parser state to know that the pattern we're about to parse is
|
|
// implicitly mutable. Bound variables can be changed to mutable explicitly
|
|
// if desired by using a 'var' pattern.
|
|
assert(InVarOrLetPattern == IVOLP_NotInVarOrLet &&
|
|
"for-each loops cannot exist inside other patterns");
|
|
InVarOrLetPattern = IVOLP_ImplicitlyImmutable;
|
|
pattern = parseTypedPattern();
|
|
assert(InVarOrLetPattern == IVOLP_ImplicitlyImmutable);
|
|
InVarOrLetPattern = IVOLP_NotInVarOrLet;
|
|
}
|
|
|
|
if (pattern.isNull())
|
|
// Recover by creating a "_" pattern.
|
|
pattern = makeParserErrorResult(new (Context) AnyPattern(SourceLoc()));
|
|
|
|
// Bound variables all get their initial values from the generator.
|
|
pattern.get()->markHasNonPatternBindingInit();
|
|
|
|
SourceLoc InLoc;
|
|
parseToken(tok::kw_in, InLoc, diag::expected_foreach_in);
|
|
|
|
ParserResult<Expr> Container;
|
|
if (Tok.is(tok::l_brace)) {
|
|
SourceLoc LBraceLoc = Tok.getLoc();
|
|
diagnose(LBraceLoc, diag::expected_foreach_container);
|
|
Container = makeParserErrorResult(new (Context) ErrorExpr(LBraceLoc));
|
|
} else {
|
|
Container = parseExprBasic(diag::expected_foreach_container);
|
|
if (Container.isNull())
|
|
Container = makeParserErrorResult(new (Context) ErrorExpr(Tok.getLoc()));
|
|
Status |= Container;
|
|
}
|
|
|
|
// Introduce a new scope and place the variables in the pattern into that
|
|
// scope.
|
|
// FIXME: We may want to merge this scope with the scope introduced by
|
|
// the stmt-brace, as in C++.
|
|
Scope S(this, ScopeKind::ForeachVars);
|
|
|
|
// Introduce variables to the current scope.
|
|
addPatternVariablesToScope(pattern.get());
|
|
|
|
// Parse the 'where' expression if present.
|
|
ParserResult<Expr> Where;
|
|
if (consumeIf(tok::kw_where)) {
|
|
Where = parseExprBasic(diag::expected_foreach_where_expr);
|
|
if (Where.isNull())
|
|
Where = makeParserErrorResult(new (Context) ErrorExpr(Tok.getLoc()));
|
|
Status |= Where;
|
|
}
|
|
|
|
|
|
// stmt-brace
|
|
ParserResult<BraceStmt> Body =
|
|
parseBraceItemList(diag::expected_foreach_lbrace);
|
|
Status |= Body;
|
|
if (Body.isNull())
|
|
Body = makeParserResult(
|
|
Body, BraceStmt::create(Context, ForLoc, {}, PreviousLoc, true));
|
|
|
|
return makeParserResult(
|
|
Status,
|
|
new (Context) ForEachStmt(LabelInfo, ForLoc, pattern.get(), InLoc,
|
|
Container.get(), Where.getPtrOrNull(),
|
|
Body.get()));
|
|
}
|
|
|
|
///
|
|
/// stmt-switch:
|
|
/// (identifier ':')? 'switch' expr-basic '{' stmt-case+ '}'
|
|
ParserResult<Stmt> Parser::parseStmtSwitch(LabeledStmtInfo LabelInfo) {
|
|
SourceLoc SwitchLoc = consumeToken(tok::kw_switch);
|
|
|
|
ParserStatus Status;
|
|
ParserResult<Expr> SubjectExpr;
|
|
SourceLoc SubjectLoc = Tok.getLoc();
|
|
if (Tok.is(tok::l_brace)) {
|
|
diagnose(SubjectLoc, diag::expected_switch_expr);
|
|
SubjectExpr = makeParserErrorResult(new (Context) ErrorExpr(SubjectLoc));
|
|
} else {
|
|
SubjectExpr = parseExprBasic(diag::expected_switch_expr);
|
|
if (SubjectExpr.hasCodeCompletion()) {
|
|
return makeParserCodeCompletionResult<Stmt>();
|
|
}
|
|
if (SubjectExpr.isNull()) {
|
|
SubjectExpr = makeParserErrorResult(new (Context) ErrorExpr(SubjectLoc));
|
|
}
|
|
Status |= SubjectExpr;
|
|
}
|
|
|
|
if (!Tok.is(tok::l_brace)) {
|
|
diagnose(Tok, diag::expected_lbrace_after_switch);
|
|
return nullptr;
|
|
}
|
|
SourceLoc lBraceLoc = consumeToken(tok::l_brace);
|
|
SourceLoc rBraceLoc;
|
|
|
|
// Reject an empty 'switch'.
|
|
if (Tok.is(tok::r_brace))
|
|
diagnose(Tok.getLoc(), diag::empty_switch_stmt);
|
|
|
|
// If there are non-case-label statements at the start of the switch body,
|
|
// raise an error and recover by parsing and discarding them.
|
|
bool DiagnosedNotCoveredStmt = false;
|
|
bool ErrorAtNotCoveredStmt = false;
|
|
while (!Tok.is(tok::kw_case) && !Tok.is(tok::kw_default)
|
|
&& !Tok.is(tok::r_brace) && !Tok.is(tok::eof)) {
|
|
if (ErrorAtNotCoveredStmt) {
|
|
// Error recovery.
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
if (!DiagnosedNotCoveredStmt) {
|
|
diagnose(Tok, diag::stmt_in_switch_not_covered_by_case);
|
|
DiagnosedNotCoveredStmt = true;
|
|
}
|
|
ASTNode NotCoveredStmt;
|
|
ParserStatus CurrStat = parseExprOrStmt(NotCoveredStmt);
|
|
if (CurrStat.isError())
|
|
ErrorAtNotCoveredStmt = true;
|
|
Status |= CurrStat;
|
|
}
|
|
|
|
SmallVector<CaseStmt*, 8> cases;
|
|
bool parsedDefault = false;
|
|
bool parsedBlockAfterDefault = false;
|
|
while (Tok.is(tok::kw_case) || Tok.is(tok::kw_default)) {
|
|
// We cannot have additional cases after a default clause. Complain on
|
|
// the first offender.
|
|
if (parsedDefault && !parsedBlockAfterDefault) {
|
|
parsedBlockAfterDefault = true;
|
|
diagnose(Tok, diag::case_after_default);
|
|
}
|
|
|
|
ParserResult<CaseStmt> Case = parseStmtCase();
|
|
Status |= Case;
|
|
if (Case.isNonNull()) {
|
|
cases.push_back(Case.get());
|
|
if (Case.get()->isDefault())
|
|
parsedDefault = true;
|
|
}
|
|
}
|
|
|
|
if (parseMatchingToken(tok::r_brace, rBraceLoc,
|
|
diag::expected_rbrace_switch, lBraceLoc)) {
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
return makeParserResult(
|
|
Status, SwitchStmt::create(LabelInfo, SwitchLoc, SubjectExpr.get(),
|
|
lBraceLoc, cases, rBraceLoc, Context));
|
|
}
|
|
|
|
static ParserStatus parseStmtCase(Parser &P, SourceLoc &CaseLoc,
|
|
SmallVectorImpl<CaseLabelItem> &LabelItems,
|
|
SmallVectorImpl<VarDecl *> &BoundDecls,
|
|
SourceLoc &ColonLoc) {
|
|
ParserStatus Status;
|
|
bool isFirst = true;
|
|
|
|
CaseLoc = P.consumeToken(tok::kw_case);
|
|
|
|
do {
|
|
GuardedPattern PatternResult;
|
|
parseGuardedPattern(P, PatternResult, Status, BoundDecls,
|
|
GuardedPatternContext::Case, isFirst);
|
|
LabelItems.push_back(CaseLabelItem(/*IsDefault=*/false,
|
|
PatternResult.ThePattern,
|
|
PatternResult.WhereLoc,
|
|
PatternResult.Guard));
|
|
isFirst = false;
|
|
} while (P.consumeIf(tok::comma));
|
|
|
|
ColonLoc = P.Tok.getLoc();
|
|
if (!P.Tok.is(tok::colon)) {
|
|
P.diagnose(P.Tok, diag::expected_case_colon, "case");
|
|
Status.setIsParseError();
|
|
} else
|
|
P.consumeToken(tok::colon);
|
|
|
|
return Status;
|
|
}
|
|
|
|
static ParserStatus
|
|
parseStmtCaseDefault(Parser &P, SourceLoc &CaseLoc,
|
|
SmallVectorImpl<CaseLabelItem> &LabelItems,
|
|
SourceLoc &ColonLoc) {
|
|
ParserStatus Status;
|
|
|
|
CaseLoc = P.consumeToken(tok::kw_default);
|
|
|
|
// We don't allow 'where' guards on a 'default' block. For recovery
|
|
// parse one if present.
|
|
SourceLoc WhereLoc;
|
|
ParserResult<Expr> Guard;
|
|
if (P.Tok.is(tok::kw_where)) {
|
|
P.diagnose(P.Tok, diag::default_with_where);
|
|
WhereLoc = P.consumeToken(tok::kw_where);
|
|
Guard = P.parseExpr(diag::expected_case_where_expr);
|
|
Status |= Guard;
|
|
}
|
|
|
|
ColonLoc = P.Tok.getLoc();
|
|
if (!P.Tok.is(tok::colon)) {
|
|
P.diagnose(P.Tok, diag::expected_case_colon, "default");
|
|
Status.setIsParseError();
|
|
} else
|
|
P.consumeToken(tok::colon);
|
|
|
|
// Create an implicit AnyPattern to represent the default match.
|
|
auto Any = new (P.Context) AnyPattern(CaseLoc);
|
|
LabelItems.push_back(
|
|
CaseLabelItem(/*IsDefault=*/true, Any, WhereLoc, Guard.getPtrOrNull()));
|
|
|
|
return Status;
|
|
}
|
|
|
|
ParserResult<CaseStmt> Parser::parseStmtCase() {
|
|
// A case block has its own scope for variables bound out of the pattern.
|
|
Scope S(this, ScopeKind::CaseVars);
|
|
|
|
ParserStatus Status;
|
|
|
|
SmallVector<CaseLabelItem, 2> CaseLabelItems;
|
|
SmallVector<VarDecl *, 4> BoundDecls;
|
|
|
|
SourceLoc CaseLoc;
|
|
SourceLoc ColonLoc;
|
|
if (Tok.is(tok::kw_case)) {
|
|
Status |=
|
|
::parseStmtCase(*this, CaseLoc, CaseLabelItems, BoundDecls, ColonLoc);
|
|
} else {
|
|
Status |= parseStmtCaseDefault(*this, CaseLoc, CaseLabelItems, ColonLoc);
|
|
}
|
|
|
|
assert(!CaseLabelItems.empty() && "did not parse any labels?!");
|
|
|
|
SmallVector<ASTNode, 8> BodyItems;
|
|
|
|
SourceLoc StartOfBody = Tok.getLoc();
|
|
if (Tok.isNot(tok::kw_case) && Tok.isNot(tok::kw_default) &&
|
|
Tok.isNot(tok::r_brace)) {
|
|
Status |= parseBraceItems(BodyItems, BraceItemListKind::Case);
|
|
} else if (Status.isSuccess()) {
|
|
diagnose(CaseLoc, diag::case_stmt_without_body,
|
|
CaseLabelItems.back().isDefault())
|
|
.highlight(SourceRange(CaseLoc, ColonLoc))
|
|
.fixItInsertAfter(ColonLoc, " break");
|
|
}
|
|
BraceStmt *Body;
|
|
if (BodyItems.empty()) {
|
|
Body = BraceStmt::create(Context, PreviousLoc, ArrayRef<ASTNode>(),
|
|
PreviousLoc, /*implicit=*/true);
|
|
} else {
|
|
Body = BraceStmt::create(Context, StartOfBody, BodyItems,
|
|
PreviousLoc, /*implicit=*/true);
|
|
}
|
|
|
|
return makeParserResult(
|
|
Status, CaseStmt::create(Context, CaseLoc, CaseLabelItems,
|
|
!BoundDecls.empty(), ColonLoc, Body));
|
|
}
|