On Wed, Nov 6, 2013 at 7:39 AM, Alp Toker <[email protected]> wrote:
>
> On 06/11/2013 14:55, Anthony Scopatz wrote:
>> Hello All,
>>
>> Could we please hear some response on this?  Thanks!
>
> Hello Anthony, Geoffrey,
>
> Thanks for the contribution.
>
> You'll need to rebase this work against current SVN, add at least a
> couple of standard lit tests and probably split it up into two or three
> bite-size chunks for review.
>
> Once that's done, it can also help to CC in others who've recently
> worked on the C API in order to get buy-in.
>
> As for the Geoffrey's build problem, it's best to ask that as a separate
> question, including details about the platform and configuration if
> you're still struggling.
>
> Good luck with the patch :-)
>
> Alp.
>
>
>
>>
>> Be Well
>> Anthony
>>
>>
>> On Sun, Nov 3, 2013 at 5:46 PM, Geoffrey Irving <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>>     Here are four patches improving libclang in various ways for use in
>>     xdress (http://xdress.org).  In order, they add
>>
>>     1. Python support for clang_formatDiagnostic.
>>     2. Access to public / private / protected specifiers.
>>     3. Template argument access (as new cursor types).
>>     4. A clang_Cursor_getDefaultArgument function.
>>
>>     They've all been tested inside xdress, but the patches themselves do
>>     not include tests.  Since questions about this stuff to cfe-dev were
>>     ignored, I want to make sure these patches will be looked at before I
>>     do potentially unnecessary work.
>>
>>     Caveat: llvm trunk does not build for me due to these errors:
>>
>>         llvm-build: error: invalid native target: 'x86_64' (not in
>>     project)
>>
>>     Therefore, these patches were written, built, and tested again
>>     release_33 plus several cherry-picked libclang commits.  It's possible
>>     they don't compile against the trunk.
>>
>>     Any ideas what would have caused this build breakage since release_33?
>>
>>     Thanks,
>>     Geoffrey

Attached are updated versions of the four patches with both python and
c unit tests.  If anyone prefers git-svn, they are also here:

    https://github.com/girving/clang/commits/xdress
    
https://github.com/girving/clang/commits/a064ac67efe8cf4cf82219e9d8988d72406d3925

Argyrios: I cc'ed you on Alp's suggestion since you touched the
libclang code recently.  Please let me know if you're up for looking
over the code, or know someone else you might be.

One change from the previous code: the new
clang_Cursor_getDefaultArgument() supports only function parameters,
since my naive method for extracting template argument defaults
(calling the getDefaultArgument() function) didn't work.  I don't
actually need template argument defaults for now, but here's the patch
that *did not* work if anyone is curious:

    
https://github.com/girving/clang/commit/72879373f65e6eb08a94dcb4d74a5ff13532db2f

Thanks!
Geoffrey
From f82b0249c68cd235ad46fb5769fc1b9d9486a584 Mon Sep 17 00:00:00 2001
From: Geoffrey Irving <[email protected]>
Date: Thu, 31 Oct 2013 15:07:18 -0700
Subject: [PATCH 1/4] [libclang/python] Expose clang_formatDiagnostic to python

---
 bindings/python/clang/cindex.py                  | 15 +++++++++++++++
 bindings/python/tests/cindex/test_diagnostics.py |  9 +++++++++
 2 files changed, 24 insertions(+)

diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index c103c70..8339e2b 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -382,6 +382,12 @@ class Diagnostic(object):
 
         return conf.lib.clang_getCString(disable)
 
+    def format(self, options=None):
+        """Format the diagnostic according to the given options."""
+        if options is None:
+            options = conf.lib.clang_defaultDiagnosticDisplayOptions()
+        return conf.lib.clang_formatDiagnostic(self, options)
+
     def __repr__(self):
         return "<Diagnostic severity %r, location %r, spelling %r>" % (
             self.severity, self.location, self.spelling)
@@ -2949,6 +2955,15 @@ functionList = [
    _CXString,
    _CXString.from_result),
 
+  ("clang_formatDiagnostic",
+   [Diagnostic, c_uint],
+   _CXString,
+   _CXString.from_result),
+
+  ("clang_defaultDiagnosticDisplayOptions",
+   [],
+   c_uint),
+
   ("clang_getElementType",
    [Type],
    Type,
diff --git a/bindings/python/tests/cindex/test_diagnostics.py b/bindings/python/tests/cindex/test_diagnostics.py
index 48ab617..6796a70 100644
--- a/bindings/python/tests/cindex/test_diagnostics.py
+++ b/bindings/python/tests/cindex/test_diagnostics.py
@@ -80,3 +80,12 @@ def test_diagnostic_option():
 
     assert d.option == '-Wunused-parameter'
     assert d.disable_option == '-Wno-unused-parameter'
+
+def test_diagnostic_format():
+    """Ensure that diagnostics format."""
+    tu = get_tu('int f(int i) { return unusual; }')
+    assert len(tu.diagnostics) == 1
+    d = tu.diagnostics[0]
+
+    assert (d.format() ==
+      "t.c:1:23: error: use of undeclared identifier 'unusual'")
-- 
1.8.4.1.559.gdb9bdfb

From 149529226af3b7b312a975673b3f65fe16c0e713 Mon Sep 17 00:00:00 2001
From: Geoffrey Irving <[email protected]>
Date: Wed, 6 Nov 2013 13:45:56 -0800
Subject: [PATCH 2/4] [libclang/python] Expose access control levels

---
 bindings/python/clang/cindex.py             | 52 +++++++++++++++++++++++++++++
 bindings/python/tests/cindex/test_cursor.py | 13 ++++++++
 2 files changed, 65 insertions(+)

diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index 8339e2b..c2743c5 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -501,6 +501,46 @@ class TokenKind(object):
         TokenKind._value_map[value] = kind
         setattr(TokenKind, name, kind)
 
+class AccessKind(object):
+    """Describes a specific type of an access specifier."""
+
+    # The unique kind objects, indexed by id
+    _kinds = []
+    _name_map = None
+
+    def __init__(self, value):
+        if value >= len(AccessKind._kinds):
+            AccessKind._kinds += [None] * (value - len(AccessKind._kinds) + 1)
+        if AccessKind._kinds[value] is not None:
+            raise ValueError,'AccessKind already loaded'
+        self.value = value
+        AccessKind._kinds[value] = self
+        AccessKind._name_map = None
+
+    @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 AccessKind.__dict__.items():
+                if isinstance(value,AccessKind):
+                    self._name_map[value] = key
+        return self._name_map[self]
+
+    @staticmethod
+    def from_id(id):
+        if id >= len(AccessKind._kinds) or AccessKind._kinds[id] is None:
+            raise ValueError,'Unknown access kind %d' % id
+        return AccessKind._kinds[id]
+
+    def __repr__(self):
+        return 'AccessKind.%s' % (self.name,)
+
+AccessKind.INVALID   = AccessKind(0)
+AccessKind.PUBLIC    = AccessKind(1)
+AccessKind.PROTECTED = AccessKind(2)
+AccessKind.PRIVATE   = AccessKind(3)
+
 ### Cursor Kinds ###
 
 class CursorKind(object):
@@ -1304,6 +1344,18 @@ class Cursor(Structure):
         return self._objc_type_encoding
 
     @property
+    def access(self):
+        """Return the AccessKind of the cursor.
+
+        If the cursor is a declaration, its access access control level within its
+        parent scope is returned.  Otherwise, if the cursor refers to a base specifier
+        or access specifier, the specifier itself is returned."""
+        if not hasattr(self, '_access'):
+            self._access = AccessKind.from_id(conf.lib.clang_getCXXAccessSpecifier(self))
+
+        return self._access
+
+    @property
     def hash(self):
         """Returns a hash of the cursor as an int."""
         if not hasattr(self, '_hash'):
diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py
index a27525c..dbfafef 100644
--- a/bindings/python/tests/cindex/test_cursor.py
+++ b/bindings/python/tests/cindex/test_cursor.py
@@ -1,5 +1,6 @@
 import gc
 
+from clang.cindex import AccessKind
 from clang.cindex import CursorKind
 from clang.cindex import TranslationUnit
 from clang.cindex import TypeKind
@@ -259,3 +260,15 @@ def test_referenced():
         if c.kind == CursorKind.CALL_EXPR:
             assert c.referenced.spelling == foo.spelling
             break
+
+def test_access():
+    tu = get_tu('struct s { int a; protected: int b; public: int c; private: int d; };', 'cpp')
+    s = get_cursor(tu, 's')
+    assert s.access == AccessKind.INVALID
+    expect = [AccessKind.PUBLIC, AccessKind.PROTECTED, AccessKind.PROTECTED,
+              AccessKind.PUBLIC, AccessKind.PUBLIC, AccessKind.PRIVATE, AccessKind.PRIVATE]
+    kids = list(s.get_children())
+    assert len(kids) == len(expect)
+    for c,a in zip(kids, expect):
+        assert c.access == a
+    assert repr(expect[0]) == 'AccessKind.PUBLIC'
-- 
1.8.4.1.559.gdb9bdfb

From 3e4d6435f88ed9857e12556f04188522cf917795 Mon Sep 17 00:00:00 2001
From: Geoffrey Irving <[email protected]>
Date: Thu, 7 Nov 2013 09:23:56 -0800
Subject: [PATCH 3/4] [libclang] Add template argument support

clang_Cursor_getNumTemplateArgs and clang_Cursor_getTemplateArg
now allow extracting template arguments from a specialization
declaration.  Each template argument kind is a new CXCursorKind.

In python, Cursor has new has_template_args() and get_template_args()
methods to check whether template arguments make sense and grab an
iterator to them if so.
---
 bindings/python/clang/cindex.py                  | 46 ++++++++++++++--
 bindings/python/tests/cindex/test_cursor.py      | 19 +++++++
 bindings/python/tests/cindex/test_cursor_kind.py |  4 +-
 include/clang-c/Index.h                          | 36 +++++++++++-
 test/Index/c-index-template-args.cpp             |  6 ++
 tools/c-index-test/c-index-test.c                | 66 ++++++++++++++++++++++
 tools/libclang/CIndex.cpp                        | 38 ++++++++++++-
 tools/libclang/CXCursor.cpp                      | 70 ++++++++++++++++++++++++
 tools/libclang/CXCursor.h                        |  5 ++
 tools/libclang/CXType.cpp                        |  5 ++
 tools/libclang/libclang.exports                  |  3 +
 11 files changed, 291 insertions(+), 7 deletions(-)
 create mode 100644 test/Index/c-index-template-args.cpp

diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index c2743c5..f6c3ec1 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -617,6 +617,10 @@ class CursorKind(object):
         """Test if this is a preprocessing kind."""
         return conf.lib.clang_isPreprocessing(self)
 
+    def is_template_arg(self):
+        """Test if this is a template argument kind."""
+        return conf.lib.clang_isTemplateArg(self)
+
     def is_unexposed(self):
         """Test if this is an unexposed kind."""
         return conf.lib.clang_isUnexposed(self)
@@ -1139,6 +1143,18 @@ CursorKind.INCLUSION_DIRECTIVE = CursorKind(503)
 # A module import declaration.
 CursorKind.MODULE_IMPORT_DECL = CursorKind(600)
 
+###
+# Template arguments
+CursorKind.NULL_TEMPLATE_ARG = CursorKind(700)
+CursorKind.TYPE_TEMPLATE_ARG = CursorKind(701)
+CursorKind.DECLARATION_TEMPLATE_ARG = CursorKind(702)
+CursorKind.NULL_PTR_TEMPLATE_ARG = CursorKind(703)
+CursorKind.INTEGRAL_TEMPLATE_ARG = CursorKind(704)
+CursorKind.TEMPLATE_TEMPLATE_ARG = CursorKind(705)
+CursorKind.TEMPLATE_EXPANSION_TEMPLATE_ARG = CursorKind(706)
+CursorKind.EXPRESSION_TEMPLATE_ARG = CursorKind(707)
+CursorKind.PACK_TEMPLATE_ARG = CursorKind(708)
+
 ### Cursors ###
 
 class Cursor(Structure):
@@ -1205,10 +1221,6 @@ class Cursor(Structure):
     @property
     def spelling(self):
         """Return the spelling of the entity pointed at by the cursor."""
-        if not self.kind.is_declaration():
-            # FIXME: clang_getCursorSpelling should be fixed to not assert on
-            # this, for consistency with clang_getCursorUSR.
-            return None
         if not hasattr(self, '_spelling'):
             self._spelling = conf.lib.clang_getCursorSpelling(self)
 
@@ -1413,6 +1425,19 @@ class Cursor(Structure):
         for i in range(0, num_args):
             yield conf.lib.clang_Cursor_getArgument(self, i)
 
+    def has_template_args(self):
+        """Is this cursor a declaration with template arguments?
+        Note: returns True for templatized classes with zero arguments."""
+        return conf.lib.clang_Cursor_getNumTemplateArgs(self) >= 0
+
+    def get_template_args(self):
+        """Return an iterator for accessing the template arguments of this cursor."""
+        num = conf.lib.clang_Cursor_getNumTemplateArgs(self)
+        if num < 0:
+            raise ValueError("Cursor has no notion of template arguments")
+        for i in range(num):
+            yield conf.lib.clang_Cursor_getTemplateArg(self, i)
+
     def get_children(self):
         """Return an iterator for accessing the children of this cursor."""
 
@@ -3242,6 +3267,10 @@ functionList = [
    [CursorKind],
    bool),
 
+  ("clang_isTemplateArg",
+   [CursorKind],
+   bool),
+
   ("clang_isUnexposed",
    [CursorKind],
    bool),
@@ -3282,6 +3311,15 @@ functionList = [
    Cursor,
    Cursor.from_result),
 
+  ("clang_Cursor_getNumTemplateArgs",
+   [Cursor],
+   c_int),
+
+  ("clang_Cursor_getTemplateArg",
+   [Cursor, c_uint],
+   Cursor,
+   Cursor.from_result),
+
   ("clang_Cursor_isBitField",
    [Cursor],
    bool),
diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py
index dbfafef..69a6be7 100644
--- a/bindings/python/tests/cindex/test_cursor.py
+++ b/bindings/python/tests/cindex/test_cursor.py
@@ -272,3 +272,22 @@ def test_access():
     for c,a in zip(kids, expect):
         assert c.access == a
     assert repr(expect[0]) == 'AccessKind.PUBLIC'
+
+def test_template_args():
+    tu = get_tu('''
+struct none {};
+template<class A,int b> struct base {};
+typedef base<char,7> derived;
+    ''', 'cpp')
+    for s in 'none','base':
+      assert not get_cursor(tu, s).has_template_args()
+    derived = get_cursor(tu, 'derived')
+    derived = derived.type.get_canonical().get_declaration()
+    assert derived.has_template_args()
+    args = list(derived.get_template_args())
+    assert len(args) == 2
+    assert args[0].kind == CursorKind.TYPE_TEMPLATE_ARG
+    assert args[0].spelling == 'char'
+    assert args[0].type.kind == TypeKind.CHAR_S
+    assert args[1].kind == CursorKind.INTEGRAL_TEMPLATE_ARG
+    assert args[1].spelling == '7'
diff --git a/bindings/python/tests/cindex/test_cursor_kind.py b/bindings/python/tests/cindex/test_cursor_kind.py
index 8cabc51..75fd766 100644
--- a/bindings/python/tests/cindex/test_cursor_kind.py
+++ b/bindings/python/tests/cindex/test_cursor_kind.py
@@ -13,6 +13,7 @@ def test_get_all_kinds():
     assert CursorKind.OBJ_SELF_EXPR in kinds
     assert CursorKind.MS_ASM_STMT in kinds
     assert CursorKind.MODULE_IMPORT_DECL in kinds
+    assert CursorKind.TYPE_TEMPLATE_ARG in kinds
 
 def test_kind_groups():
     """Check that every kind classifies to exactly one group."""
@@ -34,7 +35,8 @@ def test_kind_groups():
 
     for k in CursorKind.get_all_kinds():
         group = [n for n in ('is_declaration', 'is_reference', 'is_expression',
-                             'is_statement', 'is_invalid', 'is_attribute')
+                             'is_statement', 'is_invalid', 'is_attribute',
+                             'is_template_arg')
                  if getattr(k, n)()]
 
         if k in (   CursorKind.TRANSLATION_UNIT,
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index cd130e3..dd28407 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -2113,7 +2113,20 @@ enum CXCursorKind {
    */
   CXCursor_ModuleImportDecl              = 600,
   CXCursor_FirstExtraDecl                = CXCursor_ModuleImportDecl,
-  CXCursor_LastExtraDecl                 = CXCursor_ModuleImportDecl
+  CXCursor_LastExtraDecl                 = CXCursor_ModuleImportDecl,
+
+  /* Template arguments */
+  CXCursor_NullTemplateArg               = 700,
+  CXCursor_TypeTemplateArg               = 701,
+  CXCursor_DeclarationTemplateArg        = 702,
+  CXCursor_NullPtrTemplateArg            = 703,
+  CXCursor_IntegralTemplateArg           = 704,
+  CXCursor_TemplateTemplateArg           = 705,
+  CXCursor_TemplateExpansionTemplateArg  = 706,
+  CXCursor_ExpressionTemplateArg         = 707,
+  CXCursor_PackTemplateArg               = 708,
+  CXCursor_FirstTemplateArg              = CXCursor_NullTemplateArg,
+  CXCursor_LastTemplateArg               = CXCursor_PackTemplateArg
 };
 
 /**
@@ -2234,6 +2247,12 @@ CINDEX_LINKAGE unsigned clang_isTranslationUnit(enum CXCursorKind);
  * element, such as a preprocessor directive or macro instantiation.
  */
 CINDEX_LINKAGE unsigned clang_isPreprocessing(enum CXCursorKind);
+
+/***
+ * \brief Determine whether the given cursor represents a template
+ * argument.
+ */
+CINDEX_LINKAGE unsigned clang_isTemplateArg(enum CXCursorKind);
   
 /***
  * \brief Determine whether the given cursor represents a currently
@@ -2788,6 +2807,21 @@ CINDEX_LINKAGE int clang_Cursor_getNumArguments(CXCursor C);
 CINDEX_LINKAGE CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i);
 
 /**
+ * \brief Count the template arguments of a declaration
+ *
+ * For other cursors, -1 is returned.
+ */
+CINDEX_LINKAGE int clang_Cursor_getNumTemplateArgs(CXCursor C);
+
+/**
+ * \brief Retrieve one template argument of a declaration
+ *
+ * If the cursor has the wrong type, or the index is out of range, and invalid
+ * cursor is returned.
+ */
+CINDEX_LINKAGE CXCursor clang_Cursor_getTemplateArg(CXCursor C, unsigned i);
+
+/**
  * \brief Determine whether two CXTypes represent the same type.
  *
  * \returns non-zero if the CXTypes represent the same type and
diff --git a/test/Index/c-index-template-args.cpp b/test/Index/c-index-template-args.cpp
new file mode 100644
index 0000000..bda3ecf
--- /dev/null
+++ b/test/Index/c-index-template-args.cpp
@@ -0,0 +1,6 @@
+// RUN: c-index-test -test-print-template-args all %s | FileCheck %s
+// CHECK: TypedefDecl=derived:6:22 (Definition) [template args 2] [i=0] [spelling=char] [argkind=type] [type=char] [typekind=Char_S] [i=1] [spelling=7] [argkind=integral]
+
+struct none {};
+template<class A,int b> struct base {};
+typedef base<char,7> derived;
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index 90a6528..e80e79d 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -1285,6 +1285,69 @@ static enum CXChildVisitResult PrintBitWidth(CXCursor cursor, CXCursor p,
 }
 
 /******************************************************************************/
+/* Template argument testing.                                                 */
+/******************************************************************************/
+
+static void PrintTemplateArgsHelper(CXCursor cursor) {
+  const int n = clang_Cursor_getNumTemplateArgs(cursor);
+  if (n >= 0)
+    printf(" [template args %d]", n);
+  for (int i = 0; i < n; ++i) {
+    CXCursor arg = clang_Cursor_getTemplateArg(cursor, i);
+    enum CXCursorKind kind = clang_getCursorKind(arg);
+    assert(clang_isTemplateArg(kind) && "bad clang_Cursor_getTemplateArg result");
+    CXString spelling = clang_getCursorSpelling(arg);
+    printf(" [i=%d] [spelling=%s]", i, clang_getCString(spelling));
+    clang_disposeString(spelling);
+
+    switch (kind) {
+    case CXCursor_NullTemplateArg:
+      printf(" [argkind=null]");
+      break;
+    case CXCursor_TypeTemplateArg:
+      PrintTypeAndTypeKind(clang_getCursorType(arg), " [argkind=type] [type=%s] [typekind=%s]");
+      break;
+    case CXCursor_DeclarationTemplateArg:
+      printf(" [argkind=decl]");
+      break;
+    case CXCursor_NullPtrTemplateArg:
+      printf(" [argkind=nullptr]");
+      break;
+    case CXCursor_IntegralTemplateArg:
+      printf(" [argkind=integral]");
+      break;
+    case CXCursor_TemplateTemplateArg:
+      printf(" [argkind=template]");
+      break;
+    case CXCursor_TemplateExpansionTemplateArg:
+      printf(" [argkind=templateexpansion]");
+      break;
+    case CXCursor_ExpressionTemplateArg:
+      printf(" [argkind=expression]");
+      break;
+    case CXCursor_PackTemplateArg:
+      printf(" [argkind=pack]");
+      break;
+    default:
+      printf(" [argkind=unknown-%d]", (int)kind);
+    }
+  }
+}
+
+static enum CXChildVisitResult PrintTemplateArgs(CXCursor cursor, CXCursor p,
+                                                 CXClientData d) {
+  if (!clang_isInvalid(clang_getCursorKind(cursor))) {
+    PrintCursor(cursor, NULL);
+    PrintTemplateArgsHelper(cursor);
+    const CXType type = clang_getCanonicalType(clang_getCursorType(cursor));
+    const CXCursor decl = clang_getTypeDeclaration(type);
+    PrintTemplateArgsHelper(decl);
+    printf("\n");
+  }
+  return CXChildVisit_Recurse;
+}
+
+/******************************************************************************/
 /* Loading ASTs/source.                                                       */
 /******************************************************************************/
 
@@ -3862,6 +3925,9 @@ int cindextest_main(int argc, const char **argv) {
   else if (argc > 2 && strcmp(argv[1], "-test-print-bitwidth") == 0)
     return perform_test_load_source(argc - 2, argv + 2, "all",
                                     PrintBitWidth, 0);
+  else if (argc > 2 && strcmp(argv[1], "-test-print-template-args") == 0)
+    return perform_test_load_source(argc - 2, argv + 2, "all",
+                                    PrintTemplateArgs, 0);
   else if (argc > 1 && strcmp(argv[1], "-print-usr") == 0) {
     if (argc > 2)
       return print_usrs(argv + 2, argv + argc);
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 3e74128..8a1f3a1 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -537,6 +537,12 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) {
           A->getInterfaceLoc()->getTypeLoc().getLocStart(), TU));
   }
 
+  // For now template arguments have no children.
+  // This should possibly change to account for general
+  // expression, parameter packs, etc.
+  if (clang_isTemplateArg(Cursor.kind))
+    return false;
+
   // If pointing inside a macro definition, check if the token is an identifier
   // that was ever defined as a macro. In such a case, create a "pseudo" macro
   // expansion cursor for that token.
@@ -3352,6 +3358,14 @@ CXString clang_getCursorSpelling(CXCursor C) {
     return cxstring::createRef("packed");
   }
 
+  if (clang_isTemplateArg(C.kind)) {
+    SmallString<64> Str;
+    llvm::raw_svector_ostream OS(Str);
+    const PrintingPolicy Policy = getCursorContext(C).getPrintingPolicy();
+    getCursorTemplateArg(C)->print(Policy,OS);
+    return cxstring::createDup(OS.str());
+  }
+
   return cxstring::createEmpty();
 }
 
