Index: tools/g-ir-scanner
===================================================================
--- tools/g-ir-scanner	(revision 996)
+++ tools/g-ir-scanner	(working copy)
@@ -41,6 +41,7 @@
                         'site-packages')
 sys.path.insert(0, path)
 
+from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
 from giscanner.ast import Include
 from giscanner.cachestore import CacheStore
 from giscanner.dumper import compile_introspection_binary
@@ -313,6 +314,12 @@
 
     namespace = glibtransformer.parse()
 
+    ap = AnnotationParser(namespace, ss, transformer)
+    try:
+        ap.parse()
+    except InvalidAnnotationError, e:
+        raise SystemExit("ERROR in annotation: %s" % (str(e), ))
+
     # Write out AST
     writer = Writer(namespace, libraries, transformer.get_includes())
     data = writer.get_xml()
Index: tests/scanner/annotation-1.0-expected.gir
===================================================================
--- tests/scanner/annotation-1.0-expected.gir	(revision 996)
+++ tests/scanner/annotation-1.0-expected.gir	(working copy)
@@ -296,5 +296,12 @@
         <type name="none" c:type="void"/>
       </return-value>
     </function>
+    <record name="Struct" c:type="_AnnotationStruct">
+      <field name="objects" writable="1">
+        <array zero-terminated="0" c:type="AnnotationObject*" fixed-size="10">
+          <type name="Object"/>
+        </array>
+      </field>
+    </record>
   </namespace>
 </repository>
Index: tests/scanner/annotation.c
===================================================================
--- tests/scanner/annotation.c	(revision 996)
+++ tests/scanner/annotation.c	(working copy)
@@ -50,7 +50,7 @@
  *
  * This is a test for in arguments
  *
- * @inarg: (in): This is an argument test
+ * @inarg: (in): (transfer none): This is an argument test
  * Return value: an int
  */
 gint
@@ -246,7 +246,7 @@
 /**
  * annotation_object_compute_sum_n:
  * @object: a #GObject
- * @nums: (array length=n_nums): Sequence of numbers
+ * @nums: (array length=n_nums zero-terminated=0): Sequence of numbers
  * @n_nums: Length of number array
  *
  * Test taking an array with length parameter
Index: tests/scanner/annotation.h
===================================================================
--- tests/scanner/annotation.h	(revision 996)
+++ tests/scanner/annotation.h	(working copy)
@@ -83,4 +83,9 @@
 char **  annotation_return_array        (int             *length);
 void     annotation_versioned           (void);
 
+struct _AnnotationStruct
+{
+  AnnotationObject *objects[10];
+};
+
 #endif /* __ANNOTATION_OBJECT_H__ */
Index: tests/scanner/annotation-1.0-expected.tgir
===================================================================
--- tests/scanner/annotation-1.0-expected.tgir	(revision 996)
+++ tests/scanner/annotation-1.0-expected.tgir	(working copy)
@@ -277,5 +277,12 @@
         <type name="none"/>
       </return-value>
     </function>
+    <record name="Struct">
+      <field name="objects" writable="1">
+        <array fixed-size="10">
+          <type name="Object"/>
+        </array>
+      </field>
+    </record>
   </namespace>
 </repository>
Index: giscanner/scannerlexer.l
===================================================================
--- giscanner/scannerlexer.l	(revision 996)
+++ giscanner/scannerlexer.l	(working copy)
@@ -200,187 +200,27 @@
 
 
 static void
-parse_gtkdoc (GISourceScanner *scanner,
-	      gchar           *symbol,
-	      int             *c1,
-	      int             *c2)
-{
-  gboolean isline = FALSE;
-  GString *line_buf;
-  char *line;
-  gchar **parts;
-  GISourceDirective *directive;
-  char *name,*value;
-  GSList *directives;
-  GSList *options = NULL;
-  char *rname;
-  int n_parts;
-
-  line_buf = g_string_new ("");
-
-  do 
-    {
-      *c1 = *c2;
-      if (*c1 == '\n')
-        {
-          isline = TRUE;
-          break;
-        }
-      g_string_append_c (line_buf, *c1);
-      *c2 = input();
-    } while (*c2 != EOF && !(*c1 == '*' && *c2 == '/'));
-  
-  if (!isline)
-    {
-      g_string_free (line_buf, TRUE);
-      return;
-    }
-
-  line = g_string_free (line_buf, FALSE);
-
-  /* Ignore lines that don't have a : - this is a hack but avoids
-   * trying to parse too many things as annotations
-   */
-  if (!strchr (line, ':'))
-    {
-      g_free (line);
-      return;
-    }
-
-  parts = g_strsplit (line, ":", 3);
-  n_parts = g_strv_length (parts);
-
-  if (g_ascii_strcasecmp (parts[0], "eprecated") == 0)
-    {
-      if (n_parts == 3)
-	options = g_slist_prepend (options, g_strdup_printf ("%s: %s", parts[1], parts[2]));
-      else if (n_parts == 2)
-	options = g_slist_prepend (options, g_strdup (parts[1]));
-      else
-	options = g_slist_prepend (options, g_strdup (""));
-      name = parts[0];
-      value = NULL;
-    }
-  else if (g_ascii_strcasecmp (parts[0], "ince") == 0)
-    {
-      if (n_parts == 2)
-	options = g_slist_prepend (options, g_strdup (parts[1]));
-      else
-	options = g_slist_prepend (options, g_strdup (""));
-      name = parts[0];
-      value = NULL;
-    }
-  else if (n_parts >= 2)
-    {
-      name = parts[0];
-
-      if (n_parts == 3) 
-        {
-          char *ptr = g_strdup (parts[1]);
-	  char *start;
-	  char *end;
-
-	  options = NULL;
-	  start = strchr (ptr, '(');
-	  while (start != NULL) 
-	    {
-	      end = strchr (start, ')');
-	      if (end)
-		{
-		  options = g_slist_prepend (options, g_strndup (start+1, end-(start+1)));
-		  start = strchr (end+1, '(');
-		}
-	      else
-		{
-		  break;
-		}
-	    }
-	  g_free (ptr);
-          value = parts[2];
-        } 
-      else
-        value = parts[1];
-    }
-  else /* parts == 1 */
-    {
-      name = parts[0];
-      value = NULL;
-    }
-
-  /*
-   * Special cases for global annotations.
-   * Context-sensitive parsing would probably be the right way to go.
-   */
-  if (g_ascii_strncasecmp ("eturn", name, 5) == 0)
-    rname = "return";
-  else if (g_ascii_strncasecmp ("eprecated", name, 9) == 0)
-    rname = "deprecated";
-  else if (g_ascii_strncasecmp ("ince", name, 4) == 0)
-    rname = "since";
-  else
-    rname = name;
-
-  directive = gi_source_directive_new (rname, value, options);
-  directives = g_hash_table_lookup (scanner->directives_map, symbol);
-  directives = g_slist_prepend (directives, directive);
-  g_hash_table_replace (scanner->directives_map,
-			g_strdup (symbol), directives);
-
-  g_strfreev (parts);
-  g_free (line);
-}
-
-
-static void
 parse_comment (GISourceScanner *scanner)
 {
-  GString *symbol = NULL;
-  gboolean startofline = FALSE, have_symbol = FALSE, start1 = FALSE, start_symbol = FALSE;
+  GString *comment;
   int c1, c2;
 
   c1 = input();
   c2 = input();
 
+  comment = g_string_new ("");
+
   while (c2 != EOF && !(c1 == '*' && c2 == '/'))
     {
-      if (c1 == ':')
-        have_symbol = TRUE;
-      else if (c1 == '\n')
-         start1 = TRUE;
-      else if (c1 == '*' && start1)
-         start_symbol = TRUE;
-      else if (!have_symbol && start_symbol) 
-        {
-          if (!symbol)
-            symbol = g_string_new ("");
-          if (c1 != ' ')
-            g_string_append_c (symbol, c1);
-        }
+      g_string_append_c (comment, c1);
 
-      if (c1 == '\n') 
-        {
-          ++lineno;
-          startofline = TRUE;
-        }
-
       c1 = c2;
       c2 = input();
 
-      if ((c1 != '*' && c1 != ' '))
-          startofline = FALSE;
-
-      if (startofline && (c1 == ' ') && ((c2 == '@') || (c2 == 'r') || (c2 == 'R') || (c2 == 'D') || (c2 == 'S')))
-        {
-           c1 = c2;
-           c2 = input();
-           if (symbol)
-             parse_gtkdoc (scanner, symbol->str, &c1, &c2);
-        }
     }
 
-  if (symbol)
-    g_string_free (symbol, TRUE);
-  
+  scanner->comments = g_slist_prepend (scanner->comments,
+                                       g_string_free (comment, FALSE));
 }
 
 static int
