From: Andreas Rottmann <a.rottmann@gmx.at>
Subject: [PATCH] Bug 556475 – support Shadows: annotation

Add support for the 'Rename To:' annotation for functions and methods.

Signed-off-by: Andreas Rottmann <a.rottmann@gmx.at>

---
 gir/glib-2.0.c                             |   30 +++++++++++++++++++++++-
 giscanner/annotationparser.py              |   26 ++++++++++++++++++++-
 giscanner/ast.py                           |   20 ++++++++++++++++
 tests/scanner/annotation-1.0-expected.gir  |   21 ++++++++++++++++
 tests/scanner/annotation-1.0-expected.tgir |   16 ++++++++++++
 tests/scanner/annotation.c                 |   35 ++++++++++++++++++++++++++++
 tests/scanner/annotation.h                 |    7 +++++
 7 files changed, 153 insertions(+), 2 deletions(-)

diff --git a/gir/glib-2.0.c b/gir/glib-2.0.c
index ec98c4d..ac4bcae 100644
--- a/gir/glib-2.0.c
+++ b/gir/glib-2.0.c
@@ -19,4 +19,32 @@
  * @context: (allow-none):
  */
 
- 
+/**
+ * g_idle_add_full:
+ *
+ * Rename to: g_idle_add
+ */
+
+/**
+ * g_child_watch_add_full:
+ *
+ * Rename to: g_child_watch_add
+ */
+
+/**
+ * g_io_add_watch_full:
+ *
+ * Rename to: g_io_add_watch
+ */
+
+/**
+ * g_timeout_add_full:
+ *
+ * Rename to: g_timeout_add
+ */
+
+/**
+ * g_timeout_add_seconds_full:
+ *
+ * Rename to: g_timeout_add_seconds
+ */
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 0798b80..b5e5281 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -46,6 +46,7 @@ TAG_DEPRECATED = 'deprecated'
 TAG_RETURNS = 'returns'
 TAG_RETURNS_ALT = 'return value'
 TAG_ATTRIBUTES = 'attributes'
+TAG_RENAME_TO = 'rename to'
 
 # Options - annotations for parameters and return values
 OPT_ALLOW_NONE = 'allow-none'
@@ -98,6 +99,8 @@ class DocTag(object):
         self.options = {}
         self.comment = None
 
+    def __repr__(self):
+        return '<DocTag %r %r>' % (self.name, self.options)
 
 class Option(object):
 
@@ -255,8 +258,10 @@ class AnnotationApplier(object):
         return block.get(tag_name)
 
     def parse(self, namespace):
-        for node in namespace.nodes:
+        self._namespace = namespace
+        for node in namespace.nodes[:]:
             self._parse_node(node)
+        del self._namespace
 
     # Boring parsing boilerplate.
 
@@ -379,6 +384,7 @@ class AnnotationApplier(object):
         self._parse_node_common(func, block)
         self._parse_params(func, func.parameters, block)
         self._parse_return(func, func.retval, block)
+        self._parse_rename_to_func(func, block)
         if block:
             func.doc = block.comment
 
@@ -594,6 +600,24 @@ class AnnotationApplier(object):
         for key, value in annos_tag.options.iteritems():
             node.attributes.append((key, value.one()))
 
+    def _parse_rename_to_func(self, node, block):
+        rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
+        if rename_to_tag is None:
+            return
+        new_name = rename_to_tag.value
+
+        shadowed = []
+
+        def filter_shadowed(n):
+            if isinstance(n, Function) and n.symbol == new_name:
+                shadowed.append(n)
+                return False
+            return True
+
+        self._namespace.filter_recursive(filter_shadowed)
+        assert len(shadowed) == 1
+        node.name = shadowed[0].name
+
     def _guess_direction(self, node):
         if node.direction:
             return node.direction
diff --git a/giscanner/ast.py b/giscanner/ast.py
index d2bae87..f89ad2c 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -158,6 +158,8 @@ class Node(object):
     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, self.name)
 
+    def filter_children(self, pred):
+        pass
 
 class Namespace(Node):
 
@@ -170,6 +172,13 @@ class Namespace(Node):
         return '%s(%r, %r, %r)' % (self.__class__.__name__, self.name,
                                    self.version, self.nodes)
 
+    def filter_recursive(self, pred):
+
+        def recursive_pred(node):
+            node.filter_children(pred)
+            return pred(node)
+
+        self.nodes = filter(recursive_pred, self.nodes)
 
 class Include(Node):
 
@@ -370,6 +379,11 @@ class Record(Node):
         self.doc = None
         self.methods = []
 
+    def filter_children(self, pred):
+        self.fields = filter(pred, self.fields)
+        self.constructors = filter(pred, self.constructors)
+        self.methods = filter(pred, self.methods)
+
 # BW compat, remove
 Struct = Record
 
@@ -418,6 +432,12 @@ class Class(Node):
         self.fields = []
         self.doc = None
 
