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