Index: giscanner/girparser.py
===================================================================
--- giscanner/girparser.py	(revision 996)
+++ giscanner/girparser.py	(working copy)
@@ -154,7 +154,9 @@
         for iface in node.findall(_corens('prerequisites')):
             obj.prerequisities.append(iface.attrib['name'])
         for method in node.findall(_corens('method')):
-            obj.methods.append(self._parse_function_common(method, Function))
+            func = self._parse_function_common(method, Function)
+            func.is_method = True
+            obj.methods.append(func)
         for ctor in node.findall(_corens('constructor')):
             obj.constructors.append(
                 self._parse_function_common(ctor, Function))
@@ -286,8 +288,9 @@
         if self._include_parsing:
             return
         for method in node.findall(_corens('method')):
-            obj.methods.append(
-                self._parse_function_common(method, Function))
+            func = self._parse_function_common(method, Function)
+            func.is_method = True
+            obj.methods.append(func)
         for ctor in node.findall(_corens('constructor')):
             obj.constructors.append(
                 self._parse_function_common(ctor, Function))
Index: giscanner/glibtransformer.py
===================================================================
--- giscanner/glibtransformer.py	(revision 996)
+++ giscanner/glibtransformer.py	(working copy)
@@ -28,7 +28,7 @@
 from .ast import (Callback, Constant, Enum, Function, Member, Namespace,
                   Parameter, Property, Return, Struct, Type, Alias,
                   Union, Field, type_name_from_ctype,
-                  default_array_types, TYPE_UINT8, PARAM_DIRECTION_IN)
+                  default_array_types, TYPE_UINT8, PARAM_TRANSFER_FULL)
 from .transformer import Names
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
@@ -85,8 +85,6 @@
     def __init__(self, transformer, noclosure=False):
         self._transformer = transformer
         self._noclosure = noclosure
-        self._transformer.set_container_types(['GList*', 'GSList*'],
-                                              ['GHashTable*'])
         self._namespace_name = None
         self._names = Names()
         self._uscore_type_names = {}
@@ -348,6 +346,7 @@
         self._remove_attribute(func.name)
         func.name = methname
         target_klass.static_methods.append(func)
+        func.is_method = True
         return func
 
     def _parse_method(self, func):
@@ -434,6 +433,7 @@
             # We don't need the "this" parameter
             del func.parameters[0]
             klass.methods.append(func)
+            func.is_method = True
         else:
             klass.constructors.append(func)
         return func
@@ -632,7 +632,7 @@
             rctype = signal_info.attrib['return']
             rtype = Type(self._transformer.parse_ctype(rctype), rctype)
             return_ = Return(rtype, signal_info.attrib['return'])
-            return_.transfer = 'full'
+            return_.transfer = PARAM_TRANSFER_FULL
             signal = GLibSignal(signal_info.attrib['name'], return_)
             for i, parameter in enumerate(signal_info.findall('param')):
                 if i == 0:
@@ -790,25 +790,6 @@
     def _resolve_property(self, prop):
         prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
 
-    def _adjust_transfer(self, param):
-        if not (param.transfer is None or param.transfer_inferred):
-            return
-
-        # Do GLib/GObject-specific type transformations here
-        node = self._lookup_node(param.type.name)
-        if node is None:
-            return
-
-        if isinstance(param, Parameter):
-            if param.direction != PARAM_DIRECTION_IN:
-                transfer = 'full'
-            else:
-                transfer = 'none'
-        else:
-            transfer = 'full'
-
-        param.transfer = transfer
-
     def _adjust_throws(self, func):
         if func.parameters == []:
             return
@@ -826,12 +807,10 @@
         self._resolve_parameters(func.parameters)
         func.retval.type = self._resolve_param_type(func.retval.type)
         self._adjust_throws(func)
-        self._adjust_transfer(func.retval)
 
     def _resolve_parameters(self, parameters):
         for parameter in parameters:
             parameter.type = self._resolve_param_type(parameter.type)
-            self._adjust_transfer(parameter)
 
     def _resolve_field(self, field):
         if isinstance(field, Callback):
Index: giscanner/transformer.py
===================================================================
--- giscanner/transformer.py	(revision 996)
+++ giscanner/transformer.py	(working copy)
@@ -19,14 +19,12 @@
 #
 
 import os
-import re
 
 from .ast import (Callback, Enum, Function, Namespace, Member,
-                  Parameter, Return, Array, Struct, Field,
-                  Type, Alias, Interface, Class, Node, Union,
-                  List, Map, Varargs, Constant, type_name_from_ctype,
-                  type_names, default_array_types, default_out_types,
-                  TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
+                  Parameter, Return, Struct, Field,
+                  Type, Array, Alias, Interface, Class, Node, Union,
+                  Varargs, Constant, type_name_from_ctype,
+                  type_names, TYPE_STRING, BASIC_GIR_TYPES)
 from .config import DATADIR
 from .glibast import GLibBoxed
 from .girparser import GIRParser
@@ -75,8 +73,6 @@
         self._strip_prefix = ''
         self._includes = set()
         self._includepaths = []
-        self._list_ctypes = []
-        self._map_ctypes = []
 
     def get_names(self):
         return self._names
@@ -84,10 +80,6 @@
     def get_includes(self):
         return self._includes
 
-    def set_container_types(self, list_ctypes, map_ctypes):
-        self._list_ctypes = list_ctypes
-        self._map_ctypes = map_ctypes
-
     def set_strip_prefix(self, strip_prefix):
         self._strip_prefix = strip_prefix
 
@@ -211,7 +203,6 @@
 
     def _create_enum(self, symbol):
         members = []