@@ -3821,6 +3835,24 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
     return cxstring::createRef("ModuleImport");
   case CXCursor_OMPParallelDirective:
       return cxstring::createRef("OMPParallelDirective");
+  case CXCursor_NullTemplateArg:
+    return cxstring::createRef("NullTemplateArg");
+  case CXCursor_TypeTemplateArg:
+    return cxstring::createRef("TypeTemplateArg");
+  case CXCursor_DeclarationTemplateArg:
+    return cxstring::createRef("DeclarationTemplateArg");
+  case CXCursor_NullPtrTemplateArg:
+    return cxstring::createRef("NullPtrTemplateArg");
+  case CXCursor_IntegralTemplateArg:
+    return cxstring::createRef("IntegralTemplateArg");
+  case CXCursor_TemplateTemplateArg:
+    return cxstring::createRef("TemplateTemplateArg");
+  case CXCursor_TemplateExpansionTemplateArg:
+    return cxstring::createRef("TemplateExpansionTemplateArg");
+  case CXCursor_ExpressionTemplateArg:
+    return cxstring::createRef("ExpressionTemplateArg");
+  case CXCursor_PackTemplateArg:
+    return cxstring::createRef("PackTemplateArg");
   }
 
   llvm_unreachable("Unhandled CXCursorKind");
