Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
7142 lines (6214 sloc) 245 KB
//===--- ParseDecl.cpp - Swift Language Parser for Declarations -----------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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/ParsedSyntaxRecorder.h"
#include "swift/Parse/ParseSILSupport.h"
#include "swift/Parse/SyntaxParsingContext.h"
#include "swift/Syntax/SyntaxKind.h"
#include "swift/Subsystems.h"
#include "swift/AST/Attr.h"
#include "swift/AST/LazyResolver.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/Statistic.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
void PersistentParserState::parseMembers(IterableDeclContext *IDC) {
SourceFile &SF = *IDC->getDecl()->getDeclContext()->getParentSourceFile();
assert(!SF.hasInterfaceHash() &&
"Cannot delay parsing if we care about the interface hash.");
assert(SF.Kind != SourceFileKind::SIL && "cannot delay parsing SIL");
unsigned BufferID = *SF.getBufferID();
// MarkedPos is not useful for delayed parsing because we know where we should
// jump the parser to. However, we should recover the MarkedPos here in case
// the PersistentParserState will be used to continuously parse the rest of
// the file linearly.
llvm::SaveAndRestore<ParserPosition> Pos(MarkedPos, ParserPosition());
// Lexer diaganostics have been emitted during skipping, so we disable lexer's
// diagnostic engine here.
Parser TheParser(BufferID, SF, /*No Lexer Diags*/nullptr, nullptr, this);
// Disable libSyntax creation in the delayed parsing.
TheParser.SyntaxContext->disable();
TheParser.parseDeclListDelayed(IDC);
}
/// 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;
// 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_property)) {
assert(isInSILMode() &&
"'sil_property' should only be a keyword in SIL mode");
SIL->parseSILProperty(*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)) {
SyntaxContext->addToken(Tok, LeadingTrivia, TrailingTrivia);
TokReceiver->finalize();
}
return FoundTopLevelCodeToExecute;
}
ParserResult<AvailableAttr> Parser::parseExtendedAvailabilitySpecList(
SourceLoc AtLoc, SourceLoc AttrLoc, StringRef AttrName) {
// 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;
};
struct VersionArg {
llvm::VersionTuple Version;
SourceRange Range;
SourceLoc DelimiterLoc;
bool empty() const {
return Version.empty();
}
};
StringRef Platform = Tok.getText();
StringRef Message, Renamed;
VersionArg Introduced, Deprecated, Obsoleted;
auto PlatformAgnostic = PlatformAgnosticAvailabilityKind::None;
SyntaxParsingContext AvailabilitySpecContext(
SyntaxContext, SyntaxKind::AvailabilitySpecList);
bool HasUpcomingEntry = false;
{
SyntaxParsingContext EntryContext(SyntaxContext,
SyntaxKind::AvailabilityArgument);
consumeToken();
if (consumeIf(tok::comma)) {
HasUpcomingEntry = true;
}
}
bool AnyAnnotations = false;
bool AnyArgumentInvalid = false;
int ParamIndex = 0;
while (HasUpcomingEntry) {
SyntaxParsingContext EntryContext(SyntaxContext,
SyntaxKind::AvailabilityArgument);
auto ArgumentLoc = Tok.getLoc();
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) {
diagnose(ArgumentLoc, diag::attr_availability_expected_option, AttrName)
.highlight(SourceRange(ArgumentLoc));
if (Tok.is(tok::code_complete) && CodeCompletion) {
CodeCompletion->completeDeclAttrParam(DAK_Available, ParamIndex);
consumeToken(tok::code_complete);
} else {
consumeIf(tok::identifier);
}
return nullptr;
}
consumeToken();
auto diagnoseDuplicate = [&](bool WasEmpty) {
if (!WasEmpty) {
diagnose(ArgumentLoc, diag::attr_availability_invalid_duplicate,
ArgumentKindStr);
}
};
switch (ArgumentKind) {
case IsMessage:
case IsRenamed: {
// Items with string arguments.
if (findAttrValueDelimiter()) {
consumeToken();
} else {
diagnose(Tok, diag::attr_availability_expected_equal, AttrName,
ArgumentKindStr);
AnyArgumentInvalid = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
break;
}
if (!Tok.is(tok::string_literal)) {
diagnose(AttrLoc, diag::attr_expected_string_literal, AttrName);
AnyArgumentInvalid = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
break;
}
auto Value = getStringLiteralIfNotInterpolated(
AttrLoc, ("'" + ArgumentKindStr + "'").str());
consumeToken();
if (!Value) {
AnyArgumentInvalid = true;
break;
}
if (ArgumentKind == IsMessage) {
diagnoseDuplicate(Message.empty());
Message = Value.getValue();
} else {
ParsedDeclName parsedName = parseDeclName(Value.getValue());
if (!parsedName) {
diagnose(AttrLoc, diag::attr_availability_invalid_renamed, AttrName);
AnyArgumentInvalid = true;
break;
}
diagnoseDuplicate(Renamed.empty());
Renamed = Value.getValue();
}
SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument);
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.
SourceLoc DelimiterLoc;
if (findAttrValueDelimiter()) {
DelimiterLoc = Tok.getLoc();
consumeToken();
} else {
diagnose(Tok, diag::attr_availability_expected_equal, AttrName,
ArgumentKindStr);
AnyArgumentInvalid = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
break;
}
auto &VerArg =
(ArgumentKind == IsIntroduced)
? Introduced
: (ArgumentKind == IsDeprecated) ? Deprecated : Obsoleted;
bool VerArgWasEmpty = VerArg.empty();
if (parseVersionTuple(
VerArg.Version, VerArg.Range,
Diagnostic(diag::attr_availability_expected_version, AttrName))) {
AnyArgumentInvalid = true;
if (peekToken().isAny(tok::r_paren, tok::comma))
consumeToken();
}
VerArg.DelimiterLoc = DelimiterLoc;
diagnoseDuplicate(VerArgWasEmpty);
SyntaxContext->createNodeInPlace(SyntaxKind::AvailabilityLabeledArgument);
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");
}
// Parse the trailing comma
if (consumeIf(tok::comma)) {
HasUpcomingEntry = true;
} else {
HasUpcomingEntry = false;
}
}
if (!AnyAnnotations) {
diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName,
/*isDeclModifier*/ false);
}
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" || Platform == "_PackageDescription")) {
if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::Deprecated) {
diagnose(AttrLoc,
diag::attr_availability_platform_agnostic_expected_deprecated_version,
AttrName, Platform);
return nullptr;
}
if (PlatformAgnostic == PlatformAgnosticAvailabilityKind::Unavailable) {
diagnose(AttrLoc, diag::attr_availability_platform_agnostic_infeasible_option,
"unavailable", AttrName, Platform);
return nullptr;
}
assert(PlatformAgnostic == PlatformAgnosticAvailabilityKind::None);
if (!SomeVersion) {
diagnose(AttrLoc, diag::attr_availability_platform_agnostic_expected_option,
AttrName, Platform);
return nullptr;
}
PlatformKind = PlatformKind::none;
PlatformAgnostic = (Platform == "swift") ?
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific :
PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific;
}
if (AnyArgumentInvalid)
return nullptr;
if (!PlatformKind.hasValue()) {
diagnose(AttrLoc, diag::attr_availability_unknown_platform,
Platform, AttrName);
return nullptr;
}
// Warn if any version is specified for non-specific platform '*'.
if (Platform == "*" && SomeVersion) {
auto diag = diagnose(AttrLoc,
diag::attr_availability_nonspecific_platform_unexpected_version,
AttrName);
if (!Introduced.empty())
diag.fixItRemove(SourceRange(Introduced.DelimiterLoc,
Introduced.Range.End));
if (!Deprecated.empty())
diag.fixItRemove(SourceRange(Deprecated.DelimiterLoc,
Deprecated.Range.End));
if (!Obsoleted.empty())
diag.fixItRemove(SourceRange(Obsoleted.DelimiterLoc,
Obsoleted.Range.End));
return nullptr;
}
auto Attr = new (Context)
AvailableAttr(AtLoc, SourceRange(AttrLoc, Tok.getLoc()),
PlatformKind.getValue(),
Message, Renamed,
Introduced.Version, Introduced.Range,
Deprecated.Version, Deprecated.Range,
Obsoleted.Version, Obsoleted.Range,
PlatformAgnostic,
/*Implicit=*/false);
return makeParserResult(Attr);
}
bool Parser::parseSpecializeAttributeArguments(
swift::tok ClosingBrace, bool &DiscardAttribute, Optional<bool> &Exported,
Optional<SpecializeAttr::SpecializationKind> &Kind,
swift::TrailingWhereClause *&TrailingWhereClause) {
SyntaxParsingContext ContentContext(SyntaxContext,
SyntaxKind::SpecializeAttributeSpecList);
// Parse optional "exported" and "kind" labeled parameters.
while (!Tok.is(tok::kw_where)) {
SyntaxParsingContext ArgumentContext(SyntaxContext,
SyntaxKind::LabeledSpecializeEntry);
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.
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);
}
return true;
}
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;
TrailingWhereClause *trailingWhereClause = nullptr;
if (!parseSpecializeAttributeArguments(ClosingBrace, DiscardAttribute,
exported, kind, trailingWhereClause)) {
return false;
}
// 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();
DeclNameLoc MemberNameLoc;
DeclName MemberName;
ParserResult<TypeRepr> ProtocolType;
{
SyntaxParsingContext ContentContext(
SyntaxContext, SyntaxKind::ImplementsAttributeArguments);
ProtocolType = parseType();
Status |= ProtocolType;
if (!(Status.shouldStopParsing() || consumeIf(tok::comma))) {
diagnose(Tok.getLoc(), diag::attr_expected_comma, AttrName,
/*DeclModifier=*/false);
Status.setIsParseError();
}
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));
}
void Parser::parseObjCSelector(SmallVector<Identifier, 4> &Names,
SmallVector<SourceLoc, 4> &NameLocs,
bool &IsNullarySelector) {
IsNullarySelector = true;
SyntaxParsingContext SelectorContext(SyntaxContext, SyntaxKind::ObjCSelector);
while (true) {
SyntaxParsingContext SelectorPieceContext(SyntaxContext,
SyntaxKind::ObjCSelectorPiece);
// Empty selector piece.
if (Tok.is(tok::colon)) {
Names.push_back(Identifier());
NameLocs.push_back(Tok.getLoc());
IsNullarySelector = false;
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();
IsNullarySelector = false;
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(), ":");
IsNullarySelector = false;
}
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(), ":");
IsNullarySelector = false;
continue;
}
// We don't know what happened. Break out.
break;
}
// We didn't parse anything, don't create a ObjCSelectorPiece
SelectorPieceContext.setTransparent();
break;
}
}
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
DeclAttrKind DK) {
// Ok, it is a valid attribute, eat it, and then process it.
StringRef AttrName = Tok.getText();
SourceLoc Loc = consumeToken();
bool DiscardAttribute = false;
// Diagnose duplicated attributes.
const DeclAttribute *DuplicateAttribute = nullptr;
if (!DeclAttribute::allowMultipleAttributes(DK))
if ((DuplicateAttribute = Attributes.getAttribute(DK))) {
// Delay issuing the diagnostic until we parse the attribute.
DiscardAttribute = true;
}
// If 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;
}
if (Context.LangOpts.Target.isOSBinFormatCOFF()) {
if (DK == DAK_WeakLinked) {
diagnose(Loc, diag::attr_unsupported_on_target, AttrName,
Context.LangOpts.Target.str());
DiscardAttribute = true;
}
}
// Filled in during parsing. If there is a duplicate
// diagnostic this can be used for better error presentation.
SourceRange AttrRange;
switch (DK) {
case DAK_Count:
llvm_unreachable("DAK_Count should not appear in parsing switch");
case DAK_RawDocComment:
case DAK_ObjCBridged:
case DAK_RestatedObjCConformance:
case DAK_SynthesizedProtocol:
case DAK_ClangImporterSynthesizedType:
case DAK_Custom:
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 if (Tok.getText() == "releasenone")
kind = EffectsKind::ReleaseNone;
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_ReferenceOwnership: {
// Handle weak/unowned/unowned(unsafe).
auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak
: ReferenceOwnership::Unowned;
SourceLoc EndLoc = Loc;
if (Kind == ReferenceOwnership::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 = ReferenceOwnership::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) ReferenceOwnershipAttr(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(
Loc, ("'" + AttrName + "'").str());
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;
consumeIdentifier(&name);
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(
Loc, ("'" + AttrName + "'").str());
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 or PackageDescription
// version 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)
// or, if we have
// @available(_PackageDescription 4.2)
// we will synthesize
// @available(_PackageDescription, introduced: 4.2)
for (auto *Spec : Specs) {
PlatformKind Platform;
llvm::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 *PlatformAgnosticVersionSpec =
dyn_cast<PlatformAgnosticVersionConstraintAvailabilitySpec>(Spec)) {
Platform = PlatformKind::none;
Version = PlatformAgnosticVersionSpec->getVersion();
VersionRange = PlatformAgnosticVersionSpec->getVersionSrcRange();
PlatformAgnostic = PlatformAgnosticVersionSpec->isLanguageVersionSpecific() ?
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific :
PlatformAgnosticAvailabilityKind::PackageDescriptionVersionSpecific;
} else {
continue;
}
Attributes.add(new (Context)
AvailableAttr(AtLoc, AttrRange,
Platform,
/*Message=*/StringRef(),
/*Rename=*/StringRef(),
/*Introduced=*/Version,
/*IntroducedRange=*/VersionRange,
/*Deprecated=*/llvm::VersionTuple(),
/*DeprecatedRange=*/SourceRange(),
/*Obsoleted=*/llvm::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;
}
auto AvailabilityAttr = parseExtendedAvailabilitySpecList(AtLoc, Loc,
AttrName);
DiscardAttribute |= AvailabilityAttr.isParseError();
if (!consumeIf(tok::r_paren)) {
if (!DiscardAttribute) {
diagnose(Tok.getLoc(), diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
}
return false;
}
if (!DiscardAttribute) {
Attributes.add(AvailabilityAttr.get());
} else {
return false;
}
break;
}
case DAK_PrivateImport: {
// Parse the leading '('.
if (Tok.isNot(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
SourceLoc LParenLoc = consumeToken(tok::l_paren);
Optional<StringRef> filename;
{
SyntaxParsingContext ContentContext(
SyntaxContext, SyntaxKind::NamedAttributeStringArgument);
// Parse 'sourceFile'.
if (Tok.getText() != "sourceFile") {
diagnose(LParenLoc, diag::attr_private_import_expected_sourcefile);
return false;
}
auto ForLoc = consumeToken();
// Parse ':'.
if (Tok.getKind() != tok::colon) {
diagnose(ForLoc, diag::attr_private_import_expected_colon);
return false;
}
auto ColonLoc = consumeToken(tok::colon);
// Parse '"'function-name'"'
if (Tok.isNot(tok::string_literal)) {
diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name);
return false;
}
filename = getStringLiteralIfNotInterpolated(Loc, "_private");
if (!filename.hasValue()) {
diagnose(ColonLoc, diag::attr_private_import_expected_sourcefile_name);
return false;
}
consumeToken(tok::string_literal);
}
// Parse the matching ')'.
SourceLoc RParenLoc;
bool Invalid = parseMatchingToken(tok::r_paren, RParenLoc,
diag::attr_private_import_expected_rparen,
LParenLoc);
if (Invalid)
return false;
auto *attr = PrivateImportAttr::create(Context, AtLoc, Loc, LParenLoc,
*filename, RParenLoc);
Attributes.add(attr);
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) and populate
// the inout parameters
SmallVector<Identifier, 4> Names;
SmallVector<SourceLoc, 4> NameLocs;
bool NullarySelector = true;
parseObjCSelector(Names, NameLocs, NullarySelector);
// 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 (NullarySelector) {
// 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_ObjCRuntimeName: {
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::objc_runtime_name_must_be_identifier);
return false;
}
auto name = 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) ObjCRuntimeNameAttr(name, AtLoc, range,
/*implicit*/ false));
break;
}
case DAK_DynamicReplacement: {
// Parse the leading '('.
if (Tok.isNot(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
SourceLoc LParenLoc = consumeToken(tok::l_paren);
DeclName replacedFunction;
{
SyntaxParsingContext ContentContext(
SyntaxContext, SyntaxKind::NamedAttributeStringArgument);
// Parse 'for'.
if (Tok.getText() != "for") {
diagnose(Loc, diag::attr_dynamic_replacement_expected_for);
return false;
}
auto ForLoc = consumeToken();
// Parse ':'.
if (Tok.getText() != ":") {
diagnose(ForLoc, diag::attr_dynamic_replacement_expected_colon);
return false;
}
consumeToken(tok::colon);
{
SyntaxParsingContext ContentContext(SyntaxContext,
SyntaxKind::DeclName);
DeclNameLoc loc;
replacedFunction = parseUnqualifiedDeclName(
true, loc, diag::attr_dynamic_replacement_expected_function,
/*allowOperators*/ true, /*allowZeroArgCompoundNames*/ true,
/*allowDeinitAndSubscript*/ true);
}
}
// Parse the matching ')'.
SourceLoc RParenLoc;
bool Invalid = parseMatchingToken(
tok::r_paren, RParenLoc, diag::attr_dynamic_replacement_expected_rparen,
LParenLoc);
if (Invalid) {
return false;
}
DynamicReplacementAttr *attr = DynamicReplacementAttr::create(
Context, AtLoc, Loc, LParenLoc, replacedFunction, 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(llvm::VersionTuple &Version,
SourceRange &Range,
const Diagnostic &D) {
SyntaxParsingContext VersionContext(SyntaxContext, SyntaxKind::VersionTuple);
// 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 = llvm::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 = llvm::VersionTuple(major, minor, micro);
} else {
Version = llvm::VersionTuple(major, minor);
}
return false;
}
/// Check whether the attributes have already established an initializer
/// context within the given set of attributes.
static PatternBindingInitializer *findAttributeInitContent(
DeclAttributes &Attributes) {
for (auto custom : Attributes.getAttributes<CustomAttr>()) {
if (auto initContext = custom->getInitContext())
return initContext;
}
return nullptr;
}
/// \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());
auto checkInvalidAttrName = [&](StringRef invalidName,
StringRef correctName,
DeclAttrKind kind,
Optional<Diag<StringRef, StringRef>> diag = None) {
if (DK == DAK_Count && Tok.getText() == invalidName) {
DK = kind;
if (diag) {
diagnose(Tok, *diag, invalidName, correctName)
.fixItReplace(Tok.getLoc(), correctName);
}
}
};
// Check if attr is availability, and suggest available instead
checkInvalidAttrName("availability", "available", DAK_Available, diag::attr_renamed);
// Check if attr is inlineable, and suggest inlinable instead
checkInvalidAttrName("inlineable", "inlinable", DAK_Inlinable, diag::attr_name_close_match);
// In Swift 5 and above, these become hard errors. In Swift 4.2, emit a
// warning for compatibility. Otherwise, don't diagnose at all.
if (Context.isSwiftVersionAtLeast(5)) {
checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed);
checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed);
} else if (Context.isSwiftVersionAtLeast(4, 2)) {
checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline, diag::attr_renamed_warning);
checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable, diag::attr_renamed_warning);
} else {
checkInvalidAttrName("_versioned", "usableFromInline", DAK_UsableFromInline);
checkInvalidAttrName("_inlineable", "inlinable", DAK_Inlinable);
}
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;
}
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 if (Tok.isContextualKeyword("unknown")) {
diagnose(Tok, diag::unknown_attribute, "unknown");
} else {
// Parse a custom attribute.
auto type = parseType(diag::expected_type);
if (type.hasCodeCompletion() || type.isNull()) {
if (Tok.is(tok::l_paren))
skipSingle();
return true;
}
// Parse the optional arguments.
SourceLoc lParenLoc, rParenLoc;
SmallVector<Expr *, 2> args;
SmallVector<Identifier, 2> argLabels;
SmallVector<SourceLoc, 2> argLabelLocs;
Expr *trailingClosure = nullptr;
bool hasInitializer = false;
// 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;
if (Tok.isFollowingLParen()) {
SyntaxParsingContext InitCtx(SyntaxContext,
SyntaxKind::InitializerClause);
// 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.
Optional<ParseFunctionBody> initParser;
if (!CurDeclContext->isLocalContext()) {
initContext = findAttributeInitContent(Attributes);
if (!initContext)
initContext = new (Context) PatternBindingInitializer(CurDeclContext);
initParser.emplace(*this, initContext);
}
ParserStatus status = parseExprList(tok::l_paren, tok::r_paren,
/*isPostfix=*/false,
/*isExprBasic=*/true,
lParenLoc, args, argLabels,
argLabelLocs,
rParenLoc,
trailingClosure,
SyntaxKind::FunctionCallArgumentList);
if (status.hasCodeCompletion())
return true;
assert(!trailingClosure && "Cannot parse a trailing closure here");
hasInitializer = true;
}
// Form the attribute.
auto attr = CustomAttr::create(Context, AtLoc, type.get(), hasInitializer,
initContext, lParenLoc, args, argLabels,
argLabelLocs, rParenLoc);
Attributes.add(attr);
return false;
}
// 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()) {
BacktrackingScope backtrack(*this);
skipSingle();
// If we found '->', or 'throws' after paren, it's likely a parameter
// of function type.
if (Tok.isNot(tok::arrow, tok::kw_throws, tok::kw_rethrows,
tok::kw_throw))
backtrack.cancelBacktrack();
}
return true;
}
// Ok, it is a valid attribute, eat it, and then process it.
StringRef Text = Tok.getText();
SourceLoc Loc = consumeToken();
StringRef conventionName;
StringRef witnessMethodProtocol;
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:
case TAK_escaping:
case TAK_noescape:
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;
case TAK__opaqueReturnTypeOf: {
// Parse the mangled decl name and index.
auto beginLoc = Tok.getLoc();
if (!consumeIfNotAtStartOfLine(tok::l_paren)) {
diagnose(Tok, diag::attr_expected_lparen, "_opaqueReturnTypeOf", false);
return true;
}
if (!Tok.is(tok::string_literal)) {
diagnose(Tok, diag::opened_attribute_id_value);
return true;
}
auto mangling = Tok.getText().slice(1, Tok.getText().size() - 1);
consumeToken(tok::string_literal);
if (!Tok.is(tok::comma)) {
diagnose(Tok, diag::attr_expected_comma, "_opaqueReturnTypeOf", false);
return true;
}
consumeToken(tok::comma);
if (!Tok.is(tok::integer_literal)) {
diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf");
return true;
}
unsigned index;
if (Tok.getText().getAsInteger(10, index)) {
diagnose(Tok, diag::attr_expected_string_literal, "_opaqueReturnTypeOf");
return true;
}
consumeToken(tok::integer_literal);
SourceLoc endLoc;
parseMatchingToken(tok::r_paren, endLoc,
diag::expected_rparen_expr_list,
beginLoc);
Attributes.setOpaqueReturnTypeOf(mangling, index);
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;
bool error = 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)) {
// Consume any remaining attributes for better error recovery.
error = true;
}
} while (Tok.is(tok::at_sign));
return error;
}
/// \verbatim
/// modifier-list
/// /* empty */
// modifier modifier-list
// modifier
// 'private'
// 'private' '(' 'set' ')'
// 'fileprivate'
// 'fileprivate' '(' 'set' )'
// 'internal'
// 'internal' '(' 'set' ')'
// 'public'
// 'open'
// 'weak'
// 'unowned'
// 'unowned' '(' 'safe' ')'
// 'unowned' '(' 'unsafe' ')'
// 'optional'
// 'required'
// 'lazy'
// 'final'
// 'dynamic'
// 'prefix'
// 'postfix'
// 'infix'
// 'override'
// 'mutating
// 'nonmutating'
// '__consuming'
// 'convenience'
bool Parser::parseDeclModifierList(DeclAttributes &Attributes,
SourceLoc &StaticLoc,
StaticSpellingKind &StaticSpelling) {
SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::ModifierList);
bool isError = false;
bool hasModifier = false;
while (true) {
switch (Tok.getKind()) {
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.
isError |=
parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, DAK_AccessControl);
hasModifier = true;
continue;
}
// Context sensitive keywords.
case tok::identifier: {
if (Tok.isEscapedIdentifier())
break;
DeclAttrKind Kind = llvm::StringSwitch<DeclAttrKind>(Tok.getText())
#define CONTEXTUAL_CASE(KW, CLASS) .Case(#KW, DAK_##CLASS)
#define CONTEXTUAL_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS)
#define CONTEXTUAL_DECL_ATTR_ALIAS(KW, CLASS) CONTEXTUAL_CASE(KW, CLASS)
#define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS)
#include <swift/AST/Attr.def>
#undef CONTEXTUAL_CASE
.Default(DAK_Count);
if (Kind == DAK_Count)
break;
SyntaxParsingContext ModContext(SyntaxContext,
SyntaxKind::DeclModifier);
isError |= parseNewDeclAttribute(Attributes, /*AtLoc=*/{}, Kind);
hasModifier = true;
continue;
}
case tok::kw_static: {
// 'static' is not handled as an attribute in AST.
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);
hasModifier = true;
continue;
}
case tok::kw_class: {
// If 'class' is a modifier on another decl kind, like var or func,
// then treat it as a modifier.
{
BacktrackingScope Scope(*this);
consumeToken(tok::kw_class);
// When followed by an 'override' or CC token inside a class,
// treat 'class' as a modifier; in the case of a following CC
// token, we cannot be sure there is no intention to override
// or witness something static.
if (isStartOfDecl() || (isa<ClassDecl>(CurDeclContext) &&
(Tok.is(tok::code_complete) ||
Tok.getRawText().equals("override")))) {
/* We're OK */
} else {
// This 'class' is a real ClassDecl introducer.
break;
}
}
if (StaticLoc.isValid()) {
diagnose(Tok, diag::decl_already_static,
StaticSpellingKind::KeywordClass)
.highlight(StaticLoc)
.fixItRemove(Tok.getLoc());
} else {
StaticLoc = Tok.getLoc();
StaticSpelling = StaticSpellingKind::KeywordClass;
}
SyntaxParsingContext ModContext(SyntaxContext, SyntaxKind::DeclModifier);
consumeToken(tok::kw_class);
hasModifier = true;
continue;
}
case tok::unknown:
// Eat an invalid token in decl modifier context. Error tokens are
// diagnosed by the lexer, so we don't need to emit another diagnostic.
consumeToken(tok::unknown);
hasModifier = true;
continue;
default:
break;
}
// If we don't have any modifiers, don't bother to construct an empty list.
if (!hasModifier)
ListContext.setTransparent();
// If we 'break' out of the switch, modifier list has ended.
return isError;
}
}
/// 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
/// 'some' 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::Default;
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"));
}
/// 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?");
}
}
static unsigned skipUntilMatchingRBrace(Parser &P, bool &HasPoundDirective,
SyntaxParsingContext *&SyntaxContext) {
HasPoundDirective = false;
bool isRootCtx = SyntaxContext->isRoot();
SyntaxParsingContext BlockItemListContext(SyntaxContext,
SyntaxKind::CodeBlockItemList);
if (isRootCtx) {
BlockItemListContext.setTransparent();
}
SyntaxParsingContext BlockItemContext(SyntaxContext,
SyntaxKind::CodeBlockItem);
SyntaxParsingContext BodyContext(SyntaxContext, SyntaxKind::TokenList);
unsigned OpenBraces = 1;
while (OpenBraces != 0 && P.Tok.isNot(tok::eof)) {
HasPoundDirective |= P.Tok.isAny(tok::pound_sourceLocation, tok::pound_line,
tok::pound_if, tok::pound_else, tok::pound_endif, tok::pound_elseif);
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;
}
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::pound_warning:
case tok::pound_error:
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);
}
// Similarly, when 'case' appears inside a function, it's probably a switch
// case, not an enum case declaration.
if (Tok.is(tok::kw_case)) {
return !isa<AbstractFunctionDecl>(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();
SourceLoc EndLoc = PreviousLoc;
backtrackToPosition(BeginParserPosition);
SourceLoc BeginLoc = 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::setLocalDiscriminatorToParamList(ParameterList *PL) {
for (auto P : *PL) {
if (!P->hasName() || P->isImplicit())
continue;
setLocalDiscriminator(P);
}
}
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();
}
/// 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) {
ParserPosition BeginParserPosition;
if (isCodeCompletionFirstPass())
BeginParserPosition = getParserPosition();
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;
SyntaxParsingContext DeclListCtx(SyntaxContext,
SyntaxKind::MemberDeclList);
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 (IfConfigResult.hasCodeCompletion() && isCodeCompletionFirstPass()) {
consumeDecl(BeginParserPosition, Flags,
CurDeclContext->isModuleScopeContext());
return makeParserError();
}
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;
}
if (Tok.isAny(tok::pound_warning, tok::pound_error)) {
auto Result = parseDeclPoundDiagnostic();
if (Result.isNonNull())
Handler(Result.get());
return Result;
}
SyntaxParsingContext DeclParsingContext(SyntaxContext,
SyntaxContextKind::Decl);
// Note that we're parsing a declaration.
StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
StructureMarkerKind::Declaration);
// Parse attributes.
DeclAttributes Attributes;
if (Tok.hasComment())
Attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
bool FoundCCTokenInAttr;
parseDeclAttributeList(Attributes, FoundCCTokenInAttr);
// Parse modifiers.
// Keep track of where and whether we see a contextual keyword on the decl.
SourceLoc StaticLoc;
StaticSpellingKind StaticSpelling = StaticSpellingKind::None;
parseDeclModifierList(Attributes, StaticLoc, StaticSpelling);
// We emit diagnostics for 'try let ...' in parseDeclVar().
SourceLoc tryLoc;
if (Tok.is(tok::kw_try) && peekToken().isAny(tok::kw_let, tok::kw_var))
tryLoc = consumeToken(tok::kw_try);
ParserResult<Decl> DeclResult;
// Save the original token, in case code-completion needs it.
auto OrigTok = Tok;
bool MayNeedOverrideCompletion = false;
switch (Tok.getKind()) {
case tok::kw_import:
DeclParsingContext.setCreateSyntax(SyntaxKind::ImportDecl);
DeclResult = parseDeclImport(Flags, Attributes);
break;
case tok::kw_extension:
DeclParsingContext.setCreateSyntax(SyntaxKind::ExtensionDecl);
DeclResult = parseDeclExtension(Flags, Attributes);
break;
case tok::kw_let:
case tok::kw_var: {
// Collect all modifiers into a modifier list.
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;
if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass())
break;
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:
DeclParsingContext.setCreateSyntax(SyntaxKind::AssociatedtypeDecl);
DeclResult = parseDeclAssociatedType(Flags, Attributes);
break;
case tok::kw_enum:
DeclParsingContext.setCreateSyntax(SyntaxKind::EnumDecl);
DeclResult = parseDeclEnum(Flags, Attributes);
break;
case tok::kw_case: {
llvm::SmallVector<Decl *, 4> Entries;
DeclParsingContext.setCreateSyntax(SyntaxKind::EnumCaseDecl);
DeclResult = parseDeclEnumCase(Flags, Attributes, Entries);
if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass())
break;
std::for_each(Entries.begin(), Entries.end(), Handler);
if (auto *D = DeclResult.getPtrOrNull())
markWasHandled(D);
break;
}
case tok::kw_class:
DeclParsingContext.setCreateSyntax(SyntaxKind::ClassDecl);
DeclResult = parseDeclClass(Flags, Attributes);
break;
case tok::kw_struct:
DeclParsingContext.setCreateSyntax(SyntaxKind::StructDecl);
DeclResult = parseDeclStruct(Flags, Attributes);
break;
case tok::kw_init:
DeclParsingContext.setCreateSyntax(SyntaxKind::InitializerDecl);
DeclResult = parseDeclInit(Flags, Attributes);
break;
case tok::kw_deinit:
DeclParsingContext.setCreateSyntax(SyntaxKind::DeinitializerDecl);
DeclResult = parseDeclDeinit(Flags, Attributes);
break;
case tok::kw_operator:
DeclParsingContext.setCreateSyntax(SyntaxKind::OperatorDecl);
DeclResult = parseDeclOperator(Flags, Attributes);
break;
case tok::kw_precedencegroup:
DeclParsingContext.setCreateSyntax(SyntaxKind::PrecedenceGroupDecl);
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.setCreateSyntax(SyntaxKind::FunctionDecl);
DeclResult = parseDeclFunc(StaticLoc, StaticSpelling, Flags, Attributes);
StaticLoc = SourceLoc(); // we handled static if present.
MayNeedOverrideCompletion = true;
break;
case tok::kw_subscript: {
DeclParsingContext.setCreateSyntax(SyntaxKind::SubscriptDecl);
llvm::SmallVector<Decl *, 4> Entries;
DeclResult = parseDeclSubscript(StaticLoc, StaticSpelling, Flags,
Attributes, Entries);
StaticLoc = SourceLoc(); // we handled static if present.
if (DeclResult.hasCodeCompletion() && isCodeCompletionFirstPass())
break;
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;
case tok::pound:
if (Tok.isAtStartOfLine() &&
peekToken().is(tok::code_complete) &&
Tok.getLoc().getAdvancedLoc(1) == peekToken().getLoc()) {
consumeToken();
if (CodeCompletion)
CodeCompletion->completeAfterPoundDirective();
consumeToken(tok::code_complete);
DeclResult = makeParserCodeCompletionResult<Decl>();
break;
}
LLVM_FALLTHROUGH;
case tok::pound_if:
case tok::pound_sourceLocation:
case tok::pound_line:
case tok::pound_warning:
case tok::pound_error:
// 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>();
}
if (DeclResult.isParseError() && Tok.is(tok::code_complete)) {
if (MayNeedOverrideCompletion && 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;
SourceLoc introducerLoc;
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());
introducerLoc = OrigTok.getLoc();
break;
default:
// Other tokens are already accounted for.
break;
}
if (StaticSpelling == StaticSpellingKind::KeywordStatic) {
Keywords.push_back(getTokenText(tok::kw_static));
} else if (StaticSpelling == StaticSpellingKind::KeywordClass) {
Keywords.push_back(getTokenText(tok::kw_class));
}
for (auto attr : Attributes) {
Keywords.push_back(attr->getAttrName());
}
CodeCompletion->completeNominalMemberBeginning(Keywords,
introducerLoc);
}
DeclResult = makeParserCodeCompletionStatus();
consumeToken(tok::code_complete);
}
if (auto SF = CurDeclContext->getParentSourceFile()) {
if (!getScopeInfo().isInactiveConfigBlock()) {
for (auto Attr : Attributes) {
if (isa<ObjCAttr>(Attr) ||
/* Pre Swift 5 dymamic implied @objc */
(!Context.LangOpts.isSwiftVersionAtLeast(5) &&
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() &&
!isa<TopLevelCodeDecl>(CurDeclContext)) {
// 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::parseDeclListDelayed(IterableDeclContext *IDC) {
auto DelayedState = State->takeDelayedDeclListState(IDC);
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);
Decl *D = const_cast<Decl*>(IDC->getDecl());
SourceLoc LBLoc = consumeToken(tok::l_brace);
SourceLoc RBLoc;
Diag<> Id;
switch (D->getKind()) {
case DeclKind::Extension: Id = diag::expected_rbrace_extension; break;
case DeclKind::Enum: Id = diag::expected_rbrace_enum; break;
case DeclKind::Protocol: Id = diag::expected_rbrace_protocol; break;
case DeclKind::Class: Id = diag::expected_rbrace_class; break;
case DeclKind::Struct: Id = diag::expected_rbrace_struct; break;
default:
llvm_unreachable("Bad iterable decl context kinds.");
}
if (auto *ext = dyn_cast<ExtensionDecl>(D)) {
parseDeclList(ext->getBraces().Start, RBLoc, Id,
ParseDeclOptions(DelayedState->Flags),
ext);
ext->setBraces({LBLoc, RBLoc});
} else {
auto *ntd = cast<NominalTypeDecl>(D);
parseDeclList(ntd->getBraces().Start, RBLoc, Id,
ParseDeclOptions(DelayedState->Flags),
ntd);
ntd->setBraces({LBLoc, RBLoc});
}
}
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);
}
}
});
}
/// 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);
}
/// 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)) {
SyntaxParsingContext ClassTypeContext(SyntaxContext,
SyntaxKind::ClassRestrictionType);
// 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) {
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;
}
ParserResult<Decl> Result;
SyntaxParsingContext DeclContext(SyntaxContext,
SyntaxKind::MemberDeclListItem);
if (loadCurrentSyntaxNodeFromCache()) {
return ParserStatus();
}
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;
}
/// Parse the members in a struct/class/enum/protocol/extension.
///
/// \verbatim
/// decl* '}'
/// \endverbatim
bool Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc,
Diag<> ErrorDiag, ParseDeclOptions Options,
IterableDeclContext *IDC) {
ParserStatus Status;
bool PreviousHadSemi = true;
{
SyntaxParsingContext ListContext(SyntaxContext, SyntaxKind::MemberDeclList);
while (Tok.isNot(tok::r_brace)) {
Status |= parseDeclItem(PreviousHadSemi, Options,
[&](Decl *D) { IDC->addMember(D); });
if (Tok.isAny(tok::eof, tok::pound_endif, tok::pound_else,
tok::pound_elseif)) {
IsInputIncomplete = true;
break;
}
}
}
if (parseMatchingToken(tok::r_brace, RBLoc, ErrorDiag, LBLoc)) {
// Synthesize an r_brace syntax node if the token is absent
SyntaxContext->synthesize(tok::r_brace, RBLoc);
}
// Increase counter.
if (auto *stat = Context.Stats) {
stat->getFrontendCounters().NumIterableDeclContextParsed ++;
}
// 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();
}
bool Parser::canDelayMemberDeclParsing() {
// If explicitly disabled, respect the flag.
if (!DelayBodyParsing)
return false;
// Recovering parser status later for #sourceLocation is not-trivial and
// it may not worth it.
if (InPoundLineEnvironment)
return false;
// Skip until the matching right curly bracket; if we find a pound directive,
// we can't lazily parse.
BacktrackingScope BackTrack(*this);
bool HasPoundDirective;
skipUntilMatchingRBrace(*this, HasPoundDirective, SyntaxContext);
if (!HasPoundDirective)
BackTrack.cancelBacktrack();
return !BackTrack.willBacktrack();
}
bool Parser::delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc,
SourceLoc PosBeforeLB,
ParseDeclOptions Options,
IterableDeclContext *IDC) {
bool error = false;
if (Tok.is(tok::r_brace)) {
RBLoc = consumeToken();
} else {
RBLoc = Tok.getLoc();
error = true;
}
State->delayDeclList(IDC, Options.toRaw(), CurDeclContext, { LBLoc, RBLoc },
PosBeforeLB);
return error;
}
/// 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;
SyntaxParsingContext BlockContext(SyntaxContext, SyntaxKind::MemberDeclBlock);
SourceLoc LBLoc, RBLoc;
auto PosBeforeLB = Tok.getLoc();
if (parseToken(tok::l_brace, LBLoc, diag::expected_lbrace_extension)) {
LBLoc = PreviousLoc;
RBLoc = LBLoc;
status.setIsParseError();
} else {
ContextChange CC(*this, ext);
Scope S(this, ScopeKind::Extension);
ParseDeclOptions Options(PD_HasContainerType | PD_InExtension);
if (canDelayMemberDeclParsing()) {
if (delayParsingDeclList(LBLoc, RBLoc, PosBeforeLB, Options, ext))
status.setIsParseError();
} else {
if (parseDeclList(LBLoc, RBLoc, diag::expected_rbrace_extension,
Options, ext))
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);
}
ParserResult<PoundDiagnosticDecl> Parser::parseDeclPoundDiagnostic() {
bool isError = Tok.is(tok::pound_error);
SyntaxParsingContext LocalContext(SyntaxContext,
isError ? SyntaxKind::PoundErrorDecl : SyntaxKind::PoundWarningDecl);
SourceLoc startLoc =
consumeToken(isError ? tok::pound_error : tok::pound_warning);
SourceLoc lParenLoc = Tok.getLoc();
bool hadLParen = consumeIf(tok::l_paren);
if (!Tok.is(tok::string_literal)) {
// Catch #warning(oops, forgot the quotes)
SourceLoc wordsStartLoc = Tok.getLoc();
skipUntilTokenOrEndOfLine(tok::r_paren);
SourceLoc wordsEndLoc = getEndOfPreviousLoc();
auto diag = diagnose(wordsStartLoc,
diag::pound_diagnostic_expected_string, isError);
if (wordsEndLoc != wordsStartLoc) {
diag.fixItInsert(wordsStartLoc, hadLParen ? "\"" : "(\"")
.fixItInsert(wordsEndLoc, Tok.is(tok::r_paren) ? "\"" : "\")");
}
// Consume the right paren to finish the decl, if it's there.
consumeIf(tok::r_paren);
return makeParserError();
}
auto string = parseExprStringLiteral();
if (string.isNull())
return makeParserError();
auto messageExpr = string.get();
SourceLoc rParenLoc = Tok.getLoc();
bool hadRParen = consumeIf(tok::r_paren);
if (!Tok.isAtStartOfLine() && Tok.isNot(tok::eof)) {
diagnose(Tok.getLoc(),
diag::extra_tokens_pound_diagnostic_directive, isError);
return makeParserError();
}
if (!hadLParen && !hadRParen) {
// Catch if the user forgot parentheses around the string, e.g.
// #warning "foo"
diagnose(lParenLoc, diag::pound_diagnostic_expected_parens, isError)
.highlight(messageExpr->getSourceRange())
.fixItInsert(messageExpr->getStartLoc(), "(")
.fixItInsertAfter(messageExpr->getEndLoc(), ")");
return makeParserError();
} else if (hadRParen && !hadLParen) {
// Catch if the user forgot a left paren before the string, e.g.
// #warning "foo")
diagnose(messageExpr->getStartLoc(), diag::pound_diagnostic_expected,
"(", isError)
.fixItInsert(messageExpr->getStartLoc(), "(");
return makeParserError();
} else if (hadLParen && !hadRParen) {
// Catch if the user forgot a right paren after the string, e.g.
// #warning("foo"
diagnose(messageExpr->getEndLoc(), diag::pound_diagnostic_expected,
")", isError)
.fixItInsertAfter(messageExpr->getEndLoc(), ")");
return makeParserError();
}
if (messageExpr->getKind() == ExprKind::InterpolatedStringLiteral) {
diagnose(messageExpr->getStartLoc(), diag::pound_diagnostic_interpolation,
isError)
.highlight(messageExpr->getSourceRange());
return makeParserError();
}
ParserStatus Status;
return makeParserResult(Status,
new (Context) PoundDiagnosticDecl(CurDeclContext, isError,
startLoc, rParenLoc,
cast<StringLiteralExpr>(messageExpr)));
}
ParserStatus Parser::parseLineDirective(bool isLine) {
SyntaxParsingContext PoundSourceLocation(SyntaxContext,
SyntaxKind::PoundSourceLocation);
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;
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();
}
{
SyntaxParsingContext Args(SyntaxContext,
SyntaxKind::PoundSourceLocationArgs);
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(Loc, "'#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);
}
if (Tok.isNot(tok::r_paren)) {
diagnose(Tok, 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(Loc, "'#line'");
if (!Filename.hasValue())
return makeParserError();
}
const char *LastTokTextEnd = Tok.getText().end();
// 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;
// Lexing of next token must be deferred until after virtual file setup.
consumeToken(isLine ? tok::string_literal : tok::r_paren);
InPoundLineEnvironment = true;
return makeParserSuccess();
}
/// 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();
llvm::Optional<SyntaxParsingContext> TmpCtxt;
TmpCtxt.emplace(SyntaxContext);
TmpCtxt->setTransparent();
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)) {
TmpCtxt->setBackTracking();
TmpCtxt.reset();
// 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);
}
TmpCtxt.reset();
auto *TAD = new (Context) TypeAliasDecl(TypeAliasLoc, EqualLoc, Id,