Introduce a new libclang API to determine the platform availability of

a given entity, so that we can tell when the entity was
introduced/deprecated/obsoleted on each platform for which we have an
annotation. Addresses <rdar://problem/11365715>.

llvm-svn: 156347
This commit is contained in:
Douglas Gregor 2012-05-08 00:14:45 +00:00
parent ececf9f0ae
commit d6225d304e
6 changed files with 269 additions and 3 deletions

View File

@ -133,6 +133,29 @@ enum CXAvailabilityKind {
CXAvailability_NotAccessible CXAvailability_NotAccessible
}; };
/**
* \brief Describes a version number of the form major.minor.subminor.
*/
typedef struct CXVersion {
/**
* \brief The major version number, e.g., the '10' in '10.7.3'. A negative
* value indicates that there is no version number at all.
*/
int Major;
/**
* \brief The minor version number, e.g., the '7' in '10.7.3'. This value
* will be negative if no minor version number was provided, e.g., for
* version '10'.
*/
int Minor;
/**
* \brief The subminor version number, e.g., the '3' in '10.7.3'. This value
* will be negative if no minor or subminor version number was provided,
* e.g., in version '10' or '10.7'.
*/
int Subminor;
} CXVersion;
/** /**
* \defgroup CINDEX_STRING String manipulation routines * \defgroup CINDEX_STRING String manipulation routines
* *
@ -2165,7 +2188,8 @@ enum CXLinkageKind {
CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor); CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
/** /**
* \brief Determine the availability of the entity that this cursor refers to. * \brief Determine the availability of the entity that this cursor refers to,
* taking the current target platform into account.
* *
* \param cursor The cursor to query. * \param cursor The cursor to query.
* *
@ -2174,6 +2198,94 @@ CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
CINDEX_LINKAGE enum CXAvailabilityKind CINDEX_LINKAGE enum CXAvailabilityKind
clang_getCursorAvailability(CXCursor cursor); clang_getCursorAvailability(CXCursor cursor);
/**
* Describes the availability of a given entity on a particular platform, e.g.,
* a particular class might only be available on Mac OS 10.7 or newer.
*/
typedef struct CXPlatformAvailability {
/**
* \brief A string that describes the platform for which this structure
* provides availability information.
*
* Possible values are "ios" or "macosx".
*/
CXString Platform;
/**
* \brief The version number in which this entity was introduced.
*/
CXVersion Introduced;
/**
* \brief The version number in which this entity was deprecated (but is
* still available).
*/
CXVersion Deprecated;
/**
* \brief The version number in which this entity was obsoleted, and therefore
* is no longer available.
*/
CXVersion Obsoleted;
/**
* \brief Whether the entity is unconditionally unavailable on this platform.
*/
int Unavailable;
/**
* \brief An optional message to provide to a user of this API, e.g., to
* suggest replacement APIs.
*/
CXString Message;
} CXPlatformAvailability;
/**
* \brief Determine the availability of the entity that this cursor refers to
* on any platforms for which availability information is known.
*
* \param cursor The cursor to query.
*
* \param always_deprecated If non-NULL, will be set to indicate whether the
* entity is deprecated on all platforms.
*
* \param deprecated_message If non-NULL, will be set to the message text
* provided along with the unconditional deprecation of this entity. The client
* is responsible for deallocating this string.
*
* \param always_unavailabile If non-NULL, will be set to indicate whether the
* entity is unavailable on all platforms.
*
* \param unavailable_message If non-NULL, will be set to the message text
* provided along with the unconditional unavailability of this entity. The
* client is responsible for deallocating this string.
*
* \param availability If non-NULL, an array of CXPlatformAvailability instances
* that will be populated with platform availability information, up to either
* the number of platforms for which availability information is available (as
* returned by this function) or \c availability_size, whichever is smaller.
*
* \param availability_size The number of elements available in the
* \c availability array.
*
* \returns The number of platforms (N) for which availability information is
* available (which is unrelated to \c availability_size).
*
* Note that the client is responsible for calling
* \c clang_disposeCXPlatformAvailability to free each of the
* platform-availability structures returned. There are
* \c min(N, availability_size) such structures.
*/
CINDEX_LINKAGE int
clang_getCursorPlatformAvailability(CXCursor cursor,
int *always_deprecated,
CXString *deprecated_message,
int *always_unavailable,
CXString *unavailable_message,
CXPlatformAvailability *availability,
int availability_size);
/**
* \brief Free the memory associated with a \c CXPlatformAvailability structure.
*/
CINDEX_LINKAGE void
clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability);
/** /**
* \brief Describe the "language" of the entity referred to by a cursor. * \brief Describe the "language" of the entity referred to by a cursor.
*/ */

View File