-        directives = symbol.directives()
         for child in symbol.base_type.child_list:
             name = strip_common_prefix(symbol.ident, child.ident).lower()
             members.append(Member(name,
@@ -220,7 +211,6 @@
 
         enum_name = self.remove_prefix(symbol.ident)
         enum = Enum(enum_name, symbol.ident, members)
-        self._parse_version(enum, directives)
         self._names.type_names[symbol.ident] = (None, enum)
         return enum
 
@@ -228,71 +218,12 @@
         return Member(symbol.ident, symbol.base_type.name,
                       symbol.ident)
 
-    def _parse_deprecated(self, node, directives):
-        deprecated = directives.get('deprecated', False)
-        if deprecated:
-            deprecated_value = deprecated[0]
-            if ':' in deprecated_value:
-                # Split out gtk-doc version
-                (node.deprecated_version, node.deprecated) = \
-                    [x.strip() for x in deprecated_value.split(':', 1)]
-            else:
-                # No version, just include str
-                node.deprecated = deprecated_value.strip()
-
-    def _parse_version(self, node, directives):
-        version = directives.get('since', False)
-        if version:
-            version_value = version[0]
-            node.version = version_value.strip()
-
-    def _pair_array(self, params, array):
-        if not array.type.length_param_name:
-            return
-        target_name = array.type.length_param_name
-        for i, param in enumerate(params):
-            if param.name == array.type.length_param_name:
-                array.type.length_param_index = i
-                return
-        raise ValueError("Unmatched length parameter name %r"\
-                             % (target_name, ))
-
-    def _pair_annotations(self, params, return_):
-        names = {}
-        for param in params:
-            if param.name in names:
-                raise ValueError("Duplicate parameter name %r"\
-                                     % (param.name, ))
-            names[param.name] = 1
-            if isinstance(param.type, Array):
-                self._pair_array(params, param)
-
-        if isinstance(return_.type, Array):
-            self._pair_array(params, return_)
-
-    # We take the annotations from the parser as strings; here we
-    # want to split them into components, so:
-    # (transfer full) -> {'transfer' : [ 'full' ]}
-
-    def _parse_options(self, options):
-        ret = {}
-        ws_re = re.compile(r'\s+')
-        for opt in options:
-            items = ws_re.split(opt)
-            ret[items[0]] = items[1:]
-        return ret
-
     def _create_function(self, symbol):
-        directives = symbol.directives()
         parameters = list(self._create_parameters(
-            symbol.base_type, directives))
-        return_ = self._create_return(symbol.base_type.base_type,
-                                      directives.get('return', {}))
-        self._pair_annotations(parameters, return_)
+            symbol.base_type))
+        return_ = self._create_return(symbol.base_type.base_type)
         name = self._strip_namespace_func(symbol.ident)
         func = Function(name, return_, parameters, symbol.ident)
-        self._parse_version(func, directives)
-        self._parse_deprecated(func, directives)
         return func
 
     def _create_source_type(self, source_type):
@@ -312,47 +243,42 @@
             value = 'any'
         return value
 
-    def _create_parameters(self, base_type, directives=None):
-        if directives is None:
-            dirs = {}
-        else:
-            dirs = directives
+    def _create_parameters(self, base_type):
 
         # warn if we see annotations for unknown parameters
         param_names = set(child.ident for child in base_type.child_list)
-        dirs_for = set(dirs)
-        dirs_for = dirs_for.difference(param_names)
-        dirs_for.discard('return')
-        dirs_for.discard('deprecated')
-        dirs_for.discard('since')
-        if dirs_for:
-            print 'Unexpected annotations for %s, parameters are %s' % (
-                list(dirs_for), list(param_names), )
-
         for child in base_type.child_list:
-            yield self._create_parameter(
-                child, dirs.get(child.ident, {}))
+            yield self._create_parameter(child)
 
     def _create_member(self, symbol):