@@ -4056,7 +4088,11 @@ unsigned clang_isTranslationUnit(enum CXCursorKind K) {
 unsigned clang_isPreprocessing(enum CXCursorKind K) {
   return K >= CXCursor_FirstPreprocessing && K <= CXCursor_LastPreprocessing;
 }
-  
+
+unsigned clang_isTemplateArg(enum CXCursorKind K) {
+  return K >= CXCursor_FirstTemplateArg && K <= CXCursor_LastTemplateArg;
+}
+
 unsigned clang_isUnexposed(enum CXCursorKind K) {
   switch (K) {
     case CXCursor_UnexposedDecl:
diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp
index c75c061..f9fec95 100644
--- a/tools/libclang/CXCursor.cpp
+++ b/tools/libclang/CXCursor.cpp
@@ -518,6 +518,34 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
   return C;
 }
 
+static CXCursorKind GetCursorKind(const TemplateArgument *A) {
+  assert(A && "Invalid arguments!");
+  switch (A->getKind()) {
+    #define TACase(Kind) \
+      case TemplateArgument::Kind: \
+        return CXCursor_##Kind##TemplateArg;
+    TACase(Null)
+    TACase(Type)
+    TACase(Declaration)
+    TACase(NullPtr)
+    TACase(Integral)
+    TACase(Template)
+    TACase(TemplateExpansion)
+    TACase(Expression)
+    TACase(Pack)
+  }
+
+  llvm_unreachable("Invalid TemplateArgument::Kind");
+}
+
+CXCursor cxcursor::MakeCXCursor(const TemplateArgument* A,
+                                const Decl* Parent,
+                                CXTranslationUnit TU) {
+  assert(A && Parent && TU && "Invalid arguments!");
+  CXCursor C = { GetCursorKind(A), 0, { Parent, A, TU } };
+  return C;
+}
+
 CXCursor cxcursor::MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, 
                                                SourceLocation Loc, 
                                                CXTranslationUnit TU) {
@@ -818,6 +846,10 @@ const Decl *cxcursor::getCursorParentDecl(CXCursor Cursor) {
   return static_cast<const Decl *>(Cursor.data[0]);
 }
 
+const TemplateArgument *cxcursor::getCursorTemplateArg(CXCursor Cursor) {
+  return static_cast<const TemplateArgument *>(Cursor.data[1]);
+}
+
 ASTContext &cxcursor::getCursorContext(CXCursor Cursor) {
   return getCursorASTUnit(Cursor)->getASTContext();
 }
@@ -1005,6 +1037,44 @@ CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i) {
   return clang_getNullCursor();
 }
 
+// Return the number of template arguments and a pointer to the first.
+// On failure, -1 is returned.  Logic borrowed from TypePrinter::printTag
+static int getTemplateArgs(const Decl *D, const TemplateArgument **args) {
+  const ClassTemplateSpecializationDecl *Spec
+    = dyn_cast<ClassTemplateSpecializationDecl>(D);
+  if (!Spec)
+    return -1;
+  if (TypeSourceInfo *TAW = Spec->getTypeAsWritten()) {
+    const TemplateSpecializationType *TST =
+      cast<TemplateSpecializationType>(TAW->getType());
+    *args = TST->getArgs();
+    return TST->getNumArgs();
+  } else {
+    const TemplateArgumentList &TA = Spec->getTemplateArgs();
+    *args = TA.data();
+    return TA.size();
+  }
+}
+
+int clang_Cursor_getNumTemplateArgs(CXCursor C) {
+  if (!clang_isDeclaration(C.kind))
+    return -1;
+  const Decl *D = getCursorDecl(C);
+  const TemplateArgument *args;
+  return getTemplateArgs(D,&args);
+}
+
+CXCursor clang_Cursor_getTemplateArg(CXCursor C, unsigned i) {
+  if (!clang_isDeclaration(C.kind))
+    return clang_getNullCursor();
+  const Decl *D = getCursorDecl(C);
+  const TemplateArgument *args;
+  const int count = getTemplateArgs(D,&args);
+  if (count < 0 || i >= unsigned(count))
+    return clang_getNullCursor();
+  return MakeCXCursor(args+i, D, getCursorTU(C));
+}
+
 } // end: extern "C"
 
 //===----------------------------------------------------------------------===//
diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h
index 957d519..dc3a9c7 100644
--- a/tools/libclang/CXCursor.h
+++ b/tools/libclang/CXCursor.h
@@ -43,6 +43,7 @@ class TemplateName;
 class TypeDecl;
 class VarDecl;
 class IdentifierInfo;
+class TemplateArgument;
   
 namespace cxcursor {
 
@@ -56,6 +57,9 @@ CXCursor MakeCXCursor(const clang::Decl *D, CXTranslationUnit TU,
 CXCursor MakeCXCursor(const clang::Stmt *S, const clang::Decl *Parent,
                       CXTranslationUnit TU,
                       SourceRange RegionOfInterest = SourceRange());
+CXCursor MakeCXCursor(const clang::TemplateArgument* A,
+                      const clang::Decl* Parent,
+                      CXTranslationUnit TU);
 CXCursor MakeCXCursorInvalid(CXCursorKind K, CXTranslationUnit TU = 0);
 
 /// \brief Create an Objective-C superclass reference at the given location.
@@ -246,6 +250,7 @@ const Expr *getCursorExpr(CXCursor Cursor);
 const Stmt *getCursorStmt(CXCursor Cursor);
 const Attr *getCursorAttr(CXCursor Cursor);
 const Decl *getCursorParentDecl(CXCursor Cursor);
+const TemplateArgument *getCursorTemplateArg(CXCursor Cursor);
 
 ASTContext &getCursorContext(CXCursor Cursor);
 ASTUnit *getCursorASTUnit(CXCursor Cursor);
diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp
index 1e2cb18..12dc145 100644
--- a/tools/libclang/CXType.cpp
+++ b/tools/libclang/CXType.cpp
@@ -214,6 +214,11 @@ CXType clang_getCursorType(CXCursor C) {
     return MakeCXType(QualType(), TU);
   }
 
+  if (C.kind == CXCursor_TypeTemplateArg) {
+    const TemplateArgument *TA = getCursorTemplateArg(C);
+    return MakeCXType(TA->getAsType(), TU);
+  }
+
   return MakeCXType(QualType(), TU);
 }
 
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index 9bf26c9..2cba0fb 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -11,11 +11,13 @@ clang_Cursor_getCommentRange
 clang_Cursor_getParsedComment
 clang_Cursor_getRawCommentText
 clang_Cursor_getNumArguments
+clang_Cursor_getNumTemplateArgs
 clang_Cursor_getObjCDeclQualifiers
 clang_Cursor_getObjCPropertyAttributes
 clang_Cursor_getObjCSelectorIndex
 clang_Cursor_getSpellingNameRange
 clang_Cursor_getTranslationUnit
+clang_Cursor_getTemplateArg
 clang_Cursor_getReceiverType
 clang_Cursor_isBitField
 clang_Cursor_isDynamicCall
@@ -251,6 +253,7 @@ clang_isPreprocessing
 clang_isReference
 clang_isRestrictQualifiedType
 clang_isStatement
+clang_isTemplateArg
 clang_isTranslationUnit
 clang_isUnexposed
 clang_isVirtualBase
-- 
1.8.4.1.559.gdb9bdfb

From a064ac67efe8cf4cf82219e9d8988d72406d3925 Mon Sep 17 00:00:00 2001
From: Geoffrey Irving <[email protected]>
Date: Thu, 7 Nov 2013 10:36:43 -0800
Subject: [PATCH 4/4] [libclang] Add clang_Cursor_getDefaultArgument

This information is often reachable via children, but parameter
declarations can have expression children even if they have no
default arguments (Base<int,3> var).  A dedicated accessor is much
cleaner.

This version supports function parameter defaults only, not default
template arguments, since the naive method of accessing default
template arguments didn't work: getDefaultArgument() returned null
for both NonTypeTemplateParmDecl and TemplateTemplateParmDecl where
I expected it to be usefl.
---
 bindings/python/clang/cindex.py             | 10 ++++++++++
 bindings/python/tests/cindex/test_cursor.py |  7 +++++++
 include/clang-c/Index.h                     |  8 ++++++++
 test/Index/c-index-default-args.cpp         |  6 ++++++
 test/Index/recursive-cxx-member-calls.cpp   |  2 +-
 tools/c-index-test/c-index-test.c           |  4 ++++
 tools/libclang/CIndex.cpp                   | 11 +++++++++++
 tools/libclang/libclang.exports             |  1 +
 8 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 test/Index/c-index-default-args.cpp

diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py
index f6c3ec1..a3fa8ed 100644
--- a/bindings/python/clang/cindex.py
+++ b/bindings/python/clang/cindex.py
@@ -1419,6 +1419,11 @@ class Cursor(Structure):
         """Returns the raw comment text associated with that Cursor"""
         return conf.lib.clang_Cursor_getRawCommentText(self)
 
+    @property
+    def default_argument(self):
+        """The default argument of a parameter declaration, or None"""
+        return conf.lib.clang_Cursor_getDefaultArgument(self)
+
     def get_arguments(self):
         """Return an iterator for accessing the arguments of this cursor."""
         num_args = conf.lib.clang_Cursor_getNumArguments(self)
@@ -3334,6 +3339,11 @@ functionList = [
    _CXString,
    _CXString.from_result),
 
+  ("clang_Cursor_getDefaultArgument",
+   [Cursor],
+   Cursor,
+   Cursor.from_result),
+
   ("clang_Type_getAlignOf",
    [Type],
    c_longlong),
diff --git a/bindings/python/tests/cindex/test_cursor.py b/bindings/python/tests/cindex/test_cursor.py
index 69a6be7..d091ed5 100644
--- a/bindings/python/tests/cindex/test_cursor.py
+++ b/bindings/python/tests/cindex/test_cursor.py
@@ -291,3 +291,10 @@ typedef base<char,7> derived;
     assert args[0].type.kind == TypeKind.CHAR_S
     assert args[1].kind == CursorKind.INTEGRAL_TEMPLATE_ARG
     assert args[1].spelling == '7'
+
+def test_default_argument():
+  tu = get_tu('void foo(int a=7);', 'cpp')
+  a = get_cursor(tu, 'a').default_argument
+  assert a.kind == CursorKind.INTEGER_LITERAL
+  assert a.extent.start.column == 16
+  assert a.extent.end.column   == 17
diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h
index dd28407..31efcc4 100644
--- a/include/clang-c/Index.h
+++ b/include/clang-c/Index.h
@@ -3526,6 +3526,14 @@ CINDEX_LINKAGE CXString clang_Cursor_getBriefCommentText(CXCursor C);
 CINDEX_LINKAGE CXComment clang_Cursor_getParsedComment(CXCursor C);
 
 /**
+ * \brief Retrieve the default argument of a parameter declaration.
+ *
+ * If no default exists, an invalid cursor is returned.  For now, only
+ * function parameter defaults are handled.
+ */
+CINDEX_LINKAGE CXCursor clang_Cursor_getDefaultArgument(CXCursor C);
+
+/**
  * @}
  */
 
diff --git a/test/Index/c-index-default-args.cpp b/test/Index/c-index-default-args.cpp
new file mode 100644
index 0000000..3b854a8
--- /dev/null
+++ b/test/Index/c-index-default-args.cpp
@@ -0,0 +1,6 @@
+// RUN: c-index-test -test-load-source all %s | FileCheck %s
+// CHECK: c-index-default-args.cpp:6:6: FunctionDecl=foo:6:6 Extent=[6:1 - 6:20]
+// CHECK: c-index-default-args.cpp:6:14: ParmDecl=a:6:14 (Definition) DefaultExtent=[6:18 - 6:19] Extent=[6:10 - 6:19]
+// CHECK: c-index-default-args.cpp:6:18: IntegerLiteral= Extent=[6:18 - 6:19]
+
+void foo(int a = 7);
diff --git a/test/Index/recursive-cxx-member-calls.cpp b/test/Index/recursive-cxx-member-calls.cpp
index 2cd8d13..8fe47e6 100644
--- a/test/Index/recursive-cxx-member-calls.cpp
+++ b/test/Index/recursive-cxx-member-calls.cpp
@@ -1744,7 +1744,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo * Name) {
 // CHECK: 60:3: TypeRef=class llvm::StringRef:38:7 Extent=[60:3 - 60:12]
 // CHECK: 60:27: ParmDecl=Start:60:27 (Definition) Extent=[60:20 - 60:32]
 // CHECK: 60:20: TypeRef=size_t:2:25 Extent=[60:20 - 60:26]
-// CHECK: 60:41: ParmDecl=N:60:41 (Definition) Extent=[60:34 - 60:49]
+// CHECK: 60:41: ParmDecl=N:60:41 (Definition) DefaultExtent=[60:45 - 60:49] Extent=[60:34 - 60:49]
 // CHECK: 60:34: TypeRef=size_t:2:25 Extent=[60:34 - 60:40]
 // CHECK: 60:45: DeclRefExpr=npos:41:23 Extent=[60:45 - 60:49]
 // CHECK: 60:57: CompoundStmt= Extent=[60:57 - 62:4]
diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c
index e80e79d..dc5b94c 100644
--- a/tools/c-index-test/c-index-test.c
+++ b/tools/c-index-test/c-index-test.c
@@ -794,6 +794,10 @@ static void PrintCursor(CXCursor Cursor,
 
     PrintCursorComments(Cursor, ValidationData);
 
+    const CXCursor Default = clang_Cursor_getDefaultArgument(Cursor);
+    if (!clang_isInvalid(Default.kind))
+      PrintRange(clang_getCursorExtent(Default), "DefaultExtent");
+
     {
       unsigned PropAttrs = clang_Cursor_getObjCPropertyAttributes(Cursor, 0);
       if (PropAttrs != CXObjCPropertyAttr_noattr) {
diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp
index 8a1f3a1..71d438b 100644
--- a/tools/libclang/CIndex.cpp
+++ b/tools/libclang/CIndex.cpp
@@ -6220,6 +6220,17 @@ CXComment clang_Cursor_getParsedComment(CXCursor C) {
   return cxcomment::createCXComment(FC, getCursorTU(C));
 }
 
+CXCursor clang_Cursor_getDefaultArgument(CXCursor C) {
+  if (clang_isDeclaration(C.kind)) {
+    const Decl *D = getCursorDecl(C);
+    if (const ParmVarDecl *PD = dyn_cast<ParmVarDecl>(D)) {
+      if (const Expr *E = PD->getDefaultArg())
+        return MakeCXCursor(E, D, getCursorTU(C));
+    }
+  }
+  return clang_getNullCursor();
+}
+
 CXModule clang_Cursor_getModule(CXCursor C) {
   if (C.kind == CXCursor_ModuleImportDecl) {
     if (const ImportDecl *ImportD =
diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports
index 2cba0fb..4d5a713 100644
--- a/tools/libclang/libclang.exports
+++ b/tools/libclang/libclang.exports
@@ -8,6 +8,7 @@ clang_CXXMethod_isVirtual
 clang_Cursor_getArgument
 clang_Cursor_getBriefCommentText
 clang_Cursor_getCommentRange
+clang_Cursor_getDefaultArgument
 clang_Cursor_getParsedComment
 clang_Cursor_getRawCommentText
 clang_Cursor_getNumArguments
-- 
1.8.4.1.559.gdb9bdfb

_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to