From ec22f6cf591d4f3530b77c0de9848c104f8d830b Mon Sep 17 00:00:00 2001
From: matt rice <ratmice@gmail.com>
Date: Sun, 3 Jun 2012 19:57:15 -0700
Subject: [PATCH] add attributes/methods to gcc.NamespaceDecl and is_builtin
 to gcc.Declaration

gcc.NamespaceDecl:
  add attributes: alias_of, namespaces, and declarations.
  add methods: unalias

gcc.Declaration
  add is_builtin attribute.

docs/tree.rst:
  fix gcc.NamespaceDecl.lookup which was documented as .locate.
  document new attributes/methods

tests/plugin/namespace: New tests.
---
 docs/tree.rst                     |   32 ++++++++++++++++-
 gcc-python-tree.c                 |   58 ++++++++++++++++++++++++++++++
 gcc-python-wrappers.h             |   10 +++++
 generate-tree-c.py                |   18 +++++++++
 tests/plugin/namespace/input.cc   |   22 +++++++++++
 tests/plugin/namespace/script.py  |   71 +++++++++++++++++++++++++++++++++++++
 tests/plugin/namespace/stdout.txt |   56 +++++++++++++++++++++++++++++
 7 files changed, 266 insertions(+), 1 deletions(-)
 create mode 100644 tests/plugin/namespace/input.cc
 create mode 100644 tests/plugin/namespace/script.py
 create mode 100644 tests/plugin/namespace/stdout.txt

diff --git a/docs/tree.rst b/docs/tree.rst
index 8c2c0a0..100c4ae 100644
--- a/docs/tree.rst
+++ b/docs/tree.rst
@@ -226,6 +226,10 @@ Declarations
       arguments of C++ methods: the initial `this` argument is a
       compiler-generated :py:class:`gcc.ParmDecl`.
 
+   .. py:attribute:: is_builtin
+
+      (bool) Is this declaration a compiler-builtin?
+
 .. py:class:: gcc.FieldDecl
 
    A subclass of :py:class:`gcc.Declaration` indicating the declaration of a
@@ -332,11 +336,37 @@ Declarations
 
    A subclass of :py:class:`gcc.Declaration` representing a C++ namespace
 
-   .. py:method:: locate(name)
+   .. py:attribute:: alias_of
+
+      The :py:class:`gcc.NamespaceDecl` which this namespace is an alias of
+      or None if this namespace is not an alias.
+
+   .. py:attribute:: declarations
+
+      .. note:: This attribute is only usable with non-alias namespaces.
+                Accessing it on an alias will lead to a RuntimeError exception.
+
+      List of :py:class:`gcc.Declaration` objects in this namespace.
+      This attribute is only valid for non-aliases
+
+   .. py:attribute:: namespaces
+
+      .. note:: This attribute is only usable with non-alias namespaces.
+                Accessing it on an alias will lead to a RuntimeError exception.
+
+      List of :py:class:`gcc.NamespaceDecl` objects nested in this namespace.
+      This attribute is only valid for non-aliases
+
+   .. py:method:: lookup(name)
 
       Locate the given name within the namespace, returning a
       :py:class:`gcc.Tree` or `None`
 
+   .. py:method:: unalias()
+
+      Always returns a :py:class:`gcc.NamespaceDecl` object which is not an alias.
+      Returns self if this namespace is not an alias.
+
 
   ..        Declaration
   ..            ClassMethodDecl
diff --git a/gcc-python-tree.c b/gcc-python-tree.c
index db35be6..179c0b2 100644
--- a/gcc-python-tree.c
+++ b/gcc-python-tree.c
@@ -21,6 +21,7 @@
 #include "gcc-python.h"
 #include "gcc-python-wrappers.h"
 #include "gcc-python-compat.h"
+#include "cp/cp-tree.h"
 #include "gimple.h"
 
 #include "cp/cp-tree.h" /* for TFF_* for use by gcc_FunctionDecl_get_fullname */
@@ -43,6 +44,9 @@ __typeof__ (decl_as_string) decl_as_string __attribute__ ((weak));
 /* Similar for namespace_binding: */
 __typeof__ (namespace_binding) namespace_binding __attribute__ ((weak));
 