-        ctype = symbol.base_type.type
-        if (ctype == CTYPE_POINTER and
+        source_type = symbol.base_type
+        if (source_type.type == CTYPE_POINTER and
             symbol.base_type.base_type.type == CTYPE_FUNCTION):
             node = self._create_callback(symbol)
         else:
-            opts = {}
-            if ctype == CTYPE_ARRAY:
-                opts['array'] = []
+            # Special handling for fields; we don't have annotations on them
+            # to apply later, yet.
+            if source_type.type == CTYPE_ARRAY:
+                ctype = self._create_source_type(source_type)
+                canonical_ctype = self._canonicalize_ctype(ctype)
+                if canonical_ctype[-1] == '*':
+                    derefed_name = canonical_ctype[:-1]
+                else:
+                    derefed_name = canonical_ctype
+                derefed_name = self.resolve_param_type(derefed_name)
+                ftype = Array(ctype, self.parse_ctype(derefed_name))
                 child_list = list(symbol.base_type.child_list)
+                ftype.zeroterminated = False
                 if child_list:
-                    size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
-                    opts['array'].append(size_opt)
-            ftype = self._create_type(symbol.base_type, opts,
-                                      is_param=False, is_retval=False)
+                    ftype.size = '%d' % (child_list[0].const_int, )
+            else:
+                ftype = self._create_type(symbol.base_type,
+                                          is_param=False, is_retval=False)
             ftype = self.resolve_param_type(ftype)
             # Fields are assumed to be read-write
             # (except for Objects, see also glibtransformer.py)
-            node = Field(symbol.ident, ftype, symbol.ident,
-                       readable=True, writable=True, bits=symbol.const_int)
+            node = Field(symbol.ident, ftype, ftype.name,
+                         readable=True, writable=True, bits=symbol.const_int)
         return node
 
     def _create_typedef(self, symbol):
@@ -428,7 +354,7 @@
         else:
             return derefed_typename
 
-    def _create_type(self, source_type, options, is_param, is_retval):
+    def _create_type(self, source_type, is_param, is_retval):
         ctype = self._create_source_type(source_type)
         if ctype == 'va_list':
             raise SkipError()
@@ -437,186 +363,38 @@
         elif ctype == 'FILE*':
             raise SkipError
 
-        canonical_ctype = self._canonicalize_ctype(ctype)
+        is_member = not (is_param or is_retval)
+        # Here we handle basic type parsing; most of the heavy lifting
+        # and inference comes in annotationparser.py when we merge
+        # in annotation data.
+        derefed_name = self.parse_ctype(ctype, is_member)
+        rettype = Type(derefed_name, ctype)
+        rettype.canonical = self._canonicalize_ctype(ctype)
+        derefed_ctype = ctype.replace('*', '')
+        rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
 
-        # Now check for a list/map/array type
-        if canonical_ctype in self._list_ctypes:
-            param = options.get('element-type')
-            if param:
-                contained_type = self.parse_ctype(param[0])
-            else:
-                contained_type = None
-            derefed_name = self.parse_ctype(ctype)
-            rettype = List(derefed_name,
-                           ctype,
-                           contained_type)
-        elif canonical_ctype in self._map_ctypes:
-            param = options.get('element-type')
-            if param:
-                key_type = self.parse_ctype(param[0])
-                value_type = self.parse_ctype(param[1])
-            else:
-                key_type = None
-                value_type = None
-            derefed_name = self.parse_ctype(ctype)
-            rettype = Map(derefed_name,
-                          ctype,
-                          key_type, value_type)
-        elif ((is_param and canonical_ctype in default_array_types
-               and not 'out' in options)
-              or ('array' in options)):
-            if canonical_ctype[-1] == '*':
-                derefed_name = canonical_ctype[:-1]
-            else:
-                derefed_name = canonical_ctype
-            rettype = Array(ctype,
-                            self.parse_ctype(derefed_name))
-            array_opts = dict([opt.split('=')
-                               for opt in options.get('array', [])])
-            if 'length' in array_opts:
-                rettype.length_param_name = array_opts['length']
-                rettype.zeroterminated = False
-            if 'fixed-size' in array_opts:
-                rettype.size = array_opts['fixed-size']
-                rettype.zeroterminated = False
-            if 'zero-terminated' in array_opts:
-                rettype.zeroterminated = array_opts['zero-terminated'] != '0'
-        else:
-            derefed_name = self.parse_ctype(ctype,
-                                            not (is_param or is_retval))
-            rettype = Type(derefed_name, ctype)
-
-        # Deduce direction for some types passed by reference that
-        # aren't arrays; modifies the options array.
-        if ('array' not in options and
-            not ('out' in options or
-                 'in' in options or
-                 'inout' in options or
-                 'in-out' in options) and
-            source_type.type == CTYPE_POINTER and
-            derefed_name in default_out_types):
-            options['out'] = []
-
-        if 'transfer' in options:
-            # Transfer is specified, we don't question it.
-            return rettype
-
         canontype = type_name_from_ctype(ctype)
-
-        # Since no transfer is specified, we drop into a bunch of
-        # heuristics to guess it.  This mutates the options array to
-        # set the 'transfer' option.
-        # Note that we inferred the transfer
-        options['transfer-inferred'] = []
-        stype = source_type
-        if canontype == TYPE_STRING:
-            # It's a string - we just look at 'const'
-            if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
-                options['transfer'] = ['none']
-            else:
-                options['transfer'] = ['full']
-        elif 'array' in options or stype.type == CTYPE_ARRAY:
-            # It's rare to mutate arrays in public GObject APIs
-            options['transfer'] = ['none']
-        elif (canontype in BASIC_GIR_TYPES or
-              canontype == TYPE_NONE or
-              stype.type == CTYPE_ENUM):
-            # Basic types default to 'none'
-            options['transfer'] = ['none']
-        elif (stype.type == CTYPE_POINTER and
-              stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
-            # Anything with 'const' gets none
-            options['transfer'] = ['none']
-        elif is_param and stype.type == CTYPE_POINTER:
-            # For generic pointer types, let's look at the argument
-            # direction.  An out/inout argument gets full, everything
-            # else none.
-            if ('out' in options or
-                'inout' in options or
-                'in-out' in options):
-                options['transfer'] = ['full']
-            else:
-                options['transfer'] = ['none']
-        else:
-            # For anything else we default to none for parameters;
-            # this covers enums and possibly some other corner cases.
-            # Return values of structures and the like will end up
-            # full.
-            if is_param:
-                options['transfer'] = ['none']
-            else:
-                options['transfer'] = ['full']
-
+        if ((canontype == TYPE_STRING or
+             source_type.type == CTYPE_POINTER) and
+            source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
+            rettype.is_const = True
         return rettype
 
-    def _handle_generic_param_options(self, param, options):
-        for option, data in options.iteritems():
-            if option == 'transfer':
-                if data:
-                    depth = data[0]
-                    if depth not in ('none', 'container', 'full'):
-                        raise ValueError("Invalid transfer %r" % (depth, ))
-                else:
-                    depth = 'full'
-                param.transfer = depth
-            elif option == 'transfer-inferred':
-                # This is a purely internal flag; we don't expect
-                # people to write it
-                param.transfer_inferred = True
-
-    def _create_parameter(self, symbol, options):
-        options = self._parse_options(options)
+    def _create_parameter(self, symbol):
         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
             ptype = Varargs()
-            if 'transfer' not in options:
-                options['transfer'] = ['none']
         else:
-            ptype = self._create_type(symbol.base_type, options,
+            ptype = self._create_type(symbol.base_type,
                                       is_param=True, is_retval=False)
             ptype = self.resolve_param_type(ptype)
         param = Parameter(symbol.ident, ptype)
-        for option, data in options.iteritems():
-            if option in ['in-out', 'inout']:
-                param.direction = 'inout'
-            elif option == 'in':
-                param.direction = 'in'
-            elif option == 'out':
-                param.direction = 'out'
-            elif option == 'allow-none':
-                param.allow_none = True
-            elif option.startswith(('element-type', 'array')):
-                pass
-            elif option in ('transfer', 'transfer-inferred'):
-                pass
-            else:
-                print 'Unhandled parameter annotation option: %r' % (
-                    option, )
-        self._handle_generic_param_options(param, options)
-
-        assert param.transfer is not None, param
         return param
 
-    def _create_return(self, source_type, options=None):
-        if options is None:
-            options_map = {}
-        else:
-            options_map = self._parse_options(options)
-        rtype = self._create_type(source_type, options_map,
+    def _create_return(self, source_type):
+        rtype = self._create_type(source_type,
                                   is_param=False, is_retval=True)
         rtype = self.resolve_param_type(rtype)
         return_ = Return(rtype)
-        self._handle_generic_param_options(return_, options_map)
-        for option, data in options_map.iteritems():
-            if option in ('transfer', 'transfer-inferred',
-                          'element-type', 'out'):
-                pass
-            elif option.startswith(('element-type', 'array')):
-                pass
-            else:
-                print 'Unhandled return type annotation option: %r' % (
-                    option, )
-
-        assert return_.transfer is not None, return_
         return return_
 
     def _create_const(self, symbol):
@@ -645,7 +423,6 @@
         return union
 
     def _create_struct(self, symbol):
-        directives = symbol.directives()
         struct = self._typedefs_ns.get(symbol.ident, None)
         if struct is None:
             # This is a bit of a hack; really we should try
@@ -663,12 +440,9 @@
             if field:
                 struct.fields.append(field)
 
-        self._parse_version(struct, directives)
-
         return struct
 
     def _create_union(self, symbol):
-        directives = symbol.directives()
         union = self._typedefs_ns.get(symbol.ident, None)
         if union is None:
             # This is a bit of a hack; really we should try
@@ -686,24 +460,17 @@
             if field:
                 union.fields.append(field)
 
-        self._parse_version(union, directives)
-
         return union
 
     def _create_callback(self, symbol):
-        directives = symbol.directives()
-        parameters = self._create_parameters(symbol.base_type.base_type,
-            directives)
-        retval = self._create_return(symbol.base_type.base_type.base_type,
-            directives.get('return', {}))
+        parameters = self._create_parameters(symbol.base_type.base_type)
+        retval = self._create_return(symbol.base_type.base_type.base_type)
         if symbol.ident.find('_') > 0:
             name = self.remove_prefix(symbol.ident, True)
         else:
             name = self.remove_prefix(symbol.ident)
         callback = Callback(name, retval, list(parameters), symbol.ident)
 
-        self._parse_version(callback, directives)
-
         return callback
 
     def _typepair_to_str(self, item):
@@ -781,19 +548,6 @@
             ptype.name = self.resolve_type_name_full(ptype.name,
                                                      self.ctype_of(ptype),
                                                      names, **kwargs)
-            if isinstance(ptype, (Array, List)):
-                if ptype.element_type is not None:
-                    ptype.element_type = \
-                        self.resolve_param_type_full(ptype.element_type,
-                                                     names, **kwargs)
-            if isinstance(ptype, Map):
-                if ptype.key_type is not None:
-                    ptype.key_type = \
-                        self.resolve_param_type_full(ptype.key_type,
-                                                     names, **kwargs)
-                    ptype.value_type = \
-                        self.resolve_param_type_full(ptype.value_type,
-                                                     names, **kwargs)
         elif isinstance(ptype, basestring):
             return self.resolve_type_name_full(ptype, None, names, **kwargs)
         else:
Index: giscanner/sourcescanner.c
===================================================================
--- giscanner/sourcescanner.c	(revision 996)
+++ giscanner/sourcescanner.c	(working copy)
@@ -60,8 +60,6 @@
       if (symbol->base_type)
         ctype_free (symbol->base_type);
       g_free (symbol->const_string);
-      g_slist_foreach (symbol->directives, (GFunc)gi_source_directive_free, NULL);
-      g_slist_free (symbol->directives);
       g_slice_free (GISourceSymbol, symbol);
     }
 }
@@ -178,28 +176,6 @@
   return func;
 }
 
-GISourceDirective *
-gi_source_directive_new (const gchar *name,
-			 const gchar *value,
-			 GSList *options)
-{
-  GISourceDirective *directive;
-    
-  directive = g_slice_new (GISourceDirective);
-  directive->name = g_strdup (name);
-  directive->value = g_strdup (value);
-  directive->options = options;
-  return directive;
-}
-
-void
-gi_source_directive_free (GISourceDirective *directive)
-{
-  g_free (directive->name);
-  g_free (directive->value);
-  g_slice_free (GISourceDirective, directive);
-}
-
 GISourceScanner *
 gi_source_scanner_new (void)
 {
@@ -208,7 +184,6 @@
   scanner = g_slice_new0 (GISourceScanner);
   scanner->typedef_table = g_hash_table_new_full (g_str_hash, g_str_equal,
 						  g_free, NULL);
-  scanner->directives_map = g_hash_table_new (g_str_hash, g_str_equal);
   scanner->struct_or_union_or_enum_table =
     g_hash_table_new_full (g_str_hash, g_str_equal,
 			   g_free, (GDestroyNotify)gi_source_symbol_unref);
@@ -221,10 +196,11 @@
 {
   g_free (scanner->current_filename);
 
-  g_hash_table_destroy (scanner->directives_map);
   g_hash_table_destroy (scanner->typedef_table);
   g_hash_table_destroy (scanner->struct_or_union_or_enum_table);
 
+  g_slist_foreach (scanner->comments, (GFunc)g_free, NULL);
+  g_slist_free (scanner->comments);
   g_slist_foreach (scanner->symbols, (GFunc)gi_source_symbol_unref, NULL);
   g_slist_free (scanner->symbols);
 
@@ -295,8 +271,7 @@
 }
 
 GSList *
-gi_source_scanner_get_directives(GISourceScanner  *scanner,
-				 const gchar      *name)
+gi_source_scanner_get_comments(GISourceScanner  *scanner)
 {
-  return g_hash_table_lookup (scanner->directives_map, name);
+  return g_slist_reverse (scanner->comments);
 }
Index: giscanner/sourcescanner.h
===================================================================
--- giscanner/sourcescanner.h	(revision 996)
+++ giscanner/sourcescanner.h	(working copy)
@@ -31,7 +31,6 @@
 typedef struct _GISourceScanner GISourceScanner;
 typedef struct _GISourceSymbol GISourceSymbol;
 typedef struct _GISourceType GISourceType;
-typedef struct _GISourceDirective GISourceDirective;
 
 typedef enum
 {
@@ -102,7 +101,7 @@
   gboolean macro_scan;
   GSList *symbols;
   GList *filenames;
-  GHashTable *directives_map;
+  GSList *comments;
   GHashTable *typedef_table;
   GHashTable *struct_or_union_or_enum_table;
 };
@@ -117,7 +116,6 @@
   gboolean const_int_set;
   int const_int;
   char *const_string;
-  GSList *directives; /* list of GISourceDirective */
 };
 
 struct _GISourceType
@@ -131,13 +129,6 @@
   GList *child_list; /* list of GISourceSymbol */
 };
 
-struct _GISourceDirective
-{
-  char *name;
-  char *value;
-  GSList *options; /* list of options (key=value) */
-};
-
 GISourceScanner *   gi_source_scanner_new              (void);
 gboolean            gi_source_scanner_lex_filename     (GISourceScanner  *igenerator,
 						        const gchar      *filename);
@@ -148,8 +139,7 @@
 void                gi_source_scanner_set_macro_scan   (GISourceScanner  *scanner,
 							gboolean          macro_scan);
 GSList *            gi_source_scanner_get_symbols      (GISourceScanner  *scanner);
-GSList *            gi_source_scanner_get_directives   (GISourceScanner  *scanner,
-							const gchar      *name);
+GSList *            gi_source_scanner_get_comments     (GISourceScanner  *scanner);
 void                gi_source_scanner_free             (GISourceScanner  *scanner);
 
 GISourceSymbol *    gi_source_symbol_new               (GISourceSymbolType  type);
@@ -157,11 +147,6 @@
 GISourceSymbol *    gi_source_symbol_ref               (GISourceSymbol     *symbol);
 void                gi_source_symbol_unref             (GISourceSymbol     *symbol);
 
-GISourceDirective * gi_source_directive_new            (const gchar 	   *name,
-							const gchar 	   *value,
-							GSList             *options);
-void                gi_source_directive_free           (GISourceDirective  *directive);
-
 /* Private */
 void                gi_source_scanner_add_symbol       (GISourceScanner  *scanner,
 							GISourceSymbol   *symbol);
Index: giscanner/giscannermodule.c
===================================================================
--- giscanner/giscannermodule.c	(revision 996)
+++ giscanner/giscannermodule.c	(working copy)
@@ -64,11 +64,6 @@
 
 typedef struct {
   PyObject_HEAD
-  GISourceDirective *directive;
-} PyGISourceDirective;
-
-typedef struct {
-  PyObject_HEAD
   GISourceType *type;
 } PyGISourceType;
 
@@ -77,7 +72,6 @@
 typedef struct {
   PyObject_HEAD
   GISourceSymbol *symbol;
-  PyObject *directives;
 } PyGISourceSymbol;
 
 typedef struct {
@@ -85,76 +79,11 @@
   GISourceScanner *scanner;
 } PyGISourceScanner;
 
-NEW_CLASS (PyGISourceDirective, "SourceDirective", GISourceDirective);
 NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol);
 NEW_CLASS (PyGISourceType, "SourceType", GISourceType);
 NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner);
 
 
