From: Douglas Gregor Date: Sat, 26 Mar 2011 03:35:55 +0000 (+0000) Subject: Extend the new 'availability' attribute with support for an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b53e417ba487f4193ef3b0485b420e0fdae643a2;p=clang Extend the new 'availability' attribute with support for an 'unavailable' argument, which specifies that the declaration to which the attribute appertains is unavailable on that platform. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@128329 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index 03bdc334f7..308315cb32 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -46,6 +46,7 @@ class Argument { string Name = name; } +class BoolArgument : Argument; class IdentifierArgument : Argument; class IntArgument : Argument; class StringArgument : Argument; @@ -140,7 +141,8 @@ def AsmLabel : InheritableAttr { def Availability : InheritableAttr { let Spellings = ["availability"]; let Args = [IdentifierArgument<"platform">, VersionArgument<"introduced">, - VersionArgument<"deprecated">, VersionArgument<"obsoleted">]; + VersionArgument<"deprecated">, VersionArgument<"obsoleted">, + BoolArgument<"unavailable">]; let AdditionalMembers = [{static llvm::StringRef getPrettyPlatformName(llvm::StringRef Platform) { return llvm::StringSwitch(Platform) diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 544318356c..9aada07121 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -445,6 +445,8 @@ def err_availability_unknown_change : Error< "'obsoleted'">; def err_availability_redundant : Error< "redundant %0 availability change; only the last specified change will " "be used">; +def warn_availability_and_unavailable : Warning< + "'unavailable' availability overrides all other availability information">; // Language specific pragmas // - Generic warnings diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index d15ef01b09..b506dba037 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -121,6 +121,9 @@ class Parser : public CodeCompletionHandler { /// \brief Identifier for "obsoleted". IdentifierInfo *Ident_obsoleted; + /// \brief Identifier for "unavailable". + IdentifierInfo *Ident_unavailable; + /// C++0x contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_override; diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 8e05c62880..fa2f94379e 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -62,20 +62,24 @@ private: /// The number of expression arguments this attribute has. /// The expressions themselves are stored after the object. - unsigned NumArgs; + unsigned NumArgs : 16; /// True if Microsoft style: declspec(foo). - bool DeclspecAttribute; + unsigned DeclspecAttribute : 1; /// True if C++0x-style: [[foo]]. - bool CXX0XAttribute; + unsigned CXX0XAttribute : 1; /// True if already diagnosed as invalid. - mutable bool Invalid; + mutable unsigned Invalid : 1; /// True if this has the extra information associated with an /// availability attribute. - bool IsAvailability; + unsigned IsAvailability : 1; + + /// \brief The location of the 'unavailable' keyword in an + /// availability attribute. + SourceLocation UnavailableLoc; /// The next attribute in the current position. AttributeList *NextInPosition; @@ -127,11 +131,13 @@ private: const AvailabilityChange &introduced, const AvailabilityChange &deprecated, const AvailabilityChange &obsoleted, + SourceLocation unavailable, bool declspec, bool cxx0x) : AttrName(attrName), ScopeName(scopeName), ParmName(parmName), AttrLoc(attrLoc), ScopeLoc(scopeLoc), ParmLoc(parmLoc), NumArgs(0), DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), - Invalid(false), IsAvailability(true), NextInPosition(0), NextInPool(0) { + Invalid(false), IsAvailability(true), UnavailableLoc(unavailable), + NextInPosition(0), NextInPool(0) { new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); @@ -315,6 +321,11 @@ public: assert(getKind() == AT_availability && "Not an availability attribute"); return getAvailabilitySlot(ObsoletedSlot); } + + SourceLocation getUnavailableLoc() const { + assert(getKind() == AT_availability && "Not an availability attribute"); + return UnavailableLoc; + } }; /// A factory, from which one makes pools, from which one creates @@ -435,12 +446,14 @@ public: const AvailabilityChange &introduced, const AvailabilityChange &deprecated, const AvailabilityChange &obsoleted, + SourceLocation unavailable, bool declspec = false, bool cxx0x = false) { void *memory = allocate(AttributeFactory::AvailabilityAllocSize); return add(new (memory) AttributeList(attrName, attrLoc, scopeName, scopeLoc, parmName, parmLoc, introduced, deprecated, obsoleted, + unavailable, declspec, cxx0x)); } @@ -557,10 +570,12 @@ public: const AvailabilityChange &introduced, const AvailabilityChange &deprecated, const AvailabilityChange &obsoleted, + SourceLocation unavailable, bool declspec = false, bool cxx0x = false) { AttributeList *attr = pool.create(attrName, attrLoc, scopeName, scopeLoc, parmName, parmLoc, - introduced, deprecated, obsoleted, declspec, cxx0x); + introduced, deprecated, obsoleted, unavailable, + declspec, cxx0x); add(attr); return attr; } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 0e871ecc4c..c081b0d4a9 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -268,6 +268,17 @@ static AvailabilityResult CheckAvailability(ASTContext &Context, if (A->getPlatform()->getName() != TargetPlatform) return AR_Available; + // Make sure that this declaration has not been marked 'unavailable'. + if (A->getUnavailable()) { + if (Message) { + Message->clear(); + llvm::raw_string_ostream Out(*Message); + Out << "not available on " << PrettyPlatformName; + } + + return AR_Unavailable; + } + // Make sure that this declaration has already been introduced. if (!A->getIntroduced().empty() && TargetMinVersion < A->getIntroduced()) { diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 976e60f0c9..d553687837 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -484,6 +484,7 @@ VersionTuple Parser::ParseVersionTuple(SourceRange &Range) { /// 'introduced' '=' version /// 'deprecated' '=' version /// 'removed' = version +/// 'unavailable' void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, SourceLocation AvailabilityLoc, ParsedAttributes &attrs, @@ -521,9 +522,11 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, Ident_introduced = PP.getIdentifierInfo("introduced"); Ident_deprecated = PP.getIdentifierInfo("deprecated"); Ident_obsoleted = PP.getIdentifierInfo("obsoleted"); + Ident_unavailable = PP.getIdentifierInfo("unavailable"); } // Parse the set of introductions/deprecations/removals. + SourceLocation UnavailableLoc; do { if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_availability_expected_change); @@ -533,6 +536,20 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, IdentifierInfo *Keyword = Tok.getIdentifierInfo(); SourceLocation KeywordLoc = ConsumeToken(); + if (Keyword == Ident_unavailable) { + if (UnavailableLoc.isValid()) { + Diag(KeywordLoc, diag::err_availability_redundant) + << Keyword << SourceRange(UnavailableLoc); + } + UnavailableLoc = KeywordLoc; + + if (Tok.isNot(tok::comma)) + break; + + ConsumeToken(); + continue; + } + if (Tok.isNot(tok::equal)) { Diag(Tok, diag::err_expected_equal_after) << Keyword; @@ -589,13 +606,33 @@ void Parser::ParseAvailabilityAttribute(IdentifierInfo &Availability, if (endLoc) *endLoc = RParenLoc; + // The 'unavailable' availability cannot be combined with any other + // availability changes. Make sure that hasn't happened. + if (UnavailableLoc.isValid()) { + bool Complained = false; + for (unsigned Index = Introduced; Index != Unknown; ++Index) { + if (Changes[Index].KeywordLoc.isValid()) { + if (!Complained) { + Diag(UnavailableLoc, diag::warn_availability_and_unavailable) + << SourceRange(Changes[Index].KeywordLoc, + Changes[Index].VersionRange.getEnd()); + Complained = true; + } + + // Clear out the availability. + Changes[Index] = AvailabilityChange(); + } + } + } + // Record this attribute - attrs.addNew(&Availability, AvailabilityLoc, + attrs.addNew(&Availability, AvailabilityLoc, 0, SourceLocation(), Platform, PlatformLoc, Changes[Introduced], Changes[Deprecated], - Changes[Obsoleted], false, false); + Changes[Obsoleted], + UnavailableLoc, false, false); } void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) { diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 4cdf2deec3..5bf0abd6b7 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -426,6 +426,7 @@ void Parser::Initialize() { Ident_introduced = 0; Ident_deprecated = 0; Ident_obsoleted = 0; + Ident_unavailable = 0; } /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 81bf230f94..6d1a4c86f2 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -1091,6 +1091,7 @@ static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, AvailabilityChange Introduced = Attr.getAvailabilityIntroduced(); AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated(); AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted(); + bool IsUnavailable = Attr.getUnavailableLoc().isValid(); // Ensure that Introduced < Deprecated < Obsoleted (although not all // of these steps are needed). @@ -1122,7 +1123,8 @@ static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, Platform, Introduced.Version, Deprecated.Version, - Obsoleted.Version)); + Obsoleted.Version, + IsUnavailable)); } static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) { diff --git a/test/Parser/attr-availability.c b/test/Parser/attr-availability.c index 10d55b558b..63bb5271e5 100644 --- a/test/Parser/attr-availability.c +++ b/test/Parser/attr-availability.c @@ -7,3 +7,9 @@ void f2() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsole void f3() __attribute__((availability(ios,deprecated=10.4.7,introduced=10,obsoleted=10.6,introduced=10.2))); // expected-error{{redundant 'introduced' availability change; only the last specified change will be used}} +void f4() __attribute__((availability(macosx,introduced=10.5), availability(ios,unavailable))); + +void f5() __attribute__((availability(macosx,introduced=10.5), availability(ios,unavailable, unavailable))); // expected-error{{redundant 'unavailable' availability change; only the last specified change will be used}} + +void f6() __attribute__((availability(macosx,unavailable,introduced=10.5))); // expected-warning{{warning: 'unavailable' availability overrides all other availability information}} + diff --git a/test/Sema/attr-availability-macosx.c b/test/Sema/attr-availability-macosx.c index b43da6f59f..2b7c1e06ac 100644 --- a/test/Sema/attr-availability-macosx.c +++ b/test/Sema/attr-availability-macosx.c @@ -5,6 +5,7 @@ void f1(int) __attribute__((availability(macosx,introduced=10.5))); void f2(int) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5))); void f3(int) __attribute__((availability(macosx,introduced=10.6))); void f4(int) __attribute__((availability(macosx,introduced=10.1,deprecated=10.3,obsoleted=10.5), availability(ios,introduced=2.0,deprecated=3.0))); // expected-note{{explicitly marked unavailable}} +void f5(int) __attribute__((availability(ios,introduced=3.2), availability(macosx,unavailable))); // expected-note{{function has been explicitly marked unavailable here}} void test() { f0(0); @@ -12,4 +13,5 @@ void test() { f2(0); // expected-warning{{'f2' is deprecated: first deprecated in Mac OS X 10.5}} f3(0); f4(0); // expected-error{{f4' is unavailable: obsoleted in Mac OS X 10.5}} + f5(0); // expected-error{{'f5' is unavailable: not available on Mac OS X}} }