blob: caebdf820749e52e921ae0f8b3d9da4ab892584d [file] [log] [blame]
//===--- ParseDecl.cpp - Swift Language Parser for Declarations -----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Declaration Parsing and AST Building
//
//===----------------------------------------------------------------------===//
#include "swift/Parse/Parser.h"
#include "swift/Parse/CodeCompletionCallbacks.h"
#include "swift/Parse/DelayedParsingCallbacks.h"
#include "swift/Subsystems.h"
#include "swift/AST/Attr.h"
#include "swift/AST/DebuggerClient.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/Module.h"
#include "swift/AST/ParameterList.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Fallthrough.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include <algorithm>
using namespace swift;
namespace {
/// A RAII object for deciding whether this DeclKind needs special
/// treatment when parsing in the "debugger context", and implementing
/// that treatment. The problem arises because, when lldb
/// uses swift to parse expressions, it needs to emulate the current
/// frame's scope. We do that, for instance, by making a class extension
/// and running the code in a function in that extension.
///
/// This causes two kinds of issues:
/// 1) Some DeclKinds require to be parsed in TopLevel contexts only.
/// 2) Sometimes the debugger wants a Decl to live beyond the current
/// function invocation, in which case it should be parsed at the
/// file scope level so it will be set up correctly for this purpose.
///
/// Creating an instance of this object will cause it to figure out
/// whether we are in the debugger function, whether it needs to swap
/// the Decl that is currently being parsed.
/// If you have created the object, instead of returning the result
/// with makeParserResult, use the object's fixupParserResult. If
/// no swap has occurred, these methods will work the same.
/// If the decl has been moved, then Parser::markWasHandled will be
/// called on the Decl, and you should call declWasHandledAlready
/// before you consume the Decl to see if you actually need to
/// consume it.
/// If you are making one of these objects to address issue 1, call
/// the constructor that only takes a DeclKind, and it will be moved
/// unconditionally. Otherwise pass in the Name and DeclKind and the
/// DebuggerClient will be asked whether to move it or not.
class DebuggerContextChange {
protected:
Parser &P;
Identifier Name;
SourceFile *SF;
Optional<Parser::ContextChange> CC;
public:
DebuggerContextChange (Parser &P)
: P(P), SF(nullptr) {
if (!inDebuggerContext())
return;
else
switchContext();
}
DebuggerContextChange (Parser &P, Identifier &Name, DeclKind Kind)
: P(P), Name(Name), SF(nullptr) {
if (!inDebuggerContext())
return;
bool globalize = false;
DebuggerClient *debug_client = getDebuggerClient();
if (!debug_client)
return;
globalize = debug_client->shouldGlobalize(Name, Kind);
if (globalize)
switchContext();
}
bool movedToTopLevel() {
return CC.hasValue();
}
template <typename T>
ParserResult<T>
fixupParserResult(ParserResult<T> &Result) {
ParserStatus Status = Result;
return fixupParserResult(Status, Result.getPtrOrNull());
}
template <typename T>
ParserResult<T>
fixupParserResult(T *D) {
if (CC.hasValue()) {
swapDecl(D);
}
return ParserResult<T>(D);
}
template <typename T>
ParserResult<T>
fixupParserResult(ParserStatus Status, T *D) {
if (CC.hasValue() && !Status.isError()) {
// If there is an error, don't do our splicing trick,
// just return the Decl and the status for reporting.
swapDecl(D);
}
return makeParserResult(Status, D);
}
// The destructor doesn't need to do anything, the CC's destructor will
// pop the context if we set it.
~DebuggerContextChange () {}
protected:
DebuggerClient *getDebuggerClient()
{
Module *PM = P.CurDeclContext->getParentModule();
if (!PM)
return nullptr;
else
return PM->getDebugClient();
}
bool inDebuggerContext() {
if (!P.Context.LangOpts.DebuggerSupport)
return false;
if (!P.CurDeclContext)
return false;
FuncDecl *func_decl = dyn_cast<FuncDecl>(P.CurDeclContext);
if (!func_decl)
return false;
if (!func_decl->getAttrs().hasAttribute<LLDBDebuggerFunctionAttr>())
return false;
return true;
}
void switchContext () {
SF = P.CurDeclContext->getParentSourceFile();
CC.emplace (P, SF);
}
void swapDecl (Decl *D)
{
assert (SF);
DebuggerClient *debug_client = getDebuggerClient();
assert (debug_client);
debug_client->didGlobalize(D);
SF->Decls.push_back(D);
P.markWasHandled(D);
}
};
/// An RAII type to exclude tokens contributing to private decls from the
/// interface hash of the source file. On destruct, it checks if the set of
/// attributes includes the "private" attribute; if so, it resets the MD5
/// hash of the source file to what it was when the IgnorePrivateDeclTokens
/// instance was created, thus excluding from the interface hash all tokens
/// parsed in the meantime.
struct IgnorePrivateDeclTokens {
Parser &TheParser;
DeclAttributes &Attributes;
Optional<llvm::MD5> SavedHashState;
IgnorePrivateDeclTokens(Parser &P, DeclAttributes &Attrs)
: TheParser(P), Attributes(Attrs) {
// NOTE: It's generally not safe to ignore private decls in nominal
// types. Such private decls may affect the data layout of a class/struct
// or the vtable layout of a class. So only ignore global private decls.
if (TheParser.IsParsingInterfaceTokens &&
TheParser.CurDeclContext->isModuleScopeContext()) {
SavedHashState = TheParser.SF.getInterfaceHashState();
}
}
~IgnorePrivateDeclTokens() {
if (!SavedHashState)
return;
if (auto *attr = Attributes.getAttribute<AbstractAccessibilityAttr>()) {
if (attr->getAccess() == Accessibility::Private) {
TheParser.SF.setInterfaceHashState(*SavedHashState);
}
}
}
};
}
/// \brief Main entrypoint for the parser.
///
/// \verbatim
/// top-level:
/// stmt-brace-item*
/// decl-sil [[only in SIL mode]
/// decl-sil-stage [[only in SIL mode]
/// \endverbatim
bool Parser::parseTopLevel() {
SF.ASTStage = SourceFile::Parsing;
// Prime the lexer.
if (Tok.is(tok::NUM_TOKENS))
consumeToken();
// Parse the body of the file.
SmallVector<ASTNode, 128> Items;
skipExtraTopLevelRBraces();
// If we are in SIL mode, and if the first token is the start of a sil
// declaration, parse that one SIL function and return to the top level. This
// allows type declarations and other things to be parsed, name bound, and
// type checked in batches, similar to immediate mode. This also enforces
// that SIL bodies can only be at the top level.
if (Tok.is(tok::kw_sil)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseDeclSIL();
} else if (Tok.is(tok::kw_sil_stage)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseDeclSILStage();
} else if (Tok.is(tok::kw_sil_vtable)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseSILVTable();
} else if (Tok.is(tok::kw_sil_global)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseSILGlobal();
} else if (Tok.is(tok::kw_sil_witness_table)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseSILWitnessTable();
} else if (Tok.is(tok::kw_sil_coverage_map)) {
assert(isInSILMode() && "'sil' should only be a keyword in SIL mode");
parseSILCoverageMap();
} else {
parseBraceItems(Items,
allowTopLevelCode() ? BraceItemListKind::TopLevelCode
: BraceItemListKind::TopLevelLibrary);
}
// In the case of a catastrophic parse error, consume any trailing
// #else, #elseif, or #endif and move on to the next statement or declaration
// block.
if (Tok.is(tok::pound_else) || Tok.is(tok::pound_elseif) ||
Tok.is(tok::pound_endif)) {
diagnose(Tok.getLoc(), diag::unexpected_config_block_terminator);
consumeToken();
}
// If this is a Main source file, determine if we found code that needs to be
// executed (this is used by the repl to know whether to compile and run the
// newly parsed stuff).
bool FoundTopLevelCodeToExecute = false;
if (allowTopLevelCode()) {
for (auto V : Items)
if (isa<TopLevelCodeDecl>(V.get<Decl*>()))
FoundTopLevelCodeToExecute = true;
}
// Add newly parsed decls to the module.
for (auto Item : Items)
if (Decl *D = Item.dyn_cast<Decl*>())
SF.Decls.push_back(D);
// Note that the source file is fully parsed and verify it.
SF.ASTStage = SourceFile::Parsed;
verify(SF);
// Next time start relexing from the beginning of the comment so that we can
// attach it to the token.
State->markParserPosition(Tok.getCommentRange().getStart(), PreviousLoc,
InPoundLineEnvironment);
return FoundTopLevelCodeToExecute;
}
bool Parser::skipExtraTopLevelRBraces() {
if (!Tok.is(tok::r_brace))
return false;
while (Tok.is(tok::r_brace)) {
diagnose(Tok, diag::extra_rbrace)
.fixItRemove(Tok.getLoc());
consumeToken();
}
return true;
}
static Optional<StringRef>
getStringLiteralIfNotInterpolated(Parser &P, SourceLoc Loc, const Token &Tok,
StringRef DiagText) {
SmallVector<Lexer::StringSegment, 1> Segments;
P.L->getStringLiteralSegments(Tok, Segments);
if (Segments.size() != 1 ||
Segments.front().Kind == Lexer::StringSegment::Expr) {
P.diagnose(Loc, diag::attr_interpolated_string, DiagText);
return None;
}
return P.SourceMgr.extractText(CharSourceRange(Segments.front().Loc,
Segments.front().Length));
}
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
DeclAttrKind DK) {
// Ok, it is a valid attribute, eat it, and then process it.
StringRef AttrName = Tok.getText();
SourceLoc Loc = consumeToken();
bool DiscardAttribute = false;
// Diagnose duplicated attributes.
const DeclAttribute *DuplicateAttribute = nullptr;
if (!DeclAttribute::allowMultipleAttributes(DK))
if ((DuplicateAttribute = Attributes.getAttribute(DK))) {
// Delay issuing the diagnostic until we parse the attribute.
DiscardAttribute = true;
}
if ((DK == DAK_Prefix || DK == DAK_Postfix) && !DiscardAttribute &&
Attributes.getUnaryOperatorKind() != UnaryOperatorKind::None) {
diagnose(Loc, diag::cannot_combine_attribute,
DK == DAK_Prefix ? "postfix" : "prefix");
DiscardAttribute = true;
}
// If this is a SIL-only attribute, reject it.
if ((DeclAttribute::getOptions(DK) & DeclAttribute::SILOnly) != 0 &&
!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, AttrName);
DiscardAttribute = true;
}
// Filled in during parsing. If there is a duplicate
// diagnostic this can be used for better error presentation.
SourceRange AttrRange;
switch (DK) {
case DAK_Count:
llvm_unreachable("DAK_Count should not appear in parsing switch");
case DAK_AutoClosure: {
// If we don't have "(escaping", it's just a bare @autoclosure.
if (Tok.isNot(tok::l_paren) || peekToken().getText() != "escaping") {
if (!DiscardAttribute)
Attributes.add(new (Context) AutoClosureAttr(AtLoc, Loc,
/*escaping=*/false));
break;
}
// Consume the '('.
SourceLoc lParenLoc = consumeToken(tok::l_paren);
// Consume the 'escaping'.
(void)consumeToken();
// Parse the closing ')'.
SourceLoc rParenLoc;
parseMatchingToken(tok::r_paren, rParenLoc,
diag::attr_autoclosure_expected_r_paren,
lParenLoc);
// Add the attribute.
if (!DiscardAttribute)
Attributes.add(new (Context) AutoClosureAttr(AtLoc,
SourceRange(Loc, rParenLoc),
/*escaping=*/true));
break;
}
case DAK_RawDocComment:
case DAK_ObjCBridged:
case DAK_SynthesizedProtocol:
llvm_unreachable("virtual attributes should not be parsed "
"by attribute parsing code");
case DAK_SetterAccessibility:
llvm_unreachable("handled by DAK_Accessibility");
#define SIMPLE_DECL_ATTR(_, CLASS, ...) \
case DAK_##CLASS: \
if (!DiscardAttribute) \
Attributes.add(new (Context) CLASS##Attr(AtLoc, Loc)); \
break;
#include "swift/AST/Attr.def"
case DAK_Effects: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK)); return false;
}
if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::effects_attribute_expect_option, AttrName);
return false;
}
EffectsKind kind;
if (Tok.getText() == "readonly")
kind = EffectsKind::ReadOnly;
else if (Tok.getText() == "readnone")
kind = EffectsKind::ReadNone;
else if (Tok.getText() == "readwrite")
kind = EffectsKind::ReadWrite;
else {
diagnose(Loc, diag::effects_attribute_unknown_option,
Tok.getText(), AttrName);
return false;
}
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
consumeToken(tok::identifier);
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (!DiscardAttribute)
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind));
break;
}
case DAK_Inline: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::inline_attribute_expect_option, AttrName);
return false;
}
InlineKind kind;
if (Tok.getText() == "never")
kind = InlineKind::Never;
else if (Tok.getText() == "__always")
kind = InlineKind::Always;
else {
diagnose(Loc, diag::inline_attribute_unknown_option,
Tok.getText(), AttrName);
return false;
}
consumeToken(tok::identifier);
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (!DiscardAttribute)
Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, kind));
break;
}
case DAK_Ownership: {
// Handle weak/unowned/unowned(unsafe).
Ownership Kind = AttrName == "weak" ? Ownership::Weak : Ownership::Unowned;
SourceLoc EndLoc = Loc;
if (Kind == Ownership::Unowned && Tok.is(tok::l_paren)) {
// Parse an optional specifier after unowned.
SourceLoc lp = consumeToken(tok::l_paren);
if (Tok.is(tok::identifier) && Tok.getText() == "safe") {
consumeToken();
} else if (Tok.is(tok::identifier) && Tok.getText() == "unsafe") {
consumeToken();
Kind = Ownership::Unmanaged;
} else {
diagnose(Tok, diag::attr_unowned_invalid_specifier);
consumeIf(tok::identifier);
}
SourceLoc rp;
parseMatchingToken(tok::r_paren, rp, diag::attr_unowned_expected_rparen,
lp);
EndLoc = rp;
}
if (!DiscardAttribute)
Attributes.add(new (Context) OwnershipAttr(SourceRange(Loc, EndLoc),
Kind));
break;
}
case DAK_Accessibility: {
// Diagnose using accessibility in a local scope, which isn't meaningful.
if (CurDeclContext->isLocalContext()) {
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
}
auto access = llvm::StringSwitch<Accessibility>(AttrName)
.Case("private", Accessibility::Private)
.Case("public", Accessibility::Public)
.Case("internal", Accessibility::Internal);
if (!consumeIf(tok::l_paren)) {
// Normal accessibility attribute.
AttrRange = Loc;
DuplicateAttribute = Attributes.getAttribute<AccessibilityAttr>();
if (!DuplicateAttribute)
Attributes.add(new (Context) AccessibilityAttr(AtLoc, Loc, access));
break;
}
// Parse the subject.
if (Tok.isContextualKeyword("set")) {
consumeToken();
} else {
diagnose(Loc, diag::attr_accessibility_expected_set, AttrName);
// Minimal recovery: if there's a single token and then an r_paren,
// consume them both. If there's just an r_paren, consume that.
if (!consumeIf(tok::r_paren)) {
if (Tok.isNot(tok::l_paren) && peekToken().is(tok::r_paren)) {
consumeToken();
consumeToken(tok::r_paren);
}
}
return false;
}
AttrRange = SourceRange(Loc, Tok.getLoc());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
DuplicateAttribute = Attributes.getAttribute<SetterAccessibilityAttr>();
if (!DuplicateAttribute) {
Attributes.add(new (Context) SetterAccessibilityAttr(AtLoc, AttrRange,
access));
}
break;
}
case DAK_SILGenName: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (Tok.isNot(tok::string_literal)) {
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
return false;
}
Optional<StringRef> AsmName =
getStringLiteralIfNotInterpolated(*this, Loc, Tok, AttrName);
consumeToken(tok::string_literal);
if (AsmName.hasValue())
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
else
DiscardAttribute = true;
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
// Diagnose using @_silgen_name in a local scope. These don't
// actually work.
if (CurDeclContext->isLocalContext()) {
// Emit an error, but do not discard the attribute. This enables
// better recovery in the parser.
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
}
if (!DiscardAttribute)
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
AttrRange, /*Implicit=*/false));
break;
}
case DAK_Alignment: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (Tok.isNot(tok::integer_literal)) {
diagnose(Loc, diag::alignment_must_be_positive_integer);
return false;
}
StringRef alignmentText = Tok.getText();
unsigned alignmentValue;
if (alignmentText.getAsInteger(0, alignmentValue))
llvm_unreachable("not valid integer literal token?!");
consumeToken(tok::integer_literal);
auto range = SourceRange(Loc, Tok.getRange().getStart());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
Attributes.add(new (Context) AlignmentAttr(alignmentValue, AtLoc, range,
/*implicit*/ false));
break;
}
case DAK_SwiftNativeObjCRuntimeBase: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier);
return false;
}
Identifier name = Context.getIdentifier(Tok.getText());
consumeToken(tok::identifier);
auto range = SourceRange(Loc, Tok.getRange().getStart());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
Attributes.add(new (Context) SwiftNativeObjCRuntimeBaseAttr(name,
AtLoc, range, /*implicit*/ false));
break;
}
case DAK_Semantics: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (Tok.isNot(tok::string_literal)) {
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
return false;
}
auto Value = getStringLiteralIfNotInterpolated(*this, Loc, Tok, AttrName);
consumeToken(tok::string_literal);
if (Value.hasValue())
AttrRange = SourceRange(Loc, Tok.getRange().getStart());
else
DiscardAttribute = true;
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
// Diagnose using @_semantics in a local scope. These don't
// actually work.
if (CurDeclContext->isLocalContext()) {
// Emit an error, but do not discard the attribute. This enables
// better recovery in the parser.
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
}
if (!DiscardAttribute)
Attributes.add(new (Context) SemanticsAttr(Value.getValue(), AtLoc,
AttrRange,
/*Implicit=*/false));
break;
}
case DAK_Available: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
// platform:
// *
// identifier
if (!Tok.is(tok::identifier) &&
!(Tok.isAnyOperator() && Tok.getText() == "*")) {
if (Tok.is(tok::code_complete) && CodeCompletion) {
CodeCompletion->completeDeclAttrParam(DAK_Available, 0);
consumeToken(tok::code_complete);
}
diagnose(Tok.getLoc(), diag::attr_availability_platform, AttrName)
.highlight(SourceRange(Tok.getLoc()));
consumeIf(tok::r_paren);
return false;
}
// Delay processing of platform until later, after we have
// parsed more of the attribute.
StringRef Platform = Tok.getText();
if (Platform != "*" &&
peekToken().isAny(tok::integer_literal, tok::floating_literal)) {
// We have the short form of available: @available(iOS 8.0.1, *)
SmallVector<AvailabilitySpec *, 5> Specs;
ParserStatus Status = parseAvailabilitySpecList(Specs);
if (Status.isError())
return false;
AttrRange = SourceRange(Loc, Tok.getLoc());
// For each platform version spec in the spec list, create an
// implicit AvailableAttr for the platform with the introduced
// version from the spec. For example, if we have
// @available(iOS 8.0, OSX 10.10, *):
// we will synthesize:
// @available(iOS, introduced=8.0)
// @available(OSX, introduced=10.10)
for (auto *Spec : Specs) {
auto *VersionSpec = dyn_cast<VersionConstraintAvailabilitySpec>(Spec);
if (!VersionSpec)
continue;
Attributes.add(new (Context)
AvailableAttr(AtLoc, AttrRange,
VersionSpec->getPlatform(),
/*Message=*/StringRef(),
/*Renamed=*/StringRef(),
/*Introduced=*/VersionSpec->getVersion(),
/*Deprecated=*/clang::VersionTuple(),
/*Obsoleted=*/clang::VersionTuple(),
UnconditionalAvailabilityKind::None,
/*Implicit=*/true));
}
if (!consumeIf(tok::r_paren)) {
diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
break;
}
consumeToken();
StringRef Message, Renamed;
clang::VersionTuple Introduced, Deprecated, Obsoleted;
auto Unconditional = UnconditionalAvailabilityKind::None;
bool AnyAnnotations = false;
int ParamIndex = 0;
while (consumeIf(tok::comma)) {
AnyAnnotations = true;
StringRef ArgumentKindStr = Tok.getText();
ParamIndex ++;
enum {
IsMessage, IsRenamed,
IsIntroduced, IsDeprecated, IsObsoleted,
IsUnavailable,
IsInvalid
} ArgumentKind = IsInvalid;
if (Tok.is(tok::identifier)) {
ArgumentKind =
llvm::StringSwitch<decltype(ArgumentKind)>(ArgumentKindStr)
.Case("message", IsMessage)
.Case("renamed", IsRenamed)
.Case("introduced", IsIntroduced)
.Case("deprecated", IsDeprecated)
.Case("obsoleted", IsObsoleted)
.Case("unavailable", IsUnavailable)
.Default(IsInvalid);
}
if (ArgumentKind == IsInvalid) {
DiscardAttribute = true;
diagnose(Tok.getLoc(), diag::attr_availability_expected_option,
AttrName)
.highlight(SourceRange(Tok.getLoc()));
if (Tok.is(tok::code_complete) && CodeCompletion) {
CodeCompletion->completeDeclAttrParam(DAK_Available, ParamIndex);
consumeToken(tok::code_complete);
} else {
consumeIf(tok::identifier);
}
break;
}
consumeToken();
switch (ArgumentKind) {
case IsMessage:
case IsRenamed: {
// Items with string arguments.
if (!consumeIf(tok::equal)) {
diagnose(Tok, diag::attr_availability_expected_equal,
AttrName, ArgumentKindStr);
DiscardAttribute = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
continue;
}
if (!Tok.is(tok::string_literal)) {
diagnose(Loc, diag::attr_expected_string_literal, AttrName);
DiscardAttribute = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
continue;
}
auto Value =
getStringLiteralIfNotInterpolated(*this, Loc, Tok, ArgumentKindStr);
consumeToken();
if (!Value) {
DiscardAttribute = true;
continue;
}
StringRef &ArgField = ((ArgumentKind == IsMessage) ? Message : Renamed);
ArgField = Value.getValue();
break;
}
case IsDeprecated:
if (Tok.isNot(tok::equal)) {
if (Unconditional != UnconditionalAvailabilityKind::None) {
diagnose(Tok, diag::attr_availability_unavailable_deprecated,
AttrName);
}
Unconditional = UnconditionalAvailabilityKind::Deprecated;
break;
}
SWIFT_FALLTHROUGH;
case IsIntroduced:
case IsObsoleted: {
// Items with version arguments.
if (!consumeIf(tok::equal)) {
diagnose(Tok, diag::attr_availability_expected_equal,
AttrName, ArgumentKindStr);
DiscardAttribute = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
continue;
}
auto &VersionArg = (ArgumentKind == IsIntroduced) ? Introduced :
(ArgumentKind == IsDeprecated) ? Deprecated :
Obsoleted;
SourceRange VersionRange;
if (parseVersionTuple(
VersionArg, VersionRange,
Diagnostic(diag::attr_availability_expected_version,
AttrName))) {
DiscardAttribute = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
}
break;
}
case IsUnavailable:
if (Unconditional != UnconditionalAvailabilityKind::None) {
diagnose(Tok, diag::attr_availability_unavailable_deprecated,
AttrName);
}
Unconditional = UnconditionalAvailabilityKind::Unavailable;
break;
case IsInvalid:
llvm_unreachable("handled above");
}
}
if (!AnyAnnotations) {
diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName,
DeclAttribute::isDeclModifier(DK));
DiscardAttribute = true;
}
AttrRange = SourceRange(Loc, Tok.getLoc());
if (!consumeIf(tok::r_paren)) {
if (!DiscardAttribute) {
diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
}
return false;
}
if (!DiscardAttribute) {
auto PlatformKind = platformFromString(Platform);
if (PlatformKind.hasValue()) {
Attributes.add(new (Context)
AvailableAttr(AtLoc, AttrRange,
PlatformKind.getValue(),
Message, Renamed,
Introduced,
Deprecated,
Obsoleted,
Unconditional,
/*Implicit=*/false));
} else {
// Not a known platform. Just drop the attribute.
diagnose(Loc, diag::attr_availability_unknown_platform,
Platform, AttrName);
return false;
}
}
break;
}
case DAK_ObjC: {
// Unnamed @objc attribute.
if (Tok.isNot(tok::l_paren)) {
auto attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc);
Attributes.add(attr);
break;
}
// Parse the leading '('.
SourceLoc LParenLoc = consumeToken(tok::l_paren);
// Parse the names, with trailing colons (if there are present).
SmallVector<Identifier, 4> Names;
SmallVector<SourceLoc, 4> NameLocs;
bool sawColon = false;
while (true) {
// Empty selector piece.
if (Tok.is(tok::colon)) {
Names.push_back(Identifier());
NameLocs.push_back(Tok.getLoc());
sawColon = true;
consumeToken();
continue;
}
// Name.
if (Tok.is(tok::identifier) || Tok.isKeyword()) {
Names.push_back(Context.getIdentifier(Tok.getText()));
NameLocs.push_back(Tok.getLoc());
consumeToken();
// If we have a colon, consume it.
if (Tok.is(tok::colon)) {
consumeToken();
sawColon = true;
continue;
}
// If we see a closing parentheses, we're done.
if (Tok.is(tok::r_paren)) {
// If we saw more than one identifier, there's a ':'
// missing here. Complain and pretend we saw it.
if (Names.size() > 1) {
diagnose(Tok, diag::attr_objc_missing_colon)
.fixItInsertAfter(NameLocs.back(), ":");
sawColon = true;
}
break;
}
// If we see another identifier or keyword, complain about
// the missing colon and keep going.
if (Tok.is(tok::identifier) || Tok.isKeyword()) {
diagnose(Tok, diag::attr_objc_missing_colon)
.fixItInsertAfter(NameLocs.back(), ":");
sawColon = true;
continue;
}
// We don't know what happened. Break out.
break;
}
break;
}
// Parse the matching ')'.
SourceLoc RParenLoc;
bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc,
diag::attr_objc_expected_rparen,
LParenLoc);
ObjCAttr *attr;
if (Names.empty()) {
// When there are no names, recover as if there were no parentheses.
if (!Invalid)
diagnose(LParenLoc, diag::attr_objc_empty_name);
attr = ObjCAttr::createUnnamed(Context, AtLoc, Loc);
} else if (!sawColon) {
// When we didn't see a colon, this is a nullary name.
assert(Names.size() == 1 && "Forgot to set sawColon?");
attr = ObjCAttr::createNullary(Context, AtLoc, Loc, LParenLoc,
NameLocs.front(), Names.front(),
RParenLoc);
} else {
// When we did see a colon, this is a selector.
attr = ObjCAttr::createSelector(Context, AtLoc, Loc, LParenLoc,
NameLocs, Names, RParenLoc);
}
Attributes.add(attr);
break;
}
case DAK_WarnUnusedResult: {
// @warn_unused_result with no arguments.
if (Tok.isNot(tok::l_paren)) {
auto attr = new (Context) WarnUnusedResultAttr(AtLoc, Loc, false);
Attributes.add(attr);
break;
}
// @warn_unused_result with arguments.
StringRef message;
StringRef mutableVariant;
SourceLoc lParenLoc = consumeToken();
bool invalid = false;
do {
// If we see a closing parenthesis,
if (Tok.is(tok::r_paren))
break;
if (Tok.isNot(tok::identifier)) {
diagnose(Tok, diag::attr_warn_unused_result_expected_name);
if (Tok.isNot(tok::r_paren))
skipUntil(tok::r_paren);
invalid = true;
break;
}
// Consume the identifier.
StringRef name = Tok.getText();
SourceLoc nameLoc = consumeToken();
// Figure out which parameter it is.
enum class KnownParameter {
Message,
MutableVariant
};
Optional<KnownParameter> known
= llvm::StringSwitch<Optional<KnownParameter>>(name)
.Case("message", KnownParameter::Message)
.Case("mutable_variant", KnownParameter::MutableVariant)
.Default(None);
// If we don't have the '=', complain.
SourceLoc equalLoc;
if (!consumeIf(tok::equal, equalLoc)) {
if (known)
diagnose(Tok, diag::attr_warn_unused_result_expected_eq, name);
else
diagnose(Tok, diag::attr_warn_unused_result_unknown_parameter, name);
continue;
}
// If we don't have a string, complain.
if (Tok.isNot(tok::string_literal)) {
diagnose(Tok, diag::attr_warn_unused_result_expected_string, name);
if (Tok.isNot(tok::r_paren))
skipUntil(tok::r_paren);
invalid = true;
break;
}
// Dig out the string.
auto string = getStringLiteralIfNotInterpolated(*this, nameLoc, Tok,
name.str());
consumeToken(tok::string_literal);
if (!string) {
continue;
}
if (!known) {
diagnose(nameLoc, diag::attr_warn_unused_result_unknown_parameter,
name);
continue;
}
StringRef *whichParam;
switch (*known) {
case KnownParameter::Message:
whichParam = &message;
break;
case KnownParameter::MutableVariant:
whichParam = &mutableVariant;
break;
}
if (!whichParam->empty()) {
diagnose(nameLoc, diag::attr_warn_unused_result_duplicate_parameter,
name);
}
*whichParam = *string;
} while (consumeIf(tok::comma));
// Parse the closing ')'.
SourceLoc rParenLoc;
if (Tok.isNot(tok::r_paren)) {
parseMatchingToken(tok::r_paren, rParenLoc,
diag::attr_warn_unused_result_expected_rparen,
lParenLoc);
}
if (Tok.is(tok::r_paren)) {
rParenLoc = consumeToken();
}
Attributes.add(new (Context) WarnUnusedResultAttr(AtLoc, Loc, lParenLoc,
message, mutableVariant,
rParenLoc, false));
break;
}
case DAK_MigrationId: {
if (Tok.isNot(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
StringRef ident;
StringRef patternId;
SourceLoc lParenLoc = consumeToken();
// If we don't have a string, complain.
if (Tok.isNot(tok::string_literal)) {
diagnose(Tok, diag::attr_expected_string_literal, AttrName);
if (Tok.isNot(tok::r_paren))
skipUntil(tok::r_paren);
consumeIf(tok::r_paren);
return false;
}
// Dig out the string.
auto string = getStringLiteralIfNotInterpolated(*this, Loc, Tok,
AttrName);
consumeToken(tok::string_literal);
if (!string)
return false;
ident = string.getValue();
consumeIf(tok::comma);
bool invalid = false;
do {
// If we see a closing parenthesis,
if (Tok.is(tok::r_paren))
break;
if (Tok.isNot(tok::identifier)) {
diagnose(Tok, diag::attr_migration_id_expected_name);
if (Tok.isNot(tok::r_paren))
skipUntil(tok::r_paren);
consumeIf(tok::r_paren);
invalid = true;
break;
}
// Consume the identifier.
StringRef name = Tok.getText();
SourceLoc nameLoc = consumeToken();
bool known = (name == "pattern");
// If we don't have the '=', complain.
SourceLoc equalLoc;
if (!consumeIf(tok::equal, equalLoc)) {
if (known)
diagnose(Tok, diag::attr_migration_id_expected_eq, name);
else
diagnose(Tok, diag::attr_migration_id_unknown_parameter, name);
continue;
}
// If we don't have a string, complain.
if (Tok.isNot(tok::string_literal)) {
diagnose(Tok, diag::attr_migration_id_expected_string, name);
if (Tok.isNot(tok::r_paren))
skipUntil(tok::r_paren);
consumeIf(tok::r_paren);
invalid = true;
break;
}
// Dig out the string.
auto param_string = getStringLiteralIfNotInterpolated(*this, nameLoc, Tok,
name.str());
consumeToken(tok::string_literal);
if (!param_string) {
continue;
}
if (!known) {
diagnose(nameLoc, diag::attr_migration_id_unknown_parameter,
name);
continue;
}
if (!patternId.empty()) {
diagnose(nameLoc, diag::attr_migration_id_duplicate_parameter,
name);
}
patternId = param_string.getValue();
} while (consumeIf(tok::comma));
// Parse the closing ')'.
SourceLoc rParenLoc;
if (Tok.isNot(tok::r_paren)) {
parseMatchingToken(tok::r_paren, rParenLoc,
diag::attr_migration_id_expected_rparen,
lParenLoc);
}
if (Tok.is(tok::r_paren)) {
rParenLoc = consumeToken();
}
Attributes.add(new (Context) MigrationIdAttr(AtLoc, Loc, lParenLoc,
ident, patternId,
rParenLoc, false));
break;
}
}
if (DuplicateAttribute) {
diagnose(Loc, diag::duplicate_attribute, DeclAttribute::isDeclModifier(DK))
.highlight(AttrRange);
diagnose(DuplicateAttribute->getLocation(), diag::previous_attribute, DeclAttribute::isDeclModifier(DK))
.highlight(DuplicateAttribute->getRange());
}
// If this is a decl modifier spelled with an @, emit an error and remove it
// with a fixit.
if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK))
diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc);
return false;
}
bool Parser::parseVersionTuple(clang::VersionTuple &Version,
SourceRange &Range,
const Diagnostic &D) {
// A version number is either an integer (8), a float (8.1), or a
// float followed by a dot and an integer (8.1.0).
if (!Tok.isAny(tok::integer_literal, tok::floating_literal)) {
diagnose(Tok, D);
return true;
}
SourceLoc StartLoc = Tok.getLoc();
if (Tok.is(tok::integer_literal)) {
unsigned major = 0;
if (Tok.getText().getAsInteger(10, major)) {
// Maybe the literal was in hex. Reject that.
diagnose(Tok, D);
consumeToken();
return true;
}
Version = clang::VersionTuple(major);
Range = SourceRange(StartLoc, Tok.getLoc());
consumeToken();
return false;
}
unsigned major = 0, minor = 0;
StringRef majorPart, minorPart;
std::tie(majorPart, minorPart) = Tok.getText().split('.');
if (majorPart.getAsInteger(10, major) || minorPart.getAsInteger(10, minor)) {
// Reject things like 0.1e5 and hex literals.
diagnose(Tok, D);
consumeToken();
return true;
}
Range = SourceRange(StartLoc, Tok.getLoc());
consumeToken();
if (consumeIf(tok::period)) {
unsigned micro = 0;
if (!Tok.is(tok::integer_literal) ||
Tok.getText().getAsInteger(10, micro)) {
// Reject things like 0.1e5 and hex literals.
diagnose(Tok, D);
if (Tok.is(tok::integer_literal) ||
peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
return true;
}
Range = SourceRange(StartLoc, Tok.getLoc());
consumeToken();
Version = clang::VersionTuple(major, minor, micro);
} else {
Version = clang::VersionTuple(major, minor);
}
return false;
}
/// \verbatim
/// attribute:
/// '_silgen_name' '(' identifier ')'
/// 'semantics' '(' identifier ')'
/// 'infix' '=' numeric_constant
/// 'unary'
/// 'stdlib'
/// 'weak'
/// 'inout'
/// 'unowned'
/// 'unowned' '(' 'safe' ')'
/// 'unowned' '(' 'unsafe' ')'
/// 'noreturn'
/// 'optional'
/// 'mutating'
/// ( 'private' | 'internal' | 'public' )
/// ( 'private' | 'internal' | 'public' ) '(' 'set' ')'
/// 'requires_stored_property_inits'
/// \endverbatim
///
/// Note that various attributes (like mutating, weak, and unowned) are parsed
/// but rejected since they have context-sensitive keywords.
///
bool Parser::parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc) {
// If this not an identifier, the attribute is malformed.
if (Tok.isNot(tok::identifier) &&
Tok.isNot(tok::kw_in) &&
Tok.isNot(tok::kw_inout)) {
diagnose(Tok, diag::expected_attribute_name);
return true;
}
// If the attribute follows the new representation, switch
// over to the alternate parsing path.
DeclAttrKind DK = DeclAttribute::getAttrKindFromString(Tok.getText());
if (DK == DAK_Count && Tok.getText() == "availability") {
// We renamed @availability to @available, so if we see the former,
// treat it as the latter and emit a Fix-It.
DK = DAK_Available;
diagnose(Tok, diag::attr_availability_renamed)
.fixItReplace(Tok.getLoc(), "available");
}
if (DK != DAK_Count && !DeclAttribute::shouldBeRejectedByParser(DK))
return parseNewDeclAttribute(Attributes, AtLoc, DK);
if (TypeAttributes::getAttrKindFromString(Tok.getText()) != TAK_Count)
diagnose(Tok, diag::type_attribute_applied_to_decl);
else
diagnose(Tok, diag::unknown_attribute, Tok.getText());
// Recover by eating @foo when foo is not known.
consumeToken();
return true;
}
bool Parser::canParseTypeAttribute() {
TypeAttributes attrs; // ignored
return !parseTypeAttribute(attrs, /*justChecking*/ true);
}
/// \verbatim
/// attribute-type:
/// 'noreturn'
/// \endverbatim
///
/// \param justChecking - if true, we're just checking whether we
/// canParseTypeAttribute; don't emit any diagnostics, and there's
/// no need to actually record the attribute
bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
// If this not an identifier, the attribute is malformed.
if (Tok.isNot(tok::identifier) &&
// These are keywords that we accept as attribute names.
Tok.isNot(tok::kw_in) && Tok.isNot(tok::kw_inout)) {
if (!justChecking)
diagnose(Tok, diag::expected_attribute_name);
return true;
}
// Determine which attribute it is, and diagnose it if unknown.
TypeAttrKind attr = TypeAttributes::getAttrKindFromString(Tok.getText());
// noescape is only valid as a type attribute in SIL mode.
if (attr == TAK_noescape && !isInSILMode())
attr = TAK_Count;
if (attr == TAK_Count) {
if (justChecking) return true;
auto declAttrID = DeclAttribute::getAttrKindFromString(Tok.getText());
if (declAttrID == DAK_Count) {
// Not a decl or type attribute.
diagnose(Tok, diag::unknown_attribute, Tok.getText());
} else {
// Otherwise this is a valid decl attribute so they should have put it on
// the decl instead of the type.
// If this is the first attribute, and if we are on a simple decl, emit a
// fixit to move the attribute. Otherwise, we don't have the location of
// the @ sign, or we don't have confidence that the fixit will be right.
if (!Attributes.empty() || StructureMarkers.empty() ||
StructureMarkers.back().Kind != StructureMarkerKind::Declaration ||
StructureMarkers.back().Loc.isInvalid() ||
peekToken().is(tok::equal)) {
diagnose(Tok, diag::decl_attribute_applied_to_type);
} else {
// Otherwise, this is the first type attribute and we know where the
// declaration is. Emit the same diagnostic, but include a fixit to
// move the attribute. Unfortunately, we don't have enough info to add
// the attribute to DeclAttributes.
diagnose(Tok, diag::decl_attribute_applied_to_type)
.fixItRemove(SourceRange(Attributes.AtLoc, Tok.getLoc()))
.fixItInsert(StructureMarkers.back().Loc,
"@" + Tok.getText().str()+" ");
}
}
// Recover by eating @foo when foo is not known.
consumeToken();
// Recovery by eating "@foo=bar" if present.
if (consumeIf(tok::equal)) {
if (Tok.is(tok::identifier) ||
Tok.is(tok::integer_literal) ||
Tok.is(tok::floating_literal))
consumeToken();
}
return true;
}
// Ok, it is a valid attribute, eat it, and then process it.
StringRef Text = Tok.getText();
SourceLoc Loc = consumeToken();
// Diagnose duplicated attributes.
if (justChecking) {
// do nothing
} else if (Attributes.has(attr)) {
diagnose(Loc, diag::duplicate_attribute, /*isModifier=*/false);
} else {
Attributes.setAttr(attr, Loc);
}
// Handle any attribute-specific processing logic.
// In just-checking mode, we only need additional parsing for the "cc"
// attribute. (Note that we're never in just-checking mode in SIL mode.)
if (justChecking)
return false;
switch (attr) {
default: break;
case TAK_local_storage:
case TAK_out:
case TAK_in:
case TAK_owned:
case TAK_unowned_inner_pointer:
case TAK_guaranteed:
case TAK_deallocating:
case TAK_autoreleased:
case TAK_callee_owned:
case TAK_callee_guaranteed:
case TAK_objc_metatype:
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, Text);
Attributes.clearAttribute(attr);
}
break;
// Ownership attributes.
case TAK_sil_weak:
case TAK_sil_unowned:
Attributes.clearAttribute(attr);
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, "local_storage");
return false;
}
if (Attributes.hasOwnership()) {
diagnose(Loc, diag::duplicate_attribute, /*isModifier*/false);
break;
}
if (!justChecking)
Attributes.setAttr(attr, Loc);
break;
// 'inout' attribute.
case TAK_inout:
if (!isInSILMode()) {
diagnose(Loc, diag::inout_not_attribute);
return false;
}
break;
case TAK_opened: {
// Parse the opened existential ID string in parens
SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc;
Attributes.setAttr(TAK_opened, beginLoc);
if (consumeIfNotAtStartOfLine(tok::l_paren)) {
if (Tok.is(tok::string_literal)) {
UUID openedID;
idLoc = Tok.getLoc();
auto literalText = Tok.getText().slice(1, Tok.getText().size() - 1);
llvm::SmallString<UUID::StringBufferSize> text(literalText);
if (auto openedID = UUID::fromString(text.c_str())) {
Attributes.OpenedID = openedID;
} else {
diagnose(Tok, diag::opened_attribute_id_value);
}
consumeToken();
} else {
diagnose(Tok, diag::opened_attribute_id_value);
}
parseMatchingToken(tok::r_paren, endLoc,
diag::opened_attribute_expected_rparen,
beginLoc);
} else {
diagnose(Tok, diag::opened_attribute_expected_lparen);
}
if (!isInSILMode()) {
diagnose(Loc, diag::only_allowed_in_sil, "opened");
Attributes.clearAttribute(TAK_opened);
}
break;
}
// Convention attribute.
case TAK_convention: {
// Parse the convention name in parens.
SourceLoc beginLoc = Tok.getLoc(), nameLoc, endLoc;
StringRef name;
if (consumeIfNotAtStartOfLine(tok::l_paren)) {
if (Tok.is(tok::identifier)) {
nameLoc = Tok.getLoc();
name = Tok.getText();
consumeToken();
} else if (!justChecking) {
diagnose(Tok, diag::convention_attribute_expected_name);
}
// Parse the ')'. We can't use parseMatchingToken if we're in
// just-checking mode.
if (!justChecking) {
parseMatchingToken(tok::r_paren, endLoc,
diag::convention_attribute_expected_rparen,
beginLoc);
} else if (!consumeIf(tok::r_paren)) {
return true;
}
} else if (!justChecking) {
diagnose(Tok, diag::convention_attribute_expected_lparen);
}
// Don't validate the CC in just-checking mode.
if (justChecking) return false;
if (!name.empty())
Attributes.convention = name;
return false;
}
}
return false;
}
/// \verbatim
/// attribute-list:
/// /*empty*/
/// attribute-list-clause attribute-list
/// attribute-list-clause:
/// '@' attribute
/// \endverbatim
bool Parser::parseDeclAttributeList(DeclAttributes &Attributes,
bool &FoundCCToken,
bool StopAtTypeAttributes,
bool InParam) {
FoundCCToken = false;
while (Tok.is(tok::at_sign)) {
if (peekToken().is(tok::code_complete)) {
consumeToken(tok::at_sign);
consumeToken(tok::code_complete);
FoundCCToken = true;
continue;
}
SourceLoc AtLoc = Tok.getLoc();
// If StopAtTypeAttributes is true, then we sniff to see if the following
// attribute is a type attribute. If so, we stop here, instead of producing
// an error on it.
if (StopAtTypeAttributes) {
Token next = peekToken();
auto Kind = TypeAttributes::getAttrKindFromString(next.getText());
// noescape is only valid as a decl attribute and type attribute (in SIL
// mode) but we disambiguate it as a decl attribute.
if (Kind == TAK_noescape)
Kind = TAK_Count;
if (Kind != TAK_Count)
return false;
}
consumeToken();
if (parseDeclAttribute(Attributes, AtLoc))
return true;
}
return false;
}
/// \brief This is the internal implementation of \c parseTypeAttributeList,
/// which we expect to be inlined to handle the common case of an absent
/// attribute list.
///
/// \verbatim
/// attribute-list:
/// /*empty*/
/// attribute-list-clause attribute-list
/// attribute-list-clause:
/// '@' attribute
/// '@' attribute ','? attribute-list-clause
/// \endverbatim
bool Parser::parseTypeAttributeListPresent(TypeAttributes &Attributes) {
Attributes.AtLoc = Tok.getLoc();
do {
if (parseToken(tok::at_sign, diag::expected_in_attribute_list) ||
parseTypeAttribute(Attributes))
return true;
} while (Tok.is(tok::at_sign));
return false;
}
static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) {
return Tok.isContextualKeyword("operator") &&
(Tok2.isContextualKeyword("prefix") ||
Tok2.isContextualKeyword("postfix") ||
Tok2.isContextualKeyword("infix"));
}
static bool isKeywordPossibleDeclStart(const Token &Tok) {
switch (Tok.getKind()) {
case tok::at_sign:
case tok::kw_case:
case tok::kw_class:
case tok::kw_deinit:
case tok::kw_enum:
case tok::kw_extension:
case tok::kw_func:
case tok::kw_import:
case tok::kw_init:
case tok::kw_internal:
case tok::kw_let:
case tok::kw_operator:
case tok::kw_private:
case tok::kw_protocol:
case tok::kw_public:
case tok::kw_static:
case tok::kw_struct:
case tok::kw_subscript:
case tok::kw_typealias:
case tok::kw_var:
case tok::pound_if:
case tok::pound_line:
case tok::identifier:
return true;
case tok::kw_try:
// 'try' is not a valid way to start a decl, but we special-case 'try let'
// and 'try var' for better recovery.
return true;
default:
return false;
}
}
/// Given a current token of 'unowned', check to see if it is followed by a
/// "(safe)" or "(unsafe)" specifier.
static bool isParenthesizedUnowned(Parser &P) {
assert(P.Tok.getText() == "unowned" && P.peekToken().is(tok::l_paren) &&
"Invariant violated");
// Look ahead to parse the parenthesized expression.
Parser::BacktrackingScope Backtrack(P);
P.consumeToken(tok::identifier);
P.consumeToken(tok::l_paren);
return P.Tok.is(tok::identifier) && P.peekToken().is(tok::r_paren) &&
(P.Tok.getText() == "safe" || P.Tok.getText() == "unsafe");
}
bool Parser::isStartOfDecl() {
// If this is obviously not the start of a decl, then we're done.
if (!isKeywordPossibleDeclStart(Tok)) return false;
// The protocol keyword needs more checking to reject "protocol<Int>".
if (Tok.is(tok::kw_protocol)) {
const Token &Tok2 = peekToken();
return !Tok2.isAnyOperator() || !Tok2.getText().equals("<");
}
// The 'try' case is only for simple local recovery, so we only bother to
// check 'let' and 'var' right now.
if (Tok.is(tok::kw_try))
return peekToken().isAny(tok::kw_let, tok::kw_var);
// Otherwise, the only hard case left is the identifier case.
if (Tok.isNot(tok::identifier)) return true;
// If this is an operator declaration, handle it.
const Token &Tok2 = peekToken();
if (isStartOfOperatorDecl(Tok, Tok2))
return true;
// If this can't possibly be a contextual keyword, then this identifier is
// not interesting. Bail out.
if (!Tok.isContextualDeclKeyword())
return false;
// If it might be, we do some more digging.
// If this is 'unowned', check to see if it is valid.
if (Tok.getText() == "unowned" && Tok2.is(tok::l_paren) &&
isParenthesizedUnowned(*this)) {
Parser::BacktrackingScope Backtrack(*this);
consumeToken(tok::identifier);
consumeToken(tok::l_paren);
consumeToken(tok::identifier);
consumeToken(tok::r_paren);
return isStartOfDecl();
}
// If the next token is obviously not the start of a decl, bail early.
if (!isKeywordPossibleDeclStart(Tok2))
return false;
// Otherwise, do a recursive parse.
Parser::BacktrackingScope Backtrack(*this);
consumeToken(tok::identifier);
return isStartOfDecl();
}
void Parser::consumeDecl(ParserPosition BeginParserPosition,
ParseDeclOptions Flags,
bool IsTopLevel) {
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);
SourceLoc EndLoc = DelayedDeclEnd.isValid() &&
SourceMgr.isBeforeInBuffer(Tok.getLoc(), DelayedDeclEnd) ?
DelayedDeclEnd : Tok.getLoc();
State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, Flags.toRaw(),
CurDeclContext, { BeginLoc, EndLoc },
BeginParserPosition.PreviousLoc);
if (IsTopLevel) {
// 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);
}
}
void Parser::setLocalDiscriminator(ValueDecl *D) {
// If we're not in a local context, this is unnecessary.
if (!CurLocalContext || !D->getDeclContext()->isLocalContext())
return;
if (auto TD = dyn_cast<TypeDecl>(D))
if (!getScopeInfo().isInactiveConfigBlock())
SF.LocalTypeDecls.push_back(TD);
Identifier name = D->getName();
unsigned discriminator = CurLocalContext->claimNextNamedDiscriminator(name);
D->setLocalDiscriminator(discriminator);
}
void Parser::delayParseFromBeginningToHere(ParserPosition BeginParserPosition,
ParseDeclOptions Flags) {
auto CurLoc = Tok.getLoc();
backtrackToPosition(BeginParserPosition);
SourceLoc BeginLoc = Tok.getLoc();
SourceLoc EndLoc = CurLoc;
State->delayDecl(PersistentParserState::DelayedDeclKind::Decl,
Flags.toRaw(),
CurDeclContext, {BeginLoc, EndLoc},
BeginParserPosition.PreviousLoc);
skipUntil(tok::eof);
}
/// \brief Parse a single syntactic declaration and return a list of decl
/// ASTs. This can return multiple results for var decls that bind to multiple
/// values, structs that define a struct decl and a constructor, etc.
///
/// \verbatim
/// decl:
/// decl-typealias
/// decl-extension
/// decl-let
/// decl-var
/// decl-class
/// decl-func
/// decl-enum
/// decl-struct
/// decl-import
/// decl-operator
/// \endverbatim
ParserStatus Parser::parseDecl(SmallVectorImpl<Decl*> &Entries,
ParseDeclOptions Flags) {
ParserPosition BeginParserPosition;
if (isCodeCompletionFirstPass())
BeginParserPosition = getParserPosition();
SourceLoc tryLoc;
(void)consumeIf(tok::kw_try, tryLoc);
// Note that we're parsing a declaration.
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
StructureMarkerKind::Declaration);
DeclAttributes Attributes;
IgnorePrivateDeclTokens IgnoreTokens(*this, Attributes);
if (Tok.hasComment())
Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
bool FoundCCTokenInAttr;
parseDeclAttributeList(Attributes, FoundCCTokenInAttr);
// Keep track of where and whether we see a contextual keyword on the decl.
SourceLoc StaticLoc;
StaticSpellingKind StaticSpelling = StaticSpellingKind::None;
ParserResult<Decl> DeclResult;
ParserStatus Status;
while (1) {
switch (Tok.getKind()) {
// Modifiers
case tok::kw_static:
if (StaticLoc.isValid()) {
diagnose(Tok, diag::decl_already_static,
StaticSpellingKind::KeywordStatic)
.highlight(StaticLoc)
.fixItRemove(Tok.getLoc());
} else {
StaticLoc = Tok.getLoc();
StaticSpelling = StaticSpellingKind::KeywordStatic;
}
consumeToken(tok::kw_static);
continue;
// 'class' is a modifier on func, but is also a top-level decl.
case tok::kw_class: {
SourceLoc ClassLoc = consumeToken(tok::kw_class);
// If 'class' is a modifier on another decl kind, like var or func,
// then treat it as a modifier.
if (isStartOfDecl()) {
if (StaticLoc.isValid()) {
diagnose(Tok, diag::decl_already_static,
StaticSpellingKind::KeywordClass)
.highlight(StaticLoc).fixItRemove(ClassLoc);
} else {
StaticLoc = ClassLoc;
StaticSpelling = StaticSpellingKind::KeywordClass;
}
continue;
}
// Otherwise this is the start of a class declaration.
DeclResult = parseDeclClass(ClassLoc, Flags, Attributes);
Status = DeclResult;
break;
}
case tok::kw_private:
case tok::kw_internal:
case tok::kw_public:
// We still model these specifiers as attributes.
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Accessibility);
continue;
// Context sensitive keywords.
case tok::identifier:
// FIXME: This is ridiculous, this all needs to be sucked into the
// declparsing goop.
if (Tok.isContextualKeyword("weak") ||
Tok.isContextualKeyword("unowned")) {
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Ownership);
continue;
}
if (Tok.isContextualKeyword("optional")) {
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Optional);
continue;
}
if (Tok.isContextualKeyword("required")) {
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Required);
continue;
}
if (Tok.isContextualKeyword("lazy")) {
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Lazy);
continue;
}
if (Tok.isContextualKeyword("final")) {
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_Final);
continue;
}
if (Tok.isContextualKeyword("dynamic")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Dynamic);
continue;
}
if (Tok.isContextualKeyword("prefix")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Prefix);
continue;
}
if (Tok.isContextualKeyword("postfix")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Postfix);
continue;
}
if (Tok.isContextualKeyword("indirect")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Indirect);
continue;
}
if (Tok.isContextualKeyword("infix")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Infix);
continue;
}
if (Tok.isContextualKeyword("override")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Override);
continue;
}
if (Tok.isContextualKeyword("mutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Mutating);
continue;
}
if (Tok.isContextualKeyword("nonmutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_NonMutating);
continue;
}
if (Tok.isContextualKeyword("convenience")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Convenience);
continue;
}
// Otherwise this is not a context-sensitive keyword.
SWIFT_FALLTHROUGH;
// Obvious nonsense.
default:
if (FoundCCTokenInAttr) {
if (!CodeCompletion) {
delayParseFromBeginningToHere(BeginParserPosition, Flags);
} else {
CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(),
false);
}
}
diagnose(Tok, diag::expected_decl);
return makeParserErrorResult<Decl>();
// Unambiguous top level decls.
case tok::kw_import:
DeclResult = parseDeclImport(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_extension:
DeclResult = parseDeclExtension(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_let:
case tok::kw_var:
Status = parseDeclVar(Flags, Attributes, Entries, StaticLoc,
StaticSpelling, tryLoc);
StaticLoc = SourceLoc(); // we handled static if present.
break;
case tok::kw_typealias:
DeclResult = parseDeclTypeAlias(!(Flags & PD_DisallowTypeAliasDef),
Flags.contains(PD_InProtocol),
Attributes);
Status = DeclResult;
break;
case tok::kw_enum:
DeclResult = parseDeclEnum(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_case:
Status = parseDeclEnumCase(Flags, Attributes, Entries);
break;
case tok::kw_struct:
DeclResult = parseDeclStruct(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_init:
DeclResult = parseDeclInit(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_deinit:
DeclResult = parseDeclDeinit(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_operator:
DeclResult = parseDeclOperator(Flags, Attributes);
Status = DeclResult;
break;
case tok::kw_protocol:
DeclResult = parseDeclProtocol(Flags, Attributes);
Status = DeclResult;
break;
case tok::pound_if: {
auto IfConfigResult = parseDeclIfConfig(Flags);
Status = IfConfigResult;
if (auto ICD = IfConfigResult.getPtrOrNull()) {
// The IfConfigDecl is ahead of its members in source order.
Entries.push_back(ICD);
// Copy the active members into the entries list.
for (auto activeMember : ICD->getActiveMembers()) {
Entries.push_back(activeMember);
}
}
break;
}
case tok::pound_line:
Status = parseLineDirective();
break;
case tok::kw_func:
DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes);
Status = DeclResult;
StaticLoc = SourceLoc(); // we handled static if present.
break;
case tok::kw_subscript:
if (StaticLoc.isValid()) {
diagnose(Tok, diag::subscript_static, StaticSpelling)
.fixItRemove(SourceRange(StaticLoc));
StaticLoc = SourceLoc();
}
Status = parseDeclSubscript(Flags, Attributes, Entries);
break;
case tok::code_complete:
Status = makeParserCodeCompletionStatus();
if (CodeCompletion) {
// if we need to complete an override, we need to collect which keywords
// have already been specified by the developer; so that we do not
// duplicate them in code completion strings
SmallVector<StringRef, 3> Keywords;
// FIXME: need to handle the case where this line contains multiple decls
backtrackToPosition(ParserPosition(L->getStateForBeginningOfTokenLoc(
Lexer::getLocForStartOfLine(SourceMgr, Tok.getLoc())), SourceLoc()));
while (!Tok.is(tok::code_complete)) {
Keywords.push_back(Tok.getText());
consumeToken();
}
CodeCompletion->completeNominalMemberBeginning(Keywords);
}
break;
}
// If we 'break' out of the switch, break out of the loop too.
break;
}
if (auto SF = CurDeclContext->getParentSourceFile()) {
for (auto Attr : Attributes) {
if (isa<ObjCAttr>(Attr) || isa<DynamicAttr>(Attr))
SF->AttrsRequiringFoundation.insert({Attr->getKind(), Attr});
}
}
if (FoundCCTokenInAttr) {
if (CodeCompletion) {
CodeCompletion->completeDeclAttrKeyword(DeclResult.getPtrOrNull(),
isInSILMode(),
false);
} else {
delayParseFromBeginningToHere(BeginParserPosition, Flags);
return makeParserSuccess();
}
}
if (Status.hasCodeCompletion() && isCodeCompletionFirstPass() &&
!CurDeclContext->isModuleScopeContext()) {
// Only consume non-toplevel decls.
consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false);
// Pretend that there was no error.
return makeParserSuccess();
}
if (DeclResult.isNonNull()) {
Decl *D = DeclResult.get();
if (!declWasHandledAlready(D))
Entries.push_back(DeclResult.get());
}
if (Tok.is(tok::semi)) {
SourceLoc TrailingSemiLoc = consumeToken(tok::semi);
if (Status.isSuccess())
Entries.back()->TrailingSemiLoc = TrailingSemiLoc;
}
if (Status.isSuccess()) {
// If we parsed 'class' or 'static', but didn't handle it above, complain
// about it.
if (StaticLoc.isValid())
diagnose(Entries.back()->getLoc(), diag::decl_not_static,
StaticSpelling)
.fixItRemove(SourceRange(StaticLoc));
}
return Status;
}
void Parser::parseDeclDelayed() {
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 decl.
restoreParserPosition(BeginParserPosition);
// Re-enter the lexical scope.
Scope S(this, DelayedState->takeScope());
ContextChange CC(*this, DelayedState->ParentContext);
SmallVector<Decl *, 2> Entries;
parseDecl(Entries, ParseDeclOptions(DelayedState->Flags));
}
/// \brief Parse an 'import' declaration, doing no token skipping on error.
///
/// \verbatim
/// decl-import:
/// 'import' attribute-list import-kind? import-path
/// import-kind:
/// 'typealias'
/// 'struct'
/// 'class'
/// 'enum'
/// 'protocol'
/// 'var'
/// 'func'
/// import-path:
/// any-identifier ('.' any-identifier)*
/// \endverbatim
ParserResult<ImportDecl> Parser::parseDeclImport(ParseDeclOptions Flags,
DeclAttributes &Attributes) {
SourceLoc ImportLoc = consumeToken(tok::kw_import);
DebuggerContextChange DCC (*this);
if (!CodeCompletion && !DCC.movedToTopLevel() && !(Flags & PD_AllowTopLevel)) {
diagnose(ImportLoc, diag::decl_inner_scope);
return nullptr;
}
ImportKind Kind = ImportKind::Module;
SourceLoc KindLoc;
if (Tok.isKeyword()) {
switch (Tok.getKind()) {
case tok::kw_typealias:
Kind = ImportKind::Type;
break;
case tok::kw_struct:
Kind = ImportKind::Struct;
break;
case tok::kw_class:
Kind = ImportKind::Class;
break;
case tok::kw_enum:
Kind = ImportKind::Enum;
break;
case tok::kw_protocol:
Kind = ImportKind::Protocol;
break;
case tok::kw_var:
case tok::kw_let:
Kind = ImportKind::Var;
break;
case tok::kw_func:
Kind = ImportKind::Func;
break;
default:
diagnose(Tok, diag::expected_identifier_in_decl, "import");
return nullptr;
}
KindLoc = consumeToken();
}
SmallVector<std::pair<Identifier, SourceLoc>, 8> ImportPath;
do {
if (Tok.is(tok::code_complete)) {
consumeToken();
if (CodeCompletion) {
CodeCompletion->completeImportDecl(ImportPath);
}
return makeParserCodeCompletionStatus();
}
ImportPath.push_back(std::make_pair(Identifier(), Tok.getLoc()));
if (parseAnyIdentifier(ImportPath.back().first,
diag::expected_identifier_in_decl, "import"))
return nullptr;
} while (consumeIf(tok::period));
if (Tok.is(tok::code_complete)) {
// We omit the code completion token if it immediately follows the module
// identifiers.
auto BufferId = SourceMgr.getCodeCompletionBufferID();
auto IdEndOffset = SourceMgr.getLocOffsetInBuffer(ImportPath.back().second,
BufferId) + ImportPath.back().first.str().size();
auto CCTokenOffset = SourceMgr.getLocOffsetInBuffer(SourceMgr.
getCodeCompletionLoc(), BufferId);
if (IdEndOffset == CCTokenOffset) {
consumeToken();
}
}
if (Kind != ImportKind::Module && ImportPath.size() == 1) {
diagnose(ImportPath.front().second, diag::decl_expected_module_name);
return nullptr;
}
auto *ID = ImportDecl::create(Context, CurDeclContext, ImportLoc, Kind,
KindLoc, ImportPath);
ID->getAttrs() = Attributes;
return DCC.fixupParserResult(ID);
}
/// \brief Parse an inheritance clause.
///
/// \verbatim
/// inheritance:
/// ':' inherited (',' inherited)*
///
/// inherited:
/// 'class'
/// type-identifier
/// \endverbatim
ParserStatus Parser::parseInheritance(SmallVectorImpl<TypeLoc> &Inherited,
SourceLoc *classRequirementLoc) {
consumeToken(tok::colon);
// Clear out the class requirement location.
if (classRequirementLoc)
*classRequirementLoc = SourceLoc();
ParserStatus Status;
SourceLoc prevComma;
do {
// Parse the 'class' keyword for a class requirement.
if (Tok.is(tok::kw_class)) {
// If we aren't allowed to have a class requirement here, complain.
auto classLoc = consumeToken();
if (!classRequirementLoc) {
SourceLoc endLoc = Tok.is(tok::comma) ? Tok.getLoc() : classLoc;
diagnose(classLoc, diag::invalid_class_requirement)
.fixItRemove(SourceRange(classLoc, endLoc));
continue;
}
// If we already saw a class requirement, complain.
if (classRequirementLoc->isValid()) {
diagnose(classLoc, diag::redundant_class_requirement)
.highlight(*classRequirementLoc)
.fixItRemove(SourceRange(prevComma, classLoc));
continue;
}
// If the class requirement was not the first requirement, complain.
if (!Inherited.empty()) {
SourceLoc properLoc = Inherited[0].getSourceRange().Start;
diagnose(classLoc, diag::late_class_requirement)
.fixItInsert(properLoc, "class, ")
.fixItRemove(SourceRange(prevComma, classLoc));
}
// Record the location of the 'class' keyword.
*classRequirementLoc = classLoc;
continue;
}
// Parse the inherited type (which must be a protocol).
ParserResult<TypeRepr> Ty = parseTypeIdentifier();
Status |= Ty;
// Record the type.
if (Ty.isNonNull())
Inherited.push_back(Ty.get());
// Check for a ',', which indicates that there are more protocols coming.
} while (consumeIf(tok::comma, prevComma));
return Status;
}
enum class TokenProperty {
None,
StartsWithLess,
};
static ParserStatus parseIdentifierDeclName(Parser &P, Identifier &Result,
SourceLoc &Loc, tok ResyncT1,
tok ResyncT2, tok ResyncT3,
tok ResyncT4,
TokenProperty ResyncP1,
const Diagnostic &D) {
switch (P.Tok.getKind()) {
case tok::identifier:
Result = P.Context.getIdentifier(P.Tok.getText());
Loc = P.Tok.getLoc();
P.consumeToken();
return makeParserSuccess();
default:
P.checkForInputIncomplete();
if (!D.is(diag::invalid_diagnostic))
P.diagnose(P.Tok, D);
if (P.Tok.isKeyword() &&
(P.peekToken().is(ResyncT1) || P.peekToken().is(ResyncT2) ||
P.peekToken().is(ResyncT3) || P.peekToken().is(ResyncT4) ||
(ResyncP1 != TokenProperty::None &&
P.startsWithLess(P.peekToken())))) {
llvm::SmallString<32> Name(P.Tok.getText());
// Append an invalid character so that nothing can resolve to this name.
Name += "#";
Result = P.Context.getIdentifier(Name.str());
Loc = P.Tok.getLoc();
P.consumeToken();
// Return success because we recovered.
return makeParserSuccess();
}
return makeParserError();
}
}
template <typename... DiagArgTypes, typename... ArgTypes>
static ParserStatus
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
tok ResyncT1, tok ResyncT2, Diag<DiagArgTypes...> ID,
ArgTypes... Args) {
return parseIdentifierDeclName(P, Result, L, ResyncT1, ResyncT2,
tok::unknown, tok::unknown,
TokenProperty::None,
Diagnostic(ID, Args...));
}
template <typename... DiagArgTypes, typename... ArgTypes>
static ParserStatus
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
tok ResyncT1, tok ResyncT2, tok ResyncT3,
Diag<DiagArgTypes...> ID, ArgTypes... Args) {
return parseIdentifierDeclName(P, Result, L, ResyncT1, ResyncT2, ResyncT3,
tok::unknown, TokenProperty::None,
Diagnostic(ID, Args...));
}
template <typename... DiagArgTypes, typename... ArgTypes>
static ParserStatus
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
tok ResyncT1, tok ResyncT2, tok ResyncT3, tok ResyncT4,
Diag<DiagArgTypes...> ID, ArgTypes... Args) {
return parseIdentifierDeclName(P, Result, L, ResyncT1, ResyncT2, ResyncT3,
ResyncT4, TokenProperty::None,
Diagnostic(ID, Args...));
}
template <typename... DiagArgTypes, typename... ArgTypes>
static ParserStatus
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
tok ResyncT1, tok ResyncT2, TokenProperty ResyncP1,
Diag<DiagArgTypes...> ID, ArgTypes... Args) {
return parseIdentifierDeclName(P, Result, L, ResyncT1, ResyncT2, tok::unknown,
tok::unknown,
ResyncP1, Diagnostic(ID, Args...));
}
/// \brief Parse an 'extension' declaration.
///
/// \verbatim
/// extension:
/// 'extension' attribute-list type inheritance? where-clause?
/// '{' decl* '}'
/// \endverbatim
ParserResult<ExtensionDecl>
Parser::parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes) {
SourceLoc ExtensionLoc = consumeToken(tok::kw_extension);
DebuggerContextChange DCC (*this);
// Parse the type being extended.
ParserStatus status;
ParserResult<TypeRepr> extendedType = parseType(diag::extension_type_expected);
status |= extendedType;
// Parse optional inheritance clause.
SmallVector<TypeLoc, 2> Inherited;
if (Tok.is(tok::colon))
status |= parseInheritance(Inherited, /*classRequirementLoc=*/nullptr);
// Parse the optional where-clause.
TrailingWhereClause *trailingWhereClause = nullptr;
if (Tok.is(tok::kw_where)) {
SourceLoc whereLoc;
SmallVector<RequirementRepr, 4> requirements;
if (!parseGenericWhereClause(whereLoc, requirements)) {
trailingWhereClause = TrailingWhereClause::create(Context, whereLoc,
requirements);
}
}
ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc,
extendedType.getPtrOrNull(),
Context.AllocateCopy(Inherited),
CurDeclContext,
trailingWhereClause);
ext->getAttrs() = Attributes;
SmallVector<Decl*, 8> MemberDecls;
SourceLoc LBLoc, RBLoc;
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_extension)) {
LBLoc = PreviousLoc;
RBLoc = LBLoc;
status.setIsParseError();
} else {
// Parse the body.
ContextChange CC(*this, ext);
Scope S(this, ScopeKind::Extension);
ParserStatus BodyStatus =
parseList(tok::r_brace, LBLoc, RBLoc, tok::semi, /*OptionalSep=*/true,
/*AllowSepAfterLast=*/false, diag::expected_rbrace_extension,
[&]() -> ParserStatus {
ParseDeclOptions Options(PD_HasContainerType |
PD_InExtension);
return parseDecl(MemberDecls, Options);
});
// Don't propagate the code completion bit from members: we cannot help
// code completion inside a member decl, and our callers cannot do
// anything about it either. But propagate the error bit.
if (BodyStatus.isError())
status.setIsParseError();
}
ext->setBraces({LBLoc, RBLoc});
for (auto member : MemberDecls)
ext->addMember(member);
if (!DCC.movedToTopLevel() && !(Flags & PD_AllowTopLevel)) {
diagnose(ExtensionLoc, diag::decl_inner_scope);
status.setIsParseError();
// Tell the type checker not to touch this extension.
ext->setInvalid();
}
return DCC.fixupParserResult(status, ext);
}
ParserStatus Parser::parseLineDirective() {
SourceLoc Loc = consumeToken(tok::pound_line);
bool WasInPoundLineEnvironment = InPoundLineEnvironment;
if (WasInPoundLineEnvironment) {
SourceMgr.closeVirtualFile(Loc);
InPoundLineEnvironment = false;
}
// #line\n returns to the main buffer.
if (Tok.isAtStartOfLine()) {
if (!WasInPoundLineEnvironment) {
diagnose(Tok, diag::unexpected_line_directive);
return makeParserError();
}
return makeParserSuccess();
}
// #line 42 "file.swift"\n
if (Tok.isNot(tok::integer_literal)) {
diagnose(Tok, diag::expected_line_directive_number);
return makeParserError();
}
unsigned StartLine = 0;
if (Tok.getText().getAsInteger(0, StartLine)) {
diagnose(Tok, diag::expected_line_directive_number);
return makeParserError();
}
if (StartLine == 0) {
diagnose(Tok, diag::line_directive_line_zero);
return makeParserError();
}
consumeToken();
if (Tok.isNot(tok::string_literal)) {
diagnose(Tok, diag::expected_line_directive_name);
return makeParserError();
}
auto Filename = getStringLiteralIfNotInterpolated(*this, Loc, Tok, "#line");
if (!Filename.hasValue())
return makeParserError();
// FIXME: This will be incorrect if there is trailing whitespace at the end
// of the #line.
SourceLoc Begin = Lexer::getSourceLoc(Tok.getText().end()).getAdvancedLoc(1);
int LineOffset = StartLine - SourceMgr.getLineNumber(Begin);
consumeToken(tok::string_literal);
if (!Tok.isAtStartOfLine()) {
diagnose(Tok.getLoc(), diag::extra_tokens_line_directive);
return makeParserError();
}
// Create a new virtual file for the region started by the #line marker.
bool isNewFile = SourceMgr.openVirtualFile(Begin, Filename.getValue(),
LineOffset);
assert(isNewFile);
(void)isNewFile;
InPoundLineEnvironment = true;
return makeParserSuccess();
}
ParserResult<IfConfigDecl> Parser::parseDeclIfConfig(ParseDeclOptions Flags) {
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
StructureMarkerKind::IfConfig);
bool foundActive = false;
SmallVector<IfConfigDeclClause, 4> Clauses;
while (1) {
bool isElse = Tok.is(tok::pound_else);
SourceLoc ClauseLoc = consumeToken();
Expr *Condition = nullptr;
ConfigParserState ConfigState;
if (isElse) {
ConfigState.setConditionActive(!foundActive);
} else {
if (Tok.isAtStartOfLine())
diagnose(ClauseLoc, diag::expected_build_configuration_expression);
// Evaluate the condition.
ParserResult<Expr> Configuration = parseExprSequence(diag::expected_expr,
true, true);
if (Configuration.isNull())
return makeParserError();
Condition = Configuration.get();
// Evaluate the condition, to validate it.
ConfigState = evaluateConfigConditionExpr(Condition);
}
foundActive |= ConfigState.isConditionActive();
if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof))
diagnose(Tok.getLoc(), diag::extra_tokens_config_directive);
Optional<Scope> scope;
if (!ConfigState.isConditionActive())
scope.emplace(this, ScopeKind::Brace, /*inactiveConfigBlock=*/true);
SmallVector<Decl*, 8> Decls;
ParserStatus Status;
while (Tok.isNot(tok::pound_else) && Tok.isNot(tok::pound_endif) &&
Tok.isNot(tok::pound_elseif)) {
Status = parseDecl(Decls, Flags);
if (Status.isError()) {
diagnose(Tok, diag::expected_close_to_config_stmt);
skipUntilConfigBlockClose();
break;
}
}
Clauses.push_back(IfConfigDeclClause(ClauseLoc, Condition,
Context.AllocateCopy(Decls),
ConfigState.isConditionActive()));
if (Tok.isNot(tok::pound_elseif) && Tok.isNot(tok::pound_else))
break;
if (isElse)
diagnose(Tok, diag::expected_close_after_else);
}
SourceLoc EndLoc;
bool HadMissingEnd = parseConfigEndIf(EndLoc);
IfConfigDecl *ICD = new (Context) IfConfigDecl(CurDeclContext,
Context.AllocateCopy(Clauses),
EndLoc, HadMissingEnd);
return makeParserResult(ICD);
}
/// \brief Parse a typealias decl.
///
/// \verbatim
/// decl-typealias:
/// 'typealias' identifier inheritance? '=' type
/// \endverbatim
ParserResult<TypeDecl> Parser::parseDeclTypeAlias(bool WantDefinition,
bool isAssociatedType,
DeclAttributes &Attributes) {
SourceLoc TypeAliasLoc = consumeToken(tok::kw_typealias);
Identifier Id;
SourceLoc IdLoc;
ParserStatus Status;
Status |=
parseIdentifierDeclName(*this, Id, IdLoc, tok::colon, tok::equal,
diag::expected_identifier_in_decl, "typealias");
if (Status.isError())
return nullptr;
DebuggerContextChange DCC(*this, Id, DeclKind::TypeAlias);
// Parse optional inheritance clause.
// FIXME: Allow class requirements here.
SmallVector<TypeLoc, 2> Inherited;
if (isAssociatedType && Tok.is(tok::colon))
Status |= parseInheritance(Inherited, /*classRequirementLoc=*/nullptr);
ParserResult<TypeRepr> UnderlyingTy;
if (WantDefinition || Tok.is(tok::equal)) {
if (parseToken(tok::equal, diag::expected_equal_in_typealias)) {
Status.setIsParseError();
return Status;
}
UnderlyingTy = parseType(diag::expected_type_in_typealias);
Status |= UnderlyingTy;
if (UnderlyingTy.isNull())
return Status;
}
// If this is an associated type, build the AST for it.
if (isAssociatedType) {
auto assocType = new (Context) AssociatedTypeDecl(
CurDeclContext,
TypeAliasLoc, Id, IdLoc,
UnderlyingTy.getPtrOrNull());
assocType->getAttrs() = Attributes;
if (!Inherited.empty())
assocType->setInherited(Context.AllocateCopy(Inherited));
addToScope(assocType);
return makeParserResult(Status, assocType);
}
// Otherwise, build a typealias.
TypeAliasDecl *TAD =
new (Context) TypeAliasDecl(TypeAliasLoc, Id, IdLoc,
UnderlyingTy.getPtrOrNull(),
CurDeclContext);
TAD->getAttrs() = Attributes;
addToScope(TAD);
return DCC.fixupParserResult(Status, TAD);
}
/// This function creates an accessor function (with no body) for a computed
/// property or subscript.
static FuncDecl *createAccessorFunc(SourceLoc DeclLoc, ParameterList *param,
TypeLoc ElementTy,
ParameterList *Indices, SourceLoc StaticLoc,
Parser::ParseDeclOptions Flags,
AccessorKind Kind,
AddressorKind addressorKind,
Parser *P, SourceLoc AccessorKeywordLoc) {
// First task, set up the value argument list. This is the "newValue" name
// (for setters) followed by the index list (for subscripts). For
// non-subscript getters, this degenerates down to "()".
//
// We put the 'newValue' argument before the subscript index list as a
// micro-optimization for Objective-C thunk generation.
ParameterList *ValueArg;
{
SmallVector<ParamDecl*, 2> ValueArgElements;
SourceLoc StartLoc, EndLoc;
if (param) {
assert(param->size() == 1 &&
"Should only have a single parameter in the list");
ValueArgElements.push_back(param->get(0));
StartLoc = param->getStartLoc();
EndLoc = param->getEndLoc();
}
if (Indices) {
Indices = Indices->clone(P->Context, ParameterList::Implicit);
ValueArgElements.append(Indices->begin(), Indices->end());
if (StartLoc.isInvalid()) {
StartLoc = Indices->getStartLoc();
EndLoc = Indices->getEndLoc();
}
}
ValueArg = ParameterList::create(P->Context, StartLoc, ValueArgElements,
EndLoc);
}
// Create the parameter list(s) for the getter.
SmallVector<ParameterList*, 4> Params;
// Add the implicit 'self' to Params, if needed.
if (Flags & Parser::PD_HasContainerType)
Params.push_back(ParameterList::createSelf(DeclLoc, P->CurDeclContext));
// Add the "(value)" and subscript indices parameter clause.
Params.push_back(ValueArg);
TypeLoc ReturnType;
// Getters return the value type.
if (Kind == AccessorKind::IsGetter) {
ReturnType = ElementTy.clone(P->Context);
// Addressors return Unsafe{,Mutable}Pointer<T>, plus sometimes an
// owner or pinned owner.
} else if (Kind == AccessorKind::IsAddressor ||
Kind == AccessorKind::IsMutableAddressor) {
// Construct "Unsafe{,Mutable}Pointer<T>".
TypeRepr *args[] = { ElementTy.clone(P->Context).getTypeRepr() };
// FIXME: the fact that this could resolve in the local scope is dumb.
bool isMutable = (Kind == AccessorKind::IsMutableAddressor);
Identifier name = P->Context.getIdentifier(
isMutable ? "UnsafeMutablePointer" : "UnsafePointer");
TypeRepr *resultType =
new (P->Context) GenericIdentTypeRepr(SourceLoc(), name,
P->Context.AllocateCopy(args),
SourceRange());
auto makeKnownType = [&](Type type) -> TypeRepr* {
return new (P->Context) FixedTypeRepr(type, SourceLoc());
};
auto makePairType = [&](TypeRepr *fst, TypeRepr *snd) -> TypeRepr* {
return TupleTypeRepr::create(P->Context, {fst, snd}, SourceRange(),
SourceLoc(), 2);
};
switch (addressorKind) {
case AddressorKind::NotAddressor:
llvm_unreachable("not an addressor!");
// For unsafe addressors, that's all we've got.
case AddressorKind::Unsafe:
break;
// For non-native owning addressors, the return type is actually
// (Unsafe{,Mutable}Pointer<T>, Builtin.UnknownObject)
case AddressorKind::Owning:
resultType = makePairType(resultType,
makeKnownType(P->Context.TheUnknownObjectType));
break;
// For native owning addressors, the return type is actually
// (Unsafe{,Mutable}Pointer<T>, Builtin.NativeObject)
case AddressorKind::NativeOwning:
resultType = makePairType(resultType,
makeKnownType(P->Context.TheNativeObjectType));
break;
// For native pinning addressors, the return type is actually
// (Unsafe{,Mutable}Pointer<T>, Builtin.NativeObject?)
case AddressorKind::NativePinning: {
auto optNativePtr = new (P->Context) OptionalTypeRepr(
makeKnownType(P->Context.TheNativeObjectType),
SourceLoc());
resultType = makePairType(resultType, optNativePtr);
break;
}
}
ReturnType = resultType;
// Everything else returns ().
} else {
ReturnType = TypeLoc::withoutLoc(TupleType::getEmpty(P->Context));
}
// Start the function.
auto *D = FuncDecl::create(P->Context, StaticLoc, StaticSpellingKind::None,
/* FIXME*/DeclLoc, Identifier(),
DeclLoc, SourceLoc(), AccessorKeywordLoc,
/*GenericParams=*/nullptr,
Type(), Params, ReturnType, P->CurDeclContext);
// Non-static set/willSet/didSet/materializeForSet/mutableAddress
// default to mutating. get/address default to
// non-mutating.
if (!D->isStatic()) {
switch (Kind) {
case AccessorKind::IsAddressor:
D->setAddressorKind(addressorKind);
break;
case AccessorKind::IsGetter:
break;
case AccessorKind::IsMutableAddressor:
D->setAddressorKind(addressorKind);
SWIFT_FALLTHROUGH;
case AccessorKind::IsSetter:
case AccessorKind::IsWillSet:
case AccessorKind::IsDidSet:
D->setMutating();
break;
case AccessorKind::IsMaterializeForSet:
case AccessorKind::NotAccessor:
llvm_unreachable("not parseable accessors");
}
}
return D;
}
static ParamDecl *
createSetterAccessorArgument(SourceLoc nameLoc, Identifier name,
TypeLoc elementTy, AccessorKind accessorKind,
Parser &P) {
// Add the parameter. If no name was specified, the name defaults to
// 'value'.
bool isNameImplicit = name.empty();
if (isNameImplicit) {
const char *implName =
accessorKind == AccessorKind::IsDidSet ? "oldValue" : "newValue";
name = P.Context.getIdentifier(implName);
}
auto result = new (P.Context) ParamDecl(/*IsLet*/true, SourceLoc(),
Identifier(), nameLoc, name,
Type(), P.CurDeclContext);
if (isNameImplicit)
result->setImplicit();
result->getTypeLoc() = elementTy.clone(P.Context);
// AST Walker shouldn't go into the type recursively.
result->setIsTypeLocImplicit(true);
return result;
}
/// Parse a "(value)" specifier for "set" or "willSet" if present. Create a
/// parameter list to represent the spelled argument or return null if none is
/// present.
static ParameterList *
parseOptionalAccessorArgument(SourceLoc SpecifierLoc, TypeLoc ElementTy,
Parser &P, AccessorKind Kind) {
// 'set' and 'willSet' have a (value) parameter, 'didSet' takes an (oldValue)
// parameter and 'get' and always takes a () parameter.
if (Kind != AccessorKind::IsSetter && Kind != AccessorKind::IsWillSet &&
Kind != AccessorKind::IsDidSet)
return nullptr;
SourceLoc StartLoc, NameLoc, EndLoc;
Identifier Name;
// If the SpecifierLoc is invalid, then the caller just wants us to synthesize
// the default, not actually try to parse something.
if (SpecifierLoc.isValid() && P.Tok.is(tok::l_paren)) {
StartLoc = P.consumeToken(tok::l_paren);
if (P.Tok.isNot(tok::identifier)) {
P.diagnose(P.Tok, diag::expected_accessor_name, (unsigned)Kind);
P.skipUntil(tok::r_paren, tok::l_brace);
if (P.Tok.is(tok::r_paren))
EndLoc = P.consumeToken();
else
EndLoc = StartLoc;
} else {
// We have a name.
Name = P.Context.getIdentifier(P.Tok.getText());
NameLoc = P.consumeToken();
auto DiagID =
Kind == AccessorKind::IsSetter ? diag::expected_rparen_set_name :
Kind == AccessorKind::IsWillSet ? diag::expected_rparen_willSet_name :
diag::expected_rparen_didSet_name;
// Look for the closing ')'.
P.parseMatchingToken(tok::r_paren, EndLoc, DiagID, StartLoc);
}
}
if (Name.empty()) NameLoc = SpecifierLoc;
auto param = createSetterAccessorArgument(NameLoc, Name, ElementTy, Kind, P);
return ParameterList::create(P.Context, StartLoc, param, EndLoc);
}
static unsigned skipUntilMatchingRBrace(Parser &P) {
unsigned OpenBraces = 1;
while (OpenBraces != 0 && P.Tok.isNot(tok::eof)) {
if (P.consumeIf(tok::l_brace)) {
OpenBraces++;
continue;
}
if (OpenBraces == 1 && P.Tok.is(tok::r_brace))
break;
if (P.consumeIf(tok::r_brace)) {
OpenBraces--;
continue;
}
P.consumeToken();
}
return OpenBraces;
}
static unsigned skipBracedBlock(Parser &P) {
P.consumeToken(tok::l_brace);
unsigned OpenBraces = skipUntilMatchingRBrace(P);
if (P.consumeIf(tok::r_brace))
OpenBraces--;
return OpenBraces;
}
void Parser::consumeGetSetBody(AbstractFunctionDecl *AFD,
SourceLoc LBLoc) {
SourceLoc SavedPreviousLoc = PreviousLoc;
SourceRange BodyRange;
BodyRange.Start = Tok.getLoc();
// Skip until the next '}' at the correct nesting level.
unsigned OpenBraces = skipUntilMatchingRBrace(*this);
if (OpenBraces != 1) {
// FIXME: implement some error recovery?
}
BodyRange.End = PreviousLoc;
if (DelayedParseCB->shouldDelayFunctionBodyParsing(
*this, AFD, AFD->getAttrs(), BodyRange)) {
State->delayAccessorBodyParsing(AFD, BodyRange, SavedPreviousLoc, LBLoc);
AFD->setBodyDelayed(BodyRange);
} else {
AFD->setBodySkipped(BodyRange);
}
}
static AddressorKind getImmutableAddressorKind(Token &tok) {
if (tok.isContextualKeyword("unsafeAddress")) {
return AddressorKind::Unsafe;
} else if (tok.isContextualKeyword("addressWithOwner")) {
return AddressorKind::Owning;
} else if (tok.isContextualKeyword("addressWithNativeOwner")) {
return AddressorKind::NativeOwning;
} else if (tok.isContextualKeyword("addressWithPinnedNativeOwner")) {
return AddressorKind::NativePinning;
} else {
return AddressorKind::NotAddressor;
}
}
static AddressorKind getMutableAddressorKind(Token &tok) {
if (tok.isContextualKeyword("unsafeMutableAddress")) {
return AddressorKind::Unsafe;
} else if (tok.isContextualKeyword("mutableAddressWithOwner")) {
return AddressorKind::Owning;
} else if (tok.isContextualKeyword("mutableAddressWithNativeOwner")) {
return AddressorKind::NativeOwning;
} else if (tok.isContextualKeyword("mutableAddressWithPinnedNativeOwner")) {
return AddressorKind::NativePinning;
} else {
return AddressorKind::NotAddressor;
}
}
/// Returns an accessor kind that
static StringRef getAccessorNameForDiagnostic(AccessorKind accessorKind,
AddressorKind addressorKind) {
switch (accessorKind) {
case AccessorKind::NotAccessor: llvm_unreachable("invalid");
case AccessorKind::IsGetter: return "getter";
case AccessorKind::IsSetter: return "setter";
case AccessorKind::IsDidSet: return "didSet";
case AccessorKind::IsWillSet: return "willSet";
case AccessorKind::IsMaterializeForSet: return "materializeForSet";
case AccessorKind::IsAddressor:
switch (addressorKind) {
case AddressorKind::NotAddressor: llvm_unreachable("invalid");
case AddressorKind::Unsafe: return "unsafeAddress";
case AddressorKind::Owning: return "addressWithOwner";
case AddressorKind::NativeOwning: return "addressWithNativeOwner";
case AddressorKind::NativePinning: return "addressWithPinnedNativeOwner";
}
llvm_unreachable("bad addressor kind");
case AccessorKind::IsMutableAddressor:
switch (addressorKind) {
case AddressorKind::NotAddressor: llvm_unreachable("invalid");
case AddressorKind::Unsafe: return "unsafeMutableAddress";
case AddressorKind::Owning: return "mutableAddressWithOwner";
case AddressorKind::NativeOwning: return "mutableAddressWithNativeOwner";
case AddressorKind::NativePinning: return "mutableAddressWithPinnedNativeOwner";
}
llvm_unreachable("bad addressor kind");
}
llvm_unreachable("bad accessor kind");
}
static void diagnoseRedundantAccessors(Parser &P, SourceLoc loc,
AccessorKind accessorKind,
AddressorKind addressorKind,
bool isSubscript,
FuncDecl *previousDecl) {
// Different addressor safety kinds still count as the same addressor.
if (previousDecl->getAddressorKind() != addressorKind) {
assert(accessorKind == AccessorKind::IsAddressor ||
accessorKind == AccessorKind::IsMutableAddressor);
P.diagnose(loc, diag::conflicting_property_addressor,
unsigned(isSubscript),
unsigned(accessorKind == AccessorKind::IsMutableAddressor));
// Be less specific about the previous definition.
P.diagnose(previousDecl->getLoc(), diag::previous_accessor,
accessorKind == AccessorKind::IsMutableAddressor
? "mutable addressor" : "addressor");
return;
}
P.diagnose(loc, diag::duplicate_property_accessor,
getAccessorNameForDiagnostic(accessorKind, addressorKind));
P.diagnose(previousDecl->getLoc(), diag::previous_accessor,
getAccessorNameForDiagnostic(accessorKind, addressorKind));
}
/// \brief Parse a get-set clause, optionally containing a getter, setter,
/// willSet, and/or didSet clauses. 'Indices' is a paren or tuple pattern,
/// specifying the index list for a subscript.
bool Parser::parseGetSetImpl(ParseDeclOptions Flags, ParameterList *Indices,
TypeLoc ElementTy, ParsedAccessors &accessors,
SourceLoc &LastValidLoc, SourceLoc StaticLoc,
SourceLoc VarLBLoc,
SmallVectorImpl<Decl *> &Decls) {
// Properties in protocols use sufficiently limited syntax that we have a
// special parsing loop for them. SIL mode uses the same syntax.
if (Flags.contains(PD_InProtocol) || isInSILMode()) {
while (Tok.isNot(tok::r_brace)) {
if (Tok.is(tok::eof))
return true;
// Parse any leading attributes.
DeclAttributes Attributes;
bool FoundCCToken;
parseDeclAttributeList(Attributes, FoundCCToken);
// Parse the contextual keywords for 'mutating' and 'nonmutating' before
// get and set.
if (Tok.isContextualKeyword("mutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Mutating);
} else if (Tok.isContextualKeyword("nonmutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_NonMutating);
}
AccessorKind Kind;
AddressorKind addressorKind = AddressorKind::NotAddressor;
FuncDecl **TheDeclPtr;
SourceLoc AccessorKeywordLoc = Tok.getLoc();
if (Tok.isContextualKeyword("get")) {
Kind = AccessorKind::IsGetter;
TheDeclPtr = &accessors.Get;
} else if (Tok.isContextualKeyword("set")) {
Kind = AccessorKind::IsSetter;
TheDeclPtr = &accessors.Set;
} else if (!Flags.contains(PD_InProtocol) &&
(addressorKind = getImmutableAddressorKind(Tok))
!= AddressorKind::NotAddressor) {
Kind = AccessorKind::IsAddressor;
TheDeclPtr = &accessors.Addressor;
} else if (!Flags.contains(PD_InProtocol) &&
(addressorKind = getMutableAddressorKind(Tok))
!= AddressorKind::NotAddressor) {
Kind = AccessorKind::IsMutableAddressor;
TheDeclPtr = &accessors.MutableAddressor;
} else {
AccessorKeywordLoc = SourceLoc();
diagnose(Tok, diag::expected_getset_in_protocol);
return true;
}
FuncDecl *&TheDecl = *TheDeclPtr;
SourceLoc Loc = consumeToken();
// Have we already parsed this kind of clause?
if (TheDecl) {
diagnoseRedundantAccessors(*this, Loc, Kind, addressorKind,
/*subscript*/Indices != nullptr, TheDecl);
// Forget the previous decl.
Decls.erase(std::find(Decls.begin(), Decls.end(), TheDecl));
TheDecl = nullptr; // Forget the previous decl.
}
// "set" could have a name associated with it. This isn't valid in a
// protocol, but we parse and then reject it, for better QoI.
if (Tok.is(tok::l_paren))
diagnose(Loc, diag::protocol_setter_name);
auto *ValueNameParams
= parseOptionalAccessorArgument(Loc, ElementTy, *this, Kind);
// Set up a function declaration.
TheDecl = createAccessorFunc(Loc, ValueNameParams, ElementTy, Indices,
StaticLoc, Flags, Kind, addressorKind, this,
AccessorKeywordLoc);
TheDecl->getAttrs() = Attributes;
Decls.push_back(TheDecl);
}
return false;
}
// Otherwise, we have a normal var or subscript declaration, parse the full
// complement of specifiers, along with their bodies.
// If the body is completely empty, reject it. This is at best a getter with
// an implicit fallthrough off the end.
if (Tok.is(tok::r_brace)) {
diagnose(Tok, diag::computed_property_no_accessors);
return true;
}
bool IsFirstAccessor = true;
while (Tok.isNot(tok::r_brace)) {
if (Tok.is(tok::eof))
return true;
// If there are any attributes, we are going to parse them. Because these
// attributes might not be appertaining to the accessor, but to the first
// declaration inside the implicit getter, we need to save the parser
// position and restore it later.
ParserPosition BeginParserPosition;
if (Tok.is(tok::at_sign))
BeginParserPosition = getParserPosition();
// Parse any leading attributes.
DeclAttributes Attributes;
bool FoundCCToken;
parseDeclAttributeList(Attributes, FoundCCToken);
// Parse the contextual keywords for 'mutating' and 'nonmutating' before
// get and set.
if (Tok.isContextualKeyword("mutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Mutating);
} else if (Tok.isContextualKeyword("nonmutating")) {
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_NonMutating);
}
bool isImplicitGet = false;
AccessorKind Kind;
AddressorKind addressorKind = AddressorKind::NotAddressor;
FuncDecl **TheDeclPtr;
SourceLoc AccessorKeywordLoc = Tok.getLoc();
if (Tok.isContextualKeyword("get")) {
Kind = AccessorKind::IsGetter;
TheDeclPtr = &accessors.Get;
} else if (Tok.isContextualKeyword("set")) {
Kind = AccessorKind::IsSetter;
TheDeclPtr = &accessors.Set;
} else if (Tok.isContextualKeyword("willSet")) {
Kind = AccessorKind::IsWillSet;
TheDeclPtr = &accessors.WillSet;
} else if (Tok.isContextualKeyword("didSet")) {
Kind = AccessorKind::IsDidSet;
TheDeclPtr = &accessors.DidSet;
} else if ((addressorKind = getImmutableAddressorKind(Tok))
!= AddressorKind::NotAddressor) {
Kind = AccessorKind::IsAddressor;
TheDeclPtr = &accessors.Addressor;
} else if ((addressorKind = getMutableAddressorKind(Tok))
!= AddressorKind::NotAddressor) {
Kind = AccessorKind::IsMutableAddressor;
TheDeclPtr = &accessors.MutableAddressor;
} else {
AccessorKeywordLoc = SourceLoc();
// This is an implicit getter. Might be not valid in this position,
// though. Anyway, go back to the beginning of the getter code to ensure
// that the diagnostics point to correct tokens.
if (BeginParserPosition.isValid()) {
backtrackToPosition(BeginParserPosition);
Attributes = DeclAttributes();
}
if (!IsFirstAccessor) {
// Cannot have an implicit getter after other accessor.
diagnose(Tok, diag::expected_accessor_kw);
skipUntil(tok::r_brace);
// Don't signal an error since we recovered.
return false;
}
Kind = AccessorKind::IsGetter;
TheDeclPtr = &accessors.Get;
isImplicitGet = true;
}
IsFirstAccessor = false;
// Consume the contextual keyword, if present.
SourceLoc Loc = isImplicitGet ? VarLBLoc : consumeToken();
FuncDecl *&TheDecl = *TheDeclPtr;
// Have we already parsed this kind of clause?
if (TheDecl) {
diagnoseRedundantAccessors(*this, Loc, Kind, addressorKind,
/*subscript*/Indices != nullptr, TheDecl);
// Forget the previous decl.
Decls.erase(std::find(Decls.begin(), Decls.end(), TheDecl));
TheDecl = nullptr;
}
// 'set' and 'willSet' can have an optional name.
//
// set-name ::= '(' identifier ')'
auto *ValueNamePattern =
parseOptionalAccessorArgument(Loc, ElementTy, *this, Kind);
SourceLoc LBLoc = isImplicitGet ? VarLBLoc : Tok.getLoc();
// FIXME: Use outer '{' loc if isImplicitGet.
bool ExternalAsmName = false;
if (!isImplicitGet && !consumeIf(tok::l_brace)) {
// _silgen_name'd accessors don't need bodies.
if (!Attributes.hasAttribute<SILGenNameAttr>()) {
diagnose(Tok, diag::expected_lbrace_accessor,
getAccessorNameForDiagnostic(Kind, addressorKind));
return true;
}
ExternalAsmName = true;
}
// Set up a function declaration.
TheDecl = createAccessorFunc(Loc, ValueNamePattern, ElementTy, Indices,
StaticLoc, Flags, Kind, addressorKind, this,
AccessorKeywordLoc);
TheDecl->getAttrs() = Attributes;
// Parse the body, if any.
if (ExternalAsmName) {
LastValidLoc = Loc;
} else {
Scope S(this, ScopeKind::FunctionBody);
for (auto PL : TheDecl->getParameterLists())
addParametersToScope(PL);
// Establish the new context.
ParseFunctionBody CC(*this, TheDecl);
// Parse the body.
SmallVector<ASTNode, 16> Entries;
{
llvm::SaveAndRestore<bool> T(IsParsingInterfaceTokens, false);
if (!isDelayedParsingEnabled())
parseBraceItems(Entries);
else
consumeGetSetBody(TheDecl, LBLoc);
}
SourceLoc RBLoc;
if (!isImplicitGet) {
parseMatchingToken(tok::r_brace, RBLoc, diag::expected_rbrace_in_getset,
LBLoc);
} else {
RBLoc = Tok.is(tok::r_brace) ? Tok.getLoc() : PreviousLoc;
}
if (!isDelayedParsingEnabled()) {
BraceStmt *Body = BraceStmt::create(Context, LBLoc, Entries, RBLoc);
TheDecl->setBody(Body);
}
LastValidLoc = RBLoc;
}
Decls.push_back(TheDecl);
}
return false;
}
bool Parser::parseGetSet(ParseDeclOptions Flags, ParameterList *Indices,
TypeLoc ElementTy, ParsedAccessors &accessors,
SourceLoc StaticLoc,
SmallVectorImpl<Decl *> &Decls) {
accessors.LBLoc = consumeToken(tok::l_brace);
SourceLoc LastValidLoc = accessors.LBLoc;
bool Invalid = parseGetSetImpl(Flags, Indices, ElementTy, accessors,
LastValidLoc, StaticLoc, accessors.LBLoc,
Decls);
// Parse the final '}'.
if (Invalid)
skipUntil(tok::r_brace);
parseMatchingToken(tok::r_brace, accessors.RBLoc,
diag::expected_rbrace_in_getset, accessors.LBLoc);
return Invalid;
}
void Parser::parseAccessorBodyDelayed(AbstractFunctionDecl *AFD) {
assert(!AFD->getBody() && "function should not have a parsed body");
assert(AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::Unparsed &&
"function body should be delayed");
auto AccessorParserState = State->takeAccessorBodyState(AFD);
assert(AccessorParserState.get() && "should have a valid state");
auto BeginParserPosition = getParserPosition(AccessorParserState->BodyPos);
auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc());
// 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 first token of the accessor body.
restoreParserPosition(BeginParserPosition);
// Re-enter the lexical scope.
Scope S(this, AccessorParserState->takeScope());
ParseFunctionBody CC(*this, AFD);
SmallVector<ASTNode, 16> Entries;
parseBraceItems(Entries);
BraceStmt *Body =
BraceStmt::create(Context, AccessorParserState->LBLoc, Entries,
Tok.getLoc());
AFD->setBody(Body);
}
/// \brief Parse the brace-enclosed getter and setter for a variable.
VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
ParseDeclOptions Flags,
SourceLoc StaticLoc, bool hasInitializer,
const DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls) {
bool Invalid = false;
// The grammar syntactically requires a simple identifier for the variable
// name. Complain if that isn't what we got.
VarDecl *PrimaryVar = nullptr;
{
Pattern *PrimaryPattern = pattern;
if (TypedPattern *Typed = dyn_cast<TypedPattern>(PrimaryPattern))
PrimaryPattern = Typed->getSubPattern();
if (NamedPattern *Named = dyn_cast<NamedPattern>(PrimaryPattern)) {
PrimaryVar = Named->getDecl();
}
}
if (!PrimaryVar) {
diagnose(pattern->getLoc(), diag::getset_nontrivial_pattern);
Invalid = true;
} else {
setLocalDiscriminator(PrimaryVar);
}
TypeLoc TyLoc;
if (TypedPattern *TP = dyn_cast<TypedPattern>(pattern)) {
TyLoc = TP->getTypeLoc();
} else if (!PrimaryVar) {
TyLoc = TypeLoc::withoutLoc(ErrorType::get(Context));
}
// Parse getter and setter.
ParsedAccessors accessors;
if (parseGetSet(Flags, /*Indices=*/0, TyLoc, accessors, StaticLoc, Decls))
Invalid = true;
// If we have an invalid case, bail out now.
if (!PrimaryVar)
return nullptr;
if (!TyLoc.hasLocation()) {
if (accessors.Get || accessors.Set || accessors.Addressor ||
accessors.MutableAddressor) {
diagnose(pattern->getLoc(), diag::computed_property_missing_type);
Invalid = true;
}
}
// Reject accessors on 'let's after parsing them (for better recovery).
if (PrimaryVar->isLet() && !Attributes.hasAttribute<SILStoredAttr>()) {
if (accessors.WillSet || accessors.DidSet)
diagnose(accessors.LBLoc, diag::let_cannot_be_observing_property);
else if (accessors.Addressor || accessors.MutableAddressor)
diagnose(accessors.LBLoc, diag::let_cannot_be_addressed_property);
else
diagnose(accessors.LBLoc, diag::let_cannot_be_computed_property);
PrimaryVar->setLet(false);
Invalid = true;
}
// Lazy var should not have explicit getter/setter.
// For error-recovery, we mark them as invalid.
if (Attributes.hasAttribute<LazyAttr>()){
if (accessors.Get)
accessors.Get->setInvalid();
if (accessors.Set)
accessors.Set->setInvalid();
}
accessors.record(*this, PrimaryVar, Invalid, Flags, StaticLoc,
Attributes, TyLoc, /*indices*/ nullptr, Decls);
return PrimaryVar;
}
/// Record a bunch of parsed accessors into the given abstract storage decl.
void Parser::ParsedAccessors::record(Parser &P, AbstractStorageDecl *storage,
bool invalid, ParseDeclOptions flags,
SourceLoc staticLoc,
const DeclAttributes &attrs,
TypeLoc elementTy, ParameterList *indices,
SmallVectorImpl<Decl *> &decls) {
auto flagInvalidAccessor = [&](FuncDecl *&func) {
if (func) {
func->setType(ErrorType::get(P.Context));
func->setInvalid();
}
};
auto ignoreInvalidAccessor = [&](FuncDecl *&func) {
if (func) {
flagInvalidAccessor(func);
// Forget the decl being invalidated
auto PositionInDecls = std::find(decls.begin(), decls.end(), func);
assert(PositionInDecls != decls.end());
decls.erase(PositionInDecls);
func = nullptr;
}
};
// Create an implicit accessor declaration.
auto createImplicitAccessor =
[&](AccessorKind kind, AddressorKind addressorKind,
ParameterList *argList) -> FuncDecl* {
auto accessor = createAccessorFunc(SourceLoc(), argList, elementTy, indices,
staticLoc, flags, kind, addressorKind,
&P, SourceLoc());
accessor->setImplicit();
decls.push_back(accessor);
return accessor;
};
// 'address' is exclusive with 'get', and 'mutableAddress' is
// exclusive with 'set'.
if (Addressor || MutableAddressor) {
// Require either a 'get' or an 'address' accessor if there's
// a 'mutableAddress' accessor. In principle, we could synthesize
// 'address' from 'mutableAddress', but for now we'll enforce this.
if (!Addressor && !Get) {
P.diagnose(MutableAddressor->getLoc(),
diag::mutableaddressor_without_address,
isa<SubscriptDecl>(storage));
Addressor = createImplicitAccessor(AccessorKind::IsAddressor,
AddressorKind::Unsafe,
nullptr);
// Don't allow both.
} else if (Addressor && Get) {
P.diagnose(Addressor->getLoc(), diag::addressor_with_getter,
isa<SubscriptDecl>(storage));
ignoreInvalidAccessor(Get);
}
// Disallow mutableAddress+set.
//
// Currently we don't allow the address+set combination either.
// In principle, this is an unnecessary restriction, and you can
// imagine caches that might want to vend this combination of
// accessors. But we assume that in-place gets aren't all that
// important. (For example, we don't make any effort to optimize
// them for polymorphic accesses.)
if (Set) {
if (MutableAddressor) {
P.diagnose(MutableAddressor->getLoc(), diag::mutableaddressor_with_setter,
isa<SubscriptDecl>(storage));
} else {
P.diagnose(Set->getLoc(), diag::addressor_with_setter,
isa<SubscriptDecl>(storage));
}
ignoreInvalidAccessor(Set);
}
}
// For now, we don't support the observing accessors on subscripts.
if (isa<SubscriptDecl>(storage) && (WillSet || DidSet)) {
P.diagnose(DidSet ? DidSet->getLoc() : WillSet->getLoc(),
diag::observingproperty_in_subscript, bool(DidSet));
ignoreInvalidAccessor(WillSet);
ignoreInvalidAccessor(DidSet);
}
// If this decl is invalid, mark any parsed accessors as invalid to avoid
// tripping up later invariants.
if (invalid) {
flagInvalidAccessor(Get);
flagInvalidAccessor(Set);
flagInvalidAccessor(Addressor);
flagInvalidAccessor(MutableAddressor);
flagInvalidAccessor(WillSet);
flagInvalidAccessor(DidSet);
}
// If this is a willSet/didSet observing property, record this and we're done.
if (WillSet || DidSet) {
if (Get || Set) {
P.diagnose(Get ? Get->getLoc() : Set->getLoc(),
diag::observingproperty_with_getset, bool(DidSet), bool(Set));
ignoreInvalidAccessor(Get);
ignoreInvalidAccessor(Set);
}
if (Addressor) {
if (!MutableAddressor) {
P.diagnose(WillSet ? WillSet->getLoc() : DidSet->getLoc(),
diag::observingproperty_without_mutableaddress,
bool(DidSet));
MutableAddressor =
createImplicitAccessor(AccessorKind::IsMutableAddressor,
AddressorKind::Unsafe, nullptr);
}
storage->makeAddressedWithObservers(LBLoc, Addressor, MutableAddressor,
WillSet, DidSet, RBLoc);
} else if (attrs.hasAttribute<OverrideAttr>()) {
storage->makeInheritedWithObservers(LBLoc, WillSet, DidSet, RBLoc);
} else {
storage->makeStoredWithObservers(LBLoc, WillSet, DidSet, RBLoc);
}
// Observing properties will have getters and setters synthesized by sema.
// Create their prototypes now.
Get = createImplicitAccessor(AccessorKind::IsGetter,
AddressorKind::NotAddressor, nullptr);
auto argFunc = (WillSet ? WillSet : DidSet);
auto argLoc = argFunc->getParameterLists().back()->getStartLoc();
auto argument = createSetterAccessorArgument(argLoc, Identifier(),elementTy,
AccessorKind::IsSetter, P);
auto argList = ParameterList::create(P.Context, argument);
Set = createImplicitAccessor(AccessorKind::IsSetter,
AddressorKind::NotAddressor, argList);
storage->setObservingAccessors(Get, Set, nullptr);
return;
}
// If we have addressors, at this point mark it as addressed.
if (Addressor) {
assert(!Get && !Set);
storage->makeAddressed(LBLoc, Addressor, MutableAddressor, RBLoc);
return;
}
// If this is a get+mutableAddress property, synthesize an implicit
// setter and record what we've got.
if (MutableAddressor) {
assert(Get && !Set);
auto argument =
createSetterAccessorArgument(MutableAddressor->getLoc(), Identifier(),
elementTy, AccessorKind::IsSetter, P);
auto argList = ParameterList::create(P.Context, argument);
Set = createImplicitAccessor(AccessorKind::IsSetter,
AddressorKind::NotAddressor, argList);
storage->makeComputedWithMutableAddress(LBLoc, Get, Set, nullptr,
MutableAddressor, RBLoc);
return;
}
// Otherwise, this must be a get/set property. The set is optional,
// but get is not.
if (!Get) {
// Subscripts always have to have *something*; they can't be
// purely stored.
if (isa<SubscriptDecl>(storage)) {
if (!invalid) P.diagnose(LBLoc, diag::subscript_without_get);
Get = createImplicitAccessor(AccessorKind::IsGetter,
AddressorKind::NotAddressor, nullptr);
} else if (Set) {
if (!invalid) P.diagnose(Set->getLoc(), diag::var_set_without_get);
}
}
if (Set || Get) {
if (attrs.hasAttribute<SILStoredAttr>())
// Turn this into a stored property with trivial accessors.
storage->addTrivialAccessors(Get, Set, nullptr);
else
// Turn this into a computed variable.
storage->makeComputed(LBLoc, Get, Set, nullptr, RBLoc);
} else {
// Otherwise this decl is invalid and the accessors have been rejected above.
// Make sure to at least record the braces range in the AST.
storage->setInvalidBracesRange(SourceRange(LBLoc, RBLoc));
}
}
/// \brief Parse a 'var' or 'let' declaration, doing no token skipping on error.
ParserStatus Parser::parseDeclVar(ParseDeclOptions Flags,
DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls,
SourceLoc StaticLoc,
StaticSpellingKind StaticSpelling,
SourceLoc TryLoc) {
assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None);
if (StaticLoc.isValid()) {
if (!Flags.contains(PD_HasContainerType)) {
diagnose(Tok, diag::static_var_decl_global_scope, StaticSpelling)
.fixItRemove(StaticLoc);
StaticLoc = SourceLoc();
} else if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) ||
Flags.contains(PD_InProtocol)) {
if (StaticSpelling == StaticSpellingKind::KeywordClass)
diagnose(Tok, diag::class_var_not_in_class)
.fixItReplace(StaticLoc, "static");
}
}
bool isLet = Tok.is(tok::kw_let);
assert(Tok.getKind() == tok::kw_let || Tok.getKind() == tok::kw_var);
SourceLoc VarLoc = consumeToken();
// If this is a var in the top-level of script/repl source file, wrap the
// PatternBindingDecl in a TopLevelCodeDecl, since it represents executable
// code. The VarDecl and any accessor decls (for computed properties) go in
// CurDeclContext.
//
TopLevelCodeDecl *topLevelDecl = nullptr;
if (allowTopLevelCode() && CurDeclContext->isModuleScopeContext()) {
// The body of topLevelDecl will get set later.
topLevelDecl = new (Context) TopLevelCodeDecl(CurDeclContext);
}
// If we're not in a local context, we'll need a context to parse initializers
// into (should we have one). This happens for properties and global
// variables in libraries.
PatternBindingInitializer *initContext = nullptr;
bool usedInitContext = false;
bool HasAccessors = false; // Syntactically has accessor {}'s.
ParserStatus Status;
unsigned NumDeclsInResult = Decls.size();
// In var/let decl with multiple patterns, accumulate them all in this list
// so we can build our singular PatternBindingDecl at the end.
SmallVector<PatternBindingEntry, 4> PBDEntries;
// No matter what error path we take, make sure the
// PatternBindingDecl/TopLevel code block are added.
defer {
// If we didn't parse any patterns, don't create the pattern binding decl.
if (PBDEntries.empty())
return;
// Now that we've parsed all of our patterns, initializers and accessors, we
// can finally create our PatternBindingDecl to represent the
// pattern/initializer pairs.
auto PBD = PatternBindingDecl::create(Context, StaticLoc, StaticSpelling,
VarLoc, PBDEntries, CurDeclContext);
// If we're setting up a TopLevelCodeDecl, configure it by setting up the
// body that holds PBD and we're done. The TopLevelCodeDecl is already set
// up in Decls to be returned to caller.
if (topLevelDecl) {
assert(!initContext &&
"Shouldn't need an initcontext: TopLevelCode is a local context!");
PBD->setDeclContext(topLevelDecl);
auto range = PBD->getSourceRange();
topLevelDecl->setBody(BraceStmt::create(Context, range.Start,
ASTNode(PBD), range.End, true));
Decls.insert(Decls.begin()+NumDeclsInResult, topLevelDecl);
return;
}
// If we set up an initialization context for a property or module-level
// global, check to see if we needed it and wind it down.
if (initContext) {
// If we didn't need the context, "destroy" it, which recycles it for
// the next user.
if (!usedInitContext)
Context.destroyPatternBindingContext(initContext);
else
initContext->setBinding(PBD);
}
// Otherwise return the PBD in "Decls" to the caller. We add it at a
// specific spot to get it in before any accessors, which SILGen seems to
// want.
Decls.insert(Decls.begin()+NumDeclsInResult, PBD);
};
do {
Pattern *pattern;
{
// In our recursive parse, remember that we're in a var/let pattern.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);
auto patternRes = parseTypedPattern();
if (patternRes.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (patternRes.isNull())
return makeParserError();
pattern = patternRes.get();
}
// Configure all vars with attributes, 'static' and parent pattern.
pattern->forEachVariable([&](VarDecl *VD) {
VD->setStatic(StaticLoc.isValid());
VD->getAttrs() = Attributes;
Decls.push_back(VD);
});
// Remember this pattern/init pair for our ultimate PatternBindingDecl. The
// Initializer will be added later when/if it is parsed.
PBDEntries.push_back({pattern, nullptr});
Expr *PatternInit = nullptr;
// Parse an initializer if present.
if (Tok.is(tok::equal)) {
// Record the variables that we're trying to initialize. This allows us
// to cleanly reject "var x = x" when "x" isn't bound to an enclosing
// decl (even though names aren't injected into scope when the initializer
// is parsed).
SmallVector<VarDecl *, 4> Vars;
Vars.append(DisabledVars.begin(), DisabledVars.end());
pattern->collectVariables(Vars);
llvm::SaveAndRestore<decltype(DisabledVars)>
RestoreCurVars(DisabledVars, Vars);
llvm::SaveAndRestore<decltype(DisabledVarReason)>
RestoreReason(DisabledVarReason, diag::var_init_self_referential);
// If we have no local context to parse the initial value into, create one
// for the PBD we'll eventually create. This allows us to have reasonable
// DeclContexts for any closures that may live inside of initializers.
if (!CurDeclContext->isLocalContext() && !topLevelDecl && !initContext)
initContext = Context.createPatternBindingContext(CurDeclContext);
// If we're using a local context (either a TopLevelCodeDecl or a
// PatternBindingContext) install it now so that CurDeclContext is set
// right when parsing the initializer.
Optional<ParseFunctionBody> initParser;
Optional<ContextChange> topLevelParser;
if (topLevelDecl)
topLevelParser.emplace(*this, topLevelDecl,
&State->getTopLevelContext());
if (initContext)
initParser.emplace(*this, initContext);
SourceLoc EqualLoc = consumeToken(tok::equal);
ParserResult<Expr> init = parseExpr(diag::expected_init_value);
// If this Pattern binding was not supposed to have an initializer, but it
// did, diagnose this and remove it.
if (Flags & PD_DisallowInit && init.isNonNull()) {
diagnose(EqualLoc, diag::disallowed_init);
init = nullptr;
}
// Otherwise, if this pattern binding *was* supposed (or allowed) to have
// an initializer, but it was a parse error, replace it with ErrorExpr so
// that downstream clients know that it was present (well, at least the =
// was present). This silences downstream diagnostics checking to make
// sure that some PBD's that require initializers actually had them.
if (!(Flags & PD_DisallowInit) && init.isNull())
init = makeParserResult(init, new (Context) ErrorExpr(EqualLoc));
// Remember this init for the PatternBindingDecl.
PatternInit = init.getPtrOrNull();
PBDEntries.back().setInit(PatternInit);
// If we allocated an initContext and the expression had a closure in it,
// we'll need to keep the initContext around.
if (initContext)
usedInitContext |= initParser->hasClosures();
// If we are doing second pass of code completion, we don't want to
// suddenly cut off parsing and throw away the declaration.
if (init.hasCodeCompletion() && isCodeCompletionFirstPass()) {
// Register the end of the init as the end of the delayed parsing.
DelayedDeclEnd = init.get() ? init.get()->getEndLoc() : SourceLoc();
return makeParserCodeCompletionStatus();
}
if (init.isNull())
return makeParserError();
}
// If we syntactically match the second decl-var production, with a
// var-get-set clause, parse the var-get-set clause.
if (Tok.is(tok::l_brace) && !Flags.contains(PD_InLoop)) {
HasAccessors = true;
if (auto *boundVar = parseDeclVarGetSet(pattern, Flags, StaticLoc,
PatternInit != nullptr,
Attributes, Decls)) {
if (PatternInit && !boundVar->hasStorage()) {
diagnose(pattern->getLoc(), diag::getset_init)
.highlight(PatternInit->getSourceRange());
PatternInit = nullptr;
}
}
}
// Add all parsed vardecls to this scope.
addPatternVariablesToScope(pattern);
// Propagate back types for simple patterns, like "var A, B : T".
if (TypedPattern *TP = dyn_cast<TypedPattern>(pattern)) {
if (isa<NamedPattern>(TP->getSubPattern()) && PatternInit == nullptr) {
for (unsigned i = PBDEntries.size() - 1; i != 0; --i) {
Pattern *PrevPat = PBDEntries[i-1].getPattern();
if (!isa<NamedPattern>(PrevPat) || PBDEntries[i-1].getInit())
break;
if (HasAccessors) {
// FIXME -- offer a fixit to explicitly specify the type
diagnose(PrevPat->getLoc(), diag::getset_cannot_be_implied);
Status.setIsParseError();
}
TypedPattern *NewTP = new (Context) TypedPattern(PrevPat,
TP->getTypeLoc());
NewTP->setPropagatedType();
PBDEntries[i-1].setPattern(NewTP);
}
}
}
} while (consumeIf(tok::comma));
if (HasAccessors && PBDEntries.size() > 1) {
diagnose(VarLoc, diag::disallowed_var_multiple_getset);
Status.setIsParseError();
}
if (TryLoc.isValid()) {
auto inFlightDiag = diagnose(TryLoc, diag::try_on_var_let);
if (PBDEntries.size() == 1 && PBDEntries.front().getInit() &&
!isa<ErrorExpr>(PBDEntries.front().getInit())) {
auto *init = PBDEntries.front().getInit();
inFlightDiag.fixItRemoveChars(TryLoc, VarLoc);
inFlightDiag.fixItInsert(init->getStartLoc(), "try ");
// Note: We can't use TryLoc here because it's outside the PBD source
// range.
PBDEntries.front().setInit(new (Context) TryExpr(init->getStartLoc(),
init));
}
}
// NOTE: At this point, the DoAtScopeExit object is destroyed and the PBD
// is added to the program.
return Status;
}
void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD,
const DeclAttributes &Attrs) {
auto BeginParserPosition = getParserPosition();
SourceRange BodyRange;
BodyRange.Start = Tok.getLoc();
// Consume the '{', and find the matching '}'.
unsigned OpenBraces = skipBracedBlock(*this);
if (OpenBraces != 0 && Tok.isNot(tok::code_complete)) {
assert(Tok.is(tok::eof));
// We hit EOF, and not every brace has a pair. Recover by searching
// for the next decl except variable decls and cutting off before
// that point.
backtrackToPosition(BeginParserPosition);
consumeToken(tok::l_brace);
while (Tok.is(tok::kw_var) || Tok.is(tok::kw_let) ||
(Tok.isNot(tok::eof) && !isStartOfDecl())) {
consumeToken();
}
}
BodyRange.End = PreviousLoc;
if (DelayedParseCB->shouldDelayFunctionBodyParsing(*this, AFD, Attrs,
BodyRange)) {
State->delayFunctionBodyParsing(AFD, BodyRange,
BeginParserPosition.PreviousLoc);
AFD->setBodyDelayed(BodyRange);
} else {
AFD->setBodySkipped(BodyRange);
}
}
/// \brief Parse a 'func' declaration, returning null on error. The caller
/// handles this case and does recovery as appropriate.
///
/// \verbatim
/// decl-func:
/// attribute-list? ('static' | 'class')? 'mutating'? 'func'
/// any-identifier generic-params? func-signature stmt-brace?
/// \endverbatim
///
/// \note The caller of this method must ensure that the next token is 'func'.
ParserResult<FuncDecl>
Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
ParseDeclOptions Flags, DeclAttributes &Attributes) {
assert(StaticLoc.isInvalid() || StaticSpelling != StaticSpellingKind::None);
bool HasContainerType = Flags.contains(PD_HasContainerType);
if (StaticLoc.isValid()) {
if (!HasContainerType) {
// Reject static functions at global scope.
diagnose(Tok, diag::static_func_decl_global_scope, StaticSpelling)
.fixItRemove(StaticLoc);
StaticLoc = SourceLoc();
} else if (Flags.contains(PD_InStruct) || Flags.contains(PD_InEnum) ||
Flags.contains(PD_InProtocol)) {
if (StaticSpelling == StaticSpellingKind::KeywordClass)
diagnose(Tok, diag::class_func_not_in_class)
.fixItReplace(StaticLoc, "static");
}
}
SourceLoc FuncLoc = consumeToken(tok::kw_func);
// Forgive the lexer
if (Tok.is(tok::amp_prefix)) {
Tok.setKind(tok::oper_prefix);
}
Identifier SimpleName;
SourceLoc NameLoc = Tok.getLoc();
Token NonglobalTok = Tok;
bool NonglobalError = false;
if (!(Flags & PD_AllowTopLevel) &&
!(Flags & PD_InProtocol) &&
Tok.isAnyOperator()) {
// Postpone complaining about this error till we see if the
// DCC wants to move it below.
NonglobalError = true;
}
if (parseAnyIdentifier(SimpleName, diag::expected_identifier_in_decl,
"function")) {
ParserStatus NameStatus =
parseIdentifierDeclName(*this, SimpleName, NameLoc, tok::l_paren,
tok::arrow, tok::l_brace,
diag::invalid_diagnostic);
if (NameStatus.isError())
return nullptr;
}
DebuggerContextChange DCC(*this, SimpleName, DeclKind::Func);
if (NonglobalError && !DCC.movedToTopLevel()) {
// FIXME: Recovery here is awful.
diagnose(NonglobalTok, diag::func_decl_nonglobal_operator);
return nullptr;
}
// Parse the generic-params, if present.
Optional<Scope> GenericsScope;
GenericsScope.emplace(this, ScopeKind::Generics);
GenericParamList *GenericParams;
// If the name is an operator token that ends in '<' and the following token
// is an identifier, split the '<' off as a separate token. This allows things
// like 'func ==<T>(x:T, y:T) {}' to parse as '==' with generic type variable
// '<T>' as expected.
if (SimpleName.str().size() > 1 && SimpleName.str().back() == '<'
&& Tok.is(tok::identifier)) {
SimpleName = Context.getIdentifier(SimpleName.str().
slice(0, SimpleName.str().size() - 1));
SourceLoc LAngleLoc = NameLoc.getAdvancedLoc(SimpleName.str().size());
GenericParams = parseGenericParameters(LAngleLoc);
} else {
GenericParams = maybeParseGenericParams();
}
SmallVector<ParameterList*, 8> BodyParams;
// If we're within a container, add an implicit first pattern to match the
// container type as an element named 'self'.
//
// This turns an instance function "(int)->int" on FooTy into
// "(inout self: FooTy)->(int)->int", and a static function
// "(int)->int" on FooTy into "(self: FooTy.Type)->(int)->int".
// Note that we can't actually compute the type here until Sema.
if (HasContainerType)
BodyParams.push_back(ParameterList::createSelf(NameLoc, CurDeclContext));
DefaultArgumentInfo DefaultArgs(HasContainerType);
TypeRepr *FuncRetTy = nullptr;
DeclName FullName;
SourceLoc throwsLoc;
bool rethrows;
ParserStatus SignatureStatus =
parseFunctionSignature(SimpleName, FullName, BodyParams, DefaultArgs,
throwsLoc, rethrows, FuncRetTy);
if (SignatureStatus.hasCodeCompletion() && !CodeCompletion) {
// Trigger delayed parsing, no need to continue.
return SignatureStatus;
}
// Protocol method arguments may not have default values.
if (Flags.contains(PD_InProtocol) && DefaultArgs.HasDefaultArgument) {
diagnose(FuncLoc, diag::protocol_method_argument_init);
return nullptr;
}
// Add the 'rethrows' attribute.
if (rethrows) {
Attributes.add(new (Context) RethrowsAttr(throwsLoc));
}
// Enter the arguments for the function into a new function-body scope. We
// need this even if there is no function body to detect argument name
// duplication.
FuncDecl *FD;
{
Scope S(this, ScopeKind::FunctionBody);
// Create the decl for the func and add it to the parent scope.
FD = FuncDecl::create(Context, StaticLoc, StaticSpelling,
FuncLoc, FullName, NameLoc, throwsLoc, SourceLoc(),
GenericParams, Type(), BodyParams, FuncRetTy,
CurDeclContext);
// Add the attributes here so if we need them while parsing the body
// they are available.
FD->getAttrs() = Attributes;
// Pass the function signature to code completion.
if (SignatureStatus.hasCodeCompletion())
CodeCompletion->setDelayedParsedDecl(FD);
DefaultArgs.setFunctionContext(FD);
for (auto PL : FD->getParameterLists())
addParametersToScope(PL);
setLocalDiscriminator(FD);
// Establish the new context.
ParseFunctionBody CC(*this, FD);
// Check to see if we have a "{" to start a brace statement.
if (Tok.is(tok::l_brace)) {
// Record the curly braces but nothing inside.
SF.recordInterfaceToken("{");
SF.recordInterfaceToken("}");
llvm::SaveAndRestore<bool> T(IsParsingInterfaceTokens, false);
if (Flags.contains(PD_InProtocol)) {
diagnose(Tok, diag::protocol_method_with_body);
skipUntilDeclRBrace();
} else if (!isDelayedParsingEnabled()) {
ParserResult<BraceStmt> Body =
parseBraceItemList(diag::func_decl_without_brace);
if (Body.isNull()) {
// FIXME: Should do some sort of error recovery here?
} else if (SignatureStatus.hasCodeCompletion()) {
// Code completion was inside the signature, don't attach the body.
FD->setBodySkipped(Body.get()->getSourceRange());
} else {
FD->setBody(Body.get());
}
} else {
consumeAbstractFunctionBody(FD, Attributes);
}
} else {
checkForInputIncomplete();
}
}
// Exit the scope introduced for the generic parameters.
GenericsScope.reset();
addToScope(FD);
return DCC.fixupParserResult(FD);
}
bool Parser::parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD) {
assert(!AFD->getBody() && "function should not have a parsed body");
assert(AFD->getBodyKind() == AbstractFunctionDecl::BodyKind::Unparsed &&
"function body should be delayed");
auto FunctionParserState = State->takeFunctionBodyState(AFD);
assert(FunctionParserState.get() && "should have a valid state");
auto BeginParserPosition = getParserPosition(FunctionParserState->BodyPos);
auto EndLexerState = L->getStateForEndOfTokenLoc(AFD->getEndLoc());
// 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 '{' of the function body.
restoreParserPosition(BeginParserPosition);
// Re-enter the lexical scope.
Scope S(this, FunctionParserState->takeScope());
ParseFunctionBody CC(*this, AFD);
ParserResult<BraceStmt> Body =
parseBraceItemList(diag::func_decl_without_brace);
if (Body.isNull()) {
// FIXME: Should do some sort of error recovery here?
return true;
} else {
AFD->setBody(Body.get());
}
return false;
}
/// \brief Parse a 'enum' declaration, returning true (and doing no token
/// skipping) on error.
///
/// \verbatim
/// decl-enum:
/// 'enum' attribute-list identifier generic-params? inheritance?
/// '{' decl-enum-body '}'
/// decl-enum-body:
/// decl*
/// \endverbatim
ParserResult<EnumDecl> Parser::parseDeclEnum(ParseDeclOptions Flags,
DeclAttributes &Attributes) {
SourceLoc EnumLoc = consumeToken(tok::kw_enum);
Identifier EnumName;
SourceLoc EnumNameLoc;
ParserStatus Status;
Status |=
parseIdentifierDeclName(*this, EnumName, EnumNameLoc, tok::colon,
tok::l_brace, TokenProperty::StartsWithLess,
diag::expected_identifier_in_decl, "enum");
if (Status.isError())
return nullptr;
DebuggerContextChange DCC(*this, EnumName, DeclKind::Enum);
// Parse the generic-params, if present.
GenericParamList *GenericParams = nullptr;
{
Scope S(this, ScopeKind::Generics);
GenericParams = maybeParseGenericParams();
}
EnumDecl *UD = new (Context) EnumDecl(EnumLoc, EnumName, EnumNameLoc,
{ }, GenericParams, CurDeclContext);
setLocalDiscriminator(UD);
UD->getAttrs() = Attributes;
// Parse optional inheritance clause within the context of the enum.
if (Tok.is(tok::colon)) {
ContextChange CC(*this, UD);
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*classRequirementLoc=*/nullptr);
UD->setInherited(Context.AllocateCopy(Inherited));
}
SmallVector<Decl*, 8> MemberDecls;
SourceLoc LBLoc, RBLoc;
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_enum)) {
LBLoc = PreviousLoc;
RBLoc = LBLoc;
Status.setIsParseError();
} else {
ContextChange CC(*this, UD);
Scope S(this, ScopeKind::ClassBody);
ParseDeclOptions Options(PD_HasContainerType | PD_AllowEnumElement | PD_InEnum);
if (parseNominalDeclMembers(MemberDecls, LBLoc, RBLoc,
diag::expected_rbrace_enum,
Options))
Status.setIsParseError();
}
UD->setBraces({LBLoc, RBLoc});
for (auto member : MemberDecls)
UD->addMember(member);
addToScope(UD);
if (Flags & PD_DisallowNominalTypes) {
diagnose(EnumLoc, diag::disallowed_type);
Status.setIsParseError();
UD->setInvalid();
}
return DCC.fixupParserResult(Status, UD);
}
/// \brief Parse a 'case' of an enum.
///
/// \verbatim
/// enum-case:
/// identifier type-tuple?
/// decl-enum-element:
/// 'case' attribute-list enum-case (',' enum-case)*
/// \endverbatim
ParserStatus Parser::parseDeclEnumCase(ParseDeclOptions Flags,
DeclAttributes &Attributes,
llvm::SmallVectorImpl<Decl *> &Decls) {
ParserStatus Status;
SourceLoc CaseLoc = consumeToken(tok::kw_case);
// Parse comma-separated enum elements.
SmallVector<EnumElementDecl*, 4> Elements;
SourceLoc CommaLoc;
for (;;) {
Identifier Name;
SourceLoc NameLoc;
// Consume an extraneous '.' so we can recover the case name.
SourceLoc DotLoc;
consumeIf(tok::period_prefix, DotLoc);
const bool NameIsNotIdentifier = Tok.isNot(tok::identifier);
if (parseIdentifierDeclName(*this, Name, NameLoc, tok::l_paren,
tok::kw_case, tok::colon, tok::r_brace,
diag::invalid_diagnostic).isError()) {
NameLoc = CaseLoc;
// Handle the likely case someone typed 'case X, case Y'.
if (Tok.is(tok::kw_case) && CommaLoc.isValid()) {
diagnose(Tok, diag::expected_identifier_after_case_comma);
Status.setIsParseError();
return Status;
}
// For recovery, see if the user typed something resembling a switch
// "case" label.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, Parser::IVOLP_InMatchingPattern);
parseMatchingPattern(/*isExprBasic*/false);
}
if (NameIsNotIdentifier) {
if (consumeIf(tok::colon)) {
diagnose(CaseLoc, diag::case_outside_of_switch, "case");
Status.setIsParseError();
return Status;
}
if (CommaLoc.isValid()) {
diagnose(Tok, diag::expected_identifier_after_case_comma);
Status.setIsParseError();
return Status;
}
diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'");
} else if (DotLoc.isValid()) {
diagnose(DotLoc, diag::enum_case_dot_prefix)
.fixItRemove(DotLoc);
}
// See if there's a following argument type.
ParserResult<TypeRepr> ArgType;
if (Tok.isFollowingLParen()) {
ArgType = parseTypeTupleBody();
if (ArgType.hasCodeCompletion()) {
Status.setHasCodeCompletion();
return Status;
}
if (ArgType.isNull()) {
Status.setIsParseError();
return Status;
}
}
// See if there's a raw value expression.
SourceLoc EqualsLoc;
ParserResult<Expr> RawValueExpr;
LiteralExpr *LiteralRawValueExpr = nullptr;
if (Tok.is(tok::equal)) {
EqualsLoc = consumeToken();
{
CodeCompletionCallbacks::InEnumElementRawValueRAII
InEnumElementRawValue(CodeCompletion);
RawValueExpr = parseExpr(diag::expected_expr_enum_case_raw_value);
}
if (RawValueExpr.hasCodeCompletion()) {
Status.setHasCodeCompletion();
return Status;
}
if (RawValueExpr.isNull()) {
Status.setIsParseError();
return Status;
}
// The raw value must be syntactically a simple literal.
LiteralRawValueExpr = dyn_cast<LiteralExpr>(RawValueExpr.getPtrOrNull());
if (!LiteralRawValueExpr
|| isa<InterpolatedStringLiteralExpr>(LiteralRawValueExpr)) {
diagnose(RawValueExpr.getPtrOrNull()->getLoc(),
diag::nonliteral_enum_case_raw_value);
LiteralRawValueExpr = nullptr;
}
}
// For recovery, again make sure the user didn't try to spell a switch
// case label:
// 'case Identifier:' or
// 'case Identifier where ...:'
if (Tok.is(tok::colon) || Tok.is(tok::kw_where)) {
diagnose(CaseLoc, diag::case_outside_of_switch, "case");
skipUntilDeclRBrace();
Status.setIsParseError();
return Status;
}
// Create the element.
auto *result = new (Context) EnumElementDecl(NameLoc, Name,
ArgType.getPtrOrNull(),
EqualsLoc,
LiteralRawValueExpr,
CurDeclContext);
if (NameLoc == CaseLoc) {
result->setImplicit(); // Parse error
}
result->getAttrs() = Attributes;
Elements.push_back(result);
// Continue through the comma-separated list.
if (!Tok.is(tok::comma))
break;
CommaLoc = consumeToken(tok::comma);
}
if (!(Flags & PD_AllowEnumElement)) {
diagnose(CaseLoc, diag::disallowed_enum_element);
// Don't add the EnumElementDecls unless the current context
// is allowed to have EnumElementDecls.
Status.setIsParseError();
return Status;
}
// Create and insert the EnumCaseDecl containing all the elements.
auto TheCase = EnumCaseDecl::create(CaseLoc, Elements, CurDeclContext);
Decls.push_back(TheCase);
// Insert the element decls.
std::copy(Elements.begin(), Elements.end(), std::back_inserter(Decls));
return Status;
}
/// \brief Parse the members in a struct/class/enum/protocol definition.
///
/// \verbatim
/// decl*
/// \endverbatim
bool Parser::parseNominalDeclMembers(SmallVectorImpl<Decl *> &memberDecls,
SourceLoc LBLoc, SourceLoc &RBLoc,
Diag<> ErrorDiag, ParseDeclOptions flags) {
bool previousHadSemi = true;
parseList(tok::r_brace, LBLoc, RBLoc, tok::semi, /*OptionalSep=*/true,
/*AllowSepAfterLast=*/false, ErrorDiag, [&]() -> ParserStatus {
// If the previous declaration didn't have a semicolon and this new
// declaration doesn't start a line, complain.
if (!previousHadSemi && !Tok.isAtStartOfLine()) {
SourceLoc endOfPrevious = getEndOfPreviousLoc();
diagnose(endOfPrevious, diag::declaration_same_line_without_semi)
.fixItInsert(endOfPrevious, ";");
// FIXME: Add semicolon to the AST?
}
previousHadSemi = false;
if (parseDecl(memberDecls, flags).isError())
return makeParserError();
// Check whether the previous declaration had a semicolon after it.
if (!memberDecls.empty() && memberDecls.back()->TrailingSemiLoc.isValid())
previousHadSemi = true;
return makeParserSuccess();
});
// If we found the closing brace, then the caller should not care if there
// were errors while parsing inner decls, because we recovered.
return !RBLoc.isValid();
}
/// \brief Parse a 'struct' declaration, returning true (and doing no token
/// skipping) on error.
///
/// \verbatim
/// decl-struct:
/// 'struct' attribute-list identifier generic-params? inheritance?
/// '{' decl-struct-body '}
/// decl-struct-body:
/// decl*
/// \endverbatim
ParserResult<StructDecl> Parser::parseDeclStruct(ParseDeclOptions Flags,
DeclAttributes &Attributes) {
SourceLoc StructLoc = consumeToken(tok::kw_struct);
Identifier StructName;
SourceLoc StructNameLoc;
ParserStatus Status;
Status |=
parseIdentifierDeclName(*this, StructName, StructNameLoc, tok::colon,
tok::l_brace, TokenProperty::StartsWithLess,
diag::expected_identifier_in_decl, "struct");
if (Status.isError())
return nullptr;
DebuggerContextChange DCC (*this, StructName, DeclKind::Struct);
// Parse the generic-params, if present.
GenericParamList *GenericParams = nullptr;
{
Scope S(this, ScopeKind::Generics);
GenericParams = maybeParseGenericParams();
}
StructDecl *SD = new (Context) StructDecl(StructLoc, StructName,
StructNameLoc,
{ },
GenericParams,
CurDeclContext);
setLocalDiscriminator(SD);
SD->getAttrs() = Attributes;
// Parse optional inheritance clause within the context of the struct.
if (Tok.is(tok::colon)) {
ContextChange CC(*this, SD);
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*classRequirementLoc=*/nullptr);
SD->setInherited(Context.AllocateCopy(Inherited));
}
SmallVector<Decl*, 8> MemberDecls;
SourceLoc LBLoc, RBLoc;
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_struct)) {
LBLoc = PreviousLoc;
RBLoc = LBLoc;
Status.setIsParseError();
} else {
// Parse the body.
ContextChange CC(*this, SD);
Scope S(this, ScopeKind::StructBody);
ParseDeclOptions Options(PD_HasContainerType | PD_InStruct);
if (parseNominalDeclMembers(MemberDecls, LBLoc, RBLoc,
diag::expected_rbrace_struct,
Options))
Status.setIsParseError();
}
SD->setBraces({LBLoc, RBLoc});
for (auto member : MemberDecls)
SD->addMember(member);
addToScope(SD);
if (Flags & PD_DisallowNominalTypes) {
diagnose(StructLoc, diag::disallowed_type);
Status.setIsParseError();
SD->setInvalid();
}
return DCC.fixupParserResult(Status, SD);
}
/// \brief Parse a 'class' declaration, doing no token skipping on error.
///
/// \verbatim
/// decl-class:
/// 'class' attribute-list identifier generic-params? inheritance?
/// '{' decl-class-body '}
/// decl-class-body:
/// decl*
/// \endverbatim
ParserResult<ClassDecl> Parser::parseDeclClass(SourceLoc ClassLoc,
ParseDeclOptions Flags,
DeclAttributes &Attributes) {
Identifier ClassName;
SourceLoc ClassNameLoc;
ParserStatus Status;
Status |=
parseIdentifierDeclName(*this, ClassName, ClassNameLoc, tok::colon,
tok::l_brace, TokenProperty::StartsWithLess,
diag::expected_identifier_in_decl, "class");
if (Status.isError())
return nullptr;
DebuggerContextChange DCC (*this, ClassName, DeclKind::Class);
// Parse the generic-params, if present.
GenericParamList *GenericParams = nullptr;
{
Scope S(this, ScopeKind::Generics);
GenericParams = maybeParseGenericParams();
}
// Create the class.
ClassDecl *CD = new (Context) ClassDecl(ClassLoc, ClassName, ClassNameLoc,
{ }, GenericParams, CurDeclContext);
setLocalDiscriminator(CD);
// Attach attributes.
CD->getAttrs() = Attributes;
// Parse optional inheritance clause within the context of the class.
if (Tok.is(tok::colon)) {
ContextChange CC(*this, CD);
SmallVector<TypeLoc, 2> Inherited;
Status |= parseInheritance(Inherited, /*classRequirementLoc=*/nullptr);
CD->setInherited(Context.AllocateCopy(Inherited));
}
SmallVector<Decl*, 8> MemberDecls;
SourceLoc LBLoc, RBLoc;
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_class)) {
LBLoc = PreviousLoc;
RBLoc = LBLoc;
Status.setIsParseError();
} else {
// Parse the body.
ContextChange CC(*this, CD);
Scope S(this, ScopeKind::ClassBody);
ParseDeclOptions Options(PD_HasContainerType | PD_AllowDestructor |
PD_InClass);
if (parseNominalDeclMembers(MemberDecls, LBLoc, RBLoc,
diag::expected_rbrace_class,
Options))
Status.setIsParseError();
}
CD->setBraces({LBLoc, RBLoc});
for (auto member : MemberDecls) {
CD->addMember(member);
if (isa<DestructorDecl>(member))
CD->setHasDestructor();
}
addToScope(CD);
if (Flags & PD_DisallowNominalTypes) {
diagnose(ClassLoc, diag::disallowed_type);
Status.setIsParseError();
CD->setInvalid();
}
return DCC.fixupParserResult(Status, CD);
}
/// \brief Parse a 'protocol' declaration, doing no token skipping on error.
///
/// \verbatim
/// decl-protocol:
/// protocol-head '{' protocol-member* '}'
///
/// protocol-head:
/// 'protocol' attribute-list identifier inheritance?
///
/// protocol-member:
/// decl-func
/// decl-var-simple
/// decl-typealias
/// \endverbatim
ParserResult<ProtocolDecl> Parser::
parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes) {
SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol);
SourceLoc NameLoc;
Identifier ProtocolName;
ParserStatus Status;
Status |=
parseIdentifierDeclName(*this, ProtocolName, NameLoc, tok::colon,
tok::l_brace, diag::expected_identifier_in_decl,
"protocol");
if (Status.isError())
return nullptr;
// Protocols don't support generic parameters, but people often want them and
// we want to have good error recovery if they try them out. Parse them and
// produce a specific diagnostic if present.
if (startsWithLess(Tok)) {
diagnose(Tok, diag::generic_arguments_protocol);
Scope S(this, ScopeKind::Generics);
maybeParseGenericParams();
}
DebuggerContextChange DCC (*this);
// Parse optional inheritance clause.
SmallVector<TypeLoc, 4> InheritedProtocols;
SourceLoc classRequirementLoc;
SourceLoc colonLoc;
if (Tok.is(tok::colon)) {
colonLoc = Tok.getLoc();
Status |= parseInheritance(InheritedProtocols, &classRequirementLoc);
}
ProtocolDecl *Proto
= new (Context) ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc,
ProtocolName,
Context.AllocateCopy(InheritedProtocols));
// No need to setLocalDiscriminator: protocols can't appear in local contexts.
// If there was a 'class' requirement, mark this as a class-bounded protocol.
if (classRequirementLoc.isValid())
Proto->setRequiresClass();
Proto->getAttrs() = Attributes;
ContextChange CC(*this, Proto);
Scope ProtocolBodyScope(this, ScopeKind::ProtocolBody);
// Parse the body.
{
// The list of protocol elements.
SmallVector<Decl*, 8> Members;
SourceLoc LBraceLoc;
SourceLoc RBraceLoc;
if (parseToken(tok::l_brace, LBraceLoc, diag::expected_lbrace_protocol)) {
LBraceLoc = PreviousLoc;
RBraceLoc = LBraceLoc;
Status.setIsParseError();
} else {
// Parse the members.
ParseDeclOptions Options(PD_HasContainerType |
PD_DisallowNominalTypes |
PD_DisallowInit | PD_DisallowTypeAliasDef |
PD_InProtocol);
if (parseNominalDeclMembers(Members, LBraceLoc, RBraceLoc,
diag::expected_rbrace_protocol,
Options))
Status.setIsParseError();
}
// Install the protocol elements.
Proto->setBraces({LBraceLoc, RBraceLoc});
for (auto member : Members)
Proto->addMember(member);
}
if (Flags & PD_DisallowNominalTypes) {
diagnose(ProtocolLoc, diag::disallowed_type);
Status.setIsParseError();
Proto->setInvalid();
} else if (!DCC.movedToTopLevel() && !(Flags & PD_AllowTopLevel)) {
diagnose(ProtocolLoc, diag::decl_inner_scope);
Status.setIsParseError();
Proto->setInvalid();
}
return DCC.fixupParserResult(Status, Proto);
}
/// \brief Parse a 'subscript' declaration.
///
/// \verbatim
/// decl-subscript:
/// subscript-head get-set
/// subscript-head
/// 'subscript' attribute-list parameter-clause '->' type
/// \endverbatim
ParserStatus Parser::parseDeclSubscript(ParseDeclOptions Flags,
DeclAttributes &Attributes,
SmallVectorImpl<Decl *> &Decls) {
ParserStatus Status;
SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript);
// parameter-clause
if (Tok.isNot(tok::l_paren)) {
diagnose(Tok, diag::expected_lparen_subscript);
return makeParserError();
}
SmallVector<Identifier, 4> argumentNames;
ParserResult<ParameterList> Indices
= parseSingleParameterClause(ParameterContextKind::Subscript,
&argumentNames);
if (Indices.isNull() || Indices.hasCodeCompletion())
return Indices;
// '->'
if (!Tok.is(tok::arrow)) {
diagnose(Tok, diag::expected_arrow_subscript);
return makeParserError();
}
SourceLoc ArrowLoc = consumeToken();
// type
ParserResult<TypeRepr> ElementTy = parseType(diag::expected_type_subscript);
if (ElementTy.isNull() || ElementTy.hasCodeCompletion())
return ElementTy;
// Build an AST for the subscript declaration.
DeclName name = DeclName(Context, Context.Id_subscript, argumentNames);
auto *Subscript = new (Context) SubscriptDecl(name,
SubscriptLoc, Indices.get(),
ArrowLoc, ElementTy.get(),
CurDeclContext);
Subscript->getAttrs() = Attributes;
Decls.push_back(Subscript);
// '{'
// Parse getter and setter.
ParsedAccessors accessors;
if (Tok.isNot(tok::l_brace)) {
// Subscript declarations must always have at least a getter, so they need
// to be followed by a {.
if (Flags.contains(PD_InProtocol))
diagnose(Tok, diag::expected_lbrace_subscript_protocol)
.fixItInsertAfter(ElementTy.get()->getEndLoc(), " { get set }");
else
diagnose(Tok, diag::expected_lbrace_subscript);
Status.setIsParseError();
} else {
if (parseGetSet(Flags, Indices.get(), ElementTy.get(),
accessors, /*StaticLoc=*/SourceLoc(), Decls))
Status.setIsParseError();
}
bool Invalid = false;
// Reject 'subscript' functions outside of type decls
if (!(Flags & PD_HasContainerType)) {
diagnose(SubscriptLoc, diag::subscript_decl_wrong_scope);
Invalid = true;
}
accessors.record(*this, Subscript, (Invalid || !Status.isSuccess()),
Flags, /*static*/ SourceLoc(), Attributes,
ElementTy.get(), Indices.get(), Decls);
if (Invalid) {
Subscript->setType(ErrorType::get(Context));
Subscript->setInvalid();
}
// No need to setLocalDiscriminator because subscripts cannot
// validly appear outside of type decls.
return Status;
}
ParserResult<ConstructorDecl>
Parser::parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
assert(Tok.is(tok::kw_init));
SourceLoc ConstructorLoc = consumeToken();
OptionalTypeKind Failability = OTK_None;
SourceLoc FailabilityLoc;
const bool ConstructorsNotAllowed = !(Flags & PD_HasContainerType);
// Reject constructors outside of types.
if (ConstructorsNotAllowed) {
diagnose(Tok, diag::initializer_decl_wrong_scope);
}
// Parse the '!' or '?' for a failable initializer.
if (Tok.isAny(tok::exclaim_postfix, tok::sil_exclamation) ||
(Tok.isAnyOperator() && Tok.getText() == "!")) {
Failability = OTK_ImplicitlyUnwrappedOptional;
FailabilityLoc = consumeToken();
} else if (Tok.isAny(tok::question_postfix, tok::question_infix)) {
Failability = OTK_Optional;
FailabilityLoc = consumeToken();
}
// Parse the generic-params, if present.
Scope S(this, ScopeKind::Generics);
GenericParamList *GenericParams = maybeParseGenericParams();
// Parse the parameters.
// FIXME: handle code completion in Arguments.
DefaultArgumentInfo DefaultArgs(/*hasSelf*/true);
ParameterList *BodyPattern;
DeclName FullName;
ParserStatus SignatureStatus
= parseConstructorArguments(FullName, BodyPattern, DefaultArgs);
if (SignatureStatus.hasCodeCompletion() && !CodeCompletion) {
// Trigger delayed parsing, no need to continue.
return SignatureStatus;
}
// Protocol initializer arguments may not have default values.
if (Flags.contains(PD_InProtocol) && DefaultArgs.HasDefaultArgument) {
diagnose(ConstructorLoc, diag::protocol_init_argument_init);
return nullptr;
}
// Parse 'throws' or 'rethrows'.
SourceLoc throwsLoc;
if (consumeIf(tok::kw_throws, throwsLoc)) {
// okay
} else if (consumeIf(tok::kw_rethrows, throwsLoc)) {
Attributes.add(new (Context) RethrowsAttr(throwsLoc));
}
auto *SelfDecl = ParamDecl::createSelf(ConstructorLoc, CurDeclContext);
Scope S2(this, ScopeKind::ConstructorBody);
auto *CD = new (Context) ConstructorDecl(FullName, ConstructorLoc,
Failability, FailabilityLoc,
SelfDecl, BodyPattern,
GenericParams, throwsLoc,
CurDeclContext);
CtorInitializerKind initKind = CtorInitializerKind::Designated;
if (Attributes.hasAttribute<ConvenienceAttr>())
initKind = CtorInitializerKind::Convenience;
CD->setInitKind(initKind);
// No need to setLocalDiscriminator.
DefaultArgs.setFunctionContext(CD);
// Pass the function signature to code completion.
if (SignatureStatus.hasCodeCompletion())
CodeCompletion->setDelayedParsedDecl(CD);
if (ConstructorsNotAllowed || SignatureStatus.isError()) {
// Tell the type checker not to touch this constructor.
CD->setInvalid();
}
addToScope(SelfDecl);
addParametersToScope(BodyPattern);
// '{'
if (Tok.is(tok::l_brace)) {
// Record the curly braces but nothing inside.
SF.recordInterfaceToken("{");
SF.recordInterfaceToken("}");
llvm::SaveAndRestore<bool> T(IsParsingInterfaceTokens, false);
if (Flags.contains(PD_InProtocol)) {
diagnose(Tok, diag::protocol_init_with_body);
skipUntilDeclRBrace();
} else {
// Parse the body.
ParseFunctionBody CC(*this, CD);
if (!isDelayedParsingEnabled()) {
ParserResult<BraceStmt> Body =
parseBraceItemList(diag::invalid_diagnostic);
if (!Body.isNull())
CD->setBody(Body.get());
} else {
consumeAbstractFunctionBody(CD, Attributes);
}
}
}
CD->getAttrs() = Attributes;
return makeParserResult(CD);
}
ParserResult<DestructorDecl> Parser::
parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes) {
SourceLoc DestructorLoc = consumeToken(tok::kw_deinit);
// Parse extraneous parentheses and remove them with a fixit.
if (Tok.is(tok::l_paren)) {
SourceRange ParenRange;
SourceLoc LParenLoc = consumeToken();
SourceLoc RParenLoc;
skipUntil(tok::r_paren);
if (Tok.is(tok::r_paren)) {
SourceLoc RParenLoc = consumeToken();
ParenRange = SourceRange(LParenLoc, RParenLoc);
diagnose(ParenRange.Start, diag::destructor_params)
.fixItRemoveChars(Lexer::getLocForEndOfToken(Context.SourceMgr,
DestructorLoc),
Lexer::getLocForEndOfToken(Context.SourceMgr,
ParenRange.End));
} else {
diagnose(Tok, diag::opened_destructor_expected_rparen);
diagnose(LParenLoc, diag::opening_paren);
}
}
// '{'
if (!Tok.is(tok::l_brace)) {
if (!Tok.is(tok::l_brace) && !isInSILMode()) {
diagnose(Tok, diag::expected_lbrace_destructor);
return nullptr;
}
}
auto *SelfDecl = ParamDecl::createSelf(DestructorLoc, CurDeclContext);
Scope S(this, ScopeKind::DestructorBody);
auto *DD = new (Context) DestructorDecl(Context.Id_deinit, DestructorLoc,
SelfDecl, CurDeclContext);
// Parse the body.
if (Tok.is(tok::l_brace)) {
// Record the curly braces but nothing inside.
SF.recordInterfaceToken("{");
SF.recordInterfaceToken("}");
llvm::SaveAndRestore<bool> T(IsParsingInterfaceTokens, false);
ParseFunctionBody CC(*this, DD);
if (!isDelayedParsingEnabled()) {
ParserResult<BraceStmt> Body=parseBraceItemList(diag::invalid_diagnostic);
if (!Body.isNull())
DD->setBody(Body.get());
} else {
consumeAbstractFunctionBody(DD, Attributes);
}
}
DD->getAttrs() = Attributes;
// Reject 'destructor' functions outside of classes
if (!(Flags & PD_AllowDestructor)) {
diagnose(DestructorLoc, diag::destructor_decl_outside_class);
// Tell the type checker not to touch this destructor.
DD->setInvalid();
}
return makeParserResult(DD);
}
ParserResult<OperatorDecl>
Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
SourceLoc OperatorLoc = consumeToken(tok::kw_operator);
bool AllowTopLevel = Flags.contains(PD_AllowTopLevel);
if (!Tok.isAnyOperator() && !Tok.is(tok::exclaim_postfix)) {
// A common error is to try to define an operator with something in the
// unicode plane considered to be an operator, or to try to define an
// operator like "not". Diagnose this specifically.
if (Tok.is(tok::identifier))
diagnose(Tok, diag::identifier_when_expecting_operator,
Context.getIdentifier(Tok.getText()));
else
diagnose(Tok, diag::expected_operator_name_after_operator);
// To improve recovery, check to see if we have a { right after this token.
// If so, swallow until the end } to avoid tripping over the body of the
// malformed operator decl.
if (peekToken().is(tok::l_brace)) {
consumeToken();
skipSingle();
}
return nullptr;
}
DebuggerContextChange DCC (*this);
Identifier Name = Context.getIdentifier(Tok.getText());
SourceLoc NameLoc = consumeToken();
if (!Tok.is(tok::l_brace)) {
diagnose(Tok, diag::expected_lbrace_after_operator);
return nullptr;
}
ParserResult<OperatorDecl> Result;
if (Attributes.hasAttribute<PrefixAttr>())
Result = parseDeclPrefixOperator(OperatorLoc, Name, NameLoc, Attributes);
else if (Attributes.hasAttribute<PostfixAttr>())
Result = parseDeclPostfixOperator(OperatorLoc, Name, NameLoc, Attributes);
else {
if (!Attributes.hasAttribute<InfixAttr>())
diagnose(OperatorLoc, diag::operator_decl_no_fixity);
Result = parseDeclInfixOperator(OperatorLoc, Name, NameLoc, Attributes);
}
if (Tok.is(tok::r_brace))
consumeToken();
if (!DCC.movedToTopLevel() && !AllowTopLevel) {
diagnose(OperatorLoc, diag::operator_decl_inner_scope);
return nullptr;
}
return DCC.fixupParserResult(Result);
}
ParserResult<OperatorDecl>
Parser::parseDeclPrefixOperator(SourceLoc OperatorLoc, Identifier Name,
SourceLoc NameLoc, DeclAttributes &Attributes) {
SourceLoc LBraceLoc = consumeToken(tok::l_brace);
while (!Tok.is(tok::r_brace)) {
// Currently there are no operator attributes for prefix operators.
if (Tok.is(tok::identifier))
diagnose(Tok, diag::unknown_prefix_operator_attribute, Tok.getText());
else
diagnose(Tok, diag::expected_operator_attribute);
skipUntilDeclRBrace();
return nullptr;
}
SourceLoc RBraceLoc = Tok.getLoc();
auto *Res =
new (Context) PrefixOperatorDecl(CurDeclContext, OperatorLoc,
Name, NameLoc, LBraceLoc, RBraceLoc);
Res->getAttrs() = Attributes;
return makeParserResult(Res);
}
ParserResult<OperatorDecl>
Parser::parseDeclPostfixOperator(SourceLoc OperatorLoc,
Identifier Name, SourceLoc NameLoc,
DeclAttributes &Attributes) {
SourceLoc LBraceLoc = consumeToken(tok::l_brace);
while (!Tok.is(tok::r_brace)) {
// Currently there are no operator attributes for postfix operators.
if (Tok.is(tok::identifier))
diagnose(Tok, diag::unknown_postfix_operator_attribute, Tok.getText());
else
diagnose(Tok, diag::expected_operator_attribute);
skipUntilDeclRBrace();
return nullptr;
}
SourceLoc RBraceLoc = Tok.getLoc();
auto Res =
new (Context) PostfixOperatorDecl(CurDeclContext, OperatorLoc,
Name, NameLoc, LBraceLoc, RBraceLoc);
Res->getAttrs() = Attributes;
return makeParserResult(Res);
}
ParserResult<OperatorDecl>
Parser::parseDeclInfixOperator(SourceLoc OperatorLoc, Identifier Name,
SourceLoc NameLoc, DeclAttributes &Attributes) {
SourceLoc LBraceLoc = consumeToken(tok::l_brace);
// Initialize InfixData with default attributes:
// precedence 100, associativity none, non-assignment
unsigned char precedence = 100;
Associativity associativity = Associativity::None;
bool assignment = false;
SourceLoc AssociativityLoc, AssociativityValueLoc,
PrecedenceLoc, PrecedenceValueLoc,
AssignmentLoc;
while (!Tok.is(tok::r_brace)) {
if (!Tok.is(tok::identifier)) {
diagnose(Tok, diag::expected_operator_attribute);
skipUntilDeclRBrace();
return nullptr;
}
if (Tok.getText().equals("associativity")) {
if (AssociativityLoc.isValid()) {
diagnose(Tok, diag::operator_associativity_redeclared);
skipUntilDeclRBrace();
return nullptr;
}
AssociativityLoc = consumeToken();
if (!Tok.is(tok::identifier)) {
diagnose(Tok, diag::expected_infix_operator_associativity);
skipUntilDeclRBrace();
return nullptr;
}
auto parsedAssociativity
= llvm::StringSwitch<Optional<Associativity>>(Tok.getText())
.Case("none", Associativity::None)
.Case("left", Associativity::Left)
.Case("right", Associativity::Right)
.Default(None);
if (!parsedAssociativity) {
diagnose(Tok, diag::unknown_infix_operator_associativity, Tok.getText());
skipUntilDeclRBrace();
return nullptr;
}
associativity = *parsedAssociativity;
AssociativityValueLoc = consumeToken();
continue;
}
if (Tok.getText().equals("precedence")) {
if (PrecedenceLoc.isValid()) {
diagnose(Tok, diag::operator_precedence_redeclared);
skipUntilDeclRBrace();
return nullptr;
}
PrecedenceLoc = consumeToken();
if (!Tok.is(tok::integer_literal)) {
diagnose(Tok, diag::expected_infix_operator_precedence);
skipUntilDeclRBrace();
return nullptr;
}
if (Tok.getText().getAsInteger(0, precedence)) {
diagnose(Tok, diag::invalid_infix_operator_precedence);
precedence = 255;
}
PrecedenceValueLoc = consumeToken();
continue;
}
if (Tok.getText().equals("assignment")) {
if (AssignmentLoc.isValid()) {
diagnose(Tok, diag::operator_assignment_redeclared);
skipUntilDeclRBrace();
return nullptr;
}
AssignmentLoc = consumeToken();
assignment = true;
continue;
}
diagnose(Tok, diag::unknown_infix_operator_attribute, Tok.getText());
skipUntilDeclRBrace();
return nullptr;
}
SourceLoc RBraceLoc = Tok.getLoc();
auto Res = new (Context)
InfixOperatorDecl(CurDeclContext, OperatorLoc, Name, NameLoc, LBraceLoc,
AssociativityLoc.isInvalid(), AssociativityLoc,
AssociativityValueLoc, PrecedenceLoc.isInvalid(),
PrecedenceLoc, PrecedenceValueLoc,
AssignmentLoc.isInvalid(), AssignmentLoc,
RBraceLoc,
InfixData(precedence, associativity, assignment));
Res->getAttrs() = Attributes;
return makeParserResult(Res);
}