+/* And for cp_namespace_decls: */
+__typeof__ (cp_namespace_decls) cp_namespace_decls __attribute__ ((weak));
+
 static PyObject *
 raise_cplusplus_only(const char *what)
 {
@@ -605,6 +609,60 @@ gcc_NamespaceDecl_lookup(struct PyGccTree * self, PyObject *args, PyObject *kwar
     return gcc_python_make_wrapper_tree(t_result);
 }
 
+PyObject *
+gcc_NamespaceDecl_unalias(struct PyGccTree * self, PyObject *args, PyObject *kwargs)
+{
+  tree t = self->t;
+
+  if (NULL == DECL_NAMESPACE_ALIAS(t)) {
+    return (PyObject *)self;
+  }
+
+  while (DECL_NAMESPACE_ALIAS(t)) {
+    t = DECL_NAMESPACE_ALIAS(t);
+  }
+
+  return gcc_python_make_wrapper_tree(t);
+}
+
+static PyObject *
+raise_namespace_alias(const char *what)
+{
+    return PyErr_Format(PyExc_RuntimeError,
+                        "%s is not valid for an alias",
+                        what);
+}
+
+PyObject *
+gcc_python_namespace_decl_declarations(tree t)
+{
+  if (NULL == cp_namespace_decls)
+    return raise_cplusplus_only("gcc.NamespaceDecl.declarations");
+
+  /* throw if we are an alias,
+     if we unalias it, it may return a list containing itself.  */
+  if (DECL_NAMESPACE_ALIAS(t))
+    return raise_namespace_alias("gcc.NamespaceDecl.declarations");
+
+  return gcc_tree_list_from_chain(cp_namespace_decls(t));
+}
+
+PyObject *
+gcc_python_namespace_decl_namespaces(tree t)
+{
+  /* We don't actually call cp_namespace_decls, but we only actually call
+     c++ specific macros, not sure what happens.  */
+  if (NULL == cp_namespace_decls)
+    return raise_cplusplus_only("gcc.NamespaceDecl.namespaces");
+
+  /* throw if we are an alias,
+     if we unalias it, it may return a list containing itself.  */
+  if (DECL_NAMESPACE_ALIAS(t))
+    return raise_namespace_alias("gcc.NamespaceDecl.namespaces");
+
+  return gcc_tree_list_from_chain(NAMESPACE_LEVEL(t)->namespaces);
+}
+
 /* 
    GCC's debug_tree is implemented in:
      gcc/print-tree.c
diff --git a/gcc-python-wrappers.h b/gcc-python-wrappers.h
index fb70fb4..f79a74b 100644
--- a/gcc-python-wrappers.h
+++ b/gcc-python-wrappers.h
@@ -185,6 +185,16 @@ gcc_TreeList_repr(struct PyGccTree * self);
 PyObject *
 gcc_NamespaceDecl_lookup(struct PyGccTree * self, PyObject *args, PyObject *kwargs);
 
+PyObject *
+gcc_NamespaceDecl_unalias(struct PyGccTree * self, PyObject *args, PyObject *kwargs);
+
+PyObject *
+gcc_python_namespace_decl_declarations(tree t);
+
+PyObject *
+gcc_python_namespace_decl_namespaces(tree t);
+
+
 /* gcc-python-gimple.c: */
 PyObject *
 gcc_Gimple_repr(struct PyGccGimple * self);
diff --git a/generate-tree-c.py b/generate-tree-c.py
index 457b2de..2c07c71 100644
--- a/generate-tree-c.py
+++ b/generate-tree-c.py
@@ -31,7 +31,9 @@ cu.add_include('gcc-plugin.h')
 cu.add_include("tree.h")
 cu.add_include("function.h")
 cu.add_include("basic-block.h")
+cu.add_include("cp/cp-tree.h")
 cu.add_include("c-family/c-common.h")
+cu.add_include("cp/name-lookup.h")
 
 modinit_preinit = ''
 modinit_postinit = ''
@@ -186,6 +188,9 @@ gcc_Declaration_get_location(struct PyGccTree *self, void *closure)
             add_simple_getter('is_artificial',
                               'PyBool_FromLong(DECL_ARTIFICIAL(self->t))',
                               "Is this a compiler-generated entity?")
+            add_simple_getter('is_builtin',
+                              'PyBool_FromLong(DECL_IS_BUILTIN(self->t))',
+                              "Is this declaration built in by the compiler?")
             pytype.tp_repr = '(reprfunc)gcc_Declaration_repr'
 
         if localname == 'Type':
@@ -478,10 +483,23 @@ def generate_tree_code_classes():
                                "The list of gcc.Tree for the declarations and labels in this block")
 
         if tree_type.SYM == 'NAMESPACE_DECL':
