Bug 584453 - Handle char ** correctly (and const variation)
[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 sys
24
25 from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
26                   Interface, List, Map, Parameter, Record, Return, Type, Union,
27                   Varargs,
28                   default_array_types,
29                   BASIC_GIR_TYPES,
30                   PARAM_DIRECTION_INOUT,
31                   PARAM_DIRECTION_IN,
32                   PARAM_DIRECTION_OUT,
33                   PARAM_TRANSFER_NONE,
34                   PARAM_TRANSFER_CONTAINER,
35                   PARAM_TRANSFER_FULL,
36                   TYPE_ANY, TYPE_NONE)
37 from .odict import odict
38 from .glibast import GLibBoxed
39
40 # All gtk-doc comments needs to start with this:
41 _COMMENT_HEADER = '*\n '
42
43 # Tags - annotations applyed to comment blocks
44 TAG_VFUNC = 'virtual'
45 TAG_SINCE = 'since'
46 TAG_DEPRECATED = 'deprecated'
47 TAG_RETURNS = 'returns'
48 TAG_RETURNS_ALT = 'return value'
49 TAG_ATTRIBUTES = 'attributes'
50 TAG_RENAME_TO = 'rename to'
51
52 # Options - annotations for parameters and return values
53 OPT_ALLOW_NONE = 'allow-none'
54 OPT_ARRAY = 'array'
55 OPT_ELEMENT_TYPE = 'element-type'
56 OPT_IN = 'in'
57 OPT_INOUT = 'inout'
58 OPT_INOUT_ALT = 'in-out'
59 OPT_OUT = 'out'
60 OPT_SCOPE = 'scope'
61 OPT_TRANSFER = 'transfer'
62 OPT_TYPE = 'type'
63 OPT_CLOSURE = 'closure'
64 OPT_DESTROY = 'destroy'
65
66 # Specific option values
67 OPT_VAL_BITFIELD = 'bitfield'
68
69 # Array options - array specific annotations
70 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
71 OPT_ARRAY_LENGTH = 'length'
72 OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated'
73
74
75 class InvalidAnnotationError(Exception):
76     pass
77
78
79 class DocBlock(object):
80
81     def __init__(self, name, options):
82         self.name = name
83         self.options = options
84         self.value = None
85         self.tags = odict()
86         self.comment = None
87
88     def __repr__(self):
89         return '<DocBlock %r %r>' % (self.name, self.options)
90
91     def get(self, name):
92         if name == TAG_RETURNS:
93             value = self.tags.get(name)
94             if value is None:
95                 return self.tags.get(TAG_RETURNS_ALT)
96             else:
97                 return value
98         else:
99             return self.tags.get(name)
100
101
102 class DocTag(object):
103
104     def __init__(self, name):
105         self.name = name
106         self.options = {}
107         self.comment = None
108
109     def __repr__(self):
110         return '<DocTag %r %r>' % (self.name, self.options)
111
112 class Option(object):
113
114     def __init__(self, option):
115         self._array = []
116         self._dict = {}
117         for p in option.split(' '):
118             if '=' in p:
119                 name, value = p.split('=', 1)
120             else:
121                 name = p
122                 value = None
123             self._dict[name] = value
124             if value is None:
125                 self._array.append(name)
126             else:
127                 self._array.append((name, value))
128
129     def __repr__(self):
130         return '<Option %r>' % (self._array, )
131
132     def one(self):
133         assert len(self._array) == 1
134         return self._array[0]
135
136     def flat(self):
137         return self._array
138
139     def all(self):
140         return self._dict
141
142
143 class AnnotationParser(object):
144
145     def __init__(self, namespace, source_scanner, transformer):
146         self._blocks = {}
147         self._namespace = namespace
148         self._transformer = transformer
149         for comment in source_scanner.get_comments():
150             self._parse_comment(comment)
151
152     def parse(self):
153         aa = AnnotationApplier(self._blocks, self._transformer)
154         aa.parse(self._namespace)
155
156     def _parse_comment(self, comment):
157         # We're looking for gtk-doc comments here, they look like this:
158         # /**
159         #   * symbol:
160         #
161         # Or, alternatively, with options:
162         # /**
163         #   * symbol: (name value) ...
164         #
165         # symbol is currently one of:
166         #  - function: gtk_widget_show
167         #  - signal:   GtkWidget::destroy
168         #  - property: GtkWidget:visible
169         #
170         comment = comment.lstrip()
171         if not comment.startswith(_COMMENT_HEADER):
172             return
173         comment = comment[len(_COMMENT_HEADER):]
174         comment = comment.strip()
175         if not comment.startswith('* '):
176             return
177         comment = comment[2:]
178
179         pos = comment.find('\n ')
180         if pos == -1:
181             return
182         block_header = comment[:pos]
183         block_header = block_header.strip()
184         cpos = block_header.find(': ')
185         if cpos:
186             block_name = block_header[:cpos]
187             block_options, rest = self._parse_options(block_header[cpos+2:])
188             if rest:
189                 return
190         else:
191             block_name, block_options = block_header, {}
192         block = DocBlock(block_name, block_options)
193         comment_lines = []
194         for line in comment[pos+1:].split('\n'):
195             line = line.lstrip()
196             line = line[2:].strip() # Skip ' *'
197             if not line:
198                 continue
199             if line.startswith('@'):
200                 line = line[1:]
201             elif not ': ' in line:
202                 comment_lines.append(line)
203                 continue
204             tag_name, value = self._split_tag_namevalue(line)
205             canon_name = tag_name.lower()
206             if canon_name in block.tags:
207                 print >>sys.stderr, "Multiple definition of tag %r" \
208                     % (canon_name, )
209             block.tags[canon_name] = self._create_tag(canon_name, value)
210         block.comment = '\n'.join(comment_lines)
211         self._blocks[block.name] = block
212
213     def _split_tag_namevalue(self, raw):
214         """Split a line into tag name and value"""
215         parts = raw.split(': ', 1)
216         if len(parts) == 1:
217             tag_name = parts[0]
218             value = ''
219         else:
220             tag_name, value = parts
221         return (tag_name, value)
222
223     def _create_tag(self, tag_name, value):
224         # Tag: bar
225         # Tag: bar opt1 opt2
226         tag = DocTag(tag_name)
227         tag.value = value
228         options, rest = self._parse_options(tag.value)
229         tag.options = options
230         tag.comment = rest
231         return tag
232
233     def _parse_options(self, value):
234         # (foo)
235         # (bar opt1 opt2...)
236         opened = -1
237         options = {}
238         last = None
239         for i, c in enumerate(value):
240             if c == '(' and opened == -1:
241                 opened = i+1
242             if c == ')' and opened != -1:
243                 segment = value[opened:i]
244                 parts = segment.split(' ', 1)
245                 if len(parts) == 2:
246                     name, option = parts
247                 elif len(parts) == 1:
248                     name = parts[0]
249                     option = None
250                 else:
251                     raise AssertionError
252                 if option is not None:
253                     option = Option(option)
254                 options[name] = option
255                 last = i + 2
256                 opened = -1
257
258         if last is not None:
259             rest = value[last:].strip()
260         else:
261             rest = None
262         return options, rest
263
264
265 class AnnotationApplier(object):
266
267     def __init__(self, blocks, transformer):
268         self._blocks = blocks
269         self._transformer = transformer
270
271     def _get_tag(self, block, tag_name):
272         if block is None:
273             return None
274
275         return block.get(tag_name)
276
277     def parse(self, namespace):
278         self._namespace = namespace
279         for node in namespace.nodes[:]:
280             self._parse_node(node)
281         del self._namespace
282
283     # Boring parsing boilerplate.
284
285     def _parse_node(self, node):
286         if isinstance(node, Function):
287             self._parse_function(node)
288         elif isinstance(node, Enum):
289             self._parse_enum(node)
290         elif isinstance(node, Bitfield):
291             self._parse_bitfield(node)
292         elif isinstance(node, Class):
293             self._parse_class(node)
294         elif isinstance(node, Interface):
295             self._parse_interface(node)
296         elif isinstance(node, Callback):
297             self._parse_callback(node)
298         elif isinstance(node, Record):
299             self._parse_record(node)
300         elif isinstance(node, Union):
301             self._parse_union(node)
302         elif isinstance(node, GLibBoxed):
303             self._parse_boxed(node)
304
305     def _parse_class(self, class_):
306         block = self._blocks.get(class_.type_name)
307         self._parse_node_common(class_, block)
308         self._parse_constructors(class_.constructors)
309         self._parse_methods(class_, class_.methods)
310         self._parse_vfuncs(class_, class_.virtual_methods)
311         self._parse_methods(class_, class_.static_methods)
312         self._parse_properties(class_, class_.properties)
313         self._parse_signals(class_, class_.signals)
314         self._parse_fields(class_, class_.fields)
315         if block:
316             class_.doc = block.comment
317
318     def _parse_interface(self, interface):
319         block = self._blocks.get(interface.type_name)
320         self._parse_node_common(interface, block)
321         self._parse_methods(interface, interface.methods)
322         self._parse_vfuncs(interface, interface.virtual_methods)
323         self._parse_properties(interface, interface.properties)
324         self._parse_signals(interface, interface.signals)
325         self._parse_fields(interface, interface.fields)
326         if block:
327             interface.doc = block.comment
328
329     def _parse_record(self, record):
330         block = self._blocks.get(record.symbol)
331         self._parse_node_common(record, block)
332         self._parse_constructors(record.constructors)
333         self._parse_methods(record, record.methods)
334         self._parse_fields(record, record.fields)
335         if block:
336             record.doc = block.comment
337
338     def _parse_boxed(self, boxed):
339         block = self._blocks.get(boxed.name)
340         self._parse_node_common(boxed, block)
341         self._parse_constructors(boxed.constructors)
342         self._parse_methods(boxed, boxed.methods)
343         if block:
344             boxed.doc = block.comment
345
346     def _parse_union(self, union):
347         block = self._blocks.get(union.name)
348         self._parse_node_common(union, block)
349         self._parse_fields(union, union.fields)
350         self._parse_constructors(union.constructors)
351         self._parse_methods(union, union.methods)
352         if block:
353             union.doc = block.comment
354
355     def _parse_enum(self, enum):
356         block = self._blocks.get(enum.symbol)
357         self._parse_node_common(enum, block)
358         if block:
359             enum.doc = block.comment
360             type_opt = block.options.get(OPT_TYPE)
361             if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
362                 # This is hack, but hey, it works :-)
363                 enum.__class__ = Bitfield
364
365     def _parse_bitfield(self, bitfield):
366         block = self._blocks.get(bitfield.symbol)
367         self._parse_node_common(bitfield, block)
368         if block:
369             bitfield.doc = block.comment
370
371     def _parse_constructors(self, constructors):
372         for ctor in constructors:
373             self._parse_function(ctor)
374
375     def _parse_fields(self, parent, fields):
376         for field in fields:
377             self._parse_field(parent, field)
378
379     def _parse_properties(self, parent, properties):
380         for prop in properties:
381             self._parse_property(parent, prop)
382
383     def _parse_methods(self, parent, methods):
384         for method in methods:
385             self._parse_method(parent, method)
386
387     def _parse_vfuncs(self, parent, vfuncs):
388         for vfunc in vfuncs:
389             self._parse_vfunc(parent, vfunc)
390
391     def _parse_signals(self, parent, signals):
392         for signal in signals:
393             self._parse_signal(parent, signal)
394
395     def _parse_property(self, parent, prop):
396         block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
397         self._parse_node_common(prop, block)
398         if block:
399             prop.doc = block.comment
400
401     def _parse_callback(self, callback):
402         block = self._blocks.get(callback.ctype)
403         self._parse_node_common(callback, block)
404         self._parse_params(callback, callback.parameters, block)
405         self._parse_return(callback, callback.retval, block)
406         if block:
407             callback.doc = block.comment
408
409     def _parse_callable(self, callable, block):
410         self._parse_node_common(callable, block)
411         self._parse_params(callable, callable.parameters, block)
412         self._parse_return(callable, callable.retval, block)
413         if block:
414             callable.doc = block.comment
415
416     def _parse_function(self, func):
417         block = self._blocks.get(func.symbol)
418         self._parse_callable(func, block)
419         self._parse_rename_to_func(func, block)
420
421     def _parse_signal(self, parent, signal):
422         block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
423         self._parse_node_common(signal, block)
424         # We're only attempting to name the signal parameters if
425         # the number of parameter tags (@foo) is the same or greater
426         # than the number of signal parameters
427         resolve = self._transformer.resolve_param_type
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.name = resolve(param_type.one())
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             resolve = self._transformer.resolve_param_type
527             node.type.name = resolve(param_type.one())
528
529         assert node.transfer is not None
530         if tag is not None and tag.comment is not None:
531             node.doc = tag.comment
532
533     def _extract_direction(self, node, options):
534         if (OPT_INOUT in options or
535             OPT_INOUT_ALT in options):
536             direction = PARAM_DIRECTION_INOUT
537         elif OPT_OUT in options:
538             direction = PARAM_DIRECTION_OUT
539         elif OPT_IN in options:
540             direction = PARAM_DIRECTION_IN
541         else:
542             direction = node.direction
543         return direction
544
545     def _guess_array(self, node):
546         ctype = node.type.ctype
547         if ctype is None:
548             return False
549         if not ctype.endswith('*'):
550             return False
551         if node.type.canonical in default_array_types:
552             return True
553         return False
554
555     def _extract_container_type(self, parent, node, options):
556         has_element_type = OPT_ELEMENT_TYPE in options
557         has_array = OPT_ARRAY in options
558
559         # FIXME: This is a hack :-(
560         if (not isinstance(node, Field) and
561             (not has_element_type and
562              (node.direction is None
563               or isinstance(node, Return)
564               or node.direction == PARAM_DIRECTION_IN))):
565             if self._guess_array(node):
566                 has_array = True
567
568         if has_array:
569             container_type = self._parse_array(parent, node, options)
570         elif has_element_type:
571             container_type = self._parse_element_type(parent, node, options)
572         else:
573             container_type = None
574
575         return container_type
576
577     def _parse_array(self, parent, node, options):
578         array_opt = options.get(OPT_ARRAY)
579         if array_opt:
580             array_values = array_opt.all()
581         else:
582             array_values = {}
583
584         element_type = options.get(OPT_ELEMENT_TYPE)
585         if element_type is not None:
586             element_type_name = element_type.one()
587         else:
588             element_type_name = node.type.name
589
590         container_type = Array(node.type.ctype,
591                                element_type_name)
592         container_type.is_const = node.type.is_const
593         if OPT_ARRAY_ZERO_TERMINATED in array_values:
594             container_type.zeroterminated = array_values.get(
595                 OPT_ARRAY_ZERO_TERMINATED) == '1'
596         length = array_values.get(OPT_ARRAY_LENGTH)
597         if length is not None:
598             param_index = parent.get_parameter_index(length)
599             container_type.length_param_index = param_index
600             # For in parameters we're incorrectly deferring
601             # char/unsigned char to utf8 when a length annotation
602             # is specified.
603             if (isinstance(node, Parameter) and
604                 node.type.name == 'utf8' and
605                 self._guess_direction(node) == PARAM_DIRECTION_IN):
606                 # FIXME: unsigned char/guchar should be uint8
607                 container_type.element_type = 'int8'
608         container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
609         return container_type
610
611     def _parse_element_type(self, parent, node, options):
612         element_type_opt = options.get(OPT_ELEMENT_TYPE)
613         element_type = element_type_opt.flat()
614         if node.type.name in ['GLib.List', 'GLib.SList']:
615             assert len(element_type) == 1
616             etype = Type(element_type[0])
617             container_type = List(
618                 node.type.name,
619                 node.type.ctype,
620                 self._transformer.resolve_param_type(etype))
621         elif node.type.name in ['GLib.HashTable']:
622             assert len(element_type) == 2
623             key_type = Type(element_type[0])
624             value_type = Type(element_type[1])
625             container_type = Map(
626                 node.type.name,
627                 node.type.ctype,
628                 self._transformer.resolve_param_type(key_type),
629                 self._transformer.resolve_param_type(value_type))
630         else:
631             print 'FIXME: unhandled element-type container:', node
632         return container_type
633
634     def _extract_transfer(self, parent, node, options):
635         transfer_opt = options.get(OPT_TRANSFER)
636         if transfer_opt is None:
637             transfer = self._guess_transfer(node, options)
638         else:
639             transfer = transfer_opt.one()
640             if transfer is None:
641                 transfer = PARAM_TRANSFER_FULL
642             if transfer not in [PARAM_TRANSFER_NONE,
643                                 PARAM_TRANSFER_CONTAINER,
644                                 PARAM_TRANSFER_FULL]:
645                 raise InvalidAnnotationError(
646                     "transfer for %s of %r is invalid (%r), must be one of "
647                     "none, container, full." % (node, parent.name, transfer))
648         return transfer
649
650     def _parse_node_common(self, node, block):
651         self._parse_version(node, block)
652         self._parse_deprecated(node, block)
653         self._parse_attributes(node, block)
654
655     def _parse_version(self, node, block):
656         since_tag = self._get_tag(block, TAG_SINCE)
657         if since_tag is None:
658             return
659         node.version = since_tag.value
660
661     def _parse_deprecated(self, node, block):
662         deprecated_tag = self._get_tag(block, TAG_DEPRECATED)
663         if deprecated_tag is None:
664             return
665         value = deprecated_tag.value
666         if ': ' in value:
667             version, desc = value.split(': ')
668         else:
669             desc = value
670             version = None
671         node.deprecated = desc
672         if version is not None:
673             node.deprecated_version = version
674
675     def _parse_attributes(self, node, block):
676         annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
677         if annos_tag is None:
678             return
679         for key, value in annos_tag.options.iteritems():
680             node.attributes.append((key, value.one()))
681
682     def _parse_rename_to_func(self, node, block):
683         rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
684         if rename_to_tag is None:
685             return
686         new_name = rename_to_tag.value
687
688         shadowed = []
689
690         def shadowed_filter(n):
691             if isinstance(n, Function) and n.symbol == new_name:
692                 shadowed.append(n)
693                 return False
694             return True
695
696         self._namespace.remove_matching(shadowed_filter)
697         if len(shadowed) == 1:
698             # method override; use the same (stripped) name as the overloaded
699             # method referenced.
700             # Note that 'g_timeout_add_full' may specify a new_name of
701             # 'g_timeout_add' but the *real* name desired is the stripped name
702             # of 'g_timeout_add' which is 'timeout_add' (for example).
703             node.name = shadowed[0].name
704         elif len(shadowed) == 0:
705             # literal rename, to force a particular prefix strip or whatever
706             # Example: the "nm-utils" package uses a "NM" prefix in most places
707             # but some functions have an "nm_utils_" prefix; the 'Rename To:'
708             # annotation in this case is used to strip the 'utils_' part off.
709             node.name = new_name
710         else:
711             assert False # more than two shadowed methods?  Shouldn't happen.
712
713     def _guess_direction(self, node):
714         if node.direction:
715             return node.direction
716         is_pointer = False
717         if node.type.ctype:
718             is_pointer = '*' in node.type.ctype
719
720         if is_pointer and node.type.name in BASIC_GIR_TYPES:
721             return PARAM_DIRECTION_OUT
722
723         return PARAM_DIRECTION_IN
724
725     def _guess_transfer(self, node, options):
726         if node.transfer is not None:
727             return node.transfer
728
729         # Anything with 'const' gets none
730         if node.type.is_const:
731             return PARAM_TRANSFER_NONE
732         elif node.type.name in [TYPE_NONE, TYPE_ANY]:
733             return PARAM_TRANSFER_NONE
734         elif isinstance(node.type, Varargs):
735             return PARAM_TRANSFER_NONE
736         elif isinstance(node, Parameter):
737             if node.direction in [PARAM_DIRECTION_INOUT,
738                                   PARAM_DIRECTION_OUT]:
739                 return PARAM_TRANSFER_FULL
740             # This one is a hack for compatibility; the transfer
741             # for string parameters really has no defined meaning.
742             elif node.type.canonical == 'utf8':
743                 return PARAM_TRANSFER_FULL
744             else:
745                 return PARAM_TRANSFER_NONE
746         elif isinstance(node, Return):
747             if (node.type.canonical in BASIC_GIR_TYPES or
748                 (node.type.canonical in [TYPE_NONE, TYPE_ANY] and
749                  node.type.is_const)):
750                 return PARAM_TRANSFER_NONE
751             else:
752                 return PARAM_TRANSFER_FULL
753         elif isinstance(node, Field):
754             return PARAM_TRANSFER_NONE
755         else:
756             raise AssertionError(node)