//===--- Attr.cpp - Swift Language Attr ASTs ------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements routines relating to declaration attributes. // //===----------------------------------------------------------------------===// #include "swift/AST/Attr.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" #include "swift/AST/Module.h" #include "swift/AST/Types.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/StringSwitch.h" using namespace swift; // Only allow allocation of attributes using the allocator in ASTContext. void *AttributeBase::operator new(size_t Bytes, ASTContext &C, unsigned Alignment) { return C.Allocate(Bytes, Alignment); } /// Given a name like "autoclosure", return the type attribute ID that /// corresponds to it. This returns TAK_Count on failure. /// TypeAttrKind TypeAttributes::getAttrKindFromString(StringRef Str) { return llvm::StringSwitch(Str) #define TYPE_ATTR(X) .Case(#X, TAK_##X) #include "swift/AST/Attr.def" .Default(TAK_Count); } /// Given a name like "inline", return the decl attribute ID that corresponds /// to it. Note that this is a many-to-one mapping, and that the identifier /// passed in may only be the first portion of the attribute (e.g. in the case /// of the 'unowned(unsafe)' attribute, the string passed in is 'unowned'. /// /// Also note that this recognizes both attributes like '@inline' (with no @) /// and decl modifiers like 'final'. This returns DAK_Count on failure. /// DeclAttrKind DeclAttribute::getAttrKindFromString(StringRef Str) { return llvm::StringSwitch(Str) #define DECL_ATTR(X, CLASS, ...) .Case(#X, DAK_##CLASS) #define DECL_ATTR_ALIAS(X, CLASS) .Case(#X, DAK_##CLASS) #include "swift/AST/Attr.def" .Default(DAK_Count); } /// Returns true if this attribute can appear on the specified decl. bool DeclAttribute::canAttributeAppearOnDecl(DeclAttrKind DK, const Decl *D) { unsigned Options = getOptions(DK); switch (D->getKind()) { #define DECL(Id, Parent) case DeclKind::Id: return (Options & On##Id) != 0; #include "swift/AST/DeclNodes.def" } llvm_unreachable("bad DeclKind"); } const AvailabilityAttr *DeclAttributes::getUnavailable( const ASTContext &ctx) const { for (auto Attr : *this) if (auto AvAttr = dyn_cast(Attr)) { if (AvAttr->isInvalid()) continue; // If this attribute doesn't apply to the active platform, we're done. if (!AvAttr->isActivePlatform(ctx)) continue; auto MinVersion = ctx.LangOpts.getMinPlatformVersion(); switch (AvAttr->getMinVersionAvailability(MinVersion)) { case MinVersionComparison::Available: case MinVersionComparison::PotentiallyUnavailable: break; case MinVersionComparison::Obsoleted: case MinVersionComparison::Unavailable: return AvAttr; } } return nullptr; } const AvailabilityAttr * DeclAttributes::getDeprecated(const ASTContext &ctx) const { for (auto Attr : *this) { if (auto AvAttr = dyn_cast(Attr)) { if (AvAttr->isInvalid() || !AvAttr->isActivePlatform(ctx)) continue; Optional DeprecatedVersion = AvAttr->Deprecated; if (!DeprecatedVersion.hasValue()) continue; auto MinVersion = ctx.LangOpts.getMinPlatformVersion(); // We treat the declaration as deprecated if it is deprecated on // all deployment targets. // Once availability checking is enabled by default, we should // query the type refinement context hierarchy to determine // whether a declaration is deprecated on all versions // allowed by the context containing the reference. if (DeprecatedVersion.getValue() <= MinVersion) { return AvAttr; } } } return nullptr; } void DeclAttributes::dump() const { StreamPrinter P(llvm::errs()); PrintOptions PO = PrintOptions::printEverything(); print(P, PO); } void DeclAttributes::print(ASTPrinter &Printer, const PrintOptions &Options) const { if (!DeclAttrs) return; using AttributeVector = SmallVector; AttributeVector orderedAttributes(begin(), end()); std::reverse(orderedAttributes.begin(), orderedAttributes.end()); // Process attributes in passes. AttributeVector longAttributes; AttributeVector attributes; AttributeVector modifiers; for (auto DA : orderedAttributes) { if (!Options.PrintImplicitAttrs && DA->isImplicit()) continue; if (std::find(Options.ExcludeAttrList.begin(), Options.ExcludeAttrList.end(), DA->getKind()) != Options.ExcludeAttrList.end()) continue; if (!Options.ExclusiveAttrList.empty()) { if (std::find(Options.ExclusiveAttrList.begin(), Options.ExclusiveAttrList.end(), DA->getKind()) == Options.ExclusiveAttrList.end()) continue; } AttributeVector &which = DA->isDeclModifier() ? modifiers : DA->isLongAttribute() ? longAttributes : attributes; which.push_back(DA); } for (auto DA : longAttributes) DA->print(Printer, Options); for (auto DA : attributes) DA->print(Printer, Options); for (auto DA : modifiers) DA->print(Printer, Options); } void DeclAttribute::print(ASTPrinter &Printer, const PrintOptions &Options) const { switch (getKind()) { // Handle all of the SIMPLE_DECL_ATTRs. #define SIMPLE_DECL_ATTR(X, CLASS, ...) case DAK_##CLASS: #include "swift/AST/Attr.def" case DAK_Inline: case DAK_Accessibility: case DAK_Ownership: case DAK_Effects: if (!DeclAttribute::isDeclModifier(getKind())) Printer << "@"; Printer << getAttrName(); break; case DAK_Semantics: Printer << "@semantics(\"" << cast(this)->Value << "\")"; break; case DAK_Asmname: Printer << "@asmname(\"" << cast(this)->Name << "\")"; break; case DAK_Availability: { Printer << "@availability("; auto Attr = cast(this); Printer << Attr->platformString(); if (Attr->IsUnvailable) Printer << ", unavailable"; if (Attr->Introduced) Printer << ", introduced=" << Attr->Introduced.getValue().getAsString(); if (Attr->Deprecated) Printer << ", deprecated=" << Attr->Deprecated.getValue().getAsString(); if (Attr->Obsoleted) Printer << ", obsoleted=" << Attr->Obsoleted.getValue().getAsString(); if (!Attr->Message.empty()) Printer << ", message=\"" << Attr->Message << "\""; Printer << ")"; break; } case DAK_AutoClosure: Printer << "@autoclosure"; if (cast(this)->isEscaping()) Printer << "(escaping)"; break; case DAK_ObjC: { if (Options.PrintForSIL && isImplicit()) break; Printer << "@objc"; llvm::SmallString<32> scratch; if (auto Name = cast(this)->getName()) { if (!cast(this)->isNameImplicit()) Printer << "(" << Name->getString(scratch) << ")"; } break; } case DAK_SetterAccessibility: Printer << getAttrName() << "(set)"; break; case DAK_RawDocComment: // Not printed. return; case DAK_ObjCBridged: // Not printed. return; case DAK_Count: llvm_unreachable("exceed declaration attribute kinds"); } if (isLongAttribute() && Options.PrintLongAttrsOnSeparateLines) Printer.printNewline(); else Printer << " "; } void DeclAttribute::print(llvm::raw_ostream &OS) const { StreamPrinter P(OS); print(P, PrintOptions()); } unsigned DeclAttribute::getOptions(DeclAttrKind DK) { switch (DK) { case DAK_Count: llvm_unreachable("getOptions needs a valid attribute"); #define DECL_ATTR(_, CLASS, OPTIONS, ...)\ case DAK_##CLASS: return OPTIONS; #include "swift/AST/Attr.def" } llvm_unreachable("bad DeclAttrKind"); } StringRef DeclAttribute::getAttrName() const { switch (getKind()) { case DAK_Count: llvm_unreachable("getAttrName needs a valid attribute"); #define SIMPLE_DECL_ATTR(NAME, CLASS, ...) \ case DAK_##CLASS: \ return #NAME; #include "swift/AST/Attr.def" case DAK_Asmname: return "asmname"; case DAK_Semantics: return "semantics"; case DAK_Availability: return "availability"; case DAK_AutoClosure: return "autoclosure"; case DAK_ObjC: return "objc"; case DAK_Inline: { switch (cast(this)->getKind()) { case InlineKind::Never: return "inline(never)"; case InlineKind::Always: return "inline(__always)"; } llvm_unreachable("Invalid inline kind"); } case DAK_Effects: switch (cast(this)->getKind()) { case EffectsKind::ReadNone: return "effects(readnone)"; case EffectsKind::ReadOnly: return "effects(readonly)"; case EffectsKind::ReadWrite: return "effects(readwrite)"; case EffectsKind::Unspecified: return "effects(unspecified)"; } case DAK_Accessibility: case DAK_SetterAccessibility: switch (cast(this)->getAccess()) { case Accessibility::Private: return "private"; case Accessibility::Internal: return "internal"; case Accessibility::Public: return "public"; } case DAK_Ownership: switch (cast(this)->get()) { case Ownership::Strong: llvm_unreachable("Never present in the attribute"); case Ownership::Weak: return "weak"; case Ownership::Unowned: return "unowned"; case Ownership::Unmanaged: return "unowned(unsafe)"; } case DAK_RawDocComment: return "<>"; case DAK_ObjCBridged: return "<>"; } llvm_unreachable("bad DeclAttrKind"); } ObjCAttr::ObjCAttr(SourceLoc atLoc, SourceRange baseRange, Optional name, SourceRange parenRange, ArrayRef nameLocs) : DeclAttribute(DAK_ObjC, atLoc, baseRange, /*Implicit=*/false), NameData(nullptr) { if (name) { // Store the name. assert(name->getNumSelectorPieces() == nameLocs.size()); NameData = name->getOpaqueValue(); // Store location information. ObjCAttrBits.HasTrailingLocationInfo = true; getTrailingLocations()[0] = parenRange.Start; getTrailingLocations()[1] = parenRange.End; std::memcpy(getTrailingLocations().slice(2).data(), nameLocs.data(), nameLocs.size() * sizeof(SourceLoc)); } else { ObjCAttrBits.HasTrailingLocationInfo = false; } ObjCAttrBits.ImplicitName = false; } ObjCAttr *ObjCAttr::create(ASTContext &Ctx, Optional name, bool isNameImplicit) { return new (Ctx) ObjCAttr(name, isNameImplicit); } ObjCAttr *ObjCAttr::createUnnamed(ASTContext &Ctx, SourceLoc AtLoc, SourceLoc ObjCLoc) { return new (Ctx) ObjCAttr(AtLoc, SourceRange(ObjCLoc), None, SourceRange(), { }); } ObjCAttr *ObjCAttr::createUnnamedImplicit(ASTContext &Ctx) { return new (Ctx) ObjCAttr(None, false); } ObjCAttr *ObjCAttr::createNullary(ASTContext &Ctx, SourceLoc AtLoc, SourceLoc ObjCLoc, SourceLoc LParenLoc, SourceLoc NameLoc, Identifier Name, SourceLoc RParenLoc) { unsigned size = sizeof(ObjCAttr) + 3 * sizeof(SourceLoc); void *mem = Ctx.Allocate(size, alignof(ObjCAttr)); return new (mem) ObjCAttr(AtLoc, SourceRange(ObjCLoc), ObjCSelector(Ctx, 0, Name), SourceRange(LParenLoc, RParenLoc), NameLoc); } ObjCAttr *ObjCAttr::createNullary(ASTContext &Ctx, Identifier Name, bool isNameImplicit) { return new (Ctx) ObjCAttr(ObjCSelector(Ctx, 0, Name), isNameImplicit); } ObjCAttr *ObjCAttr::createSelector(ASTContext &Ctx, SourceLoc AtLoc, SourceLoc ObjCLoc, SourceLoc LParenLoc, ArrayRef NameLocs, ArrayRef Names, SourceLoc RParenLoc) { assert(NameLocs.size() == Names.size()); unsigned size = sizeof(ObjCAttr) + (NameLocs.size() + 2) * sizeof(SourceLoc); void *mem = Ctx.Allocate(size, alignof(ObjCAttr)); return new (mem) ObjCAttr(AtLoc, SourceRange(ObjCLoc), ObjCSelector(Ctx, Names.size(), Names), SourceRange(LParenLoc, RParenLoc), NameLocs); } ObjCAttr *ObjCAttr::createSelector(ASTContext &Ctx, ArrayRef Names, bool isNameImplicit) { return new (Ctx) ObjCAttr(ObjCSelector(Ctx, Names.size(), Names), isNameImplicit); } ArrayRef ObjCAttr::getNameLocs() const { if (!hasTrailingLocationInfo()) return { }; return getTrailingLocations().slice(2); } SourceLoc ObjCAttr::getLParenLoc() const { if (!hasTrailingLocationInfo()) return SourceLoc(); return getTrailingLocations()[0]; } SourceLoc ObjCAttr::getRParenLoc() const { if (!hasTrailingLocationInfo()) return SourceLoc(); return getTrailingLocations()[1]; } ObjCAttr *ObjCAttr::clone(ASTContext &context) const { return new (context) ObjCAttr(getName(), isNameImplicit()); } AvailabilityAttr *AvailabilityAttr::createUnavailableAttr(ASTContext &C, StringRef Message, StringRef Rename) { clang::VersionTuple NoVersion; return new (C) AvailabilityAttr( SourceLoc(), SourceRange(), PlatformKind::none, Message, Rename, NoVersion, NoVersion, NoVersion, /* isUnavailable */ true, /* isImplicit */ false); } bool AvailabilityAttr::isActivePlatform(const ASTContext &ctx) const { return isPlatformActive(Platform, ctx.LangOpts); } MinVersionComparison AvailabilityAttr::getMinVersionAvailability( clang::VersionTuple minVersion) const { // Unconditionally unavailable. if (IsUnvailable) return MinVersionComparison::Unavailable; // If this entity was obsoleted before or at the minimum platform version, // consider it obsolete. if (Obsoleted && *Obsoleted <= minVersion) return MinVersionComparison::Obsoleted; // If this entity was introduced after the minimum platform version, it's // availability can only be determined dynamically. if (Introduced && *Introduced > minVersion) return MinVersionComparison::PotentiallyUnavailable; // The entity is available. return MinVersionComparison::Available; } const AvailabilityAttr *AvailabilityAttr::isUnavailable(const Decl *D) { ASTContext &ctx = D->getASTContext(); return D->getAttrs().getUnavailable(ctx); }