+            add_simple_getter('alias_of',
+                              'gcc_python_make_wrapper_tree(DECL_NAMESPACE_ALIAS(self->t))',
+                              "None if not an alias, otherwise the gcc.NamespaceDecl we alias")
+            add_simple_getter('declarations',
+                              'gcc_python_namespace_decl_declarations(self->t)',
+                              'The list of gcc.Declarations within this namespace')
+            add_simple_getter('namespaces',
+                              'gcc_python_namespace_decl_namespaces(self->t)',
+                              'The list of gcc.NamespaceDecl objects and gcc.TypeDecl of Unions nested in this namespace')
             methods.add_method('lookup',
                                '(PyCFunction)gcc_NamespaceDecl_lookup',
                                'METH_VARARGS|METH_KEYWORDS',
                                "Look up the given string within this namespace")
+            methods.add_method('unalias',
+                               '(PyCFunction)gcc_NamespaceDecl_unalias',
+                               'METH_VARARGS|METH_KEYWORDS',
+                               "A gcc.NamespaceDecl of this namespace that is not an alias")
 
         if tree_type.SYM == 'TYPE_DECL':
             getsettable.add_gsdef('pointer',
diff --git a/tests/plugin/namespace/input.cc b/tests/plugin/namespace/input.cc
new file mode 100644
index 0000000..16cf4ea
--- /dev/null
+++ b/tests/plugin/namespace/input.cc
@@ -0,0 +1,22 @@
+namespace top {
+  union top_union{
+    int i;
+    char c;
+    float f;
+  };
+  namespace sub {
+     int subs_int;
+     void subs_function(void);
+     void *foo;
+     namespace sub_sub {
+	void foo();
+     }
+  }
+  namespace sub_alias = sub;
+  namespace sub_alias_alias = sub_alias;
+  namespace foo {
+    namespace bar {
+    }
+  }
+};
+namespace top_alias_of_alias = top::sub;
diff --git a/tests/plugin/namespace/script.py b/tests/plugin/namespace/script.py
new file mode 100644
index 0000000..9080790
--- /dev/null
+++ b/tests/plugin/namespace/script.py
@@ -0,0 +1,71 @@
+#   Copyright 2012 Matt Rice <ratmice@gmail.com>
+#
+#   This is free software: you can redistribute it and/or modify it
+#   under the terms of the GNU General Public License as published by
+#   the Free Software Foundation, either version 3 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful, but
+#   WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+#   General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program.  If not, see
+#   <http://www.gnu.org/licenses/>.
+
+# Test case for gcc.NamespaceDecl
+
+import gcc
+
+def indent_print(depth, thing):
+  for i in range(depth):
+    print "  ",
+  print thing
+
+def print_namespace(ns, depth):
+  attributes = ("name", "alias_of", "declarations", "namespaces")
+  # [(method, arg),...]
+  methods = [(gcc.NamespaceDecl.unalias, None),
+	     (gcc.NamespaceDecl.lookup, "foo")]
+
+  for attr in attributes:
+    try:
+      indent_print(depth, (attr, ns.__getattribute__(attr)))
+    except Exception as e:
+      indent_print(depth, (attr, e))
+
+  for t in methods:
+    method_name = t[0].__name__
+    method = t[0]
+    try:
+      indent_print(depth, (method_name, method(ns, t[1])))
+    except Exception as e:
+      indent_print(depth, (method_name, e))
+
+  print "\n",
+
+def dump_namespaces(ns, depth):
+
+  # ignore builtin's they would just make stdout.txt painful.
+  if ns.is_builtin == False:
+    print_namespace(ns, depth)
+
+  # aliases of namespaces in particular will occur
+  # within declarations, but we don't want to call
+  # declarations on them (here).
+  if ns.alias_of == None:
+    for decl in ns.declarations:
+      if type(decl) == gcc.NamespaceDecl:
+        dump_namespaces(decl, depth + 1)
+
+    # nested namespaces..
+    for namespace in ns.namespaces:
+      dump_namespaces(namespace, depth + 1)
+
+def finish_unit_cb(*args, **kwargs):
+  # depth of -1 to ignore the global namespace itself (because its a builtin).
+  dump_namespaces(gcc.get_global_namespace(), -1)
+
+
+gcc.register_callback(gcc.PLUGIN_FINISH_UNIT, finish_unit_cb)
diff --git a/tests/plugin/namespace/stdout.txt b/tests/plugin/namespace/stdout.txt
new file mode 100644
index 0000000..c31e942
--- /dev/null
+++ b/tests/plugin/namespace/stdout.txt
@@ -0,0 +1,56 @@
+('name', 'top_alias_of_alias')
+('alias_of', gcc.NamespaceDecl('sub'))
+('declarations', RuntimeError('gcc.NamespaceDecl.declarations is not valid for an alias',))
+('namespaces', RuntimeError('gcc.NamespaceDecl.namespaces is not valid for an alias',))
+('unalias', gcc.NamespaceDecl('sub'))
+('lookup', gcc.VarDecl('foo'))
+
+('name', 'top')
+('alias_of', None)
+('declarations', [gcc.NamespaceDecl('sub_alias_alias'), gcc.NamespaceDecl('sub_alias'), gcc.TypeDecl('top_union')])
+('namespaces', [gcc.NamespaceDecl('foo'), gcc.NamespaceDecl('sub')])
+('unalias', gcc.NamespaceDecl('top'))
+('lookup', gcc.NamespaceDecl('foo'))
+
+   ('name', 'sub_alias_alias')
+   ('alias_of', gcc.NamespaceDecl('sub'))
+   ('declarations', RuntimeError('gcc.NamespaceDecl.declarations is not valid for an alias',))
+   ('namespaces', RuntimeError('gcc.NamespaceDecl.namespaces is not valid for an alias',))
+   ('unalias', gcc.NamespaceDecl('sub'))
+   ('lookup', gcc.VarDecl('foo'))
+
+   ('name', 'sub_alias')
+   ('alias_of', gcc.NamespaceDecl('sub'))
+   ('declarations', RuntimeError('gcc.NamespaceDecl.declarations is not valid for an alias',))
+   ('namespaces', RuntimeError('gcc.NamespaceDecl.namespaces is not valid for an alias',))
+   ('unalias', gcc.NamespaceDecl('sub'))
+   ('lookup', gcc.VarDecl('foo'))
+
+   ('name', 'foo')
+   ('alias_of', None)
+   ('declarations', [])
+   ('namespaces', [gcc.NamespaceDecl('bar')])
+   ('unalias', gcc.NamespaceDecl('foo'))
+   ('lookup', None)
+
+      ('name', 'bar')
+      ('alias_of', None)
+      ('declarations', [])
+      ('namespaces', [])
+      ('unalias', gcc.NamespaceDecl('bar'))
+      ('lookup', None)
+
+   ('name', 'sub')
+   ('alias_of', None)
+   ('declarations', [gcc.VarDecl('foo'), gcc.FunctionDecl('subs_function'), gcc.VarDecl('subs_int')])
+   ('namespaces', [gcc.NamespaceDecl('sub_sub')])
+   ('unalias', gcc.NamespaceDecl('sub'))
+   ('lookup', gcc.VarDecl('foo'))
+
+      ('name', 'sub_sub')
+      ('alias_of', None)
+      ('declarations', [gcc.FunctionDecl('foo')])
+      ('namespaces', [])
+      ('unalias', gcc.NamespaceDecl('sub_sub'))
+      ('lookup', gcc.FunctionDecl('foo'))
+
-- 
1.7.7.6

