Implement a new 'availability' attribute, that allows one to specify
which versions of an OS provide a certain facility. For example,
void foo()
__attribute__((availability(macosx,introduced=10.2,deprecated=10.4,obsoleted=10.6)));
says that the function "foo" was introduced in 10.2, deprecated in
10.4, and completely obsoleted in 10.6. This attribute ties in with
the deployment targets (e.g., -mmacosx-version-min=10.1 specifies that
we want to deploy back to Mac OS X 10.1). There are several concrete
behaviors that this attribute enables, as illustrated with the
function foo() above:
- If we choose a deployment target >= Mac OS X 10.4, uses of "foo"
will result in a deprecation warning, as if we had placed
attribute((deprecated)) on it (but with a better diagnostic)
- If we choose a deployment target >= Mac OS X 10.6, uses of "foo"
will result in an "unavailable" warning (in C)/error (in C++), as
if we had placed attribute((unavailable)) on it
- If we choose a deployment target prior to 10.2, foo() is
weak-imported (if it is a kind of entity that can be weak
imported), as if we had placed the weak_import attribute on it.
Naturally, there can be multiple availability attributes on a
declaration, for different platforms; only the current platform
matters when checking availability attributes.
The only platforms this attribute currently works for are "ios" and
"macosx", since we already have -mxxxx-version-min flags for them and we
have experience there with macro tricks translating down to the
deprecated/unavailable/weak_import attributes. The end goal is to open
this up to other platforms, and even extension to other "platforms"
that are really libraries (say, through a #pragma clang
define_system), but that hasn't yet been designed and we may want to
shake out more issues with this narrower problem first.
Addresses <rdar://problem/6690412>.
As a drive-by bug-fix, if an entity is both deprecated and
unavailable, we only emit the "unavailable" diagnostic.
llvm-svn: 128127
diff --git a/clang/lib/Sema/AttributeList.cpp b/clang/lib/Sema/AttributeList.cpp
index a715989..ae5ea67 100644
--- a/clang/lib/Sema/AttributeList.cpp
+++ b/clang/lib/Sema/AttributeList.cpp
@@ -36,6 +36,24 @@
}
}
+AttributeList::AttributeList(llvm::BumpPtrAllocator &Alloc,
+ IdentifierInfo *AttrName, SourceLocation AttrLoc,
+ IdentifierInfo *ScopeName, SourceLocation ScopeLoc,
+ IdentifierInfo *ParmName, SourceLocation ParmLoc,
+ const AvailabilityChange &Introduced,
+ const AvailabilityChange &Deprecated,
+ const AvailabilityChange &Obsoleted,
+ bool declspec, bool cxx0x)
+ : AttrName(AttrName), AttrLoc(AttrLoc), ScopeName(ScopeName),
+ ScopeLoc(ScopeLoc), ParmName(ParmName), ParmLoc(ParmLoc),
+ Args(0), NumArgs(0), Next(0),
+ DeclspecAttribute(declspec), CXX0XAttribute(cxx0x),
+ AvailabilityIntroduced(Introduced),
+ AvailabilityDeprecated(Deprecated),
+ AvailabilityObsoleted(Obsoleted),
+ Invalid(false) {
+}
+
AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) {
llvm::StringRef AttrName = Name->getName();
@@ -83,6 +101,7 @@
.Case("may_alias", AT_may_alias)
.Case("base_check", AT_base_check)
.Case("deprecated", AT_deprecated)
+ .Case("availability", AT_availability)
.Case("visibility", AT_visibility)
.Case("destructor", AT_destructor)
.Case("format_arg", AT_format_arg)
diff --git a/clang/lib/Sema/CodeCompleteConsumer.cpp b/clang/lib/Sema/CodeCompleteConsumer.cpp
index b7037ce..2334ab5 100644
--- a/clang/lib/Sema/CodeCompleteConsumer.cpp
+++ b/clang/lib/Sema/CodeCompleteConsumer.cpp
@@ -375,12 +375,21 @@
switch (Kind) {
case RK_Declaration:
// Set the availability based on attributes.
- Availability = CXAvailability_Available;
- if (Declaration->getAttr<UnavailableAttr>())
- Availability = CXAvailability_NotAvailable;
- else if (Declaration->getAttr<DeprecatedAttr>())
- Availability = CXAvailability_Deprecated;
+ switch (Declaration->getAvailability()) {
+ case AR_Available:
+ case AR_NotYetIntroduced:
+ Availability = CXAvailability_Available;
+ break;
+ case AR_Deprecated:
+ Availability = CXAvailability_Deprecated;
+ break;
+
+ case AR_Unavailable:
+ Availability = CXAvailability_NotAvailable;
+ break;
+ }
+
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Declaration))
if (Function->isDeleted())
Availability = CXAvailability_NotAvailable;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3c11277..3f33f14 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1074,6 +1074,57 @@
d->addAttr(::new (S.Context) UnavailableAttr(Attr.getLoc(), S.Context, Str));
}
+static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr,
+ Sema &S) {
+ IdentifierInfo *Platform = Attr.getParameterName();
+ SourceLocation PlatformLoc = Attr.getParameterLoc();
+
+ llvm::StringRef PlatformName
+ = AvailabilityAttr::getPrettyPlatformName(Platform->getName());
+ if (PlatformName.empty()) {
+ S.Diag(PlatformLoc, diag::warn_availability_unknown_platform)
+ << Platform;
+
+ PlatformName = Platform->getName();
+ }
+
+ AvailabilityChange Introduced = Attr.getAvailabilityIntroduced();
+ AvailabilityChange Deprecated = Attr.getAvailabilityDeprecated();
+ AvailabilityChange Obsoleted = Attr.getAvailabilityObsoleted();
+
+ // Ensure that Introduced < Deprecated < Obsoleted (although not all
+ // of these steps are needed).
+ if (Introduced.isValid() && Deprecated.isValid() &&
+ !(Introduced.Version < Deprecated.Version)) {
+ S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
+ << 1 << PlatformName << Deprecated.Version.getAsString()
+ << 0 << Introduced.Version.getAsString();
+ return;
+ }
+
+ if (Introduced.isValid() && Obsoleted.isValid() &&
+ !(Introduced.Version < Obsoleted.Version)) {
+ S.Diag(Introduced.KeywordLoc, diag::warn_availability_version_ordering)
+ << 2 << PlatformName << Obsoleted.Version.getAsString()
+ << 0 << Introduced.Version.getAsString();
+ return;
+ }
+
+ if (Deprecated.isValid() && Obsoleted.isValid() &&
+ !(Deprecated.Version < Obsoleted.Version)) {
+ S.Diag(Deprecated.KeywordLoc, diag::warn_availability_version_ordering)
+ << 2 << PlatformName << Obsoleted.Version.getAsString()
+ << 1 << Deprecated.Version.getAsString();
+ return;
+ }
+
+ d->addAttr(::new (S.Context) AvailabilityAttr(Attr.getLoc(), S.Context,
+ Platform,
+ Introduced.Version,
+ Deprecated.Version,
+ Obsoleted.Version));
+}
+
static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) {
// check the attribute arguments.
if (Attr.getNumArgs() != 1) {
@@ -1380,27 +1431,18 @@
// weak_import only applies to variable & function declarations.
bool isDef = false;
- if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
- isDef = (!VD->hasExternalStorage() || VD->getInit());
- } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
- isDef = FD->hasBody();
- } else if (isa<ObjCPropertyDecl>(D) || isa<ObjCMethodDecl>(D)) {
- // We ignore weak import on properties and methods
- return;
- } else if (!(S.LangOpts.ObjCNonFragileABI && isa<ObjCInterfaceDecl>(D))) {
- // Don't issue the warning for darwin as target; yet, ignore the attribute.
- if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin ||
- !isa<ObjCInterfaceDecl>(D))
+ if (!D->canBeWeakImported(isDef)) {
+ if (isDef)
+ S.Diag(Attr.getLoc(),
+ diag::warn_attribute_weak_import_invalid_on_definition)
+ << "weak_import" << 2 /*variable and function*/;
+ else if (S.Context.Target.getTriple().getOS() != llvm::Triple::Darwin ||
+ (!isa<ObjCInterfaceDecl>(D) &&
+ !isa<ObjCPropertyDecl>(D) &&
+ !isa<ObjCMethodDecl>(D)))
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type)
<< Attr.getName() << ExpectedVariableOrFunction;
- return;
- }
- // Merge should handle any subsequent violations.
- if (isDef) {
- S.Diag(Attr.getLoc(),
- diag::warn_attribute_weak_import_invalid_on_definition)
- << "weak_import" << 2 /*variable and function*/;
return;
}
@@ -2745,6 +2787,7 @@
case AttributeList::AT_analyzer_noreturn:
HandleAnalyzerNoReturnAttr (D, Attr, S); break;
case AttributeList::AT_annotate: HandleAnnotateAttr (D, Attr, S); break;
+ case AttributeList::AT_availability:HandleAvailabilityAttr(D, Attr, S); break;
case AttributeList::AT_carries_dependency:
HandleDependencyAttr (D, Attr, S); break;
case AttributeList::AT_common: HandleCommonAttr (D, Attr, S); break;
@@ -3058,7 +3101,7 @@
static bool isDeclDeprecated(Decl *D) {
do {
- if (D->hasAttr<DeprecatedAttr>())
+ if (D->isDeprecated())
return true;
} while ((D = cast_or_null<Decl>(D->getDeclContext())));
return false;
diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp
index 8717385..101ade7 100644
--- a/clang/lib/Sema/SemaDeclObjC.cpp
+++ b/clang/lib/Sema/SemaDeclObjC.cpp
@@ -28,7 +28,7 @@
NamedDecl *ND,
SourceLocation ImplLoc,
int select) {
- if (ND && ND->getAttr<DeprecatedAttr>()) {
+ if (ND && ND->isDeprecated()) {
S.Diag(ImplLoc, diag::warn_deprecated_def) << select;
if (select == 0)
S.Diag(ND->getLocation(), diag::note_method_declared_at);
@@ -1369,15 +1369,14 @@
PrevObjCMethod->setDefined(impl);
// If a method is deprecated, push it in the global pool.
// This is used for better diagnostics.
- if (Method->getAttr<DeprecatedAttr>()) {
- if (!PrevObjCMethod->getAttr<DeprecatedAttr>())
+ if (Method->isDeprecated()) {
+ if (!PrevObjCMethod->isDeprecated())
List->Method = Method;
}
// If new method is unavailable, push it into global pool
// unless previous one is deprecated.
- if (Method->getAttr<UnavailableAttr>()) {
- if (!PrevObjCMethod->getAttr<UnavailableAttr>() &&
- !PrevObjCMethod->getAttr<DeprecatedAttr>())
+ if (Method->isUnavailable()) {
+ if (PrevObjCMethod->getAvailability() < AR_Deprecated)
List->Method = Method;
}
return;
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b8f0c72..c2f3a434 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -68,7 +68,7 @@
Diag(Suppressed[I].first, Suppressed[I].second);
// Clear out the list of suppressed diagnostics, so that we don't emit
- // them again for this specialization. However, we don't remove this
+ // them again for this specialization. However, we don't obsolete this
// entry from the table, because we want to avoid ever emitting these
// diagnostics again.
Suppressed.clear();
@@ -82,25 +82,6 @@
return true;
}
- // See if the decl is deprecated.
- if (const DeprecatedAttr *DA = D->getAttr<DeprecatedAttr>())
- EmitDeprecationWarning(D, DA->getMessage(), Loc, UnknownObjCClass);
-
- // See if the decl is unavailable
- if (const UnavailableAttr *UA = D->getAttr<UnavailableAttr>()) {
- if (UA->getMessage().empty()) {
- if (!UnknownObjCClass)
- Diag(Loc, diag::err_unavailable) << D->getDeclName();
- else
- Diag(Loc, diag::warn_unavailable_fwdclass_message)
- << D->getDeclName();
- }
- else
- Diag(Loc, diag::err_unavailable_message)
- << D->getDeclName() << UA->getMessage();
- Diag(D->getLocation(), diag::note_unavailable_here) << 0;
- }
-
// See if this is a deleted function.
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
if (FD->isDeleted()) {
@@ -110,6 +91,32 @@
}
}
+ // See if this declaration is unavailable or deprecated.
+ std::string Message;
+ switch (D->getAvailability(&Message)) {
+ case AR_Available:
+ case AR_NotYetIntroduced:
+ break;
+
+ case AR_Deprecated:
+ EmitDeprecationWarning(D, Message, Loc, UnknownObjCClass);
+ break;
+
+ case AR_Unavailable:
+ if (Message.empty()) {
+ if (!UnknownObjCClass)
+ Diag(Loc, diag::err_unavailable) << D->getDeclName();
+ else
+ Diag(Loc, diag::warn_unavailable_fwdclass_message)
+ << D->getDeclName();
+ }
+ else
+ Diag(Loc, diag::err_unavailable_message)
+ << D->getDeclName() << Message;
+ Diag(D->getLocation(), diag::note_unavailable_here) << 0;
+ break;
+ }
+
// Warn if this is used but marked unused.
if (D->hasAttr<UnusedAttr>())
Diag(Loc, diag::warn_used_but_marked_unused) << D->getDeclName();
@@ -117,6 +124,23 @@
return false;
}
+/// \brief Retrieve the message suffix that should be added to a
+/// diagnostic complaining about the given function being deleted or
+/// unavailable.
+std::string Sema::getDeletedOrUnavailableSuffix(const FunctionDecl *FD) {
+ // FIXME: C++0x implicitly-deleted special member functions could be
+ // detected here so that we could improve diagnostics to say, e.g.,
+ // "base class 'A' had a deleted copy constructor".
+ if (FD->isDeleted())
+ return std::string();
+
+ std::string Message;
+ if (FD->getAvailability(&Message))
+ return ": " + Message;
+
+ return std::string();
+}
+
/// DiagnoseSentinelCalls - This routine checks on method dispatch calls
/// (and other functions in future), which have been declared with sentinel
/// attribute. It warns if call does not have the sentinel argument.
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 8fa2a5d..0a8bd8f 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1388,16 +1388,16 @@
Candidates.NoteCandidates(*this, OCD_ViableCandidates, Args, NumArgs);
return true;
- case OR_Deleted:
+ case OR_Deleted: {
Diag(StartLoc, diag::err_ovl_deleted_call)
<< Best->Function->isDeleted()
<< Name
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Range;
Candidates.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
return true;
}
+ }
assert(false && "Unreachable, bad result from BestViableFunction");
return true;
}
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index 91de095..f607d65 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -1138,10 +1138,14 @@
static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod,
ObjCPropertyDecl *Property) {
// Should we just clone all attributes over?
- if (DeprecatedAttr *A = Property->getAttr<DeprecatedAttr>())
- PropertyMethod->addAttr(A->clone(S.Context));
- if (UnavailableAttr *A = Property->getAttr<UnavailableAttr>())
- PropertyMethod->addAttr(A->clone(S.Context));
+ for (Decl::attr_iterator A = Property->attr_begin(),
+ AEnd = Property->attr_end();
+ A != AEnd; ++A) {
+ if (isa<DeprecatedAttr>(*A) ||
+ isa<UnavailableAttr>(*A) ||
+ isa<AvailabilityAttr>(*A))
+ PropertyMethod->addAttr((*A)->clone(S.Context));
+ }
}
/// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 110f8cd..7a4e68d 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6282,8 +6282,7 @@
// Best is the best viable function.
if (Best->Function &&
- (Best->Function->isDeleted() ||
- Best->Function->getAttr<UnavailableAttr>()))
+ (Best->Function->isDeleted() || Best->Function->isUnavailable()))
return OR_Deleted;
return OR_Success;
@@ -6735,7 +6734,7 @@
FunctionDecl *Fn = Cand->Function;
// Note deleted candidates, but only if they're viable.
- if (Cand->Viable && (Fn->isDeleted() || Fn->hasAttr<UnavailableAttr>())) {
+ if (Cand->Viable && (Fn->isDeleted() || Fn->isUnavailable())) {
std::string FnDesc;
OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, FnDesc);
@@ -7744,8 +7743,7 @@
Diag(Fn->getSourceRange().getBegin(), diag::err_ovl_deleted_call)
<< Best->Function->isDeleted()
<< ULE->getName()
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Fn->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
}
@@ -7928,8 +7926,7 @@
Diag(OpLoc, diag::err_ovl_deleted_oper)
<< Best->Function->isDeleted()
<< UnaryOperator::getOpcodeStr(Opc)
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Input->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
return ExprError();
@@ -8199,8 +8196,7 @@
Diag(OpLoc, diag::err_ovl_deleted_oper)
<< Best->Function->isDeleted()
<< BinaryOperator::getOpcodeStr(Opc)
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2);
return ExprError();
@@ -8349,8 +8345,7 @@
case OR_Deleted:
Diag(LLoc, diag::err_ovl_deleted_oper)
<< Best->Function->isDeleted() << "[]"
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Args[0]->getSourceRange() << Args[1]->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, 2,
"[]", LLoc);
@@ -8468,8 +8463,7 @@
Diag(UnresExpr->getMemberLoc(), diag::err_ovl_deleted_member_call)
<< Best->Function->isDeleted()
<< DeclName
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< MemExprE->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
// FIXME: Leaking incoming expressions!
@@ -8643,8 +8637,7 @@
diag::err_ovl_deleted_object_call)
<< Best->Function->isDeleted()
<< Object->getType()
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Object->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args, NumArgs);
break;
@@ -8852,8 +8845,7 @@
Diag(OpLoc, diag::err_ovl_deleted_oper)
<< Best->Function->isDeleted()
<< "->"
- << Best->Function->getMessageUnavailableAttr(
- !Best->Function->isDeleted())
+ << getDeletedOrUnavailableSuffix(Best->Function)
<< Base->getSourceRange();
CandidateSet.NoteCandidates(*this, OCD_AllCandidates, &Base, 1);
return ExprError();