@ -0,0 +1,6 @@
// Run lines below; this test is line- and column-sensitive.
void foo(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.5,obsoleted=10.7), availability(ios,introduced=3.2,deprecated=4.1)));
// RUN: c-index-test -test-load-source all %s | FileCheck %s
// CHECK: (ios, introduced=3.2, deprecated=4.1) (macosx, introduced=10.4, deprecated=10.5, obsoleted=10.7)

View File

@ -83,7 +83,7 @@ struct X0 {};
// CHECK: <invalid loc>:0:0: attribute(ibaction)= // CHECK: <invalid loc>:0:0: attribute(ibaction)=
// CHECK: c-index-api-loadTU-test.m:8:50: ParmDecl=msg:8:50 (Definition) Extent=[8:47 - 8:53] // CHECK: c-index-api-loadTU-test.m:8:50: ParmDecl=msg:8:50 (Definition) Extent=[8:47 - 8:53]
// CHECK: c-index-api-loadTU-test.m:8:47: TypeRef=id:0:0 Extent=[8:47 - 8:49] // CHECK: c-index-api-loadTU-test.m:8:47: TypeRef=id:0:0 Extent=[8:47 - 8:49]
// CHECK: c-index-api-loadTU-test.m:9:3: ObjCInstanceMethodDecl=foo:9:3 (deprecated) Extent=[9:1 - 9:35] // CHECK: c-index-api-loadTU-test.m:9:3: ObjCInstanceMethodDecl=foo:9:3 (deprecated) (always deprecated: "") Extent=[9:1 - 9:35]
// CHECK: c-index-api-loadTU-test.m:10:3: ObjCClassMethodDecl=fooC:10:3 Extent=[10:1 - 10:8] // CHECK: c-index-api-loadTU-test.m:10:3: ObjCClassMethodDecl=fooC:10:3 Extent=[10:1 - 10:8]
// CHECK: c-index-api-loadTU-test.m:14:12: ObjCInterfaceDecl=Bar:14:12 Extent=[14:1 - 18:5] // CHECK: c-index-api-loadTU-test.m:14:12: ObjCInterfaceDecl=Bar:14:12 Extent=[14:1 - 18:5]
// CHECK: c-index-api-loadTU-test.m:14:18: ObjCSuperClassRef=Foo:4:12 Extent=[14:18 - 14:21] // CHECK: c-index-api-loadTU-test.m:14:18: ObjCSuperClassRef=Foo:4:12 Extent=[14:18 - 14:21]

View File

