mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Variable declarations are declarations led by either 'var' or 'let'. It can contain multiple pattern bindings as children. For patterns, this patch only creates syntax nodes for simple identifier patterns, e.g. 'a = 3'. The rest of the pattern kinds are still left unknown (UnknownPattern).
6259 lines
216 KiB
C++
6259 lines
216 KiB
C++
//===--- ParseDecl.cpp - Swift Language Parser for Declarations -----------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://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/Parse/ParseSILSupport.h"
|
|
#include "swift/Syntax/SyntaxFactory.h"
|
|
#include "swift/Syntax/TokenSyntax.h"
|
|
#include "swift/Syntax/SyntaxParsingContext.h"
|
|
#include "swift/Subsystems.h"
|
|
#include "swift/AST/Attr.h"
|
|
#include "swift/AST/DebuggerClient.h"
|
|
#include "swift/AST/DiagnosticsParse.h"
|
|
#include "swift/AST/Initializer.h"
|
|
#include "swift/AST/Module.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "llvm/Support/Compiler.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;
|
|
using namespace syntax;
|
|
|
|
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()
|
|
{
|
|
ModuleDecl *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;
|
|
auto *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);
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// \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))
|
|
consumeTokenWithoutFeedingReceiver();
|
|
|
|
// 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");
|
|
SIL->parseDeclSIL(*this);
|
|
} else if (Tok.is(tok::kw_sil_stage)) {
|
|
assert(isInSILMode() && "'sil_stage' should only be a keyword in SIL mode");
|
|
SIL->parseDeclSILStage(*this);
|
|
} else if (Tok.is(tok::kw_sil_vtable)) {
|
|
assert(isInSILMode() &&
|
|
"'sil_vtable' should only be a keyword in SIL mode");
|
|
SIL->parseSILVTable(*this);
|
|
} else if (Tok.is(tok::kw_sil_global)) {
|
|
assert(isInSILMode() &&
|
|
"'sil_global' should only be a keyword in SIL mode");
|
|
SIL->parseSILGlobal(*this);
|
|
} else if (Tok.is(tok::kw_sil_witness_table)) {
|
|
assert(isInSILMode() &&
|
|
"'sil_witness_table' should only be a keyword in SIL mode");
|
|
SIL->parseSILWitnessTable(*this);
|
|
} else if (Tok.is(tok::kw_sil_default_witness_table)) {
|
|
assert(isInSILMode() &&
|
|
"'sil_default_witness_table' should only be a keyword in SIL mode");
|
|
SIL->parseSILDefaultWitnessTable(*this);
|
|
} else if (Tok.is(tok::kw_sil_coverage_map)) {
|
|
assert(isInSILMode() &&
|
|
"'sil_coverage_map' should only be a keyword in SIL mode");
|
|
SIL->parseSILCoverageMap(*this);
|
|
} else if (Tok.is(tok::kw_sil_scope)) {
|
|
assert(isInSILMode() && "'sil_scope' should only be a keyword in SIL mode");
|
|
SIL->parseSILScope(*this);
|
|
} 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_conditional_compilation_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 (auto *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(getParserPosition(),
|
|
InPoundLineEnvironment);
|
|
|
|
// If we are done parsing the whole file, finalize the token receiver.
|
|
if (Tok.is(tok::eof))
|
|
TokReceiver->finalize();
|
|
|
|
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::parseSpecializeAttribute(swift::tok ClosingBrace, SourceLoc AtLoc,
|
|
SourceLoc Loc, SpecializeAttr *&Attr) {
|
|
assert(ClosingBrace == tok::r_paren || ClosingBrace == tok::r_square);
|
|
SourceLoc lParenLoc = consumeToken();
|
|
bool DiscardAttribute = false;
|
|
StringRef AttrName = "_specialize";
|
|
|
|
Optional<bool> exported;
|
|
Optional<SpecializeAttr::SpecializationKind> kind;
|
|
|
|
// Parse optional "exported" and "kind" labeled parameters.
|
|
while (!Tok.is(tok::kw_where)) {
|
|
if (Tok.is(tok::identifier)) {
|
|
auto ParamLabel = Tok.getText();
|
|
if (ParamLabel != "exported" && ParamLabel != "kind") {
|
|
diagnose(Tok.getLoc(), diag::attr_specialize_unknown_parameter_name,
|
|
ParamLabel);
|
|
}
|
|
consumeToken();
|
|
if (!consumeIf(tok::colon)) {
|
|
diagnose(Tok.getLoc(), diag::attr_specialize_missing_colon, ParamLabel);
|
|
skipUntil(tok::comma, tok::kw_where);
|
|
if (Tok.is(ClosingBrace))
|
|
break;
|
|
if (Tok.is(tok::kw_where)) {
|
|
continue;
|
|
}
|
|
if (Tok.is(tok::comma)) {
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
DiscardAttribute = true;
|
|
return false;
|
|
}
|
|
if ((ParamLabel == "exported" && exported.hasValue()) ||
|
|
(ParamLabel == "kind" && kind.hasValue())) {
|
|
diagnose(Tok.getLoc(), diag::attr_specialize_parameter_already_defined,
|
|
ParamLabel);
|
|
}
|
|
if (ParamLabel == "exported") {
|
|
bool isTrue = consumeIf(tok::kw_true);
|
|
bool isFalse = consumeIf(tok::kw_false);
|
|
if (!isTrue && !isFalse) {
|
|
diagnose(Tok.getLoc(), diag::attr_specialize_expected_bool_value);
|
|
skipUntil(tok::comma, tok::kw_where);
|
|
if (Tok.is(ClosingBrace))
|
|
break;
|
|
if (Tok.is(tok::kw_where)) {
|
|
continue;
|
|
}
|
|
if (Tok.is(tok::comma)) {
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
DiscardAttribute = true;
|
|
return false;
|
|
}
|
|
if (ParamLabel == "exported") {
|
|
exported = isTrue ? true : false;
|
|
}
|
|
}
|
|
if (ParamLabel == "kind") {
|
|
SourceLoc paramValueLoc;
|
|
if (Tok.is(tok::identifier)) {
|
|
if (Tok.getText() == "partial") {
|
|
kind = SpecializeAttr::SpecializationKind::Partial;
|
|
} else if (Tok.getText() == "full") {
|
|
kind = SpecializeAttr::SpecializationKind::Full;
|
|
} else {
|
|
diagnose(Tok.getLoc(),
|
|
diag::attr_specialize_expected_partial_or_full);
|
|
}
|
|
consumeToken();
|
|
} else if (consumeIf(tok::kw_true, paramValueLoc) ||
|
|
consumeIf(tok::kw_false, paramValueLoc)) {
|
|
diagnose(paramValueLoc,
|
|
diag::attr_specialize_expected_partial_or_full);
|
|
}
|
|
}
|
|
if (!consumeIf(tok::comma)) {
|
|
diagnose(Tok.getLoc(), diag::attr_specialize_missing_comma);
|
|
skipUntil(tok::comma, tok::kw_where);
|
|
if (Tok.is(ClosingBrace))
|
|
break;
|
|
if (Tok.is(tok::kw_where)) {
|
|
continue;
|
|
}
|
|
if (Tok.is(tok::comma)) {
|
|
consumeToken();
|
|
continue;
|
|
}
|
|
DiscardAttribute = true;
|
|
return false;
|
|
}
|
|
continue;
|
|
}
|
|
diagnose(Tok.getLoc(),
|
|
diag::attr_specialize_missing_parameter_label_or_where_clause);
|
|
DiscardAttribute = true;
|
|
return false;
|
|
};
|
|
|
|
// Parse the where clause.
|
|
TrailingWhereClause *trailingWhereClause = nullptr;
|
|
if (Tok.is(tok::kw_where)) {
|
|
SourceLoc whereLoc;
|
|
SmallVector<RequirementRepr, 4> requirements;
|
|
bool firstTypeInComplete;
|
|
parseGenericWhereClause(whereLoc, requirements, firstTypeInComplete,
|
|
/* AllowLayoutConstraints */ true);
|
|
trailingWhereClause =
|
|
TrailingWhereClause::create(Context, whereLoc, requirements);
|
|
}
|
|
|
|
// Parse the closing ')' or ']'.
|
|
SourceLoc rParenLoc;
|
|
if (!consumeIf(ClosingBrace, rParenLoc)) {
|
|
if (ClosingBrace == tok::r_paren)
|
|
diagnose(lParenLoc, diag::attr_expected_rparen, AttrName,
|
|
/*DeclModifier=*/false);
|
|
else if (ClosingBrace == tok::r_square)
|
|
diagnose(lParenLoc, diag::attr_expected_rparen, AttrName,
|
|
/*DeclModifier=*/false);
|
|
return false;
|
|
}
|
|
// Not exported by default.
|
|
if (!exported.hasValue())
|
|
exported = false;
|
|
// Full specialization by default.
|
|
if (!kind.hasValue())
|
|
kind = SpecializeAttr::SpecializationKind::Full;
|
|
|
|
if (DiscardAttribute) {
|
|
Attr = nullptr;
|
|
return false;
|
|
}
|
|
// Store the attribute.
|
|
Attr = SpecializeAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc),
|
|
trailingWhereClause, exported.getValue(),
|
|
kind.getValue());
|
|
return true;
|
|
}
|
|
|
|
ParserResult<ImplementsAttr>
|
|
Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
|
|
StringRef AttrName = "_implements";
|
|
ParserStatus Status;
|
|
|
|
if (Tok.isNot(tok::l_paren)) {
|
|
diagnose(Loc, diag::attr_expected_lparen, AttrName,
|
|
/*DeclModifier=*/false);
|
|
Status.setIsParseError();
|
|
return Status;
|
|
}
|
|
|
|
SourceLoc lParenLoc = consumeToken();
|
|
|
|
ParserResult<TypeRepr> ProtocolType = parseType();
|
|
Status |= ProtocolType;
|
|
|
|
if (!(Status.shouldStopParsing() || consumeIf(tok::comma))) {
|
|
diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName,
|
|
/*DeclModifier=*/false);
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
DeclNameLoc MemberNameLoc;
|
|
DeclName MemberName;
|
|
if (!Status.shouldStopParsing()) {
|
|
MemberName =
|
|
parseUnqualifiedDeclName(/*afterDot=*/false, MemberNameLoc,
|
|
diag::attr_implements_expected_member_name,
|
|
/*allowOperators=*/true,
|
|
/*allowZeroArgCompoundNames=*/true);
|
|
if (!MemberName) {
|
|
Status.setIsParseError();
|
|
}
|
|
}
|
|
|
|
if (Status.isError()) {
|
|
skipUntil(tok::r_paren);
|
|
}
|
|
|
|
SourceLoc rParenLoc;
|
|
if (!consumeIf(tok::r_paren, rParenLoc)) {
|
|
diagnose(lParenLoc, diag::attr_expected_rparen, AttrName,
|
|
/*DeclModifier=*/false);
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
if (Status.isError()) {
|
|
return Status;
|
|
}
|
|
|
|
return ParserResult<ImplementsAttr>(
|
|
ImplementsAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc),
|
|
ProtocolType.get(), MemberName, MemberNameLoc));
|
|
}
|
|
|
|
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();
|
|
|
|
// We can only make this attribute a token list intead of an Attribute node
|
|
// because the attribute node may include '@'
|
|
SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);
|
|
|
|
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 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;
|
|
|
|
|
|
// Check 'Tok', return false if ':' or '=' cannot be found.
|
|
// Complain if '=' is found and suggest replacing it with ": ".
|
|
auto findAttrValueDelimiter = [&]() -> bool {
|
|
if (!Tok.is(tok::colon)) {
|
|
if (!Tok.is(tok::equal))
|
|
return false;
|
|
|
|
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
|
|
.fixItReplace(Tok.getLoc(), ": ");
|
|
}
|
|
return true;
|
|
};
|
|
|
|
switch (DK) {
|
|
case DAK_Count:
|
|
llvm_unreachable("DAK_Count should not appear in parsing switch");
|
|
|
|
case DAK_RawDocComment:
|
|
case DAK_ObjCBridged:
|
|
case DAK_ObjCRuntimeName:
|
|
case DAK_RestatedObjCConformance:
|
|
case DAK_SynthesizedProtocol:
|
|
llvm_unreachable("virtual attributes should not be parsed "
|
|
"by attribute parsing code");
|
|
case DAK_SetterAccess:
|
|
llvm_unreachable("handled by DAK_AccessControl");
|
|
|
|
#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::optimization_attribute_expect_option, AttrName,
|
|
"none");
|
|
return false;
|
|
}
|
|
|
|
InlineKind kind;
|
|
if (Tok.getText() == "never")
|
|
kind = InlineKind::Never;
|
|
else if (Tok.getText() == "__always")
|
|
kind = InlineKind::Always;
|
|
else {
|
|
diagnose(Loc, diag::optimization_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_Optimize: {
|
|
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::optimization_attribute_expect_option, AttrName,
|
|
"speed");
|
|
return false;
|
|
}
|
|
|
|
OptimizationMode optMode = OptimizationMode::NotSet;
|
|
if (Tok.getText() == "none")
|
|
optMode = OptimizationMode::NoOptimization;
|
|
else if (Tok.getText() == "speed")
|
|
optMode = OptimizationMode::ForSpeed;
|
|
else if (Tok.getText() == "size")
|
|
optMode = OptimizationMode::ForSize;
|
|
else {
|
|
diagnose(Loc, diag::optimization_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) OptimizeAttr(AtLoc, AttrRange, optMode));
|
|
|
|
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_AccessControl: {
|
|
|
|
// Diagnose using access control in a local scope, which isn't meaningful.
|
|
if (CurDeclContext->isLocalContext()) {
|
|
diagnose(Loc, diag::attr_only_at_non_local_scope, AttrName);
|
|
}
|
|
|
|
AccessLevel access = llvm::StringSwitch<AccessLevel>(AttrName)
|
|
.Case("private", AccessLevel::Private)
|
|
.Case("fileprivate", AccessLevel::FilePrivate)
|
|
.Case("internal", AccessLevel::Internal)
|
|
.Case("public", AccessLevel::Public)
|
|
.Case("open", AccessLevel::Open);
|
|
|
|
if (!consumeIf(tok::l_paren)) {
|
|
// Normal access control attribute.
|
|
AttrRange = Loc;
|
|
DuplicateAttribute = Attributes.getAttribute<AccessControlAttr>();
|
|
if (!DuplicateAttribute)
|
|
Attributes.add(new (Context) AccessControlAttr(AtLoc, Loc, access));
|
|
break;
|
|
}
|
|
|
|
// Parse the subject.
|
|
if (Tok.isContextualKeyword("set")) {
|
|
consumeToken();
|
|
} else {
|
|
diagnose(Loc, diag::attr_access_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<SetterAccessAttr>();
|
|
if (!DuplicateAttribute) {
|
|
Attributes.add(new (Context) SetterAccessAttr(AtLoc, AttrRange, access));
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case DAK_CDecl:
|
|
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) {
|
|
if (DK == DAK_SILGenName)
|
|
Attributes.add(new (Context) SILGenNameAttr(AsmName.getValue(), AtLoc,
|
|
AttrRange, /*Implicit=*/false));
|
|
else if (DK == DAK_CDecl)
|
|
Attributes.add(new (Context) CDeclAttr(AsmName.getValue(), AtLoc,
|
|
AttrRange, /*Implicit=*/false));
|
|
else
|
|
llvm_unreachable("out of sync with switch");
|
|
}
|
|
|
|
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)) {
|
|
diagnose(Loc, diag::alignment_must_be_positive_integer);
|
|
return false;
|
|
}
|
|
|
|
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)
|
|
//
|
|
// Similarly if we have a language version spec in the spec
|
|
// list, create an implicit AvailableAttr with the specified
|
|
// version as the introduced argument. For example, if we have
|
|
// @available(swift 3.1)
|
|
// we will synthesize
|
|
// @available(swift, introduced: 3.1)
|
|
|
|
for (auto *Spec : Specs) {
|
|
PlatformKind Platform;
|
|
clang::VersionTuple Version;
|
|
SourceRange VersionRange;
|
|
PlatformAgnosticAvailabilityKind PlatformAgnostic;
|
|
|
|
if (auto *PlatformVersionSpec =
|
|
dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) {
|
|
Platform = PlatformVersionSpec->getPlatform();
|
|
Version = PlatformVersionSpec->getVersion();
|
|
VersionRange = PlatformVersionSpec->getVersionSrcRange();
|
|
PlatformAgnostic = PlatformAgnosticAvailabilityKind::None;
|
|
|
|
} else if (auto *LanguageVersionSpec =
|
|
dyn_cast<LanguageVersionConstraintAvailabilitySpec>(Spec)) {
|
|
Platform = PlatformKind::none;
|
|
Version = LanguageVersionSpec->getVersion();
|
|
VersionRange = LanguageVersionSpec->getVersionSrcRange();
|
|
PlatformAgnostic =
|
|
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific;
|
|
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
Attributes.add(new (Context)
|
|
AvailableAttr(AtLoc, AttrRange,
|
|
Platform,
|
|
/*Message=*/StringRef(),
|
|
/*Rename=*/StringRef(),
|
|
/*Introduced=*/Version,
|
|
/*IntroducedRange=*/VersionRange,
|
|
/*Deprecated=*/clang::VersionTuple(),
|
|
/*DeprecatedRange=*/SourceRange(),
|
|
/*Obsoleted=*/clang::VersionTuple(),
|
|
/*ObsoletedRange=*/SourceRange(),
|
|
PlatformAgnostic,
|
|
/*Implicit=*/false));
|
|
}
|
|
|
|
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;
|
|
SourceRange IntroducedRange, DeprecatedRange, ObsoletedRange;
|
|
auto PlatformAgnostic = PlatformAgnosticAvailabilityKind::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 (findAttrValueDelimiter()) {
|
|
consumeToken();
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
if (ArgumentKind == IsMessage) {
|
|
Message = Value.getValue();
|
|
} else {
|
|
ParsedDeclName parsedName = parseDeclName(Value.getValue());
|
|
if (!parsedName) {
|
|
diagnose(Loc, diag::attr_availability_invalid_renamed, AttrName);
|
|
DiscardAttribute = true;
|
|
continue;
|
|
}
|
|
Renamed = Value.getValue();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IsDeprecated:
|
|
if (!findAttrValueDelimiter()) {
|
|
if (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) {
|
|
diagnose(Tok, diag::attr_availability_unavailable_deprecated,
|
|
AttrName);
|
|
}
|
|
|
|
PlatformAgnostic = PlatformAgnosticAvailabilityKind::Deprecated;
|
|
break;
|
|
}
|
|
LLVM_FALLTHROUGH;
|
|
|
|
case IsIntroduced:
|
|
case IsObsoleted: {
|
|
// Items with version arguments.
|
|
if (findAttrValueDelimiter()) {
|
|
consumeToken();
|
|
} else {
|
|
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;
|
|
|
|
auto &VersionRange = (ArgumentKind == IsIntroduced) ? IntroducedRange :
|
|
(ArgumentKind == IsDeprecated) ? DeprecatedRange :
|
|
ObsoletedRange;
|
|
|
|
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 (PlatformAgnostic != PlatformAgnosticAvailabilityKind::None) {
|
|
diagnose(Tok, diag::attr_availability_unavailable_deprecated,
|
|
AttrName);
|
|
}
|
|
|
|
PlatformAgnostic = PlatformAgnosticAvailabilityKind::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);
|
|
|
|
// Treat 'swift' as a valid version-qualifying token, when
|
|
// at least some versions were mentioned and no other
|
|
// platform-agnostic availability spec has been provided.
|
|
bool SomeVersion = (!Introduced.empty() ||
|
|
!Deprecated.empty() ||
|
|
!Obsoleted.empty());
|
|
if (!PlatformKind.hasValue() &&
|
|
Platform == "swift" &&
|
|
SomeVersion &&
|
|
PlatformAgnostic == PlatformAgnosticAvailabilityKind::None) {
|
|
PlatformKind = PlatformKind::none;
|
|
PlatformAgnostic =
|
|
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific;
|
|
}
|
|
|
|
if (PlatformKind.hasValue()) {
|
|
Attributes.add(new (Context)
|
|
AvailableAttr(AtLoc, AttrRange,
|
|
PlatformKind.getValue(),
|
|
Message, Renamed,
|
|
Introduced, IntroducedRange,
|
|
Deprecated, DeprecatedRange,
|
|
Obsoleted, ObsoletedRange,
|
|
PlatformAgnostic,
|
|
/*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_Specialize: {
|
|
if (Tok.isNot(tok::l_paren)) {
|
|
diagnose(Loc, diag::attr_expected_lparen, AttrName,
|
|
DeclAttribute::isDeclModifier(DK));
|
|
return false;
|
|
}
|
|
SpecializeAttr *Attr;
|
|
if (!parseSpecializeAttribute(tok::r_paren, AtLoc, Loc, Attr))
|
|
return false;
|
|
|
|
Attributes.add(Attr);
|
|
break;
|
|
}
|
|
|
|
case DAK_Implements: {
|
|
ParserResult<ImplementsAttr> Attr = parseImplementsAttribute(AtLoc, Loc);
|
|
if (Attr.isNonNull()) {
|
|
Attributes.add(Attr.get());
|
|
}
|
|
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 && Tok.getText() == "warn_unused_result") {
|
|
// The behavior created by @warn_unused_result is now the default. Emit a
|
|
// Fix-It to remove.
|
|
SourceLoc attrLoc = consumeToken();
|
|
|
|
// @warn_unused_result with no arguments.
|
|
if (Tok.isNot(tok::l_paren)) {
|
|
diagnose(AtLoc, diag::attr_warn_unused_result_removed)
|
|
.fixItRemove(SourceRange(AtLoc, attrLoc));
|
|
|
|
return false;
|
|
}
|
|
|
|
// @warn_unused_result with arguments.
|
|
SourceLoc lParenLoc = consumeToken();
|
|
skipUntil(tok::r_paren);
|
|
|
|
// 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();
|
|
}
|
|
|
|
diagnose(AtLoc, diag::attr_warn_unused_result_removed)
|
|
.fixItRemove(SourceRange(AtLoc, rParenLoc));
|
|
|
|
return false;
|
|
}
|
|
|
|
// FIXME: Remove this after Swift 4.
|
|
if (DK == DAK_Count && Tok.getText() == "NSKeyedArchiverClassName") {
|
|
auto activeDiag = diagnose(Tok,diag::attr_nskeyedarchiverclassname_removed);
|
|
activeDiag.fixItReplace(Tok.getLoc(), "objc");
|
|
consumeToken();
|
|
SourceLoc lParenLoc;
|
|
if (consumeIf(tok::l_paren, lParenLoc)) {
|
|
if (Tok.is(tok::string_literal)) {
|
|
activeDiag.fixItRemoveChars(Tok.getLoc(),
|
|
Tok.getLoc().getAdvancedLoc(1));
|
|
SourceLoc endLoc = Tok.getLoc().getAdvancedLoc(Tok.getLength());
|
|
activeDiag.fixItRemoveChars(endLoc.getAdvancedLoc(-1), endLoc);
|
|
}
|
|
skipUntil(tok::r_paren);
|
|
SourceLoc rParenLoc;
|
|
parseMatchingToken(tok::r_paren, rParenLoc,
|
|
diag::attr_warn_unused_result_expected_rparen,
|
|
lParenLoc);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// FIXME: Remove this after Swift 4.
|
|
if (DK == DAK_Count &&
|
|
Tok.getText() == "NSKeyedArchiverEncodeNonGenericSubclassesOnly") {
|
|
diagnose(Tok,
|
|
diag::attr_nskeyedarchiverencodenongenericsubclassesonly_removed)
|
|
.fixItRemove(SourceRange(AtLoc, Tok.getLoc()));
|
|
consumeToken();
|
|
return false;
|
|
}
|
|
|
|
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();
|
|
if (Tok.is(tok::l_paren))
|
|
skipSingle();
|
|
|
|
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());
|
|
|
|
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();
|
|
SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);
|
|
|
|
if (Tok.is(tok::l_paren) && getEndOfPreviousLoc() == Tok.getLoc()) {
|
|
ParserPosition LParenPosition = getParserPosition();
|
|
skipSingle();
|
|
// If we found '->', or 'throws' after paren, it's likely a parameter
|
|
// of function type.
|
|
if (Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows,
|
|
tok::kw_throw))
|
|
backtrackToPosition(LParenPosition);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Ok, it is a valid attribute, eat it, and then process it.
|
|
StringRef Text = Tok.getText();
|
|
SourceLoc Loc = consumeToken();
|
|
|
|
// Accumulate attribute argument '( ... )' as a token list.
|
|
SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);
|
|
|
|
bool isAutoclosureEscaping = false;
|
|
SourceRange autoclosureEscapingParenRange;
|
|
StringRef conventionName;
|
|
StringRef witnessMethodProtocol;
|
|
|
|
// Handle @autoclosure(escaping)
|
|
if (attr == TAK_autoclosure) {
|
|
// We need to do a bit of lookahead here to make sure we parse a (weird)
|
|
// type like: "@autoclosure (escaping) -> Int" correctly (escaping is the
|
|
// name of a type here). We also want to support the case where the
|
|
// function type coming up is a typealias, e.g. "@autoclosure (escaping) T".
|
|
if (Tok.is(tok::l_paren) && peekToken().getText() == "escaping") {
|
|
Parser::BacktrackingScope Backtrack(*this);
|
|
consumeToken(tok::l_paren);
|
|
consumeToken(tok::identifier);
|
|
isAutoclosureEscaping =
|
|
Tok.is(tok::r_paren) && peekToken().isNot(tok::arrow);
|
|
}
|
|
|
|
if (isAutoclosureEscaping) {
|
|
autoclosureEscapingParenRange.Start = consumeToken(tok::l_paren);
|
|
consumeToken(tok::identifier);
|
|
autoclosureEscapingParenRange.End = consumeToken(tok::r_paren);
|
|
}
|
|
} else if (attr == TAK_convention) {
|
|
SourceLoc LPLoc;
|
|
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
|
|
if (!justChecking)
|
|
diagnose(Tok, diag::convention_attribute_expected_lparen);
|
|
return true;
|
|
}
|
|
|
|
if (Tok.isNot(tok::identifier)) {
|
|
if (!justChecking)
|
|
diagnose(Tok, diag::convention_attribute_expected_name);
|
|
return true;
|
|
}
|
|
|
|
conventionName = Tok.getText();
|
|
consumeToken(tok::identifier);
|
|
|
|
if (conventionName == "witness_method") {
|
|
if (Tok.isNot(tok::colon)) {
|
|
if (!justChecking)
|
|
diagnose(Tok,
|
|
diag::convention_attribute_witness_method_expected_colon);
|
|
return true;
|
|
}
|
|
consumeToken(tok::colon);
|
|
if (Tok.isNot(tok::identifier)) {
|
|
if (!justChecking)
|
|
diagnose(Tok,
|
|
diag::convention_attribute_witness_method_expected_protocol);
|
|
return true;
|
|
}
|
|
|
|
witnessMethodProtocol = Tok.getText();
|
|
consumeToken(tok::identifier);
|
|
}
|
|
|
|
// Parse the ')'. We can't use parseMatchingToken if we're in
|
|
// just-checking mode.
|
|
if (justChecking && Tok.isNot(tok::r_paren))
|
|
return true;
|
|
|
|
SourceLoc RPLoc;
|
|
parseMatchingToken(tok::r_paren, RPLoc,
|
|
diag::convention_attribute_expected_rparen,
|
|
LPLoc);
|
|
}
|
|
|
|
|
|
// In just-checking mode, we only need to consume the tokens, and we don't
|
|
// want to do any other analysis.
|
|
if (justChecking)
|
|
return false;
|
|
|
|
// Diagnose duplicated attributes.
|
|
if (Attributes.has(attr)) {
|
|
diagnose(Loc, diag::duplicate_attribute, /*isModifier=*/false);
|
|
return false;
|
|
}
|
|
|
|
// Handle any attribute-specific processing logic.
|
|
switch (attr) {
|
|
default: break;
|
|
case TAK_autoclosure:
|
|
// Handle @autoclosure(escaping)
|
|
if (isAutoclosureEscaping) {
|
|
// @noescape @autoclosure(escaping) makes no sense.
|
|
if (Attributes.has(TAK_noescape)) {
|
|
diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure);
|
|
} else {
|
|
diagnose(Loc, Context.isSwiftVersion3()
|
|
? diag::swift3_attr_autoclosure_escaping_deprecated
|
|
: diag::attr_autoclosure_escaping_deprecated)
|
|
.fixItReplace(autoclosureEscapingParenRange, " @escaping ");
|
|
}
|
|
Attributes.setAttr(TAK_escaping, Loc);
|
|
} else if (Attributes.has(TAK_noescape) && !isInSILMode()) {
|
|
diagnose(Loc, diag::attr_noescape_implied_by_autoclosure);
|
|
}
|
|
break;
|
|
|
|
case TAK_noescape:
|
|
// You can't specify @noescape and @escaping together.
|
|
if (Attributes.has(TAK_escaping)) {
|
|
diagnose(Loc, diag::attr_escaping_conflicts_noescape);
|
|
return false;
|
|
}
|
|
|
|
// @noescape after @autoclosure is redundant.
|
|
if (Attributes.has(TAK_autoclosure) && !isInSILMode()) {
|
|
diagnose(Loc, diag::attr_noescape_implied_by_autoclosure);
|
|
}
|
|
|
|
// @noescape is deprecated and no longer used
|
|
// In SIL, the polarity of @escaping is reversed.
|
|
// @escaping is the default and @noescape is explicit.
|
|
if (!isInSILMode()) {
|
|
diagnose(Loc, Context.isSwiftVersion3()
|
|
? diag::swift3_attr_noescape_deprecated
|
|
: diag::attr_noescape_deprecated)
|
|
.fixItRemove({Attributes.AtLoc, Loc});
|
|
}
|
|
break;
|
|
case TAK_escaping:
|
|
// You can't specify @noescape and @escaping together.
|
|
if (Attributes.has(TAK_noescape)) {
|
|
diagnose(Loc, diag::attr_escaping_conflicts_noescape);
|
|
return false;
|
|
}
|
|
break;
|
|
case TAK_out:
|
|
case TAK_in:
|
|
case TAK_owned:
|
|
case TAK_unowned_inner_pointer:
|
|
case TAK_guaranteed:
|
|
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);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// Ownership attributes.
|
|
case TAK_sil_weak:
|
|
case TAK_sil_unowned:
|
|
if (!isInSILMode()) {
|
|
diagnose(Loc, diag::only_allowed_in_sil, Text);
|
|
return false;
|
|
}
|
|
|
|
if (Attributes.hasOwnership()) {
|
|
diagnose(Loc, diag::duplicate_attribute, /*isModifier*/false);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
// 'inout' attribute.
|
|
case TAK_inout:
|
|
if (!isInSILMode()) {
|
|
diagnose(Loc, diag::inout_not_attribute);
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case TAK_opened: {
|
|
if (!isInSILMode()) {
|
|
diagnose(Loc, diag::only_allowed_in_sil, "opened");
|
|
return false;
|
|
}
|
|
|
|
// Parse the opened existential ID string in parens
|
|
SourceLoc beginLoc = Tok.getLoc(), idLoc, endLoc;
|
|
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);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// Convention attribute.
|
|
case TAK_convention:
|
|
Attributes.convention = conventionName;
|
|
Attributes.conventionWitnessMethodProtocol = witnessMethodProtocol;
|
|
break;
|
|
}
|
|
|
|
Attributes.setAttr(attr, Loc);
|
|
return false;
|
|
}
|
|
|
|
/// \verbatim
|
|
/// attribute-list:
|
|
/// /*empty*/
|
|
/// attribute-list-clause attribute-list
|
|
/// attribute-list-clause:
|
|
/// '@' attribute
|
|
/// \endverbatim
|
|
bool Parser::parseDeclAttributeList(DeclAttributes &Attributes,
|
|
bool &FoundCCToken) {
|
|
FoundCCToken = false;
|
|
if (Tok.isNot(tok::at_sign))
|
|
return false;
|
|
SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList);
|
|
do {
|
|
if (peekToken().is(tok::code_complete)) {
|
|
consumeToken(tok::at_sign);
|
|
consumeToken(tok::code_complete);
|
|
FoundCCToken = true;
|
|
continue;
|
|
}
|
|
SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute);
|
|
SourceLoc AtLoc = consumeToken();
|
|
if (parseDeclAttribute(Attributes, AtLoc))
|
|
return true;
|
|
} while (Tok.is(tok::at_sign));
|
|
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
|
|
/// 'inout' attribute-list-clause attribute-list
|
|
/// '__shared' attribute-list-clause attribute-list
|
|
/// '__owned' attribute-list-clause attribute-list
|
|
/// attribute-list-clause:
|
|
/// '@' attribute
|
|
/// '@' attribute attribute-list-clause
|
|
/// \endverbatim
|
|
bool Parser::parseTypeAttributeListPresent(VarDecl::Specifier &Specifier,
|
|
SourceLoc &SpecifierLoc,
|
|
TypeAttributes &Attributes) {
|
|
Specifier = VarDecl::Specifier::Owned;
|
|
while (Tok.is(tok::kw_inout) ||
|
|
(Tok.is(tok::identifier) &&
|
|
(Tok.getRawText().equals("__shared") ||
|
|
Tok.getRawText().equals("__owned")))) {
|
|
if (SpecifierLoc.isValid()) {
|
|
diagnose(Tok, diag::parameter_specifier_repeated)
|
|
.fixItRemove(SpecifierLoc);
|
|
} else {
|
|
if (Tok.is(tok::kw_inout)) {
|
|
Specifier = VarDecl::Specifier::InOut;
|
|
} else if (Tok.is(tok::identifier)) {
|
|
if (Tok.getRawText().equals("__shared")) {
|
|
Specifier = VarDecl::Specifier::Shared;
|
|
} else if (Tok.getRawText().equals("__owned")) {
|
|
Specifier = VarDecl::Specifier::Owned;
|
|
}
|
|
}
|
|
}
|
|
SpecifierLoc = consumeToken();
|
|
}
|
|
|
|
SyntaxParsingContext AttrListCtx(SyntaxContext, SyntaxKind::AttributeList);
|
|
while (Tok.is(tok::at_sign)) {
|
|
if (Attributes.AtLoc.isInvalid())
|
|
Attributes.AtLoc = Tok.getLoc();
|
|
SyntaxParsingContext AttrCtx(SyntaxContext, SyntaxKind::Attribute);
|
|
consumeToken();
|
|
if (parseTypeAttribute(Attributes))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) {
|
|
return Tok.isContextualKeyword("operator") &&
|
|
(Tok2.isContextualKeyword("prefix") ||
|
|
Tok2.isContextualKeyword("postfix") ||
|
|
Tok2.isContextualKeyword("infix"));
|
|
}
|
|
|
|
/// \brief Diagnose issues with fixity attributes, if any.
|
|
static void diagnoseOperatorFixityAttributes(Parser &P,
|
|
DeclAttributes &Attrs,
|
|
const Decl *D) {
|
|
auto isFixityAttr = [](DeclAttribute *attr){
|
|
DeclAttrKind kind = attr->getKind();
|
|
return attr->isValid() && (kind == DAK_Prefix ||
|
|
kind == DAK_Infix ||
|
|
kind == DAK_Postfix);
|
|
};
|
|
|
|
SmallVector<DeclAttribute *, 3> fixityAttrs;
|
|
std::copy_if(Attrs.begin(), Attrs.end(),
|
|
std::back_inserter(fixityAttrs), isFixityAttr);
|
|
std::reverse(fixityAttrs.begin(), fixityAttrs.end());
|
|
|
|
for (auto it = fixityAttrs.begin(); it != fixityAttrs.end(); ++it) {
|
|
if (it != fixityAttrs.begin()) {
|
|
auto *attr = *it;
|
|
P.diagnose(attr->getLocation(), diag::mutually_exclusive_attrs,
|
|
attr->getAttrName(), fixityAttrs.front()->getAttrName(),
|
|
attr->isDeclModifier())
|
|
.fixItRemove(attr->getRange());
|
|
attr->setInvalid();
|
|
}
|
|
}
|
|
|
|
// Operator declarations must specify a fixity.
|
|
if (auto *OD = dyn_cast<OperatorDecl>(D)) {
|
|
if (fixityAttrs.empty()) {
|
|
P.diagnose(OD->getOperatorLoc(), diag::operator_decl_no_fixity);
|
|
}
|
|
}
|
|
// Infix operator is only allowed on operator declarations, not on func.
|
|
else if (isa<FuncDecl>(D)) {
|
|
if (auto *attr = Attrs.getAttribute<InfixAttr>()) {
|
|
P.diagnose(attr->getLocation(), diag::invalid_infix_on_func)
|
|
.fixItRemove(attr->getLocation());
|
|
attr->setInvalid();
|
|
}
|
|
} else {
|
|
llvm_unreachable("unexpected decl kind?");
|
|
}
|
|
}
|
|
|
|
bool swift::isKeywordPossibleDeclStart(const Token &Tok) {
|
|
switch (Tok.getKind()) {
|
|
case tok::at_sign:
|
|
case tok::kw_associatedtype:
|
|
case tok::kw_case:
|
|
case tok::kw_class:
|
|
case tok::kw_deinit:
|
|
case tok::kw_enum:
|
|
case tok::kw_extension:
|
|
case tok::kw_fileprivate:
|
|
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_precedencegroup:
|
|
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::identifier:
|
|
case tok::pound_sourceLocation:
|
|
return true;
|
|
case tok::pound_line:
|
|
// #line at the start of the line is a directive, but it's deprecated.
|
|
// #line within a line is an expression.
|
|
return Tok.isAtStartOfLine();
|
|
|
|
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;
|
|
|
|
// When 'init' appears inside another 'init', it's likely the user wants to
|
|
// invoke an initializer but forgets to prefix it with 'self.' or 'super.'
|
|
// Otherwise, expect 'init' to be the start of a declaration (and complain
|
|
// when the expectation is not fulfilled).
|
|
if (Tok.is(tok::kw_init)) {
|
|
return !isa<ConstructorDecl>(CurDeclContext);
|
|
}
|
|
|
|
// 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);
|
|
|
|
// Look through attribute list, because it may be an *type* attribute list.
|
|
if (Tok.is(tok::at_sign)) {
|
|
BacktrackingScope backtrack(*this);
|
|
while (consumeIf(tok::at_sign)) {
|
|
// If not identifier or code complete token, consider '@' as an incomplete
|
|
// attribute.
|
|
if (Tok.isNot(tok::identifier, tok::code_complete))
|
|
continue;
|
|
consumeToken();
|
|
// Eat paren after attribute name; e.g. @foo(x)
|
|
if (consumeIf(tok::l_paren)) {
|
|
while (Tok.isNot(tok::r_brace, tok::eof, tok::pound_endif)) {
|
|
if (consumeIf(tok::r_paren)) break;
|
|
skipSingle();
|
|
}
|
|
}
|
|
}
|
|
// If this attribute is the last element in the block,
|
|
// consider it is a start of incomplete decl.
|
|
if (Tok.isAny(tok::r_brace, tok::eof, tok::pound_endif))
|
|
return true;
|
|
|
|
return isStartOfDecl();
|
|
}
|
|
|
|
// 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) {
|
|
SourceLoc CurrentLoc = Tok.getLoc();
|
|
backtrackToPosition(BeginParserPosition);
|
|
SourceLoc BeginLoc = Tok.getLoc();
|
|
// Consume tokens up to code completion token.
|
|
while (Tok.isNot(tok::code_complete, tok::eof))
|
|
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);
|
|
|
|
while (SourceMgr.isBeforeInBuffer(Tok.getLoc(), CurrentLoc))
|
|
consumeToken();
|
|
|
|
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.
|
|
while (Tok.isNot(tok::eof))
|
|
consumeToken();
|
|
}
|
|
}
|
|
|
|
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.insert(TD);
|
|
|
|
Identifier name = D->getBaseName().getIdentifier();
|
|
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);
|
|
|
|
while (Tok.isNot(tok::eof))
|
|
consumeToken();
|
|
}
|
|
|
|
/// \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
|
|
ParserResult<Decl>
|
|
Parser::parseDecl(ParseDeclOptions Flags,
|
|
llvm::function_ref<void(Decl*)> Handler) {
|
|
SyntaxParsingContext DeclParsingContext(SyntaxContext,
|
|
SyntaxContextKind::Decl);
|
|
|
|
if (Tok.is(tok::pound_if)) {
|
|
auto IfConfigResult = parseIfConfig(
|
|
[&](SmallVectorImpl<ASTNode> &Decls, bool IsActive) {
|
|
Optional<Scope> scope;
|
|
if (!IsActive)
|
|
scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(),
|
|
/*inactiveConfigBlock=*/true);
|
|
|
|
ParserStatus Status;
|
|
bool PreviousHadSemi = true;
|
|
while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif,
|
|
tok::eof)) {
|
|
if (Tok.is(tok::r_brace)) {
|
|
diagnose(Tok.getLoc(),
|
|
diag::unexpected_rbrace_in_conditional_compilation_block);
|
|
// If we see '}', following declarations don't look like belong to
|
|
// the current decl context; skip them.
|
|
skipUntilConditionalBlockClose();
|
|
break;
|
|
}
|
|
Status |= parseDeclItem(PreviousHadSemi, Flags,
|
|
[&](Decl *D) {Decls.emplace_back(D);});
|
|
}
|
|
});
|
|
|
|
if (auto ICD = IfConfigResult.getPtrOrNull()) {
|
|
// The IfConfigDecl is ahead of its members in source order.
|
|
Handler(ICD);
|
|
// Copy the active members into the entries list.
|
|
for (auto activeMember : ICD->getActiveClauseElements()) {
|
|
auto *D = activeMember.get<Decl*>();
|
|
if (isa<IfConfigDecl>(D))
|
|
// Don't hoist nested '#if'.
|
|
continue;
|
|
Handler(D);
|
|
}
|
|
}
|
|
return IfConfigResult;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
while (1) {
|
|
// Save the original token, in case code-completion needs it.
|
|
auto OrigTok = Tok;
|
|
bool MayNeedOverrideCompletion = false;
|
|
|
|
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;
|
|
}
|
|
SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier);
|
|
consumeToken(tok::kw_static);
|
|
// Static modifier doesn't have more details.
|
|
SyntaxParsingContext DetailContext(SyntaxContext, SyntaxKind::TokenList);
|
|
continue;
|
|
}
|
|
// 'class' is a modifier on func, but is also a top-level decl.
|
|
case tok::kw_class: {
|
|
SourceLoc ClassLoc;
|
|
bool AsModifier;
|
|
{
|
|
BacktrackingScope Scope(*this);
|
|
ClassLoc = consumeToken(tok::kw_class);
|
|
AsModifier = isStartOfDecl();
|
|
}
|
|
// If 'class' is a modifier on another decl kind, like var or func,
|
|
// then treat it as a modifier.
|
|
if (AsModifier) {
|
|
SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier);
|
|
consumeToken(tok::kw_class);
|
|
SyntaxParsingContext DetailContext(SyntaxContext, SyntaxKind::TokenList);
|
|
if (StaticLoc.isValid()) {
|
|
diagnose(Tok, diag::decl_already_static,
|
|
StaticSpellingKind::KeywordClass)
|
|
.highlight(StaticLoc).fixItRemove(ClassLoc);
|
|
} else {
|
|
StaticLoc = ClassLoc;
|
|
StaticSpelling = StaticSpellingKind::KeywordClass;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
consumeToken(tok::kw_class);
|
|
// Otherwise this is the start of a class declaration.
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl);
|
|
DeclResult = parseDeclClass(ClassLoc, Flags, Attributes);
|
|
break;
|
|
}
|
|
|
|
case tok::kw_private:
|
|
case tok::kw_fileprivate:
|
|
case tok::kw_internal:
|
|
case tok::kw_public: {
|
|
SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier);
|
|
// We still model these specifiers as attributes.
|
|
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl);
|
|
continue;
|
|
}
|
|
// Context sensitive keywords.
|
|
case tok::identifier: {
|
|
Optional<DeclAttrKind> Kind;
|
|
// FIXME: This is ridiculous, this all needs to be sucked into the
|
|
// declparsing goop.
|
|
if (Tok.isContextualKeyword("open")) {
|
|
Kind = DAK_AccessControl;
|
|
} else if (Tok.isContextualKeyword("weak") ||
|
|
Tok.isContextualKeyword("unowned")) {
|
|
Kind = DAK_Ownership;
|
|
} else if (Tok.isContextualKeyword("optional")) {
|
|
Kind = DAK_Optional;
|
|
} else if (Tok.isContextualKeyword("required")) {
|
|
Kind = DAK_Required;
|
|
} else if (Tok.isContextualKeyword("lazy")) {
|
|
Kind = DAK_Lazy;
|
|
} else if (Tok.isContextualKeyword("final")) {
|
|
Kind = DAK_Final;
|
|
} else if (Tok.isContextualKeyword("dynamic")) {
|
|
Kind = DAK_Dynamic;
|
|
} else if (Tok.isContextualKeyword("prefix")) {
|
|
Kind = DAK_Prefix;
|
|
} else if (Tok.isContextualKeyword("postfix")) {
|
|
Kind = DAK_Postfix;
|
|
} else if (Tok.isContextualKeyword("indirect")) {
|
|
Kind = DAK_Indirect;
|
|
} else if (Tok.isContextualKeyword("infix")) {
|
|
Kind = DAK_Infix;
|
|
} else if (Tok.isContextualKeyword("override")) {
|
|
Kind = DAK_Override;
|
|
} else if (Tok.isContextualKeyword("mutating")) {
|
|
Kind = DAK_Mutating;
|
|
} else if (Tok.isContextualKeyword("nonmutating")) {
|
|
Kind = DAK_NonMutating;
|
|
} else if (Tok.isContextualKeyword("__consuming")) {
|
|
Kind = DAK_Consuming;
|
|
} else if (Tok.isContextualKeyword("convenience")) {
|
|
Kind = DAK_Convenience;
|
|
}
|
|
if (Kind) {
|
|
SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier);
|
|
parseNewDeclAttribute(Attributes, SourceLoc(), *Kind);
|
|
continue;
|
|
}
|
|
|
|
// Otherwise this is not a context-sensitive keyword.
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
case tok::pound_if:
|
|
case tok::pound_sourceLocation:
|
|
case tok::pound_line:
|
|
// We see some attributes right before these pounds.
|
|
// TODO: Emit dedicated errors for them.
|
|
LLVM_FALLTHROUGH;
|
|
|
|
// Obvious nonsense.
|
|
default:
|
|
if (FoundCCTokenInAttr) {
|
|
if (!CodeCompletion) {
|
|
delayParseFromBeginningToHere(BeginParserPosition, Flags);
|
|
} else {
|
|
CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(),
|
|
false);
|
|
}
|
|
}
|
|
|
|
diagnose(Tok, diag::expected_decl);
|
|
|
|
if (CurDeclContext) {
|
|
if (auto nominal = dyn_cast<NominalTypeDecl>(CurDeclContext)) {
|
|
diagnose(nominal->getLoc(), diag::note_in_decl_extension, false,
|
|
nominal->getName());
|
|
} else if (auto extension = dyn_cast<ExtensionDecl>(CurDeclContext)) {
|
|
if (auto repr = extension->getExtendedTypeLoc().getTypeRepr()) {
|
|
if (auto idRepr = dyn_cast<IdentTypeRepr>(repr)) {
|
|
diagnose(extension->getLoc(), diag::note_in_decl_extension, true,
|
|
idRepr->getComponentRange().front()->getIdentifier());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return makeParserErrorResult<Decl>();
|
|
|
|
case tok::unknown:
|
|
consumeToken(tok::unknown);
|
|
continue;
|
|
|
|
// Unambiguous top level decls.
|
|
case tok::kw_import:
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::ImportDecl);
|
|
DeclResult = parseDeclImport(Flags, Attributes);
|
|
break;
|
|
case tok::kw_extension:
|
|
DeclResult = parseDeclExtension(Flags, Attributes);
|
|
break;
|
|
case tok::kw_let:
|
|
case tok::kw_var: {
|
|
// Collect all modifiers into a modifier list.
|
|
DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList);
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::VariableDecl);
|
|
llvm::SmallVector<Decl *, 4> Entries;
|
|
DeclResult = parseDeclVar(Flags, Attributes, Entries, StaticLoc,
|
|
StaticSpelling, tryLoc);
|
|
StaticLoc = SourceLoc(); // we handled static if present.
|
|
MayNeedOverrideCompletion = true;
|
|
std::for_each(Entries.begin(), Entries.end(), Handler);
|
|
if (auto *D = DeclResult.getPtrOrNull())
|
|
markWasHandled(D);
|
|
break;
|
|
}
|
|
case tok::kw_typealias:
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::TypealiasDecl);
|
|
DeclResult = parseDeclTypeAlias(Flags, Attributes);
|
|
MayNeedOverrideCompletion = true;
|
|
break;
|
|
case tok::kw_associatedtype:
|
|
DeclResult = parseDeclAssociatedType(Flags, Attributes);
|
|
break;
|
|
case tok::kw_enum:
|
|
DeclResult = parseDeclEnum(Flags, Attributes);
|
|
break;
|
|
case tok::kw_case: {
|
|
llvm::SmallVector<Decl *, 4> Entries;
|
|
DeclResult = parseDeclEnumCase(Flags, Attributes, Entries);
|
|
std::for_each(Entries.begin(), Entries.end(), Handler);
|
|
if (auto *D = DeclResult.getPtrOrNull())
|
|
markWasHandled(D);
|
|
break;
|
|
}
|
|
case tok::kw_struct: {
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::StructDecl);
|
|
DeclResult = parseDeclStruct(Flags, Attributes);
|
|
break;
|
|
}
|
|
case tok::kw_init:
|
|
DeclResult = parseDeclInit(Flags, Attributes);
|
|
break;
|
|
case tok::kw_deinit:
|
|
DeclResult = parseDeclDeinit(Flags, Attributes);
|
|
break;
|
|
case tok::kw_operator:
|
|
DeclResult = parseDeclOperator(Flags, Attributes);
|
|
break;
|
|
case tok::kw_precedencegroup:
|
|
DeclResult = parseDeclPrecedenceGroup(Flags, Attributes);
|
|
break;
|
|
case tok::kw_protocol: {
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::ProtocolDecl);
|
|
DeclResult = parseDeclProtocol(Flags, Attributes);
|
|
break;
|
|
}
|
|
case tok::kw_func:
|
|
// Collect all modifiers into a modifier list.
|
|
DeclParsingContext.collectNodesInPlace(SyntaxKind::ModifierList);
|
|
DeclParsingContext.setCreateSyntax(SyntaxKind::FunctionDecl);
|
|
DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes);
|
|
StaticLoc = SourceLoc(); // we handled static if present.
|
|
MayNeedOverrideCompletion = true;
|
|
break;
|
|
|
|
case tok::kw_subscript: {
|
|
if (StaticLoc.isValid()) {
|
|
diagnose(Tok, diag::subscript_static, StaticSpelling)
|
|
.fixItRemove(SourceRange(StaticLoc));
|
|
StaticLoc = SourceLoc();
|
|
}
|
|
llvm::SmallVector<Decl *, 4> Entries;
|
|
DeclResult = parseDeclSubscript(Flags, Attributes, Entries);
|
|
std::for_each(Entries.begin(), Entries.end(), Handler);
|
|
MayNeedOverrideCompletion = true;
|
|
if (auto *D = DeclResult.getPtrOrNull())
|
|
markWasHandled(D);
|
|
break;
|
|
}
|
|
|
|
case tok::code_complete:
|
|
MayNeedOverrideCompletion = true;
|
|
DeclResult = makeParserError();
|
|
// Handled below.
|
|
break;
|
|
}
|
|
|
|
if (DeclResult.isParseError() && MayNeedOverrideCompletion &&
|
|
Tok.is(tok::code_complete)) {
|
|
DeclResult = makeParserCodeCompletionStatus();
|
|
if (CodeCompletion) {
|
|
// If we need to complete an override, collect the keywords already
|
|
// specified so that we do not duplicate them in code completion
|
|
// strings.
|
|
SmallVector<StringRef, 3> Keywords;
|
|
switch (OrigTok.getKind()) {
|
|
case tok::kw_func:
|
|
case tok::kw_subscript:
|
|
case tok::kw_var:
|
|
case tok::kw_let:
|
|
case tok::kw_typealias:
|
|
Keywords.push_back(OrigTok.getText());
|
|
break;
|
|
default:
|
|
// Other tokens are already accounted for.
|
|
break;
|
|
}
|
|
for (auto attr : Attributes) {
|
|
Keywords.push_back(attr->getAttrName());
|
|
}
|
|
CodeCompletion->completeNominalMemberBeginning(Keywords);
|
|
}
|
|
}
|
|
|
|
// If we 'break' out of the switch, break out of the loop too.
|
|
break;
|
|
}
|
|
|
|
if (auto SF = CurDeclContext->getParentSourceFile()) {
|
|
if (!getScopeInfo().isInactiveConfigBlock()) {
|
|
for (auto Attr : Attributes) {
|
|
if (isa<ObjCAttr>(Attr) || isa<DynamicAttr>(Attr))
|
|
SF->AttrsRequiringFoundation.insert(Attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FoundCCTokenInAttr) {
|
|
if (CodeCompletion) {
|
|
CodeCompletion->completeDeclAttrKeyword(DeclResult.getPtrOrNull(),
|
|
isInSILMode(),
|
|
false);
|
|
} else {
|
|
delayParseFromBeginningToHere(BeginParserPosition, Flags);
|
|
return makeParserError();
|
|
}
|
|
}
|
|
|
|
if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass() &&
|
|
!CurDeclContext->isModuleScopeContext()) {
|
|
// Only consume non-toplevel decls.
|
|
consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false);
|
|
|
|
return makeParserError();
|
|
}
|
|
|
|
if (DeclResult.isNonNull()) {
|
|
Decl *D = DeclResult.get();
|
|
if (!declWasHandledAlready(D))
|
|
Handler(DeclResult.get());
|
|
}
|
|
|
|
if (!DeclResult.isParseError()) {
|
|
// If we parsed 'class' or 'static', but didn't handle it above, complain
|
|
// about it.
|
|
if (StaticLoc.isValid())
|
|
diagnose(DeclResult.get()->getLoc(), diag::decl_not_static,
|
|
StaticSpelling)
|
|
.fixItRemove(SourceRange(StaticLoc));
|
|
}
|
|
|
|
return DeclResult;
|
|
}
|
|
|
|
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))
|
|
consumeTokenWithoutFeedingReceiver();
|
|
|
|
// 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);
|
|
|
|
parseDecl(ParseDeclOptions(DelayedState->Flags), [&](Decl *D) {
|
|
if (auto *parent = DelayedState->ParentContext) {
|
|
if (auto *NTD = dyn_cast<NominalTypeDecl>(parent)) {
|
|
NTD->addMember(D);
|
|
} else if (auto *ED = dyn_cast<ExtensionDecl>(parent)) {
|
|
ED->addMember(D);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// \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");
|
|
diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
|
|
diagnose(Tok, diag::backticks_to_escape);
|
|
return nullptr;
|
|
}
|
|
KindLoc = consumeToken();
|
|
}
|
|
|
|
std::vector<std::pair<Identifier, SourceLoc>> ImportPath;
|
|
bool HasNext;
|
|
do {
|
|
SyntaxParsingContext AccessCompCtx(SyntaxContext,
|
|
SyntaxKind::AccessPathComponent);
|
|
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;
|
|
HasNext = consumeIf(tok::period);
|
|
} while (HasNext);
|
|
|
|
// Collect all access path components to an access path.
|
|
SyntaxContext->collectNodesInPlace(SyntaxKind::AccessPath);
|
|
|
|
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,
|
|
bool allowClassRequirement,
|
|
bool allowAnyObject) {
|
|
SyntaxParsingContext InheritanceContext(SyntaxContext,
|
|
SyntaxKind::TypeInheritanceClause);
|
|
|
|
Scope S(this, ScopeKind::InheritanceClause);
|
|
consumeToken(tok::colon);
|
|
|
|
SyntaxParsingContext TypeListContext(SyntaxContext,
|
|
SyntaxKind::InheritedTypeList);
|
|
SourceLoc classRequirementLoc;
|
|
|
|
ParserStatus Status;
|
|
SourceLoc prevComma;
|
|
bool HasNextType;
|
|
do {
|
|
SyntaxParsingContext TypeContext(SyntaxContext, SyntaxKind::InheritedType);
|
|
SWIFT_DEFER {
|
|
// Check for a ',', which indicates that there are more protocols coming.
|
|
HasNextType = consumeIf(tok::comma, prevComma);
|
|
};
|
|
// Parse the 'class' keyword for a class requirement.
|
|
if (Tok.is(tok::kw_class)) {
|
|
// FIXME: class requirement will turn to an unknown type in libSyntax tree.
|
|
SyntaxParsingContext ClassTypeContext(SyntaxContext,
|
|
SyntaxContextKind::Type);
|
|
// If we aren't allowed to have a class requirement here, complain.
|
|
auto classLoc = consumeToken();
|
|
if (!allowClassRequirement) {
|
|
diagnose(classLoc, diag::unexpected_class_constraint);
|
|
|
|
// Note that it makes no sense to suggest fixing
|
|
// 'struct S : class' to 'struct S : AnyObject' for
|
|
// example; in that case we just complain about
|
|
// 'class' being invalid here.
|
|
if (allowAnyObject) {
|
|
diagnose(classLoc, diag::suggest_anyobject)
|
|
.fixItReplace(classLoc, "AnyObject");
|
|
}
|
|
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;
|
|
|
|
// Add 'AnyObject' to the inherited list.
|
|
Inherited.push_back(
|
|
new (Context) SimpleIdentTypeRepr(classLoc,
|
|
Context.getIdentifier("AnyObject")));
|
|
continue;
|
|
}
|
|
|
|
auto ParsedTypeResult = parseType();
|
|
Status |= ParsedTypeResult;
|
|
|
|
// Record the type if its a single type.
|
|
if (ParsedTypeResult.isNonNull())
|
|
Inherited.push_back(ParsedTypeResult.get());
|
|
} while (HasNextType);
|
|
|
|
return Status;
|
|
}
|
|
|
|
enum class TokenProperty {
|
|
None,
|
|
StartsWithLess,
|
|
};
|
|
|
|
static ParserStatus
|
|
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc,
|
|
StringRef DeclKindName, tok ResyncT1, tok ResyncT2,
|
|
tok ResyncT3, tok ResyncT4,
|
|
TokenProperty ResyncP1) {
|
|
if (P.Tok.is(tok::identifier)) {
|
|
Loc = P.consumeIdentifier(&Result);
|
|
|
|
// We parsed an identifier for the declaration. If we see another
|
|
// identifier, it might've been a single identifier that got broken by a
|
|
// space or newline accidentally.
|
|
if (P.Tok.isIdentifierOrUnderscore() && !P.Tok.isContextualDeclKeyword())
|
|
P.diagnoseConsecutiveIDs(Result.str(), Loc, DeclKindName);
|
|
|
|
// Return success anyway
|
|
return makeParserSuccess();
|
|
}
|
|
|
|
P.checkForInputIncomplete();
|
|
|
|
if (P.Tok.is(tok::integer_literal) || P.Tok.is(tok::floating_literal) ||
|
|
(P.Tok.is(tok::unknown) && isdigit(P.Tok.getText()[0]))) {
|
|
// Using numbers for identifiers is a common error for beginners, so it's
|
|
// worth handling this in a special way.
|
|
P.diagnose(P.Tok, diag::number_cant_start_decl_name, DeclKindName);
|
|
|
|
// Pretend this works as an identifier, which shouldn't be observable since
|
|
// actual uses of it will hit random other errors, e.g. `1()` won't be
|
|
// callable.
|
|
Result = P.Context.getIdentifier(P.Tok.getText());
|
|
Loc = P.Tok.getLoc();
|
|
P.consumeToken();
|
|
|
|
// We recovered, so this is a success.
|
|
return makeParserSuccess();
|
|
}
|
|
|
|
if (P.Tok.isKeyword()) {
|
|
P.diagnose(P.Tok, diag::keyword_cant_be_identifier, P.Tok.getText());
|
|
P.diagnose(P.Tok, diag::backticks_to_escape)
|
|
.fixItReplace(P.Tok.getLoc(), "`" + P.Tok.getText().str() + "`");
|
|
|
|
// Recover if the next token is one of the expected tokens.
|
|
auto Next = P.peekToken();
|
|
if (Next.isAny(ResyncT1, ResyncT2, ResyncT3, ResyncT4) ||
|
|
(ResyncP1 != TokenProperty::None && P.startsWithLess(Next))) {
|
|
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();
|
|
}
|
|
|
|
P.diagnose(P.Tok, diag::expected_identifier_in_decl, DeclKindName);
|
|
return makeParserError();
|
|
}
|
|
|
|
static ParserStatus
|
|
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
|
|
StringRef DeclKindName, tok ResyncT1, tok ResyncT2) {
|
|
return parseIdentifierDeclName(P, Result, L, DeclKindName, ResyncT1, ResyncT2,
|
|
tok::NUM_TOKENS, tok::NUM_TOKENS,
|
|
TokenProperty::None);
|
|
}
|
|
|
|
static ParserStatus
|
|
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
|
|
StringRef DeclKindName, tok ResyncT1, tok ResyncT2,
|
|
tok ResyncT3, tok ResyncT4) {
|
|
return parseIdentifierDeclName(P, Result, L, DeclKindName, ResyncT1, ResyncT2,
|
|
ResyncT3, ResyncT4, TokenProperty::None);
|
|
}
|
|
|
|
static ParserStatus
|
|
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
|
|
StringRef DeclKindName, tok ResyncT1, tok ResyncT2,
|
|
TokenProperty ResyncP1) {
|
|
return parseIdentifierDeclName(P, Result, L, DeclKindName, ResyncT1, ResyncT2,
|
|
tok::NUM_TOKENS, tok::NUM_TOKENS, ResyncP1);
|
|
}
|
|
|
|
static ParserStatus
|
|
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &L,
|
|
StringRef DeclKindName, tok ResyncT1, tok ResyncT2,
|
|
tok ResyncT3, TokenProperty ResyncP1) {
|
|
return parseIdentifierDeclName(P, Result, L, DeclKindName, ResyncT1, ResyncT2,
|
|
ResyncT3, tok::NUM_TOKENS, ResyncP1);
|
|
}
|
|
|
|
/// Add a fix-it to remove the space in consecutive identifiers.
|
|
/// Add a camel-cased option if it is different than the first option.
|
|
void Parser::diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc,
|
|
StringRef DeclKindName) {
|
|
assert(Tok.isAny(tok::identifier, tok::kw__));
|
|
|
|
diagnose(Tok, diag::repeated_identifier, DeclKindName);
|
|
auto Second = Tok.getText();
|
|
auto SecondLoc = consumeToken();
|
|
|
|
SourceRange FixRange(FirstLoc, SecondLoc);
|
|
// Provide two fix-its: a direct concatenation of the two identifiers
|
|
// and a camel-cased version.
|
|
//
|
|
auto DirectConcatenation = First.str() + Second.str();
|
|
diagnose(SecondLoc, diag::join_identifiers)
|
|
.fixItReplace(FixRange, DirectConcatenation);
|
|
|
|
SmallString<8> CapitalizedScratch;
|
|
auto Capitalized = camel_case::toSentencecase(Second,
|
|
CapitalizedScratch);
|
|
if (Capitalized != Second) {
|
|
auto CamelCaseConcatenation = First.str() + Capitalized.str();
|
|
diagnose(SecondLoc, diag::join_identifiers_camel_case)
|
|
.fixItReplace(FixRange, CamelCaseConcatenation);
|
|
}
|
|
}
|
|
|
|
/// Parse a Decl item in decl list.
|
|
ParserStatus Parser::parseDeclItem(bool &PreviousHadSemi,
|
|
Parser::ParseDeclOptions Options,
|
|
llvm::function_ref<void(Decl*)> handler) {
|
|
SyntaxParsingContext DeclContext(SyntaxContext, SyntaxContextKind::Decl);
|
|
if (Tok.is(tok::semi)) {
|
|
// Consume ';' without preceding decl.
|
|
diagnose(Tok, diag::unexpected_separator, ";")
|
|
.fixItRemove(Tok.getLoc());
|
|
consumeToken();
|
|
// Return success because we already recovered.
|
|
return makeParserSuccess();
|
|
}
|
|
|
|
// If the previous declaration didn't have a semicolon and this new
|
|
// declaration doesn't start a line, complain.
|
|
if (!PreviousHadSemi && !Tok.isAtStartOfLine() && !Tok.is(tok::unknown)) {
|
|
auto endOfPrevious = getEndOfPreviousLoc();
|
|
diagnose(endOfPrevious, diag::declaration_same_line_without_semi)
|
|
.fixItInsert(endOfPrevious, ";");
|
|
}
|
|
|
|
if (Tok.isAny(tok::pound_sourceLocation, tok::pound_line)) {
|
|
auto LineDirectiveStatus = parseLineDirective(Tok.is(tok::pound_line));
|
|
if (LineDirectiveStatus.isError())
|
|
skipUntilDeclRBrace(tok::semi, tok::pound_endif);
|
|
return LineDirectiveStatus;
|
|
}
|
|
|
|
auto Result = parseDecl(Options, handler);
|
|
if (Result.isParseError())
|
|
skipUntilDeclRBrace(tok::semi, tok::pound_endif);
|
|
SourceLoc SemiLoc;
|
|
PreviousHadSemi = consumeIf(tok::semi, SemiLoc);
|
|
if (PreviousHadSemi && Result.isNonNull())
|
|
Result.get()->TrailingSemiLoc = SemiLoc;
|
|
return Result;
|
|
}
|
|
|
|
/// \brief Parse the members in a struct/class/enum/protocol/extension.
|
|
///
|
|
/// \verbatim
|
|
/// decl* '}'
|
|
/// \endverbatim
|
|
bool Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc,
|
|
Diag<> ErrorDiag, ParseDeclOptions Options,
|
|
llvm::function_ref<void(Decl*)> handler) {
|
|
ParserStatus Status;
|
|
bool PreviousHadSemi = true;
|
|
{
|
|
SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::DeclList);
|
|
while (Tok.isNot(tok::r_brace)) {
|
|
Status |= parseDeclItem(PreviousHadSemi, Options, handler);
|
|
if (Tok.isAny(tok::eof, tok::pound_endif, tok::pound_else,
|
|
tok::pound_elseif)) {
|
|
IsInputIncomplete = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
parseMatchingToken(tok::r_brace, RBLoc, ErrorDiag, LBLoc);
|
|
|
|
// 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 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,
|
|
/*allowClassRequirement=*/false,
|
|
/*allowAnyObject=*/false);
|
|
|
|
// Parse the optional where-clause.
|
|
TrailingWhereClause *trailingWhereClause = nullptr;
|
|
if (Tok.is(tok::kw_where)) {
|
|
SourceLoc whereLoc;
|
|
SmallVector<RequirementRepr, 4> requirements;
|
|
bool firstTypeInComplete;
|
|
auto whereStatus = parseGenericWhereClause(whereLoc, requirements,
|
|
firstTypeInComplete);
|
|
if (whereStatus.isSuccess()) {
|
|
trailingWhereClause = TrailingWhereClause::create(Context, whereLoc,
|
|
requirements);
|
|
} else if (whereStatus.hasCodeCompletion()) {
|
|
if (CodeCompletion && firstTypeInComplete) {
|
|
CodeCompletion->completeGenericParams(extendedType.getPtrOrNull());
|
|
} else
|
|
return makeParserCodeCompletionResult<ExtensionDecl>();
|
|
}
|
|
}
|
|
|
|
ExtensionDecl *ext = ExtensionDecl::create(Context, ExtensionLoc,
|
|
extendedType.getPtrOrNull(),
|
|
Context.AllocateCopy(Inherited),
|
|
CurDeclContext,
|
|
trailingWhereClause);
|
|
ext->getAttrs() = Attributes;
|
|
|
|
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);
|
|
|
|
ParseDeclOptions Options(PD_HasContainerType | PD_InExtension);
|
|
|
|
if (parseDeclList(LBLoc, RBLoc, diag::expected_rbrace_extension,
|
|
Options, [&] (Decl *D) {ext->addMember(D);}))
|
|
status.setIsParseError();
|
|
|
|
// 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.
|
|
}
|
|
|
|
ext->setBraces({LBLoc, RBLoc});
|
|
|
|
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(bool isLine) {
|
|
SourceLoc Loc = consumeToken();
|
|
if (isLine) {
|
|
diagnose(Loc, diag::line_directive_style_deprecated)
|
|
.fixItReplace(Loc, "#sourceLocation");
|
|
}
|
|
bool WasInPoundLineEnvironment = InPoundLineEnvironment;
|
|
if (WasInPoundLineEnvironment) {
|
|
SourceMgr.closeVirtualFile(Loc);
|
|
InPoundLineEnvironment = false;
|
|
}
|
|
|
|
|
|
unsigned StartLine = 0;
|
|
Optional<StringRef> Filename;
|
|
const char *LastTokTextEnd;
|
|
if (!isLine) {
|
|
// #sourceLocation()
|
|
// #sourceLocation(file: "foo", line: 42)
|
|
if (parseToken(tok::l_paren, diag::sourceLocation_expected, "("))
|
|
return makeParserError();
|
|
|
|
// Handle the "reset" form.
|
|
if (consumeIf(tok::r_paren)) {
|
|
if (!WasInPoundLineEnvironment) {
|
|
diagnose(Tok, diag::unexpected_line_directive);
|
|
return makeParserError();
|
|
}
|
|
return makeParserSuccess();
|
|
}
|
|
|
|
if (parseSpecificIdentifier("file", diag::sourceLocation_expected,"file:")||
|
|
parseToken(tok::colon, diag::sourceLocation_expected, ":"))
|
|
return makeParserError();
|
|
|
|
if (Tok.isNot(tok::string_literal)) {
|
|
diagnose(Tok, diag::expected_line_directive_name);
|
|
return makeParserError();
|
|
}
|
|
|
|
Filename = getStringLiteralIfNotInterpolated(*this, Loc, Tok,
|
|
"#sourceLocation");
|
|
if (!Filename.hasValue())
|
|
return makeParserError();
|
|
consumeToken(tok::string_literal);
|
|
|
|
if (parseToken(tok::comma, diag::sourceLocation_expected, ",") ||
|
|
parseSpecificIdentifier("line", diag::sourceLocation_expected,"line:")||
|
|
parseToken(tok::colon, diag::sourceLocation_expected, ":"))
|
|
return makeParserError();
|
|
|
|
if (Tok.isNot(tok::integer_literal)) {
|
|
diagnose(Tok, diag::expected_line_directive_number);
|
|
return makeParserError();
|
|
}
|
|
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(tok::integer_literal);
|
|
|
|
LastTokTextEnd = Tok.getText().end();
|
|
if (parseToken(tok::r_paren, diag::sourceLocation_expected, ")"))
|
|
return makeParserError();
|
|
|
|
} else { // Legacy #line syntax.
|
|
|
|
// #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();
|
|
}
|
|
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(tok::integer_literal);
|
|
|
|
if (Tok.isNot(tok::string_literal)) {
|
|
diagnose(Tok, diag::expected_line_directive_name);
|
|
return makeParserError();
|
|
}
|
|
|
|
Filename = getStringLiteralIfNotInterpolated(*this, Loc, Tok,
|
|
"#line");
|
|
if (!Filename.hasValue())
|
|
return makeParserError();
|
|
LastTokTextEnd = Tok.getText().end();
|
|
consumeToken(tok::string_literal);
|
|
}
|
|
|
|
// Skip over trailing whitespace and a single \n to the start of the next
|
|
// line.
|
|
while (*LastTokTextEnd == ' ' || *LastTokTextEnd == '\t')
|
|
++LastTokTextEnd;
|
|
SourceLoc nextLineStartLoc = Lexer::getSourceLoc(LastTokTextEnd);
|
|
|
|
if (*LastTokTextEnd == '\n')
|
|
nextLineStartLoc = nextLineStartLoc.getAdvancedLoc(1);
|
|
else {
|
|
diagnose(Tok.getLoc(), diag::extra_tokens_line_directive);
|
|
return makeParserError();
|
|
}
|
|
|
|
int LineOffset = StartLine - SourceMgr.getLineNumber(nextLineStartLoc);
|
|
|
|
// Create a new virtual file for the region started by the #line marker.
|
|
bool isNewFile = SourceMgr.openVirtualFile(nextLineStartLoc,
|
|
Filename.getValue(), LineOffset);
|
|
assert(isNewFile);(void)isNewFile;
|
|
|
|
InPoundLineEnvironment = true;
|
|
return makeParserSuccess();
|
|
}
|
|
|
|
/// \brief Parse a typealias decl.
|
|
///
|
|
/// \verbatim
|
|
/// decl-typealias:
|
|
/// 'typealias' identifier generic-params? '=' type requirement-clause?
|
|
/// \endverbatim
|
|
ParserResult<TypeDecl> Parser::
|
|
parseDeclTypeAlias(Parser::ParseDeclOptions Flags, DeclAttributes &Attributes) {
|
|
ParserPosition startPosition = getParserPosition();
|
|
SourceLoc TypeAliasLoc = consumeToken(tok::kw_typealias);
|
|
SourceLoc EqualLoc;
|
|
Identifier Id;
|
|
SourceLoc IdLoc;
|
|
ParserStatus Status;
|
|
|
|
Status |= parseIdentifierDeclName(*this, Id, IdLoc, "typealias",
|
|
tok::colon, tok::equal);
|
|
if (Status.isError())
|
|
return nullptr;
|
|
|
|
DebuggerContextChange DCC(*this, Id, DeclKind::TypeAlias);
|
|
|
|
Optional<Scope> GenericsScope;
|
|
GenericsScope.emplace(this, ScopeKind::Generics);
|
|
|
|
// Parse a generic parameter list if it is present.
|
|
GenericParamList *genericParams = nullptr;
|
|
if (startsWithLess(Tok)) {
|
|
auto Result = parseGenericParameters();
|
|
if (Result.hasCodeCompletion() && !CodeCompletion)
|
|
return makeParserCodeCompletionStatus();
|
|
genericParams = Result.getPtrOrNull();
|
|
|
|
if (!genericParams) {
|
|
// If the parser returned null, it is an already diagnosed parse error.
|
|
} else if (!genericParams->getRequirements().empty()) {
|
|
// Reject a where clause.
|
|
diagnose(genericParams->getWhereLoc(),
|
|
diag::associated_type_generic_parameter_list)
|
|
.highlight(genericParams->getWhereClauseSourceRange());
|
|
}
|
|
}
|
|
|
|
if (Flags.contains(PD_InProtocol) && !genericParams && !Tok.is(tok::equal)) {
|
|
// If we're in a protocol and don't see an '=' this looks like leftover Swift 2
|
|
// code intending to be an associatedtype.
|
|
backtrackToPosition(startPosition);
|
|
return parseDeclAssociatedType(Flags, Attributes);
|
|
}
|
|
|
|
ParserResult<TypeRepr> UnderlyingTy;
|
|
|
|
if (Tok.is(tok::colon) || Tok.is(tok::equal)) {
|
|
SyntaxParsingContext InitCtx(SyntaxContext,
|
|
SyntaxKind::TypeInitializerClause);
|
|
if (Tok.is(tok::colon)) {
|
|
// It is a common mistake to write "typealias A : Int" instead of = Int.
|
|
// Recognize this and produce a fixit.
|
|
diagnose(Tok, diag::expected_equal_in_typealias)
|
|
.fixItReplace(Tok.getLoc(), " = ");
|
|
consumeToken(tok::colon);
|
|
} else {
|
|
EqualLoc = consumeToken(tok::equal);
|
|
}
|
|
|
|
UnderlyingTy = parseType(diag::expected_type_in_typealias);
|
|
Status |= UnderlyingTy;
|
|
if (UnderlyingTy.isNull())
|
|
return Status;
|
|
}
|
|
|
|
auto *TAD = new (Context) TypeAliasDecl(TypeAliasLoc, EqualLoc, Id, IdLoc,
|
|
/*genericParams*/nullptr,
|
|
CurDeclContext);
|
|
TAD->getUnderlyingTypeLoc() = UnderlyingTy.getPtrOrNull();
|
|
TAD->getAttrs() = Attributes;
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
ContextChange CC(*this, TAD);
|
|
Status |= parseFreestandingGenericWhereClause(genericParams);
|
|
}
|
|
|
|
// Set after parsing the where clause, which might create genericParams.
|
|
TAD->setGenericParams(genericParams);
|
|
|
|
if (UnderlyingTy.isNull()) {
|
|
diagnose(Tok, diag::expected_equal_in_typealias);
|
|
Status.setIsParseError();
|
|
return Status;
|
|
}
|
|
|
|
// Exit the scope introduced for the generic parameters.
|
|
GenericsScope.reset();
|
|
|
|
addToScope(TAD);
|
|
return DCC.fixupParserResult(Status, TAD);
|
|
}
|
|
|
|
/// \brief Parse an associatedtype decl.
|
|
///
|
|
/// \verbatim
|
|
/// decl-associatedtype:
|
|
/// 'associatedtype' identifier inheritance? ('=' type)? where-clause?
|
|
/// \endverbatim
|
|
|
|
ParserResult<TypeDecl> Parser::parseDeclAssociatedType(Parser::ParseDeclOptions Flags,
|
|
DeclAttributes &Attributes) {
|
|
SourceLoc AssociatedTypeLoc;
|
|
ParserStatus Status;
|
|
Identifier Id;
|
|
SourceLoc IdLoc;
|
|
|
|
// Look for 'typealias' here and diagnose a fixit because parseDeclTypeAlias can
|
|
// ask us to fix up leftover Swift 2 code intending to be an associatedtype.
|
|
if (Tok.is(tok::kw_typealias)) {
|
|
AssociatedTypeLoc = consumeToken(tok::kw_typealias);
|
|
diagnose(AssociatedTypeLoc, diag::typealias_inside_protocol_without_type)
|
|
.fixItReplace(AssociatedTypeLoc, "associatedtype");
|
|
} else {
|
|
AssociatedTypeLoc = consumeToken(tok::kw_associatedtype);
|
|
}
|
|
|
|
Status = parseIdentifierDeclName(*this, Id, IdLoc, "associatedtype",
|
|
tok::colon, tok::equal);
|
|
if (Status.isError())
|
|
return nullptr;
|
|
|
|
DebuggerContextChange DCC(*this, Id, DeclKind::AssociatedType);
|
|
|
|
// Reject generic parameters with a specific error.
|
|
if (startsWithLess(Tok)) {
|
|
// Introduce a throwaway scope to capture the generic parameters. We
|
|
// don't want them visible anywhere!
|
|
Scope S(this, ScopeKind::Generics);
|
|
|
|
if (auto genericParams = parseGenericParameters().getPtrOrNull()) {
|
|
diagnose(genericParams->getLAngleLoc(),
|
|
diag::associated_type_generic_parameter_list)
|
|
.fixItRemove(genericParams->getSourceRange());
|
|
}
|
|
}
|
|
|
|
// Parse optional inheritance clause.
|
|
// FIXME: Allow class requirements here.
|
|
SmallVector<TypeLoc, 2> Inherited;
|
|
if (Tok.is(tok::colon))
|
|
Status |= parseInheritance(Inherited,
|
|
/*allowClassRequirement=*/false,
|
|
/*allowAnyObject=*/true);
|
|
|
|
ParserResult<TypeRepr> UnderlyingTy;
|
|
if (Tok.is(tok::equal)) {
|
|
consumeToken(tok::equal);
|
|
UnderlyingTy = parseType(diag::expected_type_in_associatedtype);
|
|
Status |= UnderlyingTy;
|
|
if (UnderlyingTy.isNull())
|
|
return Status;
|
|
}
|
|
|
|
TrailingWhereClause *TrailingWhere = nullptr;
|
|
// Parse a 'where' clause if present.
|
|
if (Tok.is(tok::kw_where)) {
|
|
auto whereStatus = parseProtocolOrAssociatedTypeWhereClause(
|
|
TrailingWhere, /*isProtocol=*/false);
|
|
Status |= whereStatus;
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
if (!Flags.contains(PD_InProtocol)) {
|
|
diagnose(AssociatedTypeLoc, diag::associatedtype_outside_protocol)
|
|
.fixItReplace(AssociatedTypeLoc, "typealias");
|
|
Status.setIsParseError();
|
|
return Status;
|
|
}
|
|
|
|
auto assocType = new (Context)
|
|
AssociatedTypeDecl(CurDeclContext, AssociatedTypeLoc, Id, IdLoc,
|
|
UnderlyingTy.getPtrOrNull(), TrailingWhere);
|
|
assocType->getAttrs() = Attributes;
|
|
if (!Inherited.empty())
|
|
assocType->setInherited(Context.AllocateCopy(Inherited));
|
|
addToScope(assocType);
|
|
return makeParserResult(Status, assocType);
|
|
}
|
|
|
|
/// This function creates an accessor function (with no body) for a computed
|
|
/// property or subscript.
|
|
static FuncDecl *createAccessorFunc(SourceLoc DeclLoc, ParameterList *param,
|
|
GenericParamList *GenericParams,
|
|
ParameterList *Indices,
|
|
TypeLoc ElementTy,
|
|
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) {
|
|
// Create parameter declarations corresponding to each of the
|
|
// parameter declarations from the subscript declaration.
|
|
for (ParamDecl *storageParam : *Indices) {
|
|
// Clone the parameter. Do not clone the parameter type;
|
|
// this will be filled in by the type-checker.
|
|
auto accessorParam =
|
|
new (P->Context) ParamDecl(storageParam->getSpecifier(),
|
|
storageParam->getSpecifierLoc(),
|
|
storageParam->getArgumentNameLoc(),
|
|
storageParam->getArgumentName(),
|
|
storageParam->getNameLoc(),
|
|
storageParam->getName(),
|
|
Type(),
|
|
P->CurDeclContext);
|
|
accessorParam->setVariadic(storageParam->isVariadic());
|
|
|
|
// The cloned parameter is implicit.
|
|
accessorParam->setImplicit();
|
|
|
|
// It has no default arguments; these will be always be taken
|
|
// from the subscript declaration.
|
|
accessorParam->setDefaultArgumentKind(DefaultArgumentKind::None);
|
|
|
|
ValueArgElements.push_back(accessorParam);
|
|
}
|
|
|
|
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::createUnboundSelf(DeclLoc, P->CurDeclContext));
|
|
|
|
// Add the "(value)" and subscript indices parameter clause.
|
|
Params.push_back(ValueArg);
|
|
|
|
// The typechecker will always fill this in.
|
|
TypeLoc ReturnType;
|
|
|
|
// Start the function.
|
|
auto *D = FuncDecl::create(P->Context, StaticLoc, StaticSpellingKind::None,
|
|
/*FIXME FuncLoc=*/DeclLoc, Identifier(),
|
|
/*NameLoc=*/DeclLoc, /*Throws=*/false,
|
|
/*ThrowsLoc=*/SourceLoc(), AccessorKeywordLoc,
|
|
(GenericParams
|
|
? GenericParams->clone(P->CurDeclContext)
|
|
: nullptr),
|
|
Params, ReturnType,
|
|
P->CurDeclContext);
|
|
|
|
// Non-static set/willSet/didSet/materializeForSet/mutableAddress
|
|
// default to mutating. get/address default to
|
|
// non-mutating.
|
|
switch (Kind) {
|
|
case AccessorKind::IsAddressor:
|
|
D->setAddressorKind(addressorKind);
|
|
break;
|
|
|
|
case AccessorKind::IsGetter:
|
|
break;
|
|
|
|
case AccessorKind::IsMutableAddressor:
|
|
D->setAddressorKind(addressorKind);
|
|
LLVM_FALLTHROUGH;
|
|
|
|
case AccessorKind::IsSetter:
|
|
case AccessorKind::IsWillSet:
|
|
case AccessorKind::IsDidSet:
|
|
if (D->isInstanceMember())
|
|
D->setSelfAccessKind(SelfAccessKind::Mutating);
|
|
break;
|
|
|
|
case AccessorKind::IsMaterializeForSet:
|
|
case AccessorKind::NotAccessor:
|
|
llvm_unreachable("not parseable accessors");
|
|
}
|
|
|
|
return D;
|
|
}
|
|
|
|
static ParamDecl *
|
|
createSetterAccessorArgument(SourceLoc nameLoc, Identifier name,
|
|
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(VarDecl::Specifier::Owned,
|
|
SourceLoc(),SourceLoc(),
|
|
Identifier(), nameLoc, name,
|
|
Type(), P.CurDeclContext);
|
|
if (isNameImplicit)
|
|
result->setImplicit();
|
|
|
|
// 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,
|
|
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)) {
|
|
SyntaxParsingContext ParamCtx(P.SyntaxContext, SyntaxKind::AccessorParameter);
|
|
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, 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));
|
|
}
|
|
|
|
void Parser::parseAccessorAttributes(DeclAttributes &Attributes) {
|
|
bool FoundCCToken;
|
|
parseDeclAttributeList(Attributes, FoundCCToken);
|
|
SyntaxParsingContext ModifierCtx(SyntaxContext, SyntaxKind::DeclModifier);
|
|
// 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);
|
|
} else if (Tok.isContextualKeyword("__consuming")) {
|
|
parseNewDeclAttribute(Attributes, /*AtLoc*/ {}, DAK_Consuming);
|
|
} else {
|
|
ModifierCtx.setTransparent();
|
|
}
|
|
}
|
|
|
|
/// \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,
|
|
GenericParamList *GenericParams,
|
|
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;
|
|
|
|
SyntaxParsingContext AccessorCtx(SyntaxContext, SyntaxKind::AccessorDecl);
|
|
// Parse any leading attributes.
|
|
DeclAttributes Attributes;
|
|
parseAccessorAttributes(Attributes);
|
|
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 {
|
|
AccessorCtx.setTransparent();
|
|
AccessorKeywordLoc = SourceLoc();
|
|
diagnose(Tok, diag::expected_getset_in_protocol);
|
|
return true;
|
|
}
|
|
|
|
// Correct the token kind to be contextual keyword.
|
|
if (AccessorKeywordLoc.isValid()) {
|
|
Tok.setKind(tok::contextual_keyword);
|
|
}
|
|
|
|
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, *this, Kind);
|
|
|
|
// Set up a function declaration.
|
|
TheDecl = createAccessorFunc(Loc, ValueNameParams,
|
|
GenericParams, Indices, ElementTy,
|
|
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, preserve 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;
|
|
SyntaxParsingContext AccessorCtx(SyntaxContext, SyntaxKind::AccessorDecl);
|
|
// 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;
|
|
parseAccessorAttributes(Attributes);
|
|
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;
|
|
}
|
|
|
|
// Set the contextual keyword kind properly.
|
|
if (AccessorKeywordLoc.isValid()) {
|
|
Tok.setKind(tok::contextual_keyword);
|
|
}
|
|
|
|
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, *this, Kind);
|
|
|
|
SyntaxParsingContext BlockCtx(SyntaxContext, SyntaxKind::CodeBlock);
|
|
if (AccessorKeywordLoc.isInvalid()) {
|
|
// If the keyword is absent, we shouldn't make these sub-nodes.
|
|
BlockCtx.setTransparent();
|
|
AccessorCtx.setTransparent();
|
|
}
|
|
|
|
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,
|
|
GenericParams, Indices, ElementTy,
|
|
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);
|
|
|
|
if (TheDecl->isGeneric())
|
|
for (auto *GP : TheDecl->getGenericParams()->getParams())
|
|
addToScope(GP);
|
|
|
|
// 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,
|
|
GenericParamList *GenericParams,
|
|
ParameterList *Indices,
|
|
TypeLoc ElementTy, ParsedAccessors &accessors,
|
|
SourceLoc StaticLoc,
|
|
SmallVectorImpl<Decl *> &Decls) {
|
|
SyntaxParsingContext AccessorsCtx(SyntaxContext, SyntaxKind::AccessorBlock);
|
|
accessors.LBLoc = consumeToken(tok::l_brace);
|
|
SourceLoc LastValidLoc = accessors.LBLoc;
|
|
bool Invalid = parseGetSetImpl(Flags, GenericParams, Indices, ElementTy,
|
|
accessors, LastValidLoc, StaticLoc,
|
|
accessors.LBLoc, Decls);
|
|
|
|
// Collect all explicit accessors to a list.
|
|
AccessorsCtx.collectNodesInPlace(SyntaxKind::AccessorList);
|
|
// 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))
|
|
consumeTokenWithoutFeedingReceiver();
|
|
|
|
// 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);
|
|
}
|
|
|
|
static void fillInAccessorTypeErrors(Parser &P, FuncDecl *accessor,
|
|
AccessorKind kind) {
|
|
if (!accessor) return;
|
|
|
|
// Fill in the parameter types.
|
|
for (auto paramList : accessor->getParameterLists()) {
|
|
for (auto param : *paramList) {
|
|
if (param->getTypeLoc().isNull()) {
|
|
param->getTypeLoc().setType(P.Context.TheErrorType, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fill in the result type.
|
|
switch (kind) {
|
|
case AccessorKind::NotAccessor:
|
|
case AccessorKind::IsMaterializeForSet:
|
|
llvm_unreachable("should never be seen here");
|
|
|
|
// These have non-trivial returns, so fill in error.
|
|
case AccessorKind::IsGetter:
|
|
case AccessorKind::IsAddressor:
|
|
case AccessorKind::IsMutableAddressor:
|
|
accessor->getBodyResultTypeLoc().setType(P.Context.TheErrorType, true);
|
|
return;
|
|
|
|
// These return void.
|
|
case AccessorKind::IsSetter:
|
|
case AccessorKind::IsWillSet:
|
|
case AccessorKind::IsDidSet:
|
|
return;
|
|
}
|
|
llvm_unreachable("bad kind");
|
|
}
|
|
|
|
/// We weren't able to tie the given accessors to a storage declaration.
|
|
/// Fill in various slots with type errors.
|
|
static void fillInAccessorTypeErrors(Parser &P,
|
|
Parser::ParsedAccessors &accessors) {
|
|
fillInAccessorTypeErrors(P, accessors.Get, AccessorKind::IsGetter);
|
|
fillInAccessorTypeErrors(P, accessors.Set, AccessorKind::IsSetter);
|
|
fillInAccessorTypeErrors(P, accessors.Addressor, AccessorKind::IsAddressor);
|
|
fillInAccessorTypeErrors(P, accessors.MutableAddressor,
|
|
AccessorKind::IsMutableAddressor);
|
|
fillInAccessorTypeErrors(P, accessors.WillSet, AccessorKind::IsWillSet);
|
|
fillInAccessorTypeErrors(P, accessors.DidSet, AccessorKind::IsDidSet);
|
|
}
|
|
|
|
/// \brief Parse the brace-enclosed getter and setter for a variable.
|
|
VarDecl *Parser::parseDeclVarGetSet(Pattern *pattern,
|
|
ParseDeclOptions Flags,
|
|
SourceLoc StaticLoc, SourceLoc VarLoc,
|
|
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 (auto *Typed = dyn_cast<TypedPattern>(PrimaryPattern))
|
|
PrimaryPattern = Typed->getSubPattern();
|
|
if (auto *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 (auto *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, /*GenericParams=*/nullptr,
|
|
/*Indices=*/nullptr, TyLoc, accessors, StaticLoc,
|
|
Decls))
|
|
Invalid = true;
|
|
|
|
// If we have an invalid case, bail out now.
|
|
if (!PrimaryVar) {
|
|
fillInAccessorTypeErrors(*this, accessors);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!TyLoc.hasLocation()) {
|
|
if (accessors.Get || accessors.Set || accessors.Addressor ||
|
|
accessors.MutableAddressor) {
|
|
SourceLoc locAfterPattern = pattern->getLoc().getAdvancedLoc(
|
|
pattern->getBoundName().getLength());
|
|
diagnose(pattern->getLoc(), diag::computed_property_missing_type)
|
|
.fixItInsert(locAfterPattern, ": <# Type #>");
|
|
Invalid = true;
|
|
}
|
|
}
|
|
|
|
// Reject accessors on 'let's after parsing them (for better recovery).
|
|
if (PrimaryVar->isLet() && !Attributes.hasAttribute<SILStoredAttr>()) {
|
|
Diag<> DiagID;
|
|
if (accessors.WillSet || accessors.DidSet)
|
|
DiagID = diag::let_cannot_be_observing_property;
|
|
else if (accessors.Addressor || accessors.MutableAddressor)
|
|
DiagID = diag::let_cannot_be_addressed_property;
|
|
else
|
|
DiagID = diag::let_cannot_be_computed_property;
|
|
|
|
diagnose(accessors.LBLoc, DiagID).fixItReplace(VarLoc, "var");
|
|
PrimaryVar->setSpecifier(VarDecl::Specifier::Var);
|
|
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->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;
|
|
}
|
|
};
|
|
|
|
GenericParamList *genericParams = nullptr;
|
|
if (auto *subscript = dyn_cast<SubscriptDecl>(storage))
|
|
genericParams = subscript->getGenericParams();
|
|
|
|
// Create an implicit accessor declaration.
|
|
auto createImplicitAccessor =
|
|
[&](AccessorKind kind, AddressorKind addressorKind,
|
|
ParameterList *argList) -> FuncDecl* {
|
|
auto accessor = createAccessorFunc(SourceLoc(), argList,
|
|
genericParams, indices, elementTy,
|
|
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(),
|
|
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(),
|
|
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);
|
|
// Create a getter so we don't break downstream invariants by having a
|
|
// setter without a getter.
|
|
Get = createImplicitAccessor(AccessorKind::IsGetter,
|
|
AddressorKind::NotAddressor, nullptr);
|
|
} else if (Set) {
|
|
if (!invalid) P.diagnose(Set->getLoc(), diag::var_set_without_get);
|
|
// Create a getter so we don't break downstream invariants by having a
|
|
// setter without a getter.
|
|
Get = createImplicitAccessor(AccessorKind::IsGetter,
|
|
AddressorKind::NotAddressor, nullptr);
|
|
}
|
|
}
|
|
|
|
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.
|
|
ParserResult<PatternBindingDecl>
|
|
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();
|
|
StaticSpelling = StaticSpellingKind::None;
|
|
} 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);
|
|
}
|
|
|
|
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;
|
|
auto BaseContext = CurDeclContext;
|
|
|
|
// No matter what error path we take, make sure the
|
|
// PatternBindingDecl/TopLevel code block are added.
|
|
auto makeResult =
|
|
[&](ParserStatus Status) -> ParserResult<PatternBindingDecl> {
|
|
|
|
// If we didn't parse any patterns, don't create the pattern binding decl.
|
|
if (PBDEntries.empty())
|
|
return Status;
|
|
|
|
// 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, BaseContext);
|
|
|
|
// Wire up any initializer contexts we needed.
|
|
for (unsigned i : indices(PBDEntries)) {
|
|
if (auto initContext = PBDEntries[i].getInitContext())
|
|
cast<PatternBindingInitializer>(initContext)->setBinding(PBD, i);
|
|
}
|
|
|
|
// 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) {
|
|
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 makeParserResult(Status, 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);
|
|
|
|
// Always return the result for PBD.
|
|
return makeParserResult(Status, PBD);
|
|
};
|
|
SyntaxParsingContext PBListCtx(SyntaxContext, SyntaxKind::PatternBindingList);
|
|
bool HasNext;
|
|
do {
|
|
SyntaxParsingContext PatternBindingCtx(SyntaxContext,
|
|
SyntaxKind::PatternBinding);
|
|
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 makeResult(makeParserCodeCompletionStatus());
|
|
if (patternRes.isNull())
|
|
return makeResult(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, nullptr});
|
|
|
|
Expr *PatternInit = nullptr;
|
|
|
|
// Parse an initializer if present.
|
|
if (Tok.is(tok::equal)) {
|
|
SyntaxParsingContext InitCtx(SyntaxContext, SyntaxKind::InitializerClause);
|
|
// 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;
|
|
|
|
// 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 = new (Context) PatternBindingInitializer(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 set up an initialization context for a property or module-level
|
|
// global, record it.
|
|
PBDEntries.back().setInitContext(initContext);
|
|
|
|
// 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.getPtrOrNull() ? init.get()->getEndLoc() : SourceLoc();
|
|
return makeResult(makeParserCodeCompletionStatus());
|
|
}
|
|
|
|
if (init.isNull())
|
|
return makeResult(makeParserError());
|
|
}
|
|
|
|
// Parse a behavior block if present.
|
|
if (Context.LangOpts.EnableExperimentalPropertyBehaviors
|
|
&& Tok.is(tok::identifier)
|
|
&& Tok.getRawText().equals("__behavior")) {
|
|
consumeToken(tok::identifier);
|
|
auto type = parseType(diag::expected_behavior_name,
|
|
/*handle completion*/ true);
|
|
if (type.isParseError())
|
|
return makeResult(makeParserError());
|
|
if (type.hasCodeCompletion())
|
|
return makeResult(makeParserCodeCompletionStatus());
|
|
|
|
// Parse a following trailing closure argument.
|
|
// FIXME: Handle generalized parameters.
|
|
Expr *paramExpr = nullptr;
|
|
if (Tok.is(tok::l_brace)) {
|
|
// 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;
|
|
|
|
// Record the variables that we're trying to set up. 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 parameter
|
|
// 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);
|
|
|
|
// Set up a decl context for the closure.
|
|
// This will be recontextualized to a method we synthesize during
|
|
// type checking.
|
|
if (!CurDeclContext->isLocalContext() && !topLevelDecl && !initContext)
|
|
initContext = new (Context) PatternBindingInitializer(CurDeclContext);
|
|
Optional<ParseFunctionBody> initParser;
|
|
Optional<ContextChange> topLevelParser;
|
|
if (topLevelDecl)
|
|
topLevelParser.emplace(*this, topLevelDecl,
|
|
&State->getTopLevelContext());
|
|
if (initContext)
|
|
initParser.emplace(*this, initContext);
|
|
|
|
auto closure = parseExprClosure();
|
|
PBDEntries.back().setInitContext(initContext);
|
|
|
|
if (closure.isParseError())
|
|
return makeResult(makeParserError());
|
|
if (closure.hasCodeCompletion())
|
|
return makeResult(makeParserCodeCompletionStatus());
|
|
paramExpr = closure.get();
|
|
}
|
|
|
|
unsigned numVars = 0;
|
|
pattern->forEachVariable([&](VarDecl *VD) {
|
|
++numVars;
|
|
// TODO: Support parameter closure with multiple vars. This is tricky
|
|
// since the behavior's parameter type may be dependent on the
|
|
// property type, so we'd need to clone the closure expr for each var
|
|
// to re-type-check it.
|
|
if (numVars > 1 && paramExpr) {
|
|
diagnose(paramExpr->getLoc(), diag::behavior_multiple_vars);
|
|
paramExpr = nullptr;
|
|
}
|
|
|
|
VD->addBehavior(type.get(), paramExpr);
|
|
});
|
|
// If we syntactically match the second decl-var production, with a
|
|
// var-get-set clause, parse the var-get-set clause.
|
|
} else if (Tok.is(tok::l_brace)) {
|
|
HasAccessors = true;
|
|
|
|
if (auto *boundVar = parseDeclVarGetSet(pattern, Flags,
|
|
StaticLoc, VarLoc,
|
|
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 (auto *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);
|
|
}
|
|
}
|
|
}
|
|
HasNext = consumeIf(tok::comma);
|
|
} while (HasNext);
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
return makeResult(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 where-clause?
|
|
/// 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();
|
|
StaticSpelling = StaticSpellingKind::None;
|
|
} 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");
|
|
|
|
StaticSpelling = StaticSpellingKind::KeywordStatic;
|
|
}
|
|
}
|
|
}
|
|
|
|
SourceLoc FuncLoc = consumeToken(tok::kw_func);
|
|
|
|
// Parse function name.
|
|
Identifier SimpleName;
|
|
SourceLoc NameLoc;
|
|
if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix, tok::amp_prefix)) {
|
|
// 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.
|
|
auto NameStr = Tok.getText();
|
|
if (NameStr.size() > 1 && NameStr.back() == '<' &&
|
|
peekToken().is(tok::identifier)) {
|
|
NameStr = NameStr.slice(0, NameStr.size() - 1);
|
|
}
|
|
SimpleName = Context.getIdentifier(NameStr);
|
|
NameLoc = consumeStartingCharacterOfCurrentToken(tok::oper_binary_spaced,
|
|
NameStr.size());
|
|
// Within a protocol, recover from a missing 'static'.
|
|
if (Flags & PD_InProtocol) {
|
|
switch (StaticSpelling) {
|
|
case StaticSpellingKind::None: {
|
|
auto Message = Context.isSwiftVersion3()
|
|
? diag::swift3_operator_static_in_protocol
|
|
: diag::operator_static_in_protocol;
|
|
diagnose(NameLoc, Message, SimpleName.str())
|
|
.fixItInsert(FuncLoc, "static ");
|
|
StaticSpelling = StaticSpellingKind::KeywordStatic;
|
|
break;
|
|
}
|
|
|
|
case StaticSpellingKind::KeywordStatic:
|
|
// Okay, this is correct.
|
|
break;
|
|
|
|
case StaticSpellingKind::KeywordClass:
|
|
llvm_unreachable("should have been fixed above");
|
|
}
|
|
}
|
|
} else {
|
|
// This non-operator path is quite accepting of what tokens might be a name,
|
|
// because we're aggressive about recovering/providing good diagnostics for
|
|
// beginners.
|
|
auto NameStatus = parseIdentifierDeclName(
|
|
*this, SimpleName, NameLoc, "function", tok::l_paren, tok::arrow,
|
|
tok::l_brace, TokenProperty::StartsWithLess);
|
|
if (NameStatus.isError())
|
|
return nullptr;
|
|
}
|
|
|
|
DebuggerContextChange DCC(*this, SimpleName, DeclKind::Func);
|
|
|
|
// Parse the generic-params, if present.
|
|
Optional<Scope> GenericsScope;
|
|
GenericsScope.emplace(this, ScopeKind::Generics);
|
|
GenericParamList *GenericParams;
|
|
bool SignatureHasCodeCompletion = false;
|
|
auto GenericParamResult = maybeParseGenericParams();
|
|
GenericParams = GenericParamResult.getPtrOrNull();
|
|
SignatureHasCodeCompletion |= GenericParamResult.hasCodeCompletion();
|
|
if (SignatureHasCodeCompletion && !CodeCompletion)
|
|
return makeParserCodeCompletionStatus();
|
|
|
|
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::createUnboundSelf(NameLoc, CurDeclContext));
|
|
|
|
DefaultArgumentInfo DefaultArgs(HasContainerType);
|
|
TypeRepr *FuncRetTy = nullptr;
|
|
DeclName FullName;
|
|
SourceLoc throwsLoc;
|
|
bool rethrows;
|
|
ParserStatus SignatureStatus =
|
|
parseFunctionSignature(SimpleName, FullName, BodyParams, DefaultArgs,
|
|
throwsLoc, rethrows, FuncRetTy);
|
|
|
|
SignatureHasCodeCompletion |= SignatureStatus.hasCodeCompletion();
|
|
if (SignatureStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return SignatureStatus;
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
// Create the decl for the func and add it to the parent scope.
|
|
auto *FD = FuncDecl::create(Context, StaticLoc, StaticSpelling,
|
|
FuncLoc, FullName, NameLoc,
|
|
/*Throws=*/throwsLoc.isValid(), throwsLoc,
|
|
/*AccessorKeywordLoc=*/SourceLoc(),
|
|
nullptr, BodyParams, FuncRetTy,
|
|
CurDeclContext);
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
ContextChange CC(*this, FD);
|
|
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion();
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
FD->setGenericParams(GenericParams);
|
|
|
|
// 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.
|
|
{
|
|
Scope S(this, ScopeKind::FunctionBody);
|
|
|
|
diagnoseOperatorFixityAttributes(*this, Attributes, FD);
|
|
|
|
// 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 (SignatureHasCodeCompletion)
|
|
CodeCompletion->setParsedDecl(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))
|
|
consumeTokenWithoutFeedingReceiver();
|
|
|
|
// 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?
|
|
/// where-clause? '{' 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, "enum",
|
|
tok::colon, tok::l_brace,
|
|
TokenProperty::StartsWithLess);
|
|
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);
|
|
auto Result = maybeParseGenericParams();
|
|
GenericParams = Result.getPtrOrNull();
|
|
if (Result.hasCodeCompletion())
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
EnumDecl *ED = new (Context) EnumDecl(EnumLoc, EnumName, EnumNameLoc,
|
|
{ }, nullptr, CurDeclContext);
|
|
setLocalDiscriminator(ED);
|
|
ED->getAttrs() = Attributes;
|
|
|
|
ContextChange CC(*this, ED);
|
|
|
|
// Parse optional inheritance clause within the context of the enum.
|
|
if (Tok.is(tok::colon)) {
|
|
SmallVector<TypeLoc, 2> Inherited;
|
|
Status |= parseInheritance(Inherited,
|
|
/*allowClassRequirement=*/false,
|
|
/*allowAnyObject=*/false);
|
|
ED->setInherited(Context.AllocateCopy(Inherited));
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
Status |= whereStatus;
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
ED->setGenericParams(GenericParams);
|
|
|
|
SourceLoc LBLoc, RBLoc;
|
|
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_enum)) {
|
|
LBLoc = PreviousLoc;
|
|
RBLoc = LBLoc;
|
|
Status.setIsParseError();
|
|
} else {
|
|
Scope S(this, ScopeKind::ClassBody);
|
|
ParseDeclOptions Options(PD_HasContainerType | PD_AllowEnumElement | PD_InEnum);
|
|
if (parseDeclList(LBLoc, RBLoc, diag::expected_rbrace_enum,
|
|
Options, [&] (Decl *D) { ED->addMember(D); }))
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
ED->setBraces({LBLoc, RBLoc});
|
|
|
|
addToScope(ED);
|
|
|
|
return DCC.fixupParserResult(Status, ED);
|
|
}
|
|
|
|
/// \brief Parse a 'case' of an enum.
|
|
///
|
|
/// \verbatim
|
|
/// enum-case:
|
|
/// identifier type-tuple?
|
|
/// decl-enum-element:
|
|
/// 'case' attribute-list enum-case (',' enum-case)*
|
|
/// \endverbatim
|
|
ParserResult<EnumCaseDecl>
|
|
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);
|
|
|
|
// 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;
|
|
}
|
|
|
|
if (Tok.is(tok::identifier)) {
|
|
Status |= parseIdentifierDeclName(*this, Name, NameLoc, "enum 'case'",
|
|
tok::l_paren, tok::kw_case, tok::colon,
|
|
tok::r_brace);
|
|
assert(Status.isSuccess());
|
|
if (DotLoc.isValid())
|
|
diagnose(DotLoc, diag::enum_case_dot_prefix)
|
|
.fixItRemove(DotLoc);
|
|
} else {
|
|
NameLoc = CaseLoc;
|
|
bool NameIsKeyword = Tok.isKeyword();
|
|
SourceLoc TokLoc = Tok.getLoc();
|
|
StringRef TokText = Tok.getText();
|
|
|
|
// 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 (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;
|
|
}
|
|
if (NameIsKeyword) {
|
|
diagnose(TokLoc, diag::keyword_cant_be_identifier, TokText);
|
|
diagnose(TokLoc, diag::backticks_to_escape)
|
|
.fixItReplace(TokLoc, "`" + TokText.str() + "`");
|
|
} else {
|
|
diagnose(CaseLoc, diag::expected_identifier_in_decl, "enum 'case'");
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
if (!CurLocalContext) {
|
|
// A local context is needed for parsing closures. We want to parse
|
|
// them anyways for proper diagnosis.
|
|
LocalContext tempContext{};
|
|
CurLocalContext = &tempContext;
|
|
RawValueExpr = parseExpr(diag::expected_expr_enum_case_raw_value);
|
|
CurLocalContext = nullptr;
|
|
} else {
|
|
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.
|
|
TypeRepr *ArgTR = ArgType.getPtrOrNull();
|
|
auto *result = new (Context) EnumElementDecl(NameLoc, Name,
|
|
ArgTR,
|
|
ArgTR != nullptr,
|
|
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 makeParserResult(Status, TheCase);
|
|
}
|
|
|
|
/// \brief Parse a 'struct' declaration, returning true (and doing no token
|
|
/// skipping) on error.
|
|
///
|
|
/// \verbatim
|
|
/// decl-struct:
|
|
/// 'struct' attribute-list identifier generic-params? inheritance?
|
|
/// where-clause? '{' 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, "struct",
|
|
tok::colon, tok::l_brace,
|
|
TokenProperty::StartsWithLess);
|
|
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);
|
|
auto Result = maybeParseGenericParams();
|
|
GenericParams = Result.getPtrOrNull();
|
|
if (Result.hasCodeCompletion())
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
StructDecl *SD = new (Context) StructDecl(StructLoc, StructName,
|
|
StructNameLoc,
|
|
{ },
|
|
nullptr,
|
|
CurDeclContext);
|
|
setLocalDiscriminator(SD);
|
|
SD->getAttrs() = Attributes;
|
|
|
|
ContextChange CC(*this, SD);
|
|
|
|
// Parse optional inheritance clause within the context of the struct.
|
|
if (Tok.is(tok::colon)) {
|
|
SmallVector<TypeLoc, 2> Inherited;
|
|
Status |= parseInheritance(Inherited,
|
|
/*allowClassRequirement=*/false,
|
|
/*allowAnyObject=*/false);
|
|
SD->setInherited(Context.AllocateCopy(Inherited));
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
Status |= whereStatus;
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
SD->setGenericParams(GenericParams);
|
|
// Make the entities of the struct as a code block.
|
|
SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock);
|
|
SourceLoc LBLoc, RBLoc;
|
|
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_struct)) {
|
|
LBLoc = PreviousLoc;
|
|
RBLoc = LBLoc;
|
|
Status.setIsParseError();
|
|
} else {
|
|
// Parse the body.
|
|
Scope S(this, ScopeKind::StructBody);
|
|
ParseDeclOptions Options(PD_HasContainerType | PD_InStruct);
|
|
if (parseDeclList(LBLoc, RBLoc, diag::expected_rbrace_struct,
|
|
Options, [&](Decl *D) {SD->addMember(D);}))
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
SD->setBraces({LBLoc, RBLoc});
|
|
|
|
addToScope(SD);
|
|
|
|
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?
|
|
/// where-clause? '{' 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, "class",
|
|
tok::colon, tok::l_brace,
|
|
TokenProperty::StartsWithLess);
|
|
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);
|
|
auto Result = maybeParseGenericParams();
|
|
GenericParams = Result.getPtrOrNull();
|
|
if (Result.hasCodeCompletion())
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
// Create the class.
|
|
ClassDecl *CD = new (Context) ClassDecl(ClassLoc, ClassName, ClassNameLoc,
|
|
{ }, nullptr, CurDeclContext);
|
|
setLocalDiscriminator(CD);
|
|
|
|
// Attach attributes.
|
|
CD->getAttrs() = Attributes;
|
|
|
|
ContextChange CC(*this, CD);
|
|
|
|
// Parse optional inheritance clause within the context of the class.
|
|
if (Tok.is(tok::colon)) {
|
|
SmallVector<TypeLoc, 2> Inherited;
|
|
Status |= parseInheritance(Inherited,
|
|
/*allowClassRequirement=*/false,
|
|
/*allowAnyObject=*/false);
|
|
CD->setInherited(Context.AllocateCopy(Inherited));
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
Status |= whereStatus;
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
CD->setGenericParams(GenericParams);
|
|
|
|
SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock);
|
|
SourceLoc LBLoc, RBLoc;
|
|
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_class)) {
|
|
LBLoc = PreviousLoc;
|
|
RBLoc = LBLoc;
|
|
Status.setIsParseError();
|
|
} else {
|
|
// Parse the body.
|
|
Scope S(this, ScopeKind::ClassBody);
|
|
ParseDeclOptions Options(PD_HasContainerType | PD_AllowDestructor |
|
|
PD_InClass);
|
|
auto Handler = [&] (Decl *D) {
|
|
CD->addMember(D);
|
|
if (isa<DestructorDecl>(D))
|
|
CD->setHasDestructor();
|
|
};
|
|
if (parseDeclList(LBLoc, RBLoc, diag::expected_rbrace_class,
|
|
Options, Handler))
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
CD->setBraces({LBLoc, RBLoc});
|
|
|
|
addToScope(CD);
|
|
|
|
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, "protocol",
|
|
tok::colon, tok::l_brace);
|
|
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 colonLoc;
|
|
if (Tok.is(tok::colon)) {
|
|
colonLoc = Tok.getLoc();
|
|
Status |= parseInheritance(InheritedProtocols,
|
|
/*allowClassRequirement=*/true,
|
|
/*allowAnyObject=*/true);
|
|
}
|
|
|
|
TrailingWhereClause *TrailingWhere = nullptr;
|
|
// Parse a 'where' clause if present.
|
|
if (Tok.is(tok::kw_where)) {
|
|
auto whereStatus = parseProtocolOrAssociatedTypeWhereClause(
|
|
TrailingWhere, /*isProtocol=*/true);
|
|
if (whereStatus.shouldStopParsing())
|
|
return whereStatus;
|
|
}
|
|
|
|
ProtocolDecl *Proto = new (Context)
|
|
ProtocolDecl(CurDeclContext, ProtocolLoc, NameLoc, ProtocolName,
|
|
Context.AllocateCopy(InheritedProtocols), TrailingWhere);
|
|
// No need to setLocalDiscriminator: protocols can't appear in local contexts.
|
|
|
|
Proto->getAttrs() = Attributes;
|
|
|
|
ContextChange CC(*this, Proto);
|
|
Scope ProtocolBodyScope(this, ScopeKind::ProtocolBody);
|
|
|
|
// Parse the body.
|
|
{
|
|
SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock);
|
|
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_DisallowInit |
|
|
PD_InProtocol);
|
|
if (parseDeclList(LBraceLoc, RBraceLoc, diag::expected_rbrace_protocol,
|
|
Options, [&](Decl *D) {Proto->addMember(D);}))
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
// Install the protocol elements.
|
|
Proto->setBraces({LBraceLoc, RBraceLoc});
|
|
}
|
|
|
|
return DCC.fixupParserResult(Status, Proto);
|
|
}
|
|
|
|
/// \brief Parse a 'subscript' declaration.
|
|
///
|
|
/// \verbatim
|
|
/// decl-subscript:
|
|
/// subscript-head get-set
|
|
/// subscript-head
|
|
/// attribute-list? 'subscript' parameter-clause '->' type
|
|
/// \endverbatim
|
|
ParserResult<SubscriptDecl>
|
|
Parser::parseDeclSubscript(ParseDeclOptions Flags,
|
|
DeclAttributes &Attributes,
|
|
SmallVectorImpl<Decl *> &Decls) {
|
|
ParserStatus Status;
|
|
SourceLoc SubscriptLoc = consumeToken(tok::kw_subscript);
|
|
|
|
// Parse the generic-params, if present.
|
|
Optional<Scope> GenericsScope;
|
|
GenericsScope.emplace(this, ScopeKind::Generics);
|
|
GenericParamList *GenericParams;
|
|
bool SignatureHasCodeCompletion = false;
|
|
|
|
auto Result = maybeParseGenericParams();
|
|
GenericParams = Result.getPtrOrNull();
|
|
SignatureHasCodeCompletion |= Result.hasCodeCompletion();
|
|
|
|
if (SignatureHasCodeCompletion && !CodeCompletion)
|
|
return makeParserCodeCompletionStatus();
|
|
|
|
// Parse the parameter list.
|
|
SmallVector<Identifier, 4> argumentNames;
|
|
ParserResult<ParameterList> Indices
|
|
= parseSingleParameterClause(ParameterContextKind::Subscript,
|
|
&argumentNames);
|
|
Status |= Indices;
|
|
|
|
SignatureHasCodeCompletion |= Indices.hasCodeCompletion();
|
|
if (SignatureHasCodeCompletion && !CodeCompletion)
|
|
return makeParserCodeCompletionStatus();
|
|
|
|
// '->'
|
|
SourceLoc ArrowLoc;
|
|
if (!consumeIf(tok::arrow, ArrowLoc)) {
|
|
if (!Indices.isParseError())
|
|
diagnose(Tok, diag::expected_arrow_subscript);
|
|
Status.setIsParseError();
|
|
}
|
|
|
|
if (!ArrowLoc.isValid() && (Indices.isNull() || Indices.get()->size() == 0)) {
|
|
// This doesn't look much like a subscript, so let regular recovery take
|
|
// care of it.
|
|
return Status;
|
|
}
|
|
|
|
// type
|
|
ParserResult<TypeRepr> ElementTy = parseType(diag::expected_type_subscript);
|
|
Status |= ElementTy;
|
|
SignatureHasCodeCompletion |= ElementTy.hasCodeCompletion();
|
|
if (SignatureHasCodeCompletion && !CodeCompletion) {
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
if (ElementTy.isNull()) {
|
|
// Always set an element type.
|
|
ElementTy = makeParserResult(ElementTy, new (Context) ErrorTypeRepr());
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
// Build an AST for the subscript declaration.
|
|
DeclName name = DeclName(Context, DeclBaseName::createSubscript(),
|
|
argumentNames);
|
|
auto *Subscript = new (Context) SubscriptDecl(name,
|
|
SubscriptLoc, Indices.get(),
|
|
ArrowLoc, ElementTy.get(),
|
|
CurDeclContext,
|
|
nullptr);
|
|
Subscript->getAttrs() = Attributes;
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
ContextChange CC(*this, Subscript);
|
|
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion();
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
Subscript->setGenericParams(GenericParams);
|
|
|
|
// Pass the function signature to code completion.
|
|
if (SignatureHasCodeCompletion && CodeCompletion) {
|
|
CodeCompletion->setParsedDecl(Subscript);
|
|
}
|
|
|
|
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 (!Status.isError()) {
|
|
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, GenericParams,
|
|
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);
|
|
|
|
// No need to setLocalDiscriminator because subscripts cannot
|
|
// validly appear outside of type decls.
|
|
return makeParserResult(Status, Subscript);
|
|
}
|
|
|
|
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);
|
|
auto GPResult = maybeParseGenericParams();
|
|
GenericParamList *GenericParams = GPResult.getPtrOrNull();
|
|
if (GPResult.hasCodeCompletion())
|
|
return makeParserCodeCompletionStatus();
|
|
|
|
// Parse the parameters.
|
|
DefaultArgumentInfo DefaultArgs(/*inTypeContext*/true);
|
|
llvm::SmallVector<Identifier, 4> namePieces;
|
|
bool SignatureHasCodeCompletion = false;
|
|
ParserResult<ParameterList> Params
|
|
= parseSingleParameterClause(ParameterContextKind::Initializer,
|
|
&namePieces, &DefaultArgs);
|
|
|
|
SignatureHasCodeCompletion |= Params.hasCodeCompletion();
|
|
if (Params.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return makeParserCodeCompletionStatus();
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
|
|
diagnoseWhereClauseInGenericParamList(GenericParams);
|
|
|
|
auto *SelfDecl = ParamDecl::createUnboundSelf(ConstructorLoc, CurDeclContext);
|
|
DeclName FullName(Context, Context.Id_init, namePieces);
|
|
|
|
auto *CD = new (Context) ConstructorDecl(FullName, ConstructorLoc,
|
|
Failability, FailabilityLoc,
|
|
throwsLoc.isValid(), throwsLoc,
|
|
SelfDecl, Params.get(), nullptr,
|
|
CurDeclContext);
|
|
|
|
// Parse a 'where' clause if present, adding it to our GenericParamList.
|
|
if (Tok.is(tok::kw_where)) {
|
|
ContextChange(*this, CD);
|
|
|
|
auto whereStatus = parseFreestandingGenericWhereClause(GenericParams);
|
|
SignatureHasCodeCompletion |= whereStatus.hasCodeCompletion();
|
|
if (whereStatus.hasCodeCompletion() && !CodeCompletion) {
|
|
// Trigger delayed parsing, no need to continue.
|
|
return whereStatus;
|
|
}
|
|
}
|
|
|
|
CD->setGenericParams(GenericParams);
|
|
|
|
Scope S2(this, ScopeKind::ConstructorBody);
|
|
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 (SignatureHasCodeCompletion)
|
|
CodeCompletion->setParsedDecl(CD);
|
|
|
|
if (ConstructorsNotAllowed || Params.isParseError()) {
|
|
// Tell the type checker not to touch this constructor.
|
|
CD->setInvalid();
|
|
}
|
|
|
|
addToScope(SelfDecl);
|
|
addParametersToScope(Params.get());
|
|
|
|
// '{'
|
|
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()) {
|
|
if (Tok.is(tok::identifier)) {
|
|
diagnose(Tok, diag::destructor_has_name).fixItRemove(Tok.getLoc());
|
|
} else
|
|
diagnose(Tok, diag::expected_lbrace_destructor);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
auto *SelfDecl = ParamDecl::createUnboundSelf(DestructorLoc, CurDeclContext);
|
|
|
|
Scope S(this, ScopeKind::DestructorBody);
|
|
auto *DD = new (Context) DestructorDecl(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 (Attributes.hasAttribute<PostfixAttr>()) {
|
|
if (!Name.empty() && (Name.get()[0] == '?' || Name.get()[0] == '!'))
|
|
diagnose(NameLoc, diag::expected_operator_name_after_operator);
|
|
}
|
|
|
|
auto Result = parseDeclOperatorImpl(OperatorLoc, Name, NameLoc, Attributes);
|
|
|
|
if (!DCC.movedToTopLevel() && !AllowTopLevel) {
|
|
diagnose(OperatorLoc, diag::operator_decl_inner_scope);
|
|
return nullptr;
|
|
}
|
|
|
|
return DCC.fixupParserResult(Result);
|
|
}
|
|
|
|
ParserResult<OperatorDecl>
|
|
Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name,
|
|
SourceLoc NameLoc, DeclAttributes &Attributes) {
|
|
bool isPrefix = Attributes.hasAttribute<PrefixAttr>();
|
|
bool isInfix = Attributes.hasAttribute<InfixAttr>();
|
|
bool isPostfix = Attributes.hasAttribute<PostfixAttr>();
|
|
|
|
// Parse (or diagnose) a specified precedence group.
|
|
SourceLoc colonLoc;
|
|
Identifier precedenceGroupName;
|
|
SourceLoc precedenceGroupNameLoc;
|
|
if (consumeIf(tok::colon, colonLoc)) {
|
|
if (Tok.is(tok::identifier)) {
|
|
precedenceGroupName = Context.getIdentifier(Tok.getText());
|
|
precedenceGroupNameLoc = consumeToken(tok::identifier);
|
|
|
|
if (isPrefix || isPostfix)
|
|
diagnose(colonLoc, diag::precedencegroup_not_infix)
|
|
.fixItRemove({colonLoc, precedenceGroupNameLoc});
|
|
}
|
|
}
|
|
|
|
// Diagnose deprecated operator body syntax `operator + { ... }`.
|
|
SourceLoc lBraceLoc;
|
|
if (consumeIf(tok::l_brace, lBraceLoc)) {
|
|
if (isInfix && !Tok.is(tok::r_brace)) {
|
|
auto message = Context.isSwiftVersion3()
|
|
? diag::swift3_deprecated_operator_body_use_group
|
|
: diag::deprecated_operator_body_use_group;
|
|
diagnose(lBraceLoc, message);
|
|
} else {
|
|
auto message = Context.isSwiftVersion3()
|
|
? diag::swift3_deprecated_operator_body
|
|
: diag::deprecated_operator_body;
|
|
auto Diag = diagnose(lBraceLoc, message);
|
|
if (Tok.is(tok::r_brace)) {
|
|
SourceLoc lastGoodLoc = precedenceGroupNameLoc;
|
|
if (lastGoodLoc.isInvalid())
|
|
lastGoodLoc = NameLoc;
|
|
SourceLoc lastGoodLocEnd = Lexer::getLocForEndOfToken(SourceMgr,
|
|
lastGoodLoc);
|
|
SourceLoc rBraceEnd = Lexer::getLocForEndOfToken(SourceMgr, Tok.getLoc());
|
|
Diag.fixItRemoveChars(lastGoodLocEnd, rBraceEnd);
|
|
}
|
|
}
|
|
|
|
skipUntilDeclRBrace();
|
|
(void) consumeIf(tok::r_brace);
|
|
}
|
|
|
|
OperatorDecl *res;
|
|
if (Attributes.hasAttribute<PrefixAttr>())
|
|
res = new (Context) PrefixOperatorDecl(CurDeclContext, OperatorLoc,
|
|
Name, NameLoc);
|
|
else if (Attributes.hasAttribute<PostfixAttr>())
|
|
res = new (Context) PostfixOperatorDecl(CurDeclContext, OperatorLoc,
|
|
Name, NameLoc);
|
|
else
|
|
res = new (Context) InfixOperatorDecl(CurDeclContext, OperatorLoc,
|
|
Name, NameLoc, colonLoc,
|
|
precedenceGroupName,
|
|
precedenceGroupNameLoc);
|
|
|
|
diagnoseOperatorFixityAttributes(*this, Attributes, res);
|
|
|
|
res->getAttrs() = Attributes;
|
|
return makeParserResult(res);
|
|
}
|
|
|
|
ParserResult<PrecedenceGroupDecl>
|
|
Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags,
|
|
DeclAttributes &attributes) {
|
|
SourceLoc precedenceGroupLoc = consumeToken(tok::kw_precedencegroup);
|
|
DebuggerContextChange DCC (*this);
|
|
|
|
if (!CodeCompletion && !DCC.movedToTopLevel() && !(flags & PD_AllowTopLevel))
|
|
{
|
|
diagnose(precedenceGroupLoc, diag::decl_inner_scope);
|
|
return nullptr;
|
|
}
|
|
|
|
Identifier name;
|
|
SourceLoc nameLoc;
|
|
if (parseIdentifier(name, nameLoc, diag::expected_precedencegroup_name)) {
|
|
// If the identifier is missing or a keyword or something, try to skip
|
|
// skip the entire body.
|
|
if (consumeIf(tok::l_brace)) {
|
|
skipUntilDeclRBrace();
|
|
(void) consumeIf(tok::r_brace);
|
|
} else if (Tok.isNot(tok::eof) && peekToken().is(tok::l_brace)) {
|
|
consumeToken();
|
|
skipBracedBlock(*this);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
SourceLoc lbraceLoc, rbraceLoc;
|
|
SourceLoc associativityKeywordLoc, associativityValueLoc;
|
|
SourceLoc assignmentKeywordLoc, assignmentValueLoc;
|
|
SourceLoc higherThanKeywordLoc, lowerThanKeywordLoc;
|
|
SmallVector<PrecedenceGroupDecl::Relation, 4> higherThan, lowerThan;
|
|
Associativity associativity = Associativity::None;
|
|
bool assignment = false;
|
|
bool invalid = false;
|
|
|
|
// Helper functions.
|
|
auto create = [&] {
|
|
auto result = PrecedenceGroupDecl::create(CurDeclContext,
|
|
precedenceGroupLoc,
|
|
nameLoc, name, lbraceLoc,
|
|
associativityKeywordLoc,
|
|
associativityValueLoc,
|
|
associativity,
|
|
assignmentKeywordLoc,
|
|
assignmentValueLoc,
|
|
assignment,
|
|
higherThanKeywordLoc, higherThan,
|
|
lowerThanKeywordLoc, lowerThan,
|
|
rbraceLoc);
|
|
result->getAttrs() = attributes;
|
|
return result;
|
|
};
|
|
auto createInvalid = [&] {
|
|
// Use the last consumed token location as the rbrace to satisfy
|
|
// the AST invariant about a decl's source range including all of
|
|
// its components.
|
|
if (!rbraceLoc.isValid()) rbraceLoc = PreviousLoc;
|
|
|
|
auto result = create();
|
|
result->setInvalid();
|
|
return makeParserErrorResult(result);
|
|
};
|
|
|
|
// Expect the body to start here.
|
|
if (!consumeIf(tok::l_brace, lbraceLoc)) {
|
|
diagnose(Tok, diag::expected_precedencegroup_lbrace);
|
|
return createInvalid();
|
|
}
|
|
|
|
auto abortBody = [&] {
|
|
skipUntilDeclRBrace();
|
|
(void) consumeIf(tok::r_brace, rbraceLoc);
|
|
return createInvalid();
|
|
};
|
|
|
|
auto parseAttributePrefix = [&](SourceLoc &attrKeywordLoc) {
|
|
auto attrName = Tok.getText();
|
|
if (attrKeywordLoc.isValid()) {
|
|
diagnose(Tok, diag::precedencegroup_attribute_redeclared, attrName);
|
|
// We want to continue parsing after this.
|
|
invalid = true;
|
|
}
|
|
attrKeywordLoc = consumeToken(tok::identifier);
|
|
if (!consumeIf(tok::colon)) {
|
|
diagnose(Tok, diag::expected_precedencegroup_attribute_colon, attrName);
|
|
// Try to recover by allowing the colon to be missing.
|
|
}
|
|
};
|
|
|
|
// Parse the attributes in the body.
|
|
while (!consumeIf(tok::r_brace, rbraceLoc)) {
|
|
if (!Tok.is(tok::identifier)) {
|
|
diagnose(Tok, diag::expected_precedencegroup_attribute);
|
|
return abortBody();
|
|
}
|
|
|
|
auto attrName = Tok.getText();
|
|
|
|
if (attrName == "associativity") {
|
|
// "associativity" is considered as a contextual keyword.
|
|
TokReceiver->registerTokenKindChange(Tok.getLoc(),
|
|
tok::contextual_keyword);
|
|
parseAttributePrefix(associativityKeywordLoc);
|
|
|
|
if (!Tok.is(tok::identifier)) {
|
|
diagnose(Tok, diag::expected_precedencegroup_associativity);
|
|
return abortBody();
|
|
}
|
|
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::expected_precedencegroup_associativity);
|
|
parsedAssociativity = Associativity::None;
|
|
invalid = true;
|
|
}
|
|
associativity = *parsedAssociativity;
|
|
associativityValueLoc = consumeToken();
|
|
continue;
|
|
}
|
|
|
|
if (attrName == "assignment") {
|
|
parseAttributePrefix(assignmentKeywordLoc);
|
|
|
|
// "assignment" is considered as a contextual keyword.
|
|
TokReceiver->registerTokenKindChange(assignmentKeywordLoc,
|
|
tok::contextual_keyword);
|
|
if (consumeIf(tok::kw_true, assignmentValueLoc)) {
|
|
assignment = true;
|
|
} else if (consumeIf(tok::kw_false, assignmentValueLoc)) {
|
|
assignment = false;
|
|
} else {
|
|
diagnose(Tok, diag::expected_precedencegroup_assignment);
|
|
return abortBody();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
bool isLowerThan = false;
|
|
if (attrName == "higherThan" ||
|
|
(isLowerThan = (attrName == "lowerThan"))) {
|
|
// "lowerThan" and "higherThan" are contextual keywords.
|
|
TokReceiver->registerTokenKindChange(Tok.getLoc(),
|
|
tok::contextual_keyword);
|
|
parseAttributePrefix(isLowerThan ? lowerThanKeywordLoc
|
|
: higherThanKeywordLoc);
|
|
auto &relations = (isLowerThan ? lowerThan : higherThan);
|
|
|
|
do {
|
|
if (!Tok.is(tok::identifier)) {
|
|
diagnose(Tok, diag::expected_precedencegroup_relation, attrName);
|
|
return abortBody();
|
|
}
|
|
auto name = Context.getIdentifier(Tok.getText());
|
|
auto loc = consumeToken();
|
|
relations.push_back({loc, name, nullptr});
|
|
} while (consumeIf(tok::comma));
|
|
continue;
|
|
}
|
|
|
|
diagnose(Tok, diag::unknown_precedencegroup_attribute, attrName);
|
|
return abortBody();
|
|
}
|
|
|
|
auto result = create();
|
|
if (invalid) result->setInvalid();
|
|
return makeParserResult(result);
|
|
}
|