aa04ef4423f4025db1f4c59ef5c8e5f890daed1b
[gnome.gobject-introspection] / giscanner / annotationparser.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 # 02110-1301, USA.
19 #
20
21 # AnnotationParser - parses gtk-doc annotations
22
23 import re
24 import sys
25
26 from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
27                   Interface, List, Map, Parameter, Record, Return, Type, Union,
28                   Varargs,
29                   default_array_types,
30                   BASIC_GIR_TYPES,
31                   PARAM_DIRECTION_INOUT,
32                   PARAM_DIRECTION_IN,
33                   PARAM_DIRECTION_OUT,
34                   PARAM_TRANSFER_NONE,
35                   PARAM_TRANSFER_CONTAINER,
36                   PARAM_TRANSFER_FULL,
37                   TYPE_ANY, TYPE_NONE)
38 from .odict import odict
39 from .glibast import GLibBoxed
40
41 # All gtk-doc comments needs to start with this:
42 _COMMENT_HEADER = '*\n '
43
44 # Tags - annotations applyed to comment blocks
45 TAG_VFUNC = 'virtual'
46 TAG_SINCE = 'since'
47 TAG_DEPRECATED = 'deprecated'
48 TAG_RETURNS = 'returns'
49 TAG_RETURNS_ALT = 'return value'
50 TAG_ATTRIBUTES = 'attributes'
51 TAG_RENAME_TO = 'rename to'
52
53 # Options - annotations for parameters and return values
54 OPT_ALLOW_NONE = 'allow-none'
55 OPT_ARRAY = 'array'
56 OPT_ELEMENT_TYPE = 'element-type'
57 OPT_IN = 'in'
58 OPT_INOUT = 'inout'
59 OPT_INOUT_ALT = 'in-out'
60 OPT_OUT = 'out'
61 OPT_SCOPE = 'scope'
62 OPT_TRANSFER = 'transfer'
63 OPT_TYPE = 'type'
64 OPT_CLOSURE = 'closure'
65 OPT_DESTROY = 'destroy'
66 OPT_SKIP = 'skip'
67
68 # Specific option values
69 OPT_VAL_BITFIELD = 'bitfield'
70
71 # Array options - array specific annotations
72 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
73 OPT_ARRAY_LENGTH = 'length'
74 OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated'
75
76 OPT_SCOPE_ASYNC = 'async'
77 OPT_SCOPE_CALL = 'call'
78 OPT_SCOPE_NOTIFIED = 'notified'
79
80 class InvalidAnnotationError(Exception):
81     pass
82
83
84 class DocBlock(object):
85
86     def __init__(self, name, options):
87         self.name = name
88         self.options = options
89         self.value = None
90         self.tags = odict()
91         self.comment = None
92
93     def __repr__(self):
94         return '<DocBlock %r %r>' % (self.name, self.options)
95
96     def get(self, name):
97         if name == TAG_RETURNS:
98             value = self.tags.get(name)
99             if value is None:
100                 return self.tags.get(TAG_RETURNS_ALT)
101             else:
102                 return value
103         else:
104             return self.tags.get(name)
105
106
107 class DocTag(object):
108
109     def __init__(self, name):
110         self.name = name
111         self.options = {}
112         self.comment = None
113
114     def __repr__(self):
115         return '<DocTag %r %r>' % (self.name, self.options)
116
117 class Option(object):
118
119     def __init__(self, option):
120         self._array = []
121         self._dict = {}
122         for p in option.split(' '):
123             if '=' in p:
124                 name, value = p.split('=', 1)
125             else:
126                 name = p
127                 value = None
128             self._dict[name] = value
129             if value is None:
130                 self._array.append(name)
131             else:
132                 self._array.append((name, value))
133
134     def __repr__(self):
135         return '<Option %r>' % (self._array, )
136
137     def one(self):
138         assert len(self._array) == 1
139         return self._array[0]
140
141     def flat(self):
142         return self._array
143
144     def all(self):
145         return self._dict
146
147
148 class AnnotationParser(object):
149
150     def __init__(self, namespace, source_scanner, transformer):
151         self._blocks = {}
152         self._namespace = namespace
153         self._transformer = transformer
154         for comment in source_scanner.get_comments():
155             self._parse_comment(comment)
156
157     def parse(self):
158         aa = AnnotationApplier(self._blocks, self._transformer)
159         aa.parse(self._namespace)
160
161     def _parse_comment(self, comment):
162         # We're looking for gtk-doc comments here, they look like this:
163         # /**
164         #   * symbol:
165         #
166         # Or, alternatively, with options:
167         # /**
168         #   * symbol: (name value) ...
169         #
170         # symbol is currently one of:
171         #  - function: gtk_widget_show
172         #  - signal:   GtkWidget::destroy
173         #  - property: GtkWidget:visible
174         #
175         comment = comment.lstrip()
176         if not comment.startswith(_COMMENT_HEADER):
177             return
178         comment = comment[len(_COMMENT_HEADER):]
179         comment = comment.strip()
180         if not comment.startswith('* '):
181             return
182         comment = comment[2:]
183
184         pos = comment.find('\n ')
185         if pos == -1:
186             return
187         block_header = comment[:pos]
188         block_header = block_header.strip()
189         cpos = block_header.find(': ')
190         if cpos:
191             block_name = block_header[:cpos]
192             block_options, rest = self._parse_options(block_header[cpos+2:])
193             if rest:
194                 return
195         else:
196             block_name, block_options = block_header, {}
197         block = DocBlock(block_name, block_options)
198         comment_lines = []
199         for line in comment[pos+1:].split('\n'):
200             line = line.lstrip()
201             line = line[2:].strip() # Skip ' *'
202             if not line:
203                 continue
204             if line.startswith('@'):
205                 line = line[1:]
206             elif not ': ' in line:
207                 comment_lines.append(line)
208                 continue
209             tag_name, value = self._split_tag_namevalue(line)
210             canon_name = tag_name.lower()
211             if canon_name in block.tags:
212                 print >> sys.stderr, (
213                     "Symbol %s has multiple definition of tag %r" % (
214                     block_name, canon_name, ))
215             block.tags[canon_name] = self._create_tag(canon_name, value)
216         block.comment = '\n'.join(comment_lines)
217         self._blocks[block.name] = block
218
219     def _split_tag_namevalue(self, raw):
220         """Split a line into tag name and value"""
221         parts = raw.split(': ', 1)
222         if len(parts) == 1:
223             tag_name = parts[0]
224             value = ''
225             if tag_name.endswith(':'):
226                 tag_name = tag_name[:-1]
227         else:
228             tag_name, value = parts
229         return (tag_name, value)
230
231     def _create_tag(self, tag_name, value):
232         # Tag: bar
233         # Tag: bar opt1 opt2
234         tag = DocTag(tag_name)
235         tag.value = value
236         options, rest = self._parse_options(tag.value)
237         tag.options = options
238         tag.comment = rest
239         return tag
240
241     def _parse_options(self, value):
242         # (foo)
243         # (bar opt1 opt2...)
244         opened = -1
245         options = {}
246         last = None
247         for i, c in enumerate(value):
248             if c == '(' and opened == -1:
249                 opened = i+1
250             if c == ')' and opened != -1:
251                 segment = value[opened:i]
252                 parts = segment.split(' ', 1)
253                 if len(parts) == 2:
254                     name, option = parts
255                 elif len(parts) == 1:
256                     name = parts[0]
257                     option = None
258                 else:
259                     raise AssertionError
260                 if option is not None:
261                     option = Option(option)
262                 options[name] = option
263                 last = i + 2
264                 opened = -1
265
266         if last is not None:
267             rest = value[last:].strip()
268         else:
269             rest = None
270         return options, rest
271
272
273 class AnnotationApplier(object):
274
275     def __init__(self, blocks, transformer):
276         self._blocks = blocks
277         self._transformer = transformer
278
279     def _get_tag(self, block, tag_name):
280         if block is None:
281             return None
282
283         return block.get(tag_name)
284
285     def parse(self, namespace):
286         self._namespace = namespace
287         for node in namespace.nodes[:]:
288             self._parse_node(node)
289         del self._namespace
290
291     # Boring parsing boilerplate.
292
293     def _parse_node(self, node):
294         if isinstance(node, Function):
295             self._parse_function(node)
296         elif isinstance(node, Enum):
297             self._parse_enum(node)
298         elif isinstance(node, Bitfield):
299             self._parse_bitfield(node)
300         elif isinstance(node, Class):
301             self._parse_class(node)
302         elif isinstance(node, Interface):
303             self._parse_interface(node)
304         elif isinstance(node, Callback):
305             self._parse_callback(node)
306         elif isinstance(node, Record):
307             self._parse_record(node)
308         elif isinstance(node, Union):
309             self._parse_union(node)
310         elif isinstance(node, GLibBoxed):
311             self._parse_boxed(node)
312
313     def _parse_class(self, class_):
314         block = self._blocks.get(class_.type_name)
315         self._parse_node_common(class_, block)
316         self._parse_constructors(class_.constructors)
317         self._parse_methods(class_, class_.methods)
318         self._parse_vfuncs(class_, class_.virtual_methods)
319         self._parse_methods(class_, class_.static_methods)
320         self._parse_properties(class_, class_.properties)
321         self._parse_signals(class_, class_.signals)
322         self._parse_fields(class_, class_.fields)
323         if block:
324             class_.doc = block.comment
325
326     def _parse_interface(self, interface):
327         block = self._blocks.get(interface.type_name)
328         self._parse_node_common(interface, block)
329         self._parse_methods(interface, interface.methods)
330         self._parse_vfuncs(interface, interface.virtual_methods)
331         self._parse_properties(interface, interface.properties)
332         self._parse_signals(interface, interface.signals)
333         self._parse_fields(interface, interface.fields)
334         if block:
335             interface.doc = block.comment
336
337     def _parse_record(self, record):
338         block = self._blocks.get(record.symbol)
339         self._parse_node_common(record, block)
340         self._parse_constructors(record.constructors)
341         self._parse_methods(record, record.methods)
342         self._parse_fields(record, record.fields, block)
343         if block:
344             record.doc = block.comment
345
346     def _parse_boxed(self, boxed):
347         block = self._blocks.get(boxed.name)
348         self._parse_node_common(boxed, block)
349         self._parse_constructors(boxed.constructors)
350         self._parse_methods(boxed, boxed.methods)
351         if block:
352             boxed.doc = block.comment
353
354     def _parse_union(self, union):
355         block = self._blocks.get(union.name)
356         self._parse_node_common(union, block)
357         self._parse_fields(union, union.fields, block)
358         self._parse_constructors(union.constructors)
359         self._parse_methods(union, union.methods)
360         if block:
361             union.doc = block.comment
362
363     def _parse_enum(self, enum):
364         block = self._blocks.get(enum.symbol)
365         self._parse_node_common(enum, block)
366         if block:
367             enum.doc = block.comment
368             type_opt = block.options.get(OPT_TYPE)
369             if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
370                 # This is hack, but hey, it works :-)
371                 enum.__class__ = Bitfield
372
373     def _parse_bitfield(self, bitfield):
374         block = self._blocks.get(bitfield.symbol)
375         self._parse_node_common(bitfield, block)
376         if block:
377             bitfield.doc = block.comment
378
379     def _parse_constructors(self, constructors):
380         for ctor in constructors:
381             self._parse_function(ctor)
382
383     def _parse_fields(self, parent, fields, block=None):
384         for field in fields:
385             self._parse_field(parent, field, block)
386
387     def _parse_properties(self, parent, properties):
388         for prop in properties:
389             self._parse_property(parent, prop)
390
391     def _parse_methods(self, parent, methods):
392         for method in methods:
393             self._parse_method(parent, method)
394
395     def _parse_vfuncs(self, parent, vfuncs):
396         for vfunc in vfuncs:
397             self._parse_vfunc(parent, vfunc)
398
399     def _parse_signals(self, parent, signals):
400         for signal in signals:
401             self._parse_signal(parent, signal)
402
403     def _parse_property(self, parent, prop):
404         block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
405         self._parse_node_common(prop, block)
406         if block:
407             prop.doc = block.comment
408
409     def _parse_callback(self, callback):
410         block = self._blocks.get(callback.ctype)
411         self._parse_node_common(callback, block)
412         self._parse_params(callback, callback.parameters, block)
413         self._parse_return(callback, callback.retval, block)
414         if block:
415             callback.doc = block.comment
416
417     def _parse_callable(self, callable, block):
418         self._parse_node_common(callable, block)
419         for i, param in enumerate(callable.parameters):
420             if (param.type.ctype != 'GDestroyNotify' and
421                 param.type.name != 'GLib.DestroyNotify'):
422                 continue
423             if i < 2:
424                 break
425             callback_param = callable.parameters[i-2]
426             if callback_param.closure_index != -1:
427                 callback_param.scope = OPT_SCOPE_NOTIFIED
428                 callback_param.transfer = PARAM_TRANSFER_NONE
429
430         self._parse_params(callable, callable.parameters, block)
431         self._parse_return(callable, callable.retval, block)
432         if block:
433             callable.doc = block.comment
434
435     def _parse_function(self, func):
436         block = self._blocks.get(func.symbol)
437         self._parse_callable(func, block)
438         self._parse_rename_to_func(func, block)
439
440     def _parse_signal(self, parent, signal):
441         block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
442         self._parse_node_common(signal, block)
443         # We're only attempting to name the signal parameters if
444         # the number of parameter tags (@foo) is the same or greater
445         # than the number of signal parameters
446         if block and len(block.tags) > len(signal.parameters):
447             names = block.tags.items()
448         else:
449             names = []
450         for i, param in enumerate(signal.parameters):
451             if names:
452                 name, tag = names[i+1]
453                 param.name = name
454                 options = getattr(tag, 'options', {})
455                 param_type = options.get(OPT_TYPE)
456                 if param_type:
457                     param.type = self._resolve(param_type.one(), param.type)
458             else:
459                 tag = None
460             self._parse_param(signal, param, tag)
461         self._parse_return(signal, signal.retval, block)
462         if block:
463             signal.doc = block.comment
464
465     def _parse_method(self, parent, meth):
466         block = self._blocks.get(meth.symbol)
467         self._parse_function(meth)
468         virtual = self._get_tag(block, TAG_VFUNC)
469         if virtual:
470             invoker_name = virtual.value
471             matched = False
472             for vfunc in parent.virtual_methods:
473                 if vfunc.name == invoker_name:
474                     matched = True
475                     vfunc.invoker = meth.name
476                     break
477             if not matched:
478                 print "warning: unmatched virtual invoker %r for method %r" % \
479                     (invoker_name, meth.symbol)
480
481     def _parse_vfunc(self, parent, vfunc):
482         key = '%s::%s' % (parent.type_name, vfunc.name)
483         self._parse_callable(vfunc, self._blocks.get(key))
484
485     def _parse_field(self, parent, field, block=None):
486         if isinstance(field, Callback):
487             self._parse_callback(field)
488         else:
489             if not block:
490                 return
491             tag = block.get(field.name)
492             if not tag:
493                 return
494             t = tag.options.get('type')
495             if not t:
496                 return
497             field.type.name = self._transformer.resolve_type_name(t.one())
498
499     def _parse_params(self, parent, params, block):
500         for param in params:
501             tag = self._get_tag(block, param.name)
502             self._parse_param(parent, param, tag)
503
504     def _parse_return(self, parent, return_, block):
505         tag = self._get_tag(block, TAG_RETURNS)
506         self._parse_param_ret_common(parent, return_, tag)
507
508     def _parse_param(self, parent, param, tag):
509         options = getattr(tag, 'options', {})
510         if isinstance(parent, Function):
511             scope = options.get(OPT_SCOPE)
512             if scope:
513                 param.scope = scope.one()
514                 param.transfer = PARAM_TRANSFER_NONE
515             elif (param.type.ctype == 'GAsyncReadyCallback' or
516                   param.type.name == 'Gio.AsyncReadyCallback'):
517                 param.scope = OPT_SCOPE_ASYNC
518                 param.transfer = PARAM_TRANSFER_NONE
519
520             destroy = options.get(OPT_DESTROY)
521             if destroy:
522                 param.destroy_index = parent.get_parameter_index(destroy.one())
523                 self._fixup_param_destroy(parent, param)
524             closure = options.get(OPT_CLOSURE)
525             if closure:
526                 param.closure_index = parent.get_parameter_index(closure.one())
527                 self._fixup_param_closure(parent, param)
528         if isinstance(parent, Callback):
529             if OPT_CLOSURE in options:
530                 param.closure_index = parent.get_parameter_index(param.name)
531                 self._fixup_param_closure(parent, param)
532
533         self._parse_param_ret_common(parent, param, tag)
534
535     def _fixup_param_destroy(self, parent, param):
536         for p in parent.parameters:
537             if p is not param and p.destroy_index == param.destroy_index:
538                 p.destroy_index = None
539
540     def _fixup_param_closure(self, parent, param):
541         for p in parent.parameters:
542             if p is not param and p.closure_index == param.closure_index:
543                 p.closure_index = None
544
545     def _parse_param_ret_common(self, parent, node, tag):
546         options = getattr(tag, 'options', {})
547         node.direction = self._extract_direction(node, options)
548         container_type = self._extract_container_type(
549             parent, node, options)
550         if container_type is not None:
551             node.type = container_type
552         if node.direction is None:
553             node.direction = self._guess_direction(node)
554         node.transfer = self._extract_transfer(parent, node, options)
555         if OPT_ALLOW_NONE in options:
556             node.allow_none = True
557         param_type = options.get(OPT_TYPE)
558         if param_type:
559             node.type = self._resolve(param_type.one(), node.type)
560
561         assert node.transfer is not None
562         if tag is not None and tag.comment is not None:
563             node.doc = tag.comment
564
565     def _extract_direction(self, node, options):
566         if (OPT_INOUT in options or
567             OPT_INOUT_ALT in options):
568             direction = PARAM_DIRECTION_INOUT
569         elif OPT_OUT in options:
570             direction = PARAM_DIRECTION_OUT
571         elif OPT_IN in options:
572             direction = PARAM_DIRECTION_IN
573         else:
574             direction = node.direction
575         return direction
576
577     def _guess_array(self, node):
578         ctype = node.type.ctype
579         if ctype is None:
580             return False
581         if not ctype.endswith('*'):
582             return False
583         if node.type.canonical in default_array_types:
584             return True
585         return False
586
587     def _extract_container_type(self, parent, node, options):
588         has_element_type = OPT_ELEMENT_TYPE in options
589         has_array = OPT_ARRAY in options
590
591         # FIXME: This is a hack :-(
592         if (not isinstance(node, Field) and
593             (not has_element_type and
594              (node.direction is None
595               or isinstance(node, Return)
596               or node.direction == PARAM_DIRECTION_IN))):
597             if self._guess_array(node):
598                 has_array = True
599
600         if has_array:
601             container_type = self._parse_array(parent, node, options)
602         elif has_element_type:
603             container_type = self._parse_element_type(parent, node, options)
604         else:
605             container_type = None
606
607         return container_type
608
609     def _parse_array(self, parent, node, options):
610         array_opt = options.get(OPT_ARRAY)
611         if array_opt:
612             array_values = array_opt.all()
613         else:
614             array_values = {}
615
616         element_type = options.get(OPT_ELEMENT_TYPE)
617         if element_type is not None:
618             element_type_node = self._resolve(element_type.one())
619         else:
620             element_type_node = Type(node.type.name) # erase ctype
621
622         container_type = Array(node.type.ctype,
623                                element_type_node)
624         container_type.is_const = node.type.is_const
625         if OPT_ARRAY_ZERO_TERMINATED in array_values:
626             container_type.zeroterminated = array_values.get(
627                 OPT_ARRAY_ZERO_TERMINATED) == '1'
628         length = array_values.get(OPT_ARRAY_LENGTH)
629         if length is not None:
630             param_index = parent.get_parameter_index(length)
631             container_type.length_param_index = param_index
632             # For in parameters we're incorrectly deferring
633             # char/unsigned char to utf8 when a length annotation
634             # is specified.
635             if (isinstance(node, Parameter) and
636                 node.type.name == 'utf8' and
637                 self._guess_direction(node) == PARAM_DIRECTION_IN and
638                 element_type is None):
639                 # FIXME: unsigned char/guchar should be uint8
640                 container_type.element_type = Type('int8')
641         container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
642         return container_type
643
644     def _resolve(self, type_str, orig_node=None):
645         def grab_one(type_str, resolver, top_combiner, combiner):
646             """Return a complete type, and the trailing string part after it.
647             Use resolver() on each identifier, and combiner() on the parts of
648             each complete type. (top_combiner is used on the top-most type.)"""
649             bits = re.split(r'([,<>])', type_str, 1)
650             first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
651             args = [resolver(first)]
652             if sep == '<':
653                 while sep != '>':
654                     next, rest = grab_one(rest, resolver, combiner, combiner)
655                     args.append(next)
656                     sep, rest = rest[0], rest[1:]
657             else:
658                 rest = sep + rest
659             return top_combiner(*args), rest
660         def resolver(ident):
661             return self._transformer.resolve_param_type(Type(ident))
662         def combiner(base, *rest):
663             if not rest:
664                 return base
665             if (base.name in ['GLib.List', 'GLib.SList'] or
666                 base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
667                 return List(base.name, base.ctype, *rest)
668             if (base.name in ['GLib.HashTable'] or
669                 base.ctype in ['GHashTable*']) and len(rest)==2:
670                 return Map(base.name, base.ctype, *rest)
671             print "WARNING: throwing away type parameters:", type_str
672             return base
673         def top_combiner(base, *rest):
674             """For the top most type, recycle orig_node if possible."""
675             if orig_node is not None:
676                 orig_node.name = base.name
677                 base = orig_node # preserve other properties of orig_node
678             return combiner(base, *rest)
679
680         result, rest = grab_one(type_str, resolver, top_combiner, combiner)
681         if rest:
682             print "WARNING: throwing away trailing part of type:", type_str
683         return result
684
685     def _parse_element_type(self, parent, node, options):
686         element_type_opt = options.get(OPT_ELEMENT_TYPE)
687         element_type = element_type_opt.flat()
688         if (node.type.name in ['GLib.List', 'GLib.SList'] or
689             node.type.ctype in ['GList*', 'GSList*']):
690             assert len(element_type) == 1
691             container_type = List(
692                 node.type.name,
693                 node.type.ctype,
694                 self._resolve(element_type[0]))
695         elif (node.type.name in ['GLib.HashTable'] or
696               node.type.ctype in ['GHashTable*']):
697             assert len(element_type) == 2
698             container_type = Map(
699                 node.type.name,
700                 node.type.ctype,
701                 self._resolve(element_type[0]),
702                 self._resolve(element_type[1]))
703         else:
704             print 'FIXME: unhandled element-type container:', node
705         return container_type
706
707     def _extract_transfer(self, parent, node, options):
708         transfer_opt = options.get(OPT_TRANSFER)
709         if transfer_opt is None:
710             transfer = self._guess_transfer(node, options)
711         else:
712             transfer = transfer_opt.one()
713             if transfer is None:
714                 transfer = PARAM_TRANSFER_FULL
715             if transfer not in [PARAM_TRANSFER_NONE,
716                                 PARAM_TRANSFER_CONTAINER,
717                                 PARAM_TRANSFER_FULL]:
718                 raise InvalidAnnotationError(
719                     "transfer for %s of %r is invalid (%r), must be one of "
720                     "none, container, full." % (node, parent.name, transfer))
721         return transfer
722
723     def _parse_node_common(self, node, block):
724         self._parse_version(node, block)
725         self._parse_deprecated(node, block)
726         self._parse_attributes(node, block)
727         self._parse_skip(node, block)
728
729     def _parse_version(self, node, block):
730         since_tag = self._get_tag(block, TAG_SINCE)
731         if since_tag is None:
732             return
733         node.version = since_tag.value
734
735     def _parse_deprecated(self, node, block):
736         deprecated_tag = self._get_tag(block, TAG_DEPRECATED)
737         if deprecated_tag is None:
738             return
739         value = deprecated_tag.value
740         if ': ' in value:
741             version, desc = value.split(': ')
742         else:
743             desc = value
744             version = None
745         node.deprecated = desc
746         if version is not None:
747             node.deprecated_version = version
748
749     def _parse_attributes(self, node, block):
750         annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
751         if annos_tag is None:
752             return
753         for key, value in annos_tag.options.iteritems():
754             node.attributes.append((key, value.one()))
755
756     def _parse_skip(self, node, block):
757         if block is not None:
758             if OPT_SKIP in block.options:
759                 node.skip = True
760
761     def _parse_rename_to_func(self, node, block):
762         rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
763         if rename_to_tag is None:
764             return
765         new_name = rename_to_tag.value
766
767         shadowed = []
768
769         def shadowed_filter(n):
770             if isinstance(n, Function) and n.symbol == new_name:
771                 shadowed.append(n)
772                 return False
773             return True
774
775         self._namespace.remove_matching(shadowed_filter)
776         if len(shadowed) == 1:
777             # method override; use the same (stripped) name as the overloaded
778             # method referenced.
779             # Note that 'g_timeout_add_full' may specify a new_name of
780             # 'g_timeout_add' but the *real* name desired is the stripped name
781             # of 'g_timeout_add' which is 'timeout_add' (for example).
782             node.name = shadowed[0].name
783         elif len(shadowed) == 0:
784             # literal rename, to force a particular prefix strip or whatever
785             # Example: the "nm-utils" package uses a "NM" prefix in most places
786             # but some functions have an "nm_utils_" prefix; the 'Rename To:'
787             # annotation in this case is used to strip the 'utils_' part off.
788             node.name = new_name
789         else:
790             assert False # more than two shadowed methods?  Shouldn't happen.
791
792     def _guess_direction(self, node):
793         if node.direction:
794             return node.direction
795         is_pointer = False
796         if node.type.ctype:
797             is_pointer = '*' in node.type.ctype
798
799         if is_pointer and node.type.name in BASIC_GIR_TYPES:
800             return PARAM_DIRECTION_OUT
801
802         return PARAM_DIRECTION_IN
803
804     def _guess_transfer(self, node, options):
805         if node.transfer is not None:
806             return node.transfer
807
808         # Anything with 'const' gets none
809         if node.type.is_const:
810             return PARAM_TRANSFER_NONE
811         elif node.type.name in [TYPE_NONE, TYPE_ANY]:
812             return PARAM_TRANSFER_NONE
813         elif isinstance(node.type, Varargs):
814             return PARAM_TRANSFER_NONE
815         elif isinstance(node, Parameter):
816             if node.direction in [PARAM_DIRECTION_INOUT,
817                                   PARAM_DIRECTION_OUT]:
818                 return PARAM_TRANSFER_FULL
819             # This one is a hack for compatibility; the transfer
820             # for string parameters really has no defined meaning.
821             elif node.type.canonical == 'utf8':
822                 return PARAM_TRANSFER_FULL
823             else:
824                 return PARAM_TRANSFER_NONE
825         elif isinstance(node, Return):
826             if (node.type.canonical in BASIC_GIR_TYPES or
827                 (node.type.canonical in [TYPE_NONE, TYPE_ANY] and
828                  node.type.is_const)):
829                 return PARAM_TRANSFER_NONE
830             else:
831                 return PARAM_TRANSFER_FULL
832         elif isinstance(node, Field):
833             return PARAM_TRANSFER_NONE
834         else:
835             raise AssertionError(node)