Index: bindings/python/clang/cindex.py =================================================================== --- bindings/python/clang/cindex.py +++ bindings/python/clang/cindex.py @@ -496,25 +496,29 @@ setattr(TokenKind, name, kind) ### Cursor Kinds ### - -class CursorKind(object): +class BaseEnumeration(object): """ - A CursorKind describes the kind of entity that a cursor points to. - """ + Common base class for named enumerations held in sync with Index.h values. - # The unique kind objects, indexed by id. + Subclasses must define their own _kinds and _name_map members, as: _kinds = [] _name_map = None + These values hold the per-subclass instances and value-to-name mappings, + respectively. + """ + def __init__(self, value): - if value >= len(CursorKind._kinds): - CursorKind._kinds += [None] * (value - len(CursorKind._kinds) + 1) - if CursorKind._kinds[value] is not None: - raise ValueError,'CursorKind already loaded' + if value >= len(self.__class__._kinds): + self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) + if self.__class__._kinds[value] is not None: + raise ValueError,'{0} value {1} already loaded'.format( + str(self.__class__), value) self.value = value - CursorKind._kinds[value] = self - CursorKind._name_map = None + self.__class__._kinds[value] = self + self.__class__._name_map = None + def from_param(self): return self.value @@ -523,17 +527,30 @@ """Get the enumeration name of this cursor kind.""" if self._name_map is None: self._name_map = {} - for key,value in CursorKind.__dict__.items(): - if isinstance(value,CursorKind): + for key, value in self.__class__.__dict__.items(): + if isinstance(value, self.__class__): self._name_map[value] = key return self._name_map[self] - @staticmethod - def from_id(id): - if id >= len(CursorKind._kinds) or CursorKind._kinds[id] is None: - raise ValueError,'Unknown cursor kind %d' % id - return CursorKind._kinds[id] + @classmethod + def from_id(cls, id): + if id >= len(cls._kinds) or cls._kinds[id] is None: + raise ValueError,'Unknown template argument kind %d' % id + return cls._kinds[id] + def __repr__(self): + return '%s.%s' % (self.__class__, self.name,) + + +class CursorKind(BaseEnumeration): + """ + A CursorKind describes the kind of entity that a cursor points to. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None + @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" @@ -578,11 +595,6 @@ def __repr__(self): return 'CursorKind.%s' % (self.name,) -# FIXME: Is there a nicer way to expose this enumeration? We could potentially -# represent the nested structure, or even build a class hierarchy. The main -# things we want for sure are (a) simple external access to kinds, (b) a place -# to hang a description and name, (c) easy to keep in sync with Index.h. - ### # Declaration Kinds @@ -1101,6 +1113,24 @@ # A module import declaration. CursorKind.MODULE_IMPORT_DECL = CursorKind(600) + +### Template Argument Kinds ### +class TemplateArgumentKind(BaseEnumeration): + """ + A TemplateArgumentKind describes the kind of entity that a template argument + represents. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None + +TemplateArgumentKind.NULL = TemplateArgumentKind(0) +TemplateArgumentKind.TYPE = TemplateArgumentKind(1) +TemplateArgumentKind.DECLARATION = TemplateArgumentKind(2) +TemplateArgumentKind.NULLPTR = TemplateArgumentKind(3) +TemplateArgumentKind.INTEGRAL = TemplateArgumentKind(4) + ### Cursors ### class Cursor(Structure): @@ -1378,6 +1408,27 @@ for i in range(0, num_args): yield conf.lib.clang_Cursor_getArgument(self, i) + def get_num_template_arguments(self): + """Returns the number of template args associated with this cursor.""" + return conf.lib.clang_Cursor_getNumTemplateArguments(self) + + def get_template_argument_kind(self, num): + """Returns the TemplateArgumentKind for the indicated template + argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentKind(self, num) + + def get_template_argument_type(self, num): + """Returns the CXType for the indicated template argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentType(self, num) + + def get_template_argument_value(self, num): + """Returns the value of the indicated arg as a signed 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentValue(self, num) + + def get_template_argument_unsigned_value(self, num): + """Returns the value of the indicated arg as an unsigned 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) + def get_children(self): """Return an iterator for accessing the children of this cursor.""" @@ -1461,7 +1512,7 @@ ### C++ access specifiers ### -class AccessSpecifier(object): +class AccessSpecifier(BaseEnumeration): """ Describes the access of a C++ class member """ @@ -1470,34 +1521,9 @@ _kinds = [] _name_map = None - def __init__(self, value): - if value >= len(AccessSpecifier._kinds): - AccessSpecifier._kinds += [None] * (value - len(AccessSpecifier._kinds) + 1) - if AccessSpecifier._kinds[value] is not None: - raise ValueError,'AccessSpecifier already loaded' - self.value = value - AccessSpecifier._kinds[value] = self - AccessSpecifier._name_map = None - def from_param(self): return self.value - @property - def name(self): - """Get the enumeration name of this access specifier.""" - if self._name_map is None: - self._name_map = {} - for key,value in AccessSpecifier.__dict__.items(): - if isinstance(value,AccessSpecifier): - self._name_map[value] = key - return self._name_map[self] - - @staticmethod - def from_id(id): - if id >= len(AccessSpecifier._kinds) or not AccessSpecifier._kinds[id]: - raise ValueError,'Unknown access specifier %d' % id - return AccessSpecifier._kinds[id] - def __repr__(self): return 'AccessSpecifier.%s' % (self.name,) @@ -1509,7 +1535,7 @@ ### Type Kinds ### -class TypeKind(object): +class TypeKind(BaseEnumeration): """ Describes the kind of type. """ @@ -1518,39 +1544,11 @@ _kinds = [] _name_map = None - def __init__(self, value): - if value >= len(TypeKind._kinds): - TypeKind._kinds += [None] * (value - len(TypeKind._kinds) + 1) - if TypeKind._kinds[value] is not None: - raise ValueError,'TypeKind already loaded' - self.value = value - TypeKind._kinds[value] = self - TypeKind._name_map = None - - def from_param(self): - return self.value - @property - def name(self): - """Get the enumeration name of this cursor kind.""" - if self._name_map is None: - self._name_map = {} - for key,value in TypeKind.__dict__.items(): - if isinstance(value,TypeKind): - self._name_map[value] = key - return self._name_map[self] - - @property def spelling(self): """Retrieve the spelling of this TypeKind.""" return conf.lib.clang_getTypeKindSpelling(self.value) - @staticmethod - def from_id(id): - if id >= len(TypeKind._kinds) or TypeKind._kinds[id] is None: - raise ValueError,'Unknown type kind %d' % id - return TypeKind._kinds[id] - def __repr__(self): return 'TypeKind.%s' % (self.name,) @@ -1603,7 +1601,7 @@ TypeKind.DEPENDENTSIZEDARRAY = TypeKind(116) TypeKind.MEMBERPOINTER = TypeKind(117) -class RefQualifierKind(object): +class RefQualifierKind(BaseEnumeration): """Describes a specific ref-qualifier of a type.""" # The unique kind objects, indexed by id. @@ -1610,36 +1608,9 @@ _kinds = [] _name_map = None - def __init__(self, value): - if value >= len(RefQualifierKind._kinds): - num_kinds = value - len(RefQualifierKind._kinds) + 1 - RefQualifierKind._kinds += [None] * num_kinds - if RefQualifierKind._kinds[value] is not None: - raise ValueError, 'RefQualifierKind already loaded' - self.value = value - RefQualifierKind._kinds[value] = self - RefQualifierKind._name_map = None - def from_param(self): return self.value - @property - def name(self): - """Get the enumeration name of this kind.""" - if self._name_map is None: - self._name_map = {} - for key, value in RefQualifierKind.__dict__.items(): - if isinstance(value, RefQualifierKind): - self._name_map[value] = key - return self._name_map[self] - - @staticmethod - def from_id(id): - if (id >= len(RefQualifierKind._kinds) or - RefQualifierKind._kinds[id] is None): - raise ValueError, 'Unknown type kind %d' % id - return RefQualifierKind._kinds[id] - def __repr__(self): return 'RefQualifierKind.%s' % (self.name,) @@ -3314,6 +3285,27 @@ Cursor, Cursor.from_result), + ("clang_Cursor_getNumTemplateArguments", + [Cursor], + c_int), + + ("clang_Cursor_getTemplateArgumentKind", + [Cursor, c_uint], + TemplateArgumentKind.from_id), + + ("clang_Cursor_getTemplateArgumentType", + [Cursor, c_uint], + Type, + Type.from_result), + + ("clang_Cursor_getTemplateArgumentValue", + [Cursor, c_uint], + c_longlong), + + ("clang_Cursor_getTemplateArgumentUnsignedValue", + [Cursor, c_uint], + c_ulonglong), + ("clang_Cursor_isBitField", [Cursor], bool), Index: bindings/python/tests/cindex/test_cursor.py =================================================================== --- bindings/python/tests/cindex/test_cursor.py +++ bindings/python/tests/cindex/test_cursor.py @@ -1,6 +1,8 @@ +import ctypes import gc from clang.cindex import CursorKind +from clang.cindex import TemplateArgumentKind from clang.cindex import TranslationUnit from clang.cindex import TypeKind from .util import get_cursor @@ -244,6 +246,48 @@ assert arguments[0].spelling == "i" assert arguments[1].spelling == "j" +kTemplateArgTest = """\ + template + void foo(); + + template<> + void foo<-7, float, true>(); + """ + +def test_get_num_template_arguments(): + tu = get_tu(kTemplateArgTest, lang='cpp') + foos = get_cursors(tu, 'foo') + + assert foos[1].get_num_template_arguments() == 3 + +def test_get_template_argument_kind(): + tu = get_tu(kTemplateArgTest, lang='cpp') + foos = get_cursors(tu, 'foo') + + assert foos[1].get_template_argument_kind(0) == TemplateArgumentKind.INTEGRAL + assert foos[1].get_template_argument_kind(1) == TemplateArgumentKind.TYPE + assert foos[1].get_template_argument_kind(2) == TemplateArgumentKind.INTEGRAL + +def test_get_template_argument_type(): + tu = get_tu(kTemplateArgTest, lang='cpp') + foos = get_cursors(tu, 'foo') + + assert foos[1].get_template_argument_type(1).kind == TypeKind.FLOAT + +def test_get_template_argument_value(): + tu = get_tu(kTemplateArgTest, lang='cpp') + foos = get_cursors(tu, 'foo') + + assert foos[1].get_template_argument_value(0) == -7 + assert foos[1].get_template_argument_value(2) == True + +def test_get_template_argument_unsigned_value(): + tu = get_tu(kTemplateArgTest, lang='cpp') + foos = get_cursors(tu, 'foo') + + assert foos[1].get_template_argument_unsigned_value(0) == 2 ** 32 - 7 + assert foos[1].get_template_argument_unsigned_value(2) == True + def test_referenced(): tu = get_tu('void foo(); void bar() { foo(); }') foo = get_cursor(tu, 'foo') Index: include/clang-c/Index.h =================================================================== --- include/clang-c/Index.h +++ include/clang-c/Index.h @@ -2939,6 +2939,124 @@ CINDEX_LINKAGE CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i); /** + * \brief Describes the kind of a template argument. + * + * See the definition of llvm::clang::TemplateArgument::ArgKind for full + * element descriptions. + */ +enum CXTemplateArgumentKind { + CXTemplateArgumentKind_Null, + CXTemplateArgumentKind_Type, + CXTemplateArgumentKind_Declaration, + CXTemplateArgumentKind_NullPtr, + CXTemplateArgumentKind_Integral, + CXTemplateArgumentKind_Template, + CXTemplateArgumentKind_TemplateExpansion, + CXTemplateArgumentKind_Expression, + CXTemplateArgumentKind_Pack, + /* Indicates an error case, preventing the kind from being deduced. */ + CXTemplateArgumentKind_Invalid, +}; + +/** + *\brief Returns the number of template args of a function decl representing a + * template specialization. + * + * If the argument cursor cannot be converted into a template function + * declaration, -1 is returned. + * + * For example, for the following declaration and specialization: + * template + * void foo() { ... } + * + * template <> + * void foo(); + * + * The value 3 would be returned from this call. + */ +CINDEX_LINKAGE int clang_Cursor_getNumTemplateArguments(CXCursor C); + +/** + * \brief Retrieve the kind of the I'th template argument of the CXCursor C. + * + * If the argument CXCursor does not represent a FunctionDecl, an invalid + * template argument kind is returned. + * + * For example, for the following declaration and specialization: + * template + * void foo() { ... } + * + * template <> + * void foo(); + * + * For I = 0, 1, and 2, Type, Integral, and Integral will be returned, + * respectively. + */ +CINDEX_LINKAGE enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind( + CXCursor C, unsigned I); + +/** + * \brief Retrieve a CXType representing the type of a TemplateArgument of a + * function decl representing a template specialization. + * + * If the argument CXCursor does not represent a FunctionDecl whose I'th + * template argument has a kind of ArgKind::Integral, an invalid type is\ + * returned (in debug mode, this will assert). + * + * For example, for the following declaration and specialization: + * template + * void foo() { ... } + * + * template <> + * void foo(); + * + * If called with I = 0, "float", will be returned. + * Invalid types will be returned for I == 1 or 2. + */ +CINDEX_LINKAGE CXType clang_Cursor_getTemplateArgumentType(CXCursor C, + unsigned I); + +/** + * \brief Retrieve the value of an Integral TemplateArgument (of a function + * decl representing a template specialization) as a signed long long. + * + * It is undefined to call this function on a CXCursor that does not represent a + * FunctionDecl or whose I'th template argument is not an integral value. + * + * For example, for the following declaration and specialization: + * template + * void foo() { ... } + * + * template <> + * void foo(); + * + * If called with I = 1 or 2, -7 or true will be returned, respectively. + * For I == 0, this function's behavior is undefined. + */ +CINDEX_LINKAGE long long clang_Cursor_getTemplateArgumentValue(CXCursor C, + unsigned I); + +/** + * \brief Retrieve the value of an Integral TemplateArgument (of a function + * decl representing a template specialization) as an unsigned long long. + * + * It is undefined to call this function on a CXCursor that does not represent a + * FunctionDecl or whose I'th template argument is not an integral value. + * + * For example, for the following declaration and specialization: + * template + * void foo() { ... } + * + * template <> + * void foo(); + * + * If called with I = 1 or 2, 2147483649 or true will be returned, respectively. + * For I == 0, this function's behavior is undefined. + */ +CINDEX_LINKAGE unsigned long long clang_Cursor_getTemplateArgumentUnsignedValue( + CXCursor C, unsigned I); + +/** * \brief Determine whether two CXTypes represent the same type. * * \returns non-zero if the CXTypes represent the same type and Index: test/Index/preamble_macro_template.cpp =================================================================== --- test/Index/preamble_macro_template.cpp +++ test/Index/preamble_macro_template.cpp @@ -4,7 +4,7 @@ // RUN: c-index-test -write-pch %t.pch -fno-delayed-template-parsing -x c++-header %S/Inputs/preamble_macro_template.h // RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 local -fno-delayed-template-parsing -I %S/Inputs -include %t %s 2>&1 | tee %t.check.txt | FileCheck %s -// CHECK: preamble_macro_template.h:4:6: FunctionDecl=foo:4:6 (Definition) [Specialization of foo:4:6] Extent=[4:1 - 6:2] +// CHECK: preamble_macro_template.h:4:6: FunctionDecl=foo:4:6 (Definition) [Specialization of foo:4:6] [Template arg 0: type: int] Extent=[4:1 - 6:2] // CHECK: preamble_macro_template.h:4:13: ParmDecl=p:4:13 (Definition) Extent=[4:10 - 4:14] // CHECK: preamble_macro_template.h:4:16: CompoundStmt= Extent=[4:16 - 6:2] // CHECK: preamble_macro_template.h:5:3: CStyleCastExpr= Extent=[5:3 - 5:27] Index: tools/c-index-test/c-index-test.c =================================================================== --- tools/c-index-test/c-index-test.c +++ tools/c-index-test/c-index-test.c @@ -796,15 +796,33 @@ printf(" [access=%s isVirtual=%s]", accessStr, isVirtual ? "true" : "false"); } - + SpecializationOf = clang_getSpecializedCursorTemplate(Cursor); if (!clang_equalCursors(SpecializationOf, clang_getNullCursor())) { CXSourceLocation Loc = clang_getCursorLocation(SpecializationOf); CXString Name = clang_getCursorSpelling(SpecializationOf); clang_getSpellingLocation(Loc, 0, &line, &column, 0); - printf(" [Specialization of %s:%d:%d]", + printf(" [Specialization of %s:%d:%d]", clang_getCString(Name), line, column); clang_disposeString(Name); + + if (Cursor.kind == CXCursor_FunctionDecl) { + // Collect the template parameter kinds from the base template. + unsigned NumTemplateArgs = clang_Cursor_getNumTemplateArguments(Cursor); + for (unsigned I = 0; I < NumTemplateArgs; I++) { + enum CXTemplateArgumentKind TAK = + clang_Cursor_getTemplateArgumentKind(Cursor, I); + if (TAK == CXTemplateArgumentKind_Type) { + CXType T = clang_Cursor_getTemplateArgumentType(Cursor, I); + CXString S = clang_getTypeSpelling(T); + printf(" [Template arg %d: type: %s]", I, clang_getCString(S)); + clang_disposeString(S); + } else if (TAK == CXTemplateArgumentKind_Integral) { + printf(" [Template arg %d: intval: %lld]", + I, clang_Cursor_getTemplateArgumentValue(Cursor, I)); + } + } + } } clang_getOverriddenCursors(Cursor, &overridden, &num_overridden); Index: tools/libclang/CXCursor.cpp =================================================================== --- tools/libclang/CXCursor.cpp +++ tools/libclang/CXCursor.cpp @@ -1071,6 +1071,119 @@ return clang_getNullCursor(); } +int clang_Cursor_getNumTemplateArguments(CXCursor C) { + if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { + return -1; + } + + const FunctionDecl *FD = llvm::dyn_cast_or_null( + getCursorDecl(C)); + if (!FD) { + return -1; + } + + const FunctionTemplateSpecializationInfo* SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return -1; + } + + return SpecInfo->TemplateArguments->size(); +} + +static bool clang_Cursor_getTemplateArgument( + CXCursor C, unsigned I, TemplateArgument *TA) { + if (clang_getCursorKind(C) != CXCursor_FunctionDecl) { + return false; + } + + const FunctionDecl *FD = llvm::dyn_cast_or_null( + getCursorDecl(C)); + if (!FD) { + return false; + } + + const FunctionTemplateSpecializationInfo* SpecInfo = + FD->getTemplateSpecializationInfo(); + if (!SpecInfo) { + return false; + } + + if (I >= SpecInfo->TemplateArguments->size()) { + return false; + } + + *TA = SpecInfo->TemplateArguments->get(I); + return true; +} + +enum CXTemplateArgumentKind clang_Cursor_getTemplateArgumentKind(CXCursor C, + unsigned I) { + TemplateArgument TA; + if (!clang_Cursor_getTemplateArgument(C, I, &TA)) { + return CXTemplateArgumentKind_Invalid; + } + + switch (TA.getKind()) { + case TemplateArgument::Null: return CXTemplateArgumentKind_Null; + case TemplateArgument::Type: return CXTemplateArgumentKind_Type; + case TemplateArgument::Declaration: + return CXTemplateArgumentKind_Declaration; + case TemplateArgument::NullPtr: return CXTemplateArgumentKind_NullPtr; + case TemplateArgument::Integral: return CXTemplateArgumentKind_Integral; + case TemplateArgument::Template: return CXTemplateArgumentKind_Template; + case TemplateArgument::TemplateExpansion: + return CXTemplateArgumentKind_TemplateExpansion; + case TemplateArgument::Expression: return CXTemplateArgumentKind_Expression; + case TemplateArgument::Pack: return CXTemplateArgumentKind_Pack; + } + + return CXTemplateArgumentKind_Invalid; +} + +CXType clang_Cursor_getTemplateArgumentType(CXCursor C, unsigned I) { + TemplateArgument TA; + if (!clang_Cursor_getTemplateArgument(C, I, &TA)) { + return cxtype::MakeCXType(QualType(), getCursorTU(C)); + } + + if (TA.getKind() != TemplateArgument::Type) { + return cxtype::MakeCXType(QualType(), getCursorTU(C)); + } + return cxtype::MakeCXType(TA.getAsType(), getCursorTU(C)); +} + +long long clang_Cursor_getTemplateArgumentValue(CXCursor C, unsigned I) { + TemplateArgument TA; + if (!clang_Cursor_getTemplateArgument(C, I, &TA)) { + assert(0 && "Unable to retrieve TemplateArgument"); + return 0; + } + + if (TA.getKind() != TemplateArgument::Integral) { + assert(0 && "Passed template argument is not Integral"); + return 0; + } + + return TA.getAsIntegral().getSExtValue(); +} + +unsigned long long clang_Cursor_getTemplateArgumentUnsignedValue(CXCursor C, + unsigned I) { + TemplateArgument TA; + if (!clang_Cursor_getTemplateArgument(C, I, &TA)) { + assert(0 && "Unable to retrieve TemplateArgument"); + return 0; + } + + if (TA.getKind() != TemplateArgument::Integral) { + assert(0 && "Passed template argument is not Integral"); + return 0; + } + + return TA.getAsIntegral().getZExtValue(); +} + } // end: extern "C" //===----------------------------------------------------------------------===// Index: tools/libclang/libclang.exports =================================================================== --- tools/libclang/libclang.exports +++ tools/libclang/libclang.exports @@ -7,6 +7,11 @@ clang_CXXMethod_isStatic clang_CXXMethod_isVirtual clang_Cursor_getArgument +clang_Cursor_getNumTemplateArguments +clang_Cursor_getTemplateArgumentKind +clang_Cursor_getTemplateArgumentType +clang_Cursor_getTemplateArgumentValue +clang_Cursor_getTemplateArgumentUnsignedValue clang_Cursor_getBriefCommentText clang_Cursor_getCommentRange clang_Cursor_getMangling