-/* Directive */
-
-static PyObject *
-pygi_source_directive_new (GISourceDirective *directive)
-{
-  PyGISourceDirective *self;
-
-  if (directive == NULL)
-    {
-      Py_INCREF (Py_None);
-      return Py_None;
-    }
-    
-  self = (PyGISourceDirective *)PyObject_New (PyGISourceDirective,
-					      &PyGISourceDirective_Type);
-  self->directive = directive;
-  return (PyObject*)self;
-}
-
-static PyObject *
-directive_get_name (PyGISourceDirective *self,
-		    void                *context)
-{
-  return PyString_FromString (self->directive->name);
-}
-
-static PyObject *
-directive_get_value (PyGISourceDirective *self,
-		     void                *context)
-{
-  return PyString_FromString (self->directive->value);
-}
-
-static PyObject *
-directive_get_options (PyGISourceDirective *self,
-		       void                *context)
-{
-  GSList *l;
-  PyObject *list;
-  int i = 0;
-
-  if (!self->directive)
-    return Py_BuildValue("[]");
-  
-  list = PyList_New (g_slist_length (self->directive->options));
-  
-  for (l = self->directive->options; l; l = l->next)
-    {
-      PyObject *item = PyString_FromString (l->data);
-      PyList_SetItem (list, i++, item);
-      Py_INCREF (item);
-    }
-
-  Py_INCREF (list);
-  return list;
-}
-
-static const PyGetSetDef _PyGISourceDirective_getsets[] = {
-  { "name", (getter)directive_get_name, NULL, NULL},
-  { "value", (getter)directive_get_value, NULL, NULL},
-  { "options", (getter)directive_get_options, NULL, NULL},
-  { 0 }
-};
-
 /* Symbol */
 
 static PyObject *
@@ -222,26 +151,6 @@
   return PyString_FromString (self->symbol->const_string);
 }
 
-static PyObject *
-symbol_get_directives (PyGISourceSymbol *self,
-		       void             *context)
-{
-  if (!self->directives)
-    self->directives = Py_BuildValue("[]");
-  Py_INCREF (self->directives);
-  return self->directives;
-}
-
-static int
-symbol_set_directives (PyGISourceSymbol *self,
-		       PyObject         *value,
-		       void             *context)
-{
-  self->directives = value;
-  Py_INCREF(value);
-  return 0;
-}
-
 static const PyGetSetDef _PyGISourceSymbol_getsets[] = {
   /* int ref_count; */
   { "type", (getter)symbol_get_type, NULL, NULL},
@@ -251,8 +160,6 @@
   /* gboolean const_int_set; */
   { "const_int", (getter)symbol_get_const_int, NULL, NULL},  
   { "const_string", (getter)symbol_get_const_string, NULL, NULL},  
-  { "directives", (getter)symbol_get_directives,
-    (setter)symbol_set_directives, NULL},  
   { 0 }
 };
 
