in lib/Parse/ParseDecl.cpp [1868:2875]
bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
DeclAttrKind DK, bool isFromClangAttribute) {
// 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 this attribute is only permitted when concurrency is enabled, reject it.
if (DeclAttribute::isConcurrencyOnly(DK) &&
!shouldParseExperimentalConcurrency()) {
// Ignore concurrency-only attributes that come from Clang.
if (!isFromClangAttribute) {
diagnose(
Loc, diag::attr_requires_concurrency, AttrName,
DeclAttribute::isDeclModifier(DK));
}
DiscardAttribute = true;
}
// If this attribute is only permitted when distributed is enabled, reject it.
if (DeclAttribute::isDistributedOnly(DK) &&
!shouldParseExperimentalDistributed()) {
diagnose(Loc, diag::attr_requires_distributed, AttrName,
DeclAttribute::isDeclModifier(DK));
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_MainType:
if (!DiscardAttribute)
Attributes.add(new (Context) MainTypeAttr(AtLoc, Loc));
break;
case DAK_Effects: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK)); return false;
}
EffectsKind kind = EffectsKind::Unspecified;
SourceLoc customStart, customEnd;
{
SyntaxParsingContext TokListContext(SyntaxContext, SyntaxKind::TokenList);
if (Tok.isNot(tok::identifier)) {
diagnose(Loc, diag::error_in_effects_attribute, "expected identifier");
return false;
}
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 {
customStart = customEnd = Tok.getLoc();
while (Tok.isNot(tok::r_paren) && Tok.isNot(tok::eof)) {
consumeToken();
}
customEnd = Tok.getLoc();
kind = EffectsKind::Custom;
AttrRange = SourceRange(Loc, customEnd);
}
if (kind != EffectsKind::Custom) {
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) {
if (kind == EffectsKind::Custom) {
StringRef customStr = SourceMgr.extractText(
CharSourceRange(SourceMgr, customStart, customEnd));
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange,
customStr, customStart));
} else {
Attributes.add(new (Context) EffectsAttr(AtLoc, AttrRange, kind));
}
}
break;
}
case DAK_Inline: {
auto kind = parseSingleAttrOption<InlineKind>
(*this, Loc, AttrRange, AttrName, DK)
.when("never", InlineKind::Never)
.when("__always", InlineKind::Always)
.diagnoseWhenOmitted();
if (!kind)
return false;
if (!DiscardAttribute)
Attributes.add(new (Context) InlineAttr(AtLoc, AttrRange, *kind));
break;
}
case DAK_Optimize: {
auto optMode = parseSingleAttrOption<OptimizationMode>
(*this, Loc, AttrRange, AttrName, DK)
.when("speed", OptimizationMode::ForSpeed)
.when("size", OptimizationMode::ForSize)
.when("none", OptimizationMode::NoOptimization)
.diagnoseWhenOmitted();
if (!optMode)
return false;
if (!DiscardAttribute)
Attributes.add(new (Context) OptimizeAttr(AtLoc, AttrRange, *optMode));
break;
}
case DAK_Exclusivity: {
auto mode = parseSingleAttrOption<ExclusivityAttr::Mode>
(*this, Loc, AttrRange, AttrName, DK)
.when("checked", ExclusivityAttr::Mode::Checked)
.when("unchecked", ExclusivityAttr::Mode::Unchecked)
.diagnoseWhenOmitted();
if (!mode)
return false;
if (!DiscardAttribute)
Attributes.add(new (Context) ExclusivityAttr(AtLoc, AttrRange, *mode));
break;
}
case DAK_ReferenceOwnership: {
// Handle weak/unowned/unowned(unsafe).
auto Kind = AttrName == "weak" ? ReferenceOwnership::Weak
: ReferenceOwnership::Unowned;
if (Kind == ReferenceOwnership::Unowned) {
// Parse an optional specifier after unowned.
Kind = parseSingleAttrOption<ReferenceOwnership>
(*this, Loc, AttrRange, AttrName, DK)
.when("unsafe", ReferenceOwnership::Unmanaged)
.when("safe", ReferenceOwnership::Unowned)
.whenOmitted(ReferenceOwnership::Unowned)
// Recover from errors by going back to Unowned.
.getValueOr(ReferenceOwnership::Unowned);
}
else {
AttrRange = SourceRange(Loc);
}
if (!DiscardAttribute)
Attributes.add(
new (Context) ReferenceOwnershipAttr(AttrRange, Kind));
break;
}
case DAK_NonSendable: {
auto kind = parseSingleAttrOption<NonSendableKind>
(*this, Loc, AttrRange, AttrName, DK)
.when("_assumed", NonSendableKind::Assumed)
.whenOmitted(NonSendableKind::Specific);
if (!kind)
return false;
if (!DiscardAttribute)
Attributes.add(new (Context) NonSendableAttr(AtLoc, AttrRange, *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_SPIAccessControl: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
SmallVector<Identifier, 4> spiGroups;
if (!Tok.is(tok::identifier) ||
Tok.isContextualKeyword("set")) {
diagnose(getEndOfPreviousLoc(), diag::attr_access_expected_spi_name);
consumeToken();
consumeIf(tok::r_paren);
return false;
}
auto text = Tok.getText();
spiGroups.push_back(Context.getIdentifier(text));
consumeToken();
AttrRange = SourceRange(Loc, Tok.getLoc());
if (!consumeIf(tok::r_paren)) {
diagnose(Loc, diag::attr_expected_rparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
Attributes.add(SPIAccessControlAttr::create(Context, AtLoc, AttrRange,
spiGroups));
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, /*diagnoseDollarPrefix=*/false);
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;
}
if (!DiscardAttribute)
Attributes.add(new (Context) SemanticsAttr(Value.getValue(), AtLoc,
AttrRange,
/*Implicit=*/false));
break;
}
case DAK_OriginallyDefinedIn: {
auto LeftLoc = Tok.getLoc();
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
SourceLoc RightLoc;
enum class NextSegmentKind: uint8_t {
ModuleName = 0,
PlatformVersion,
};
NextSegmentKind NK = NextSegmentKind::ModuleName;
StringRef OriginalModuleName;
llvm::SmallVector<std::pair<PlatformKind, llvm::VersionTuple>, 4>
PlatformAndVersions;
StringRef AttrName = "@_originallyDefinedIn";
bool SuppressLaterDiags = false;
if (parseList(tok::r_paren, LeftLoc, RightLoc, false,
diag::originally_defined_in_missing_rparen,
SyntaxKind::AvailabilitySpecList, [&]() -> ParserStatus {
SWIFT_DEFER {
if (NK != NextSegmentKind::PlatformVersion) {
NK = (NextSegmentKind)((uint8_t)NK + (uint8_t)1);
}
};
switch (NK) {
// Parse 'module: "original_module_name"'.
case NextSegmentKind::ModuleName: {
SyntaxParsingContext argumentContext(SyntaxContext,
SyntaxKind::AvailabilityLabeledArgument);
// Parse 'module' ':'.
if (!Tok.is(tok::identifier) || Tok.getText() != "module" ||
!peekToken().is(tok::colon)) {
diagnose(Tok, diag::originally_defined_in_need_original_module_name);
SuppressLaterDiags = true;
return makeParserError();
}
consumeToken(tok::identifier);
consumeToken(tok::colon);
// Parse the next string literal as the original module name.
auto ModuleNameLoc = Tok.getLoc();
if (Tok.is(tok::string_literal)) {
auto NameOp = getStringLiteralIfNotInterpolated(Tok.getLoc(),
"original module name");
if (NameOp.hasValue())
OriginalModuleName = *NameOp;
consumeToken();
}
if (OriginalModuleName.empty()) {
diagnose(ModuleNameLoc,
diag::originally_defined_in_need_nonempty_module_name);
SuppressLaterDiags = true;
return makeParserError();
}
return makeParserSuccess();
}
// Parse 'OSX 13.13'.
case NextSegmentKind::PlatformVersion: {
SyntaxParsingContext argumentContext(SyntaxContext,
SyntaxKind::AvailabilityVersionRestriction);
if ((Tok.is(tok::identifier) || Tok.is(tok::oper_binary_spaced)) &&
(peekToken().isAny(tok::integer_literal, tok::floating_literal) ||
peekAvailabilityMacroName())) {
PlatformKind Platform;
if (peekAvailabilityMacroName()) {
// Handle availability macros first.
//
// The logic to search for macros and platform name could
// likely be handled by parseAvailabilitySpecList
// if we don't rely on parseList here.
SmallVector<AvailabilitySpec *, 4> Specs;
ParserStatus MacroStatus = parseAvailabilityMacro(Specs);
if (MacroStatus.isError())
return MacroStatus;
for (auto *Spec : Specs) {
if (auto *PlatformVersionSpec =
dyn_cast<PlatformVersionConstraintAvailabilitySpec>(Spec)) {
auto Platform = PlatformVersionSpec->getPlatform();
auto Version = PlatformVersionSpec->getVersion();
if (Version.getSubminor().hasValue() ||
Version.getBuild().hasValue()) {
diagnose(Tok.getLoc(), diag::originally_defined_in_major_minor_only);
}
PlatformAndVersions.emplace_back(Platform, Version);
} else if (auto *PlatformAgnostic =
dyn_cast<PlatformAgnosticVersionConstraintAvailabilitySpec>(Spec)) {
diagnose(PlatformAgnostic->getPlatformAgnosticNameLoc(),
PlatformAgnostic->isLanguageVersionSpecific() ?
diag::originally_defined_in_swift_version :
diag::originally_defined_in_package_description);
} else if (auto *OtherPlatform =
dyn_cast<OtherPlatformAvailabilitySpec>(Spec)) {
diagnose(OtherPlatform->getStarLoc(),
diag::originally_defined_in_missing_platform_name);
} else {
llvm_unreachable("Unexpected AvailabilitySpec kind.");
}
}
return makeParserSuccess();
}
// Parse platform name.
auto Plat = platformFromString(Tok.getText());
if (!Plat.hasValue()) {
diagnose(Tok.getLoc(),
diag::originally_defined_in_unrecognized_platform);
SuppressLaterDiags = true;
return makeParserError();
} else {
consumeToken();
Platform = *Plat;
}
// Parse version number
llvm::VersionTuple VerTuple;
SourceRange VersionRange;
if (parseVersionTuple(VerTuple, VersionRange,
Diagnostic(diag::attr_availability_expected_version, AttrName))) {
SuppressLaterDiags = true;
return makeParserError();
} else {
if (VerTuple.getSubminor().hasValue() ||
VerTuple.getBuild().hasValue()) {
diagnose(Tok.getLoc(), diag::originally_defined_in_major_minor_only);
}
// * as platform name isn't supported.
if (Platform == PlatformKind::none) {
diagnose(AtLoc, diag::originally_defined_in_missing_platform_name);
} else {
PlatformAndVersions.emplace_back(Platform, VerTuple);
}
return makeParserSuccess();
}
}
diagnose(AtLoc, diag::originally_defined_in_need_platform_version);
SuppressLaterDiags = true;
return makeParserError();
}
}
llvm_unreachable("invalid next segment kind");
}).isErrorOrHasCompletion() || SuppressLaterDiags) {
return false;
}
if (OriginalModuleName.empty()) {
diagnose(AtLoc, diag::originally_defined_in_need_nonempty_module_name);
return false;
}
if (PlatformAndVersions.empty()) {
diagnose(AtLoc, diag::originally_defined_in_need_platform_version);
return false;
}
assert(!OriginalModuleName.empty());
assert(!PlatformAndVersions.empty());
assert(NK == NextSegmentKind::PlatformVersion);
AttrRange = SourceRange(Loc, Tok.getLoc());
for (auto &Item: PlatformAndVersions) {
Attributes.add(new (Context) OriginallyDefinedInAttr(AtLoc, AttrRange,
OriginalModuleName,
Item.first,
Item.second,
/*IsImplicit*/false));
}
break;
}
case DAK_Available: {
if (!consumeIf(tok::l_paren)) {
diagnose(Loc, diag::attr_expected_lparen, AttrName,
DeclAttribute::isDeclModifier(DK));
return false;
}
if (!parseAvailability(false, AttrName, DiscardAttribute, AttrRange, AtLoc,
Loc,
[&](AvailableAttr *attr) { Attributes.add(attr); }))
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);
DeclNameRef 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 = parseDeclNameRef(loc,
diag::attr_dynamic_replacement_expected_function,
DeclNameFlag::AllowZeroArgCompoundNames |
DeclNameFlag::AllowKeywordsUsingSpecialNames |
DeclNameFlag::AllowOperators);
}
}
// 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_TypeEraser: {
// Parse 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);
ParserResult<TypeRepr> ErasedType;
bool invalid = false;
{
// Parse type-eraser type
SyntaxParsingContext ContentContext(SyntaxContext, SyntaxKind::Type);
ErasedType = parseType(diag::attr_type_eraser_expected_type_name);
invalid = ErasedType.hasCodeCompletion() || ErasedType.isNull();
}
// Parse matching ')'
SourceLoc RParenLoc;
invalid |= parseMatchingToken(tok::r_paren, RParenLoc,
diag::attr_type_eraser_expected_rparen,
LParenLoc);
if (invalid)
return false;
auto *TE = new (Context) TypeExpr(ErasedType.get());
Attributes.add(TypeEraserAttr::create(Context, AtLoc, {Loc, RParenLoc}, TE));
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, nullptr))
return false;
Attributes.add(Attr);
break;
}
case DAK_Implements: {
ParserResult<ImplementsAttr> Attr = parseImplementsAttribute(AtLoc, Loc);
if (Attr.isNonNull()) {
Attributes.add(Attr.get());
}
break;
}
case DAK_Differentiable: {
auto Attr = parseDifferentiableAttribute(AtLoc, Loc);
if (Attr.isNonNull())
Attributes.add(Attr.get());
break;
}
case DAK_Derivative: {
// `@derivative` in a local scope is not allowed.
if (CurDeclContext->isLocalContext())
diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str());
auto Attr = parseDerivativeAttribute(AtLoc, Loc);
if (Attr.isNonNull())
Attributes.add(Attr.get());
break;
}
case DAK_Transpose: {
// `@transpose` in a local scope is not allowed.
if (CurDeclContext->isLocalContext())
diagnose(Loc, diag::attr_only_at_non_local_scope, '@' + AttrName.str());
auto Attr = parseTransposeAttribute(AtLoc, Loc);
if (Attr.isNonNull())
Attributes.add(Attr.get());
break;
}
case DAK_ProjectedValueProperty: {
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::projection_value_property_not_identifier);
return false;
}
Identifier name;
consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);
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) ProjectedValuePropertyAttr(
name, AtLoc, range, /*implicit*/ false));
break;
}
case DAK_TypeSequence: {
auto range = SourceRange(Loc, Tok.getRange().getStart());
Attributes.add(TypeSequenceAttr::create(Context, AtLoc, range));
break;
}
case DAK_UnavailableFromAsync: {
StringRef message;
if (consumeIf(tok::l_paren)) {
if (!Tok.is(tok::identifier)) {
llvm_unreachable("Flag must start with an indentifier");
}
StringRef flag = Tok.getText();
if (flag != "message") {
diagnose(Tok.getLoc(), diag::attr_unknown_option, flag, AttrName);
return true;
}
consumeToken();
if (!consumeIf(tok::colon)) {
if (!Tok.is(tok::equal)) {
diagnose(Tok.getLoc(), diag::attr_expected_colon_after_label, flag);
return false;
}
diagnose(Tok.getLoc(), diag::replace_equal_with_colon_for_value)
.fixItReplace(Tok.getLoc(), ": ");
consumeToken();
}
if (!Tok.is(tok::string_literal)) {
diagnose(Tok.getLoc(), diag::attr_expected_string_literal, AttrName);
return false;
}
Optional<StringRef> value = getStringLiteralIfNotInterpolated(
Tok.getLoc(), flag);
if (!value)
return false;
Token stringTok = Tok;
consumeToken();
message = *value;
if (!consumeIf(tok::r_paren))
diagnose(stringTok.getRange().getEnd(), diag::attr_expected_rparen,
AttrName, /*isModifiler*/false)
.fixItInsertAfter(stringTok.getLoc(), ")");
}
Attributes.add(new (Context) UnavailableFromAsyncAttr(
message, AtLoc, SourceRange(Loc, Tok.getLoc()), false));
break;
}
}
if (DuplicateAttribute) {
diagnose(Loc, diag::duplicate_attribute, DeclAttribute::isDeclModifier(DK))
.highlight(AttrRange);
diagnose(DuplicateAttribute->getLocation(),
diag::previous_attribute,
DeclAttribute::isDeclModifier(DK))
.highlight(DuplicateAttribute->getRange());
}
// If this is a decl modifier spelled with an @, emit an error and remove it
// with a fixit.
if (AtLoc.isValid() && DeclAttribute::isDeclModifier(DK))
diagnose(AtLoc, diag::cskeyword_not_attribute, AttrName).fixItRemove(AtLoc);
return false;
}