From d6225d304eba88d09364c42339502712bdeb91fb Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 8 May 2012 00:14:45 +0000 Subject: [PATCH] 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 . llvm-svn: 156347 --- clang/include/clang-c/Index.h | 114 ++++++++++++++++++++- clang/test/Index/availability.c | 6 ++ clang/test/Index/c-index-api-loadTU-test.m | 2 +- clang/tools/c-index-test/c-index-test.c | 64 +++++++++++- clang/tools/libclang/CIndex.cpp | 84 +++++++++++++++ clang/tools/libclang/libclang.exports | 2 + 6 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 clang/test/Index/availability.c diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h index 98aa963176cb..7f700220dd66 100644 --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -132,6 +132,29 @@ enum CXAvailabilityKind { */ 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 @@ -2165,7 +2188,8 @@ enum CXLinkageKind { 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. * @@ -2174,6 +2198,94 @@ CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor); CINDEX_LINKAGE enum CXAvailabilityKind 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. */ diff --git a/clang/test/Index/availability.c b/clang/test/Index/availability.c new file mode 100644 index 000000000000..ddbeb3a42d9b --- /dev/null +++ b/clang/test/Index/availability.c @@ -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) diff --git a/clang/test/Index/c-index-api-loadTU-test.m b/clang/test/Index/c-index-api-loadTU-test.m index 251d73b3f819..7520330bce2e 100644 --- a/clang/test/Index/c-index-api-loadTU-test.m +++ b/clang/test/Index/c-index-api-loadTU-test.m @@ -83,7 +83,7 @@ struct X0 {}; // CHECK: :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: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: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] diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index eb2a4063e760..497c9ee6af53 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -180,6 +180,20 @@ static void PrintRange(CXSourceRange R, const char *str) { 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) { CXTranslationUnit TU = clang_Cursor_getTranslationUnit(Cursor); if (clang_isInvalid(Cursor.kind)) { @@ -197,7 +211,14 @@ static void PrintCursor(CXCursor Cursor) { unsigned RefNameRangeNr; CXSourceRange CursorExtent; CXSourceRange RefNameRange; - + int AlwaysUnavailable; + int AlwaysDeprecated; + CXString UnavailableMessage; + CXString DeprecatedMessage; + CXPlatformAvailability PlatformAvailability[2]; + int NumPlatformAvailability; + int I; + ks = clang_getCursorKindSpelling(Cursor.kind); string = want_display_name? clang_getCursorDisplayName(Cursor) : clang_getCursorSpelling(Cursor); @@ -249,6 +270,47 @@ static void PrintCursor(CXCursor Cursor) { 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)) printf(" (static)"); if (clang_CXXMethod_isVirtual(Cursor)) diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index b2de22f23a14..a508b772e531 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -5489,6 +5489,90 @@ enum CXAvailabilityKind clang_getCursorAvailability(CXCursor cursor) { 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 Minor = In.getMinor()) + Out.Minor = *Minor; + else + return Out; + + if (llvm::Optional 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(*A)) { + if (always_deprecated) + *always_deprecated = 1; + if (deprecated_message) + *deprecated_message = cxstring::createCXString(Deprecated->getMessage()); + continue; + } + + if (UnavailableAttr *Unavailable = dyn_cast(*A)) { + if (always_unavailable) + *always_unavailable = 1; + if (unavailable_message) { + *unavailable_message + = cxstring::createCXString(Unavailable->getMessage()); + } + continue; + } + + if (AvailabilityAttr *Avail = dyn_cast(*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) { if (clang_isDeclaration(cursor.kind)) return getDeclLanguage(cxcursor::getCursorDecl(cursor)); diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports index d3b64dbd0f50..594a7969e397 100644 --- a/clang/tools/libclang/libclang.exports +++ b/clang/tools/libclang/libclang.exports @@ -43,6 +43,7 @@ clang_disposeDiagnostic clang_disposeDiagnosticSet clang_disposeIndex clang_disposeOverriddenCursors +clang_disposeCXPlatformAvailability clang_disposeString clang_disposeTokens clang_disposeTranslationUnit @@ -85,6 +86,7 @@ clang_getCursorLanguage clang_getCursorLexicalParent clang_getCursorLinkage clang_getCursorLocation +clang_getCursorPlatformAvailability clang_getCursorReferenceNameRange clang_getCursorReferenced clang_getCursorResultType