@@ -555,23 +462,18 @@
 }
 
 static PyObject *
-pygi_source_scanner_get_directives (PyGISourceScanner *self,
-				    PyObject          *args)
+pygi_source_scanner_get_comments (PyGISourceScanner *self)
 {
-  GSList *l, *directives;
+  GSList *l, *comments;
   PyObject *list;
   int i = 0;
-  char *name;
   
-  if (!PyArg_ParseTuple (args, "s:SourceScanner.get_directives", &name))
-    return NULL;
+  comments = gi_source_scanner_get_comments (self->scanner);
+  list = PyList_New (g_slist_length (comments));
   
-  directives = gi_source_scanner_get_directives (self->scanner, name);
-  list = PyList_New (g_slist_length (directives));
-  
-  for (l = directives; l; l = l->next)
+  for (l = comments; l; l = l->next)
     {
-      PyObject *item = pygi_source_directive_new (l->data);
+      PyObject *item = PyString_FromString (l->data);
       PyList_SetItem (list, i++, item);
       Py_INCREF (item);
     }
@@ -581,7 +483,7 @@
 }
 
 static const PyMethodDef _PyGISourceScanner_methods[] = {
-  { "get_directives", (PyCFunction) pygi_source_scanner_get_directives, METH_VARARGS },
+  { "get_comments", (PyCFunction) pygi_source_scanner_get_comments, METH_NOARGS },
   { "get_symbols", (PyCFunction) pygi_source_scanner_get_symbols, METH_NOARGS },
   { "append_filename", (PyCFunction) pygi_source_scanner_append_filename, METH_VARARGS },
   { "parse_file", (PyCFunction) pygi_source_scanner_parse_file, METH_VARARGS },
@@ -611,7 +513,8 @@
       if (PyTuple_GetItem(tuple, 1) == Py_None)
 	continue;
 
-      g_assert(PyArg_ParseTuple(tuple, "ss", &attr, &value));
+      if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
+        return -1;
       
       escaped = g_markup_escape_text (value, -1);
       attr_length += 2 + strlen(attr) + strlen(escaped) + 2;
@@ -631,6 +534,7 @@
   char *indent_char;
   gboolean first;
   GString *attr_value;
+  int len;
   
   if (!PyArg_ParseTuple(args, "sOisi",
 			&tag_name, &attributes,
@@ -641,7 +545,10 @@
   if (attributes == Py_None || !PyList_Size(attributes))
     return PyString_FromString("");
 
-  if (calc_attrs_length(attributes, indent, self_indent) > 79)
+  len = calc_attrs_length(attributes, indent, self_indent);
+  if (len < 0)
+    return NULL;
+  if (len > 79)
     indent_len = self_indent + strlen(tag_name) + 1;
   else
     indent_len = 0;
@@ -660,7 +567,9 @@
       if (PyTuple_GetItem(tuple, 1) == Py_None)
 	continue;
 
-      g_assert(PyArg_ParseTuple(tuple, "ss", &attr, &value));
+      /* this leaks, but we exit after, so */
+      if (!PyArg_ParseTuple(tuple, "ss", &attr, &value))
+        return NULL;
 
       if (indent_len && !first)
 	{
@@ -699,9 +608,6 @@
 		       (PyMethodDef*)pyscanner_functions);
     d = PyModule_GetDict (m);
 
-    PyGISourceDirective_Type.tp_getset = (PyGetSetDef*)_PyGISourceDirective_getsets;
-    REGISTER_TYPE (d, "SourceDirective", PyGISourceDirective_Type);
-
     PyGISourceScanner_Type.tp_init = (initproc)pygi_source_scanner_init;
     PyGISourceScanner_Type.tp_methods = (PyMethodDef*)_PyGISourceScanner_methods;
     REGISTER_TYPE (d, "SourceScanner", PyGISourceScanner_Type);
Index: giscanner/sourcescanner.py
===================================================================
--- giscanner/sourcescanner.py	(revision 996)
+++ giscanner/sourcescanner.py	(working copy)
@@ -154,12 +154,6 @@
             symbol_type_name(self.type),
             self.ident)
 
-    def directives(self):
-        mapping = {}
-        for directive in self._scanner.get_directives(self._symbol.ident):
-            mapping[directive.name] = directive.options
-        return mapping
-
     @property
     def const_int(self):
         return self._symbol.const_int
@@ -227,6 +221,9 @@
         for symbol in self._scanner.get_symbols():
             yield SourceSymbol(self._scanner, symbol)
 
+    def get_comments(self):
+        return self._scanner.get_comments()
+
     def dump(self):
         print '-'*30
         for symbol in self._scanner.get_symbols():
Index: giscanner/Makefile.am
===================================================================
--- giscanner/Makefile.am	(revision 996)
+++ giscanner/Makefile.am	(working copy)
@@ -35,6 +35,7 @@
 pkgpyexec_LTLIBRARIES = _giscanner.la
 pkgpyexec_PYTHON = 		\
 	__init__.py		\
+	annotationparser.py	\
 	ast.py			\
 	cachestore.py		\
 	config.py		\
Index: giscanner/ast.py
===================================================================
--- giscanner/ast.py	(revision 996)
+++ giscanner/ast.py	(working copy)
@@ -200,7 +200,18 @@
         self.parameters = parameters
         self.symbol = symbol
         self.throws = not not throws
+        self.is_method = False
 
+    def get_parameter_index(self, name):
+        for i, parameter in enumerate(self.parameters):
+            if parameter.name == name:
+                return i + int(self.is_method)
+
+    def get_parameter(self, name):
+        for parameter in self.parameters:
+            if parameter.name == name:
+                return parameter
+
     def __repr__(self):
         return '%s(%r, %r, %r)' % (self.__class__.__name__,
                                    self.name, self.retval,
@@ -217,6 +228,9 @@
         Node.__init__(self, name)
         self.ctype = ctype
         self.resolved = False
+        self.is_const = False
+        self.canonical = None
+        self.derefed_canonical = None
 
 
 class Varargs(Type):
@@ -236,7 +250,7 @@
         self.size = None
 
     def __repr__(self):
-        return 'Array(%r of %r)' % (self.name, self.element_type, )
+        return 'Array(%r, %r)' % (self.name, self.element_type, )
 
 
 class List(Type):
@@ -282,17 +296,14 @@
         else:
             self.transfer = None
 
-        # transformer.py overrides this as needed
-        self.transfer_inferred = False
 
-
 class Parameter(TypeContainer):
 
-    def __init__(self, name, typenode, direction=PARAM_DIRECTION_IN,
+    def __init__(self, name, typenode, direction=None,
                  transfer=None, allow_none=False):
         TypeContainer.__init__(self, name, typenode, transfer)
         if direction in [PARAM_DIRECTION_IN, PARAM_DIRECTION_OUT,
-                         PARAM_DIRECTION_INOUT]:
+                         PARAM_DIRECTION_INOUT, None]:
             self.direction = direction
         else:
             self.direction = PARAM_DIRECTION_IN
@@ -325,7 +336,7 @@
         return 'Member(%r, %r)' % (self.name, self.value)
 
 
-class Struct(Node):
+class Record(Node):
 
     def __init__(self, name, symbol, disguised=False):
         Node.__init__(self, name)
@@ -334,7 +345,10 @@
         self.symbol = symbol
         self.disguised = disguised
 
+# BW compat, remove
+Struct = Record
 
+
 class Field(Node):
 
     def __init__(self, name, typenode, symbol, readable, writable, bits=None):
@@ -356,6 +370,7 @@
 
     def __init__(self, rtype, transfer=None):
         TypeContainer.__init__(self, None, rtype, transfer)
+        self.direction = PARAM_DIRECTION_OUT
 
     def __repr__(self):
         return 'Return(%r)' % (self.type, )
Index: giscanner/annotationparser.py
===================================================================
--- giscanner/annotationparser.py	(revision 0)
+++ giscanner/annotationparser.py	(revision 0)
@@ -0,0 +1,500 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2008  Johan Dahlin
+#
+# This program 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 2
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+# AnnotationParser - parses gtk-doc annotations
+
+# All gtk-doc comments needs to start with this:
+_COMMENT_HEADER = '*\n * '
+
+from .ast import (Array, Callback, Class, Enum, Function, Interface,
+                  List, Map, Parameter, Return, Record, Field, Type, Union,
+                  default_array_types,
+                  TYPE_NONE,
+                  BASIC_GIR_TYPES,
+                  PARAM_DIRECTION_INOUT,
+                  PARAM_DIRECTION_IN,
+                  PARAM_DIRECTION_OUT,
+                  PARAM_TRANSFER_NONE,
+                  PARAM_TRANSFER_CONTAINER,
+                  PARAM_TRANSFER_FULL)
+from .glibast import (GLibBoxed)
+
+
+class InvalidAnnotationError(Exception):
+    pass
+
+
+class DocBlock(object):
+
+    def __init__(self, name):
+        self.name = name
+        self.value = None
+        self.tags = {}
+
+    def __repr__(self):
+        return '<Directive %r>' % (self.name, )
+
+    def get(self, name):
+        if name == 'Returns':
+            value = self.tags.get(name)
+            if value is None:
+                return self.tags.get('Return value')
+            else:
+                return value
+        else:
+            return self.tags.get(name)
+
+
+class DocTag(object):
+
+    def __init__(self, name):
+        self.name = name
+        self.options = []
+
+
+class Option(object):
+
+    def __init__(self, option):
+        self._array = []
+        self._dict = {}
+        for p in option.split(' '):
+            if '=' in p:
+                name, value = p.split('=', 1)
+            else:
+                name = p
+                value = None
+            self._dict[name] = value
+            if value is None:
+                self._array.append(name)
+            else:
+                self._array.append((name, value))
+
+    def __repr__(self):
+        return '<Option %r>' % (self._array, )
+
+    def single(self):
+        assert len(self._array) == 1
+        return self._array[0]
+
+    def flat(self):
+        return self._array
+
+    def all(self):
+        return self._dict
+
+
+class AnnotationParser(object):
+
+    def __init__(self, namespace, source_scanner, transformer):
+        self._blocks = {}
+        self._namespace = namespace
+        self._transformer = transformer
+        for comment in source_scanner.get_comments():
+            self._parse_comment(comment)
+
+    def parse(self):
+        aa = AnnotationApplier(self._blocks, self._transformer)
+        aa.parse(self._namespace)
+
+    def _parse_comment(self, comment):
+        if not comment.startswith(_COMMENT_HEADER):
+            return
+        comment = comment[len(_COMMENT_HEADER):]
+        pos = comment.index('\n ')
+
+        block = DocBlock(comment[:pos-1])
+
+        content = comment[pos+1:]
+        for line in content.split('\n'):
+            line = line[2:].strip() # Skip ' *'
+            if not line:
+                continue
+
+            if line.startswith('@'):
+                line = line[1:]
+            elif not ': ' in line:
+                continue
+            tag = self._parse_tag(line)
+            block.tags[tag.name] = tag
+
+        self._blocks[block.name] = block
+
+    def _parse_tag(self, value):
+        # Tag: bar
+        # Tag: bar opt1 opt2
+        parts = value.split(': ', 1)
+        if len(parts) == 1:
+            tag_name = parts[0]
+            options = ''
+        else:
+            tag_name, options = parts
+        tag = DocTag(tag_name)
+        tag.value = options
+        tag.options = self._parse_options(options)
+        return tag
+
+    def _parse_options(self, value):
+        # (foo)
+        # (bar opt1 opt2...)
+        opened = -1
+        options = {}
+        for i, c in enumerate(value):
+            if c == '(' and opened == -1:
+                opened = i+1
+            if c == ')' and opened != -1:
+                segment = value[opened:i]
+                parts = segment.split(' ', 1)
+                if len(parts) == 2:
+                    name, option = parts
+                elif len(parts) == 1:
+                    name = parts[0]
+                    option = None
+                else:
+                    raise AssertionError
+                if option is not None:
+                    option = Option(option)
+                options[name] = option
+                opened = -1
+        return options
+
+
+class AnnotationApplier(object):
+
+    def __init__(self, blocks, transformer):
+        self._blocks = blocks
+        self._transformer = transformer
+
+    def _get_tag(self, block, tag_name):
+        if block is None:
+            return None
+
+        return block.get(tag_name)
+
+    def parse(self, namespace):
+        for node in namespace.nodes:
+            self._parse_node(node)
+
+    # Boring parsing boilerplate.
+
+    def _parse_node(self, node):
+        if isinstance(node, Function):
+            self._parse_function(node)
+        elif isinstance(node, Enum):
+            self._parse_enum(node)
+        elif isinstance(node, Class):
+            self._parse_class(node)
+        elif isinstance(node, Interface):
+            self._parse_interface(node)
+        elif isinstance(node, Callback):
+            self._parse_callback(node)
+        elif isinstance(node, Record):
+            self._parse_record(node)
+        elif isinstance(node, Union):
+            self._parse_union(node)
+        elif isinstance(node, GLibBoxed):
+            self._parse_boxed(node)
+
+    def _parse_class(self, class_):
+        block = self._blocks.get(class_.name)
+        self._parse_version(class_, block)
+        self._parse_constructors(class_.constructors)
+        self._parse_methods(class_.methods)
+        self._parse_methods(class_.static_methods)
+        self._parse_properties(class_.properties)
+        self._parse_signals(class_.signals)
+        self._parse_fields(class_, class_.fields)
+
+    def _parse_interface(self, interface):
+        block = self._blocks.get(interface.name)
+        self._parse_version(interface, block)
+        self._parse_methods(interface.methods)
+        self._parse_properties(interface.properties)
+        self._parse_signals(interface.signals)
+        self._parse_fields(interface, interface.fields)
+
+    def _parse_record(self, record):
+        block = self._blocks.get(record.symbol)
+        self._parse_version(record, block)
+        self._parse_constructors(record.constructors)
+        self._parse_fields(record, record.fields)
+        if isinstance(record, GLibBoxed):
+            self._parse_methods(record.methods)
+
+    def _parse_boxed(self, boxed):
+        block = self._blocks.get(boxed.name)
+        self._parse_version(boxed, block)
+        self._parse_constructors(boxed.constructors)
+        self._parse_methods(boxed.methods)
+
+    def _parse_union(self, union):
+        block = self._blocks.get(union.name)
+        self._parse_fields(union, union.fields)
+        self._parse_constructors(union.constructors)
+        if isinstance(union, GLibBoxed):
+            self._parse_methods(union.methods)
+
+    def _parse_enum(self, enum):
+        block = self._blocks.get(enum.symbol)
+        self._parse_version(enum, block)
+
+    def _parse_constructors(self, constructors):
+        for ctor in constructors:
+            self._parse_function(ctor)
+
+    def _parse_fields(self, parent, fields):
+        for field in fields:
+            self._parse_field(parent, field)
+
+    def _parse_properties(self, properties):
+        for prop in properties:
+            self._parse_property(property)
+
+    def _parse_methods(self, methods):
+        for method in methods:
+            self._parse_function(method)
+
+    def _parse_signals(self, signals):
+        for signal in signals:
+            self._parse_signal(signal)
+
+    def _parse_property(self, prop):
+        pass
+
+    def _parse_callback(self, callback):
+        block = self._blocks.get(callback.ctype)
+        self._parse_version(callback, block)
+        self._parse_params(callback, callback.parameters, block)
+        self._parse_return(callback, callback.retval, block)
+
+    def _parse_function(self, func):
+        block = self._blocks.get(func.symbol)
+        self._parse_version(func, block)
+        self._parse_deprecated(func, block)
+        self._parse_params(func, func.parameters, block)
+        self._parse_return(func, func.retval, block)
+
+    def _parse_signal(self, signal):
+        block = self._blocks.get(signal.name)
+        self._parse_version(signal, block)
+        self._parse_params(signal, signal.parameters, block)
+        self._parse_return(signal, signal.retval, block)
+
+    def _parse_field(self, parent, field):
+        if isinstance(field, Callback):
+            self._parse_callback(field)
+
+    def _parse_params(self, parent, params, block):
+        for param in params:
+            self._parse_param(parent, param, block)
+
+    def _parse_return(self, parent, return_, block):
+        tag = self._get_tag(block, 'Returns')
+        self._parse_param_ret_common(parent, return_, tag)
+
+    def _parse_param(self, parent, param, block):
+        tag = self._get_tag(block, param.name)
+        self._parse_param_ret_common(parent, param, tag)
+
+    def _parse_param_ret_common(self, parent, node, tag):
+        options = getattr(tag, 'options', {})
+        node.direction = self._extract_direction(node, options)
+        container_type = self._extract_container_type(
+            parent, node, options)
+        if container_type is not None:
+            node.type = container_type
+        if not node.direction:
+            node.direction = self._guess_direction(node)
+        node.transfer = self._extract_transfer(parent, node, options)
+        node.allow_none = 'allow-none' in options
+
+        assert node.transfer is not None
+
+    def _extract_direction(self, node, options):
+        if ('inout' in options or
+            'in-out' in options):
+            direction = PARAM_DIRECTION_INOUT
+        elif 'out' in options:
+            direction = PARAM_DIRECTION_OUT
+        elif 'in' in options:
+            direction = PARAM_DIRECTION_IN
+        else:
+            direction = node.direction
+        return direction
+
+    def _guess_array(self, node):
+        ctype = node.type.ctype
+        if ctype is None:
+            return False
+        if not ctype.endswith('*'):
+            return False
+        if node.type.canonical in default_array_types:
+            return True
+        return False
+
+    def _extract_container_type(self, parent, node, options):
+        has_element_type = 'element-type' in options
+        has_array = 'array' in options
+
+        # FIXME: This is a hack :-(
+        if (not isinstance(node, Field) and
+            (not has_element_type and
+             (node.direction is None
+              or node.direction == PARAM_DIRECTION_IN))):
+            if self._guess_array(node):
+                has_array = True
+
+        if has_array:
+            container_type = self._parse_array(parent, node, options)
+        elif has_element_type:
+            container_type = self._parse_element_type(parent, node, options)
+        else:
+            container_type = None
+
+        return container_type
+
+    def _parse_array(self, parent, node, options):
+        array_opt = options.get('array')
+        if array_opt:
+            values = array_opt.all()
+        else:
+            values = {}
+        container_type = Array(node.type.ctype, node.type.name)
+        if 'zero-terminated' in values:
+            container_type.zeroterminated = values.get(
+                'zero-terminated') == '1'
+        length = values.get('length')
+        if length is not None:
+            param_index = parent.get_parameter_index(length)
+            container_type.length_param_index = param_index
+        container_type.size = values.get('fized-size')
+        return container_type
+
+    def _parse_element_type(self, parent, node, options):
+        element_type_opt = options.get('element-type')
+        element_type = element_type_opt.flat()
+        if node.type.name in ['GLib.List', 'GLib.SList']:
+            assert len(element_type) == 1
+            etype = Type(element_type[0])
+            container_type = List(
+                node.type.name,
+                node.type.ctype,
+                self._transformer.resolve_param_type(etype))
+        elif node.type.name in ['GLib.HashTable']:
+            assert len(element_type) == 2
+            key_type = Type(element_type[0])
+            value_type = Type(element_type[1])
+            container_type = Map(
+                node.type.name,
+                node.type.ctype,
+                self._transformer.resolve_param_type(key_type),
+                self._transformer.resolve_param_type(value_type))
+        else:
+            print 'FIXME: unhandled element-type container:', node
+        return container_type
+
+    def _extract_transfer(self, parent, node, options):
+        transfer_opt = options.get('transfer')
+        if transfer_opt is None:
+            transfer = self._guess_transfer(node, options)
+        else:
+            transfer = transfer_opt.single()
+            if transfer is None:
+                transfer = PARAM_TRANSFER_FULL
+            if transfer not in [PARAM_TRANSFER_NONE,
+                                PARAM_TRANSFER_CONTAINER,
+                                PARAM_TRANSFER_FULL]:
+                raise InvalidAnnotationError(
+                    "transfer for %s of %r is invalid (%r), must be one of "
+                    "none, container, full." % (node, parent.name, transfer))
+        return transfer
+
+    def _parse_version(self, node, block):
+        since_tag = self._get_tag(block, 'Since')
+        if since_tag is None:
+            return
+        node.version = since_tag.value
+
+    def _parse_deprecated(self, node, block):
+        deprecated_tag = self._get_tag(block, 'Deprecated')
+        if deprecated_tag is None:
+            return
+        value = deprecated_tag.value
+        if ': ' in value:
+            version, desc = value.split(': ')
+        else:
+            desc = value
+            version = None
+        node.deprecated = desc
+        if version is not None:
+            node.deprecated_version = version
+
+    def _guess_direction(self, node):
+        if node.direction:
+            return node.direction
+        is_pointer = False
+        if node.type.ctype:
+            is_pointer = '*' in node.type.ctype
+
+        if is_pointer and node.type.name in BASIC_GIR_TYPES:
+            return PARAM_DIRECTION_OUT
+
+        return PARAM_DIRECTION_IN
+
+    def _guess_transfer(self, node, options):
+        if node.transfer is not None:
+            return node.transfer
+
+        tname = node.type.name
+        is_pointer = False
+        is_basic = node.type.canonical in BASIC_GIR_TYPES
+        is_derefed_basic = node.type.derefed_canonical in BASIC_GIR_TYPES
+        if node.type.ctype:
+            is_pointer = '*' in node.type.ctype
+
+        if tname == TYPE_NONE:
+            return PARAM_TRANSFER_NONE
+
+        if isinstance(node.type, Array):
+            return PARAM_TRANSFER_NONE
+        # Input basic types default to 'none'
+        if (node.direction == PARAM_DIRECTION_IN and is_basic):
+            return PARAM_TRANSFER_NONE
+        # Anything with 'const' gets none
+        if node.type.is_const:
+            return PARAM_TRANSFER_NONE
+
+        is_param = isinstance(node, Parameter)
+        is_field = isinstance(node, Field)
+        if not (is_param or is_field):
+            assert isinstance(node, Return)
+            is_return = True
+        if is_param:
+            if node.direction in [PARAM_DIRECTION_INOUT,
+                                  PARAM_DIRECTION_OUT]:
+                return PARAM_TRANSFER_FULL
+            else:
+                return PARAM_TRANSFER_NONE
+        elif is_return:
+            if is_basic:
+                return PARAM_TRANSFER_NONE
+            else:
+                return PARAM_TRANSFER_FULL
+        else:
+            return PARAM_TRANSFER_NONE