+    def filter_children(self, pred):
+        self.methods = filter(pred, self.methods)
+        self.constructors = filter(pred, self.constructors)
+        self.properties = filter(pred, self.properties)
+        self.fields = filter(pred, self.fields)
+
     def __repr__(self):
         return '%s(%r, %r, %r)' % (
             self.__class__.__name__,
diff --git a/tests/scanner/annotation-1.0-expected.gir b/tests/scanner/annotation-1.0-expected.gir
index f427345..7d6da4f 100644
--- a/tests/scanner/annotation-1.0-expected.gir
+++ b/tests/scanner/annotation-1.0-expected.gir
@@ -427,6 +427,27 @@ type.">
           <type name="GObject.Object" c:type="GObject*"/>
         </return-value>
       </method>
+      <method name="watch"
+              c:identifier="annotation_object_watch_full"
+              doc="Test overriding via the &quot;Rename To&quot; annotation.">
+        <return-value transfer-ownership="none">
+          <type name="none" c:type="void"/>
+        </return-value>
+        <parameters>
+          <parameter name="func"
+                     transfer-ownership="none"
+                     closure="2"
+                     destroy="3">
+            <type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
+          </parameter>
+          <parameter name="user_data" transfer-ownership="none">
+            <type name="any" c:type="gpointer"/>
+          </parameter>
+          <parameter name="destroy" transfer-ownership="none">
+            <type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
+          </parameter>
+        </parameters>
+      </method>
       <method name="extra_annos" c:identifier="annotation_object_extra_annos">
         <attribute name="org.foobar" value="testvalue"/>
         <return-value transfer-ownership="none">
diff --git a/tests/scanner/annotation-1.0-expected.tgir b/tests/scanner/annotation-1.0-expected.tgir
index 2075143..6b75644 100644
--- a/tests/scanner/annotation-1.0-expected.tgir
+++ b/tests/scanner/annotation-1.0-expected.tgir
@@ -324,6 +324,22 @@
           <type name="GObject.Object"/>
         </return-value>
       </method>
+      <method name="watch" c:identifier="annotation_object_watch_full">
+        <return-value transfer-ownership="none">
+          <type name="none"/>
+        </return-value>
+        <parameters>
+          <parameter name="func" transfer-ownership="none" closure="2" destroy="3">
+            <type name="ForeachFunc"/>
+          </parameter>
+          <parameter name="user_data" transfer-ownership="none">
+            <type name="any"/>
+          </parameter>
+          <parameter name="destroy" transfer-ownership="none">
+            <type name="GLib.DestroyNotify"/>
+          </parameter>
+        </parameters>
+      </method>
       <method name="extra_annos" c:identifier="annotation_object_extra_annos">
         <attribute name="org.foobar" value="testvalue"/>
         <return-value transfer-ownership="none">
diff --git a/tests/scanner/annotation.c b/tests/scanner/annotation.c
index 41508e2..902d819 100644
--- a/tests/scanner/annotation.c
+++ b/tests/scanner/annotation.c
@@ -513,6 +513,41 @@ annotation_object_do_not_use (AnnotationObject *object)
 }
 
 /**
+ * annotation_object_watch:
+ * @object: A #AnnotationObject
+ * @func: The callback
+ * @user_data: The callback data
+ *
+ * This is here just for the sake of being overriden by its
+ * annotation_object_watch_full().
+ */
+void
+annotation_object_watch (AnnotationObject *object,
+                         AnnotationForeachFunc func,
+                         gpointer user_data)
+{
+}
+
+/**
+ * annotation_object_watch_full:
+ * @object: A #AnnotationObject
+ * @func: The callback
+ * @user_data: The callback data
+ * @destroy: Destroy notification
+ *
+ * Test overriding via the "Rename To" annotation.
+ *
+ * Rename to: annotation_object_watch
+ */
+void
+annotation_object_watch_full (AnnotationObject *object,
+                              AnnotationForeachFunc func,
+                              gpointer user_data,
+                              GDestroyNotify destroy)
+{
+}
+
+/**
  * annotation_init:
  * @argc: (inout): The number of args. 
  * @argv: (inout) (array length=argc): The arguments.
diff --git a/tests/scanner/annotation.h b/tests/scanner/annotation.h
index 79a686d..8d6ccf1 100644
--- a/tests/scanner/annotation.h
+++ b/tests/scanner/annotation.h
@@ -105,6 +105,13 @@ void     annotation_object_set_data3    (AnnotationObject *object,
                                          gsize             length);
 
 GObject* annotation_object_do_not_use   (AnnotationObject *object);
+void     annotation_object_watch        (AnnotationObject *object,
+                                         AnnotationForeachFunc func,
+                                         gpointer user_data);
+void     annotation_object_watch_full   (AnnotationObject *object,
+                                         AnnotationForeachFunc func,
+                                         gpointer user_data,
+                                         GDestroyNotify destroy);
 
 void     annotation_init                (int              *argc, 
 					 char           ***argv);
-- 
tg: (1455636..) t/rename-to (depends on: master)
