diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -255,7 +255,8 @@ libclang -------- -- ... +- Introduced the new function `clang_getUnqualifiedType`, which mimics + the behavior of `QualType::getUnqualifiedType` for `CXType`. Static Analyzer --------------- diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h --- a/clang/include/clang-c/Index.h +++ b/clang/include/clang-c/Index.h @@ -3760,6 +3760,43 @@ */ CINDEX_LINKAGE CXType clang_getPointeeType(CXType T); +/** + * Retrieve the unqualified variant of the given type, removing as + * little sugar as possible. + * + * For example, given the following series of typedefs: + * + * \code + * typedef int Integer; + * typedef const Integer CInteger; + * typedef CInteger DifferenceType; + * \endcode + * + * Executing \c clang_getUnqualifiedType() on a \c CXType that + * represents \c DifferenceType, will desugar to a type representing + * \c Integer, that has no qualifiers. + * + * And, executing \c clang_getUnqualifiedType() on the type of the + * first argument of the following function declaration: + * + * \code + * void foo(const int); + * \endcode + * + * Will return a type representing \c int, removing the \c const + * qualifier. + * + * Sugar over array types is not desugared. + * + * A type can be checked for qualifiers with \c + * clang_isConstQualifiedType(), \c clang_isVolatileQualifiedType() + * and \c clang_isRestrictQualifiedType(). + * + * A type that resulted from a call to \c clang_getUnqualifiedType + * will return \c false for all of the above calls. + */ +CINDEX_LINKAGE CXType clang_getUnqualifiedType(CXType CT); + /** * Return the cursor for the declaration of the given type. */ diff --git a/clang/tools/libclang/CXType.cpp b/clang/tools/libclang/CXType.cpp --- a/clang/tools/libclang/CXType.cpp +++ b/clang/tools/libclang/CXType.cpp @@ -484,6 +484,10 @@ return MakeCXType(T, GetTU(CT)); } +CXType clang_getUnqualifiedType(CXType CT) { + return MakeCXType(GetQualType(CT).getUnqualifiedType(), GetTU(CT)); +} + CXCursor clang_getTypeDeclaration(CXType CT) { if (CT.kind == CXType_Invalid) return cxcursor::MakeCXCursorInvalid(CXCursor_NoDeclFound); diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -405,6 +405,11 @@ local: *; }; +LLVM_16 { + global: + clang_getUnqualifiedType; +}; + # Example of how to add a new symbol version entry. If you do add a new symbol # version, please update the example to depend on the version you added. # LLVM_X { diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp --- a/clang/unittests/libclang/LibclangTest.cpp +++ b/clang/unittests/libclang/LibclangTest.cpp @@ -843,6 +843,54 @@ }, nullptr); } + +TEST_F(LibclangParseTest, clang_getUnqualifiedTypeRemovesQualifiers) { + std::string Header = "header.h"; + WriteFile(Header, "void foo1(const int);\n" + "void foo2(volatile int);\n" + "void foo3(const volatile int);\n" + "void foo4(int* const);\n" + "void foo5(int* volatile);\n" + "void foo6(int* restrict);\n" + "void foo7(int* const volatile);\n" + "void foo8(int* volatile restrict);\n" + "void foo9(int* const restrict);\n" + "void foo10(int* const volatile restrict);\n"); + + auto is_qualified = [](CXType type) -> bool { + return clang_isConstQualifiedType(type) || + clang_isVolatileQualifiedType(type) || + clang_isRestrictQualifiedType(type); + }; + + auto from_CXString = [](CXString cx_string) -> std::string { + std::string string{clang_getCString(cx_string)}; + + clang_disposeString(cx_string); + + return string; + }; + + ClangTU = clang_parseTranslationUnit(Index, Header.c_str(), nullptr, 0, + nullptr, 0, TUFlags); + + Traverse([&is_qualified, &from_CXString](CXCursor cursor, CXCursor) { + if (clang_getCursorKind(cursor) == CXCursor_FunctionDecl) { + CXType arg_type = clang_getArgType(clang_getCursorType(cursor), 0); + EXPECT_TRUE(is_qualified(arg_type)) + << "Input data '" << from_CXString(clang_getCursorSpelling(cursor)) + << "' first argument does not have a qualified type."; + + CXType unqualified_arg_type = clang_getUnqualifiedType(arg_type); + EXPECT_FALSE(is_qualified(unqualified_arg_type)) + << "The type '" << from_CXString(clang_getTypeSpelling(arg_type)) + << "' was not unqualified after a call to clang_getUnqualifiedType."; + } + + return CXChildVisit_Continue; + }); +} + class LibclangRewriteTest : public LibclangParseTest { public: CXRewriter Rew = nullptr;