@ -180,6 +180,20 @@ static void PrintRange(CXSourceRange R, const char *str) {
int want_display_name = 0; int want_display_name = 0;
static void printVersion(const char *Prefix, CXVersion Version) {
if (Version.Major < 0)
return;
printf("%s%d", Prefix, Version.Major);
if (Version.Minor < 0)
return;
printf(".%d", Version.Minor);
if (Version.Subminor < 0)
return;
printf(".%d", Version.Subminor);
}
static void PrintCursor(CXCursor Cursor) { static void PrintCursor(CXCursor Cursor) {
CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor);
if (clang_isInvalid(Cursor.kind)) { if (clang_isInvalid(Cursor.kind)) {
@ -197,6 +211,13 @@ static void PrintCursor(CXCursor Cursor) {
unsigned RefNameRangeNr; unsigned RefNameRangeNr;
CXSourceRange CursorExtent; CXSourceRange CursorExtent;
CXSourceRange RefNameRange; CXSourceRange RefNameRange;
int AlwaysUnavailable;
int AlwaysDeprecated;
CXString UnavailableMessage;
CXString DeprecatedMessage;
CXPlatformAvailability PlatformAvailability[2];
int NumPlatformAvailability;
int I;
ks = clang_getCursorKindSpelling(Cursor.kind); ks = clang_getCursorKindSpelling(Cursor.kind);
string = want_display_name? clang_getCursorDisplayName(Cursor) string = want_display_name? clang_getCursorDisplayName(Cursor)
@ -249,6 +270,47 @@ static void PrintCursor(CXCursor Cursor) {
break; break;
} }
NumPlatformAvailability
= clang_getCursorPlatformAvailability(Cursor,
&AlwaysDeprecated,
&DeprecatedMessage,
&AlwaysUnavailable,
&UnavailableMessage,
PlatformAvailability, 2);
if (AlwaysUnavailable) {
printf(" (always unavailable: \"%s\")",
clang_getCString(UnavailableMessage));
} else if (AlwaysDeprecated) {
printf(" (always deprecated: \"%s\")",
clang_getCString(DeprecatedMessage));
} else {
for (I = 0; I != NumPlatformAvailability; ++I) {
if (I >= 2)
break;
printf(" (%s", clang_getCString(PlatformAvailability[I].Platform));
if (PlatformAvailability[I].Unavailable)
printf(", unavailable");
else {
printVersion(", introduced=", PlatformAvailability[I].Introduced);
printVersion(", deprecated=", PlatformAvailability[I].Deprecated);
printVersion(", obsoleted=", PlatformAvailability[I].Obsoleted);
}
if (clang_getCString(PlatformAvailability[I].Message)[0])
printf(", message=\"%s\"",
clang_getCString(PlatformAvailability[I].Message));
printf(")");
}
}
for (I = 0; I != NumPlatformAvailability; ++I) {
if (I >= 2)
break;
clang_disposeCXPlatformAvailability(PlatformAvailability + I);
}
clang_disposeString(DeprecatedMessage);
clang_disposeString(UnavailableMessage);
if (clang_CXXMethod_isStatic(Cursor)) if (clang_CXXMethod_isStatic(Cursor))
printf(" (static)"); printf(" (static)");
if (clang_CXXMethod_isVirtual(Cursor)) if (clang_CXXMethod_isVirtual(Cursor))

View File

@ -5489,6 +5489,90 @@ enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) {
return CXAvailability_Available; return CXAvailability_Available;
} }
static CXVersion convertVersion(VersionTuple In) {
CXVersion Out = { -1, -1, -1 };
if (In.empty())
return Out;
Out.Major = In.getMajor();
if (llvm::Optional<unsigned> Minor = In.getMinor())
Out.Minor = *Minor;
else
return Out;
if (llvm::Optional<unsigned> Subminor = In.getSubminor())
Out.Subminor = *Subminor;
return Out;
}
int clang_getCursorPlatformAvailability(CXCursor cursor,
int *always_deprecated,
CXString *deprecated_message,
int *always_unavailable,
CXString *unavailable_message,
CXPlatformAvailability *availability,
int availability_size) {
if (always_deprecated)
*always_deprecated = 0;
if (deprecated_message)
*deprecated_message = cxstring::createCXString("", /*DupString=*/false);
if (always_unavailable)
*always_unavailable = 0;
if (unavailable_message)
*unavailable_message = cxstring::createCXString("", /*DupString=*/false);
if (!clang_isDeclaration(cursor.kind))
return 0;
Decl *D = cxcursor::getCursorDecl(cursor);
if (!D)
return 0;
int N = 0;
for (Decl::attr_iterator A = D->attr_begin(), AEnd = D->attr_end(); A != AEnd;
++A) {
if (DeprecatedAttr *Deprecated = dyn_cast<DeprecatedAttr>(*A)) {
if (always_deprecated)
*always_deprecated = 1;
if (deprecated_message)
*deprecated_message = cxstring::createCXString(Deprecated->getMessage());
continue;
}
if (UnavailableAttr *Unavailable = dyn_cast<UnavailableAttr>(*A)) {
if (always_unavailable)
*always_unavailable = 1;
if (unavailable_message) {
*unavailable_message
= cxstring::createCXString(Unavailable->getMessage());
}
continue;
}
if (AvailabilityAttr *Avail = dyn_cast<AvailabilityAttr>(*A)) {
if (N < availability_size) {
availability[N].Platform
= cxstring::createCXString(Avail->getPlatform()->getName());
availability[N].Introduced = convertVersion(Avail->getIntroduced());
availability[N].Deprecated = convertVersion(Avail->getDeprecated());
availability[N].Obsoleted = convertVersion(Avail->getObsoleted());
availability[N].Unavailable = Avail->getUnavailable();
availability[N].Message = cxstring::createCXString(Avail->getMessage());
}
++N;
}
}
return N;
}
void clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability) {
clang_disposeString(availability->Platform);
clang_disposeString(availability->Message);
}
CXLanguageKind clang_getCursorLanguage(CXCursor cursor) { CXLanguageKind clang_getCursorLanguage(CXCursor cursor) {
if (clang_isDeclaration(cursor.kind)) if (clang_isDeclaration(cursor.kind))
return getDeclLanguage(cxcursor::getCursorDecl(cursor)); return getDeclLanguage(cxcursor::getCursorDecl(cursor));

View File

@ -43,6 +43,7 @@ clang_disposeDiagnostic
clang_disposeDiagnosticSet clang_disposeDiagnosticSet
clang_disposeIndex clang_disposeIndex
clang_disposeOverriddenCursors clang_disposeOverriddenCursors
clang_disposeCXPlatformAvailability
clang_disposeString clang_disposeString
clang_disposeTokens clang_disposeTokens
clang_disposeTranslationUnit clang_disposeTranslationUnit
@ -85,6 +86,7 @@ clang_getCursorLanguage
clang_getCursorLexicalParent clang_getCursorLexicalParent
clang_getCursorLinkage clang_getCursorLinkage
clang_getCursorLocation clang_getCursorLocation
clang_getCursorPlatformAvailability
clang_getCursorReferenceNameRange clang_getCursorReferenceNameRange
clang_getCursorReferenced clang_getCursorReferenced
clang_getCursorResultType clang_getCursorResultType