Small fix to handle @attributes: .... (.....) as a parameter to a function
[gnome.gobject-introspection] / giscanner / annotationparser.py
index 0e65ada..c693a97 100644 (file)
 
 # AnnotationParser - parses gtk-doc annotations
 
-from .ast import (Array, Callback, Class, Enum, Field, Function, Interface,
-                  List, Map, Parameter, Record, Return, Type, Union, Varargs,
+import re
+import sys
+
+from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
+                  Interface, List, Map, Parameter, Record, Return, Type, Union,
+                  Varargs,
                   default_array_types,
                   BASIC_GIR_TYPES,
                   PARAM_DIRECTION_INOUT,
@@ -38,10 +42,13 @@ from .glibast import GLibBoxed
 _COMMENT_HEADER = '*\n '
 
 # Tags - annotations applyed to comment blocks
-TAG_SINCE = 'Since'
-TAG_DEPRECATED = 'Deprecated'
-TAG_RETURNS = 'Returns'
-TAG_RETURNS_ALT = 'Return value'
+TAG_VFUNC = 'virtual'
+TAG_SINCE = 'since'
+TAG_DEPRECATED = 'deprecated'
+TAG_RETURNS = 'returns'
+TAG_RETURNS_ALT = 'return value'
+TAG_ATTRIBUTES = 'attributes'
+TAG_RENAME_TO = 'rename to'
 
 # Options - annotations for parameters and return values
 OPT_ALLOW_NONE = 'allow-none'
@@ -54,12 +61,21 @@ OPT_OUT = 'out'
 OPT_SCOPE = 'scope'
 OPT_TRANSFER = 'transfer'
 OPT_TYPE = 'type'
+OPT_CLOSURE = 'closure'
+OPT_DESTROY = 'destroy'
+OPT_SKIP = 'skip'
+
+# Specific option values
+OPT_VAL_BITFIELD = 'bitfield'
 
 # Array options - array specific annotations
 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
 OPT_ARRAY_LENGTH = 'length'
 OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated'
 
+OPT_SCOPE_ASYNC = 'async'
+OPT_SCOPE_CALL = 'call'
+OPT_SCOPE_NOTIFIED = 'notified'
 
 class InvalidAnnotationError(Exception):
     pass
@@ -67,13 +83,15 @@ class InvalidAnnotationError(Exception):
 
 class DocBlock(object):
 
-    def __init__(self, name):
+    def __init__(self, name, options):
         self.name = name
+        self.options = options
         self.value = None
         self.tags = odict()
+        self.comment = None
 
     def __repr__(self):
-        return '<DocBlock %r>' % (self.name, )
+        return '<DocBlock %r %r>' % (self.name, self.options)
 
     def get(self, name):
         if name == TAG_RETURNS:
@@ -90,8 +108,11 @@ class DocTag(object):
 
     def __init__(self, name):
         self.name = name
-        self.options = []
+        self.options = {}
+        self.comment = None
 
+    def __repr__(self):
+        return '<DocTag %r %r>' % (self.name, self.options)
 
 class Option(object):
 
@@ -142,6 +163,10 @@ class AnnotationParser(object):
         # /**
         #   * symbol:
         #
+        # Or, alternatively, with options:
+        # /**
+        #   * symbol: (name value) ...
+        #
         # symbol is currently one of:
         #  - function: gtk_widget_show
         #  - signal:   GtkWidget::destroy
@@ -159,39 +184,58 @@ class AnnotationParser(object):
         pos = comment.find('\n ')
         if pos == -1:
             return
-        block_name = comment[:pos]
-        block_name = block_name.strip()
-        if not block_name.endswith(':'):
-            return
-        block = DocBlock(block_name[:-1])
-        content = comment[pos+1:]
-        for line in content.split('\n'):
+        block_header = comment[:pos]
+        block_header = block_header.strip()
+        cpos = block_header.find(': ')
+        if cpos:
+            block_name = block_header[:cpos]
+            block_options, rest = self._parse_options(block_header[cpos+2:])
+            if rest:
+                return
+        else:
+            block_name, block_options = block_header, {}
+        block = DocBlock(block_name, block_options)
+        comment_lines = []
+        for line in comment[pos+1:].split('\n'):
             line = line.lstrip()
             line = line[2:].strip() # Skip ' *'
             if not line:
                 continue
-
             if line.startswith('@'):
                 line = line[1:]
             elif not ': ' in line:
+                comment_lines.append(line)
                 continue
-            tag = self._parse_tag(line)
-            block.tags[tag.name] = tag
-
+            tag_name, value = self._split_tag_namevalue(line)
+            canon_name = tag_name.lower()
+            if canon_name in block.tags:
+                print >> sys.stderr, (
+                    "Symbol %s has multiple definition of tag %r" % (
+                    block_name, canon_name, ))
+            block.tags[canon_name] = self._create_tag(canon_name, value)
+        block.comment = '\n'.join(comment_lines)
         self._blocks[block.name] = block
 
-    def _parse_tag(self, value):
-        # Tag: bar
-        # Tag: bar opt1 opt2
-        parts = value.split(': ', 1)
+    def _split_tag_namevalue(self, raw):
+        """Split a line into tag name and value"""
+        parts = raw.split(': ', 1)
         if len(parts) == 1:
             tag_name = parts[0]
-            options = ''
+            value = ''
+            if tag_name.endswith(':'):
+                tag_name = tag_name[:-1]
         else:
-            tag_name, options = parts
+            tag_name, value = parts
+        return (tag_name, value)
+
+    def _create_tag(self, tag_name, value):
+        # Tag: bar
+        # Tag: bar opt1 opt2
         tag = DocTag(tag_name)
-        tag.value = options
-        tag.options = self._parse_options(options)
+        tag.value = value
+        options, rest = self._parse_options(tag.value)
+        tag.options = options
+        tag.comment = rest
         return tag
 
     def _parse_options(self, value):
@@ -199,6 +243,7 @@ class AnnotationParser(object):
         # (bar opt1 opt2...)
         opened = -1
         options = {}
+        last = None
         for i, c in enumerate(value):
             if c == '(' and opened == -1:
                 opened = i+1
@@ -215,8 +260,14 @@ class AnnotationParser(object):
                 if option is not None:
                     option = Option(option)
                 options[name] = option
+                last = i + 2
                 opened = -1
-        return options
+
+        if last is not None:
+            rest = value[last:].strip()
+        else:
+            rest = None
+        return options, rest
 
 
 class AnnotationApplier(object):
@@ -232,8 +283,10 @@ class AnnotationApplier(object):
         return block.get(tag_name)
 
     def parse(self, namespace):
-        for node in namespace.nodes:
+        self._namespace = namespace
+        for node in namespace.nodes[:]:
             self._parse_node(node)
+        del self._namespace
 
     # Boring parsing boilerplate.
 
@@ -242,6 +295,8 @@ class AnnotationApplier(object):
             self._parse_function(node)
         elif isinstance(node, Enum):
             self._parse_enum(node)
+        elif isinstance(node, Bitfield):
+            self._parse_bitfield(node)
         elif isinstance(node, Class):
             self._parse_class(node)
         elif isinstance(node, Interface):
@@ -256,63 +311,90 @@ class AnnotationApplier(object):
             self._parse_boxed(node)
 
     def _parse_class(self, class_):
-        block = self._blocks.get(class_.name)
-        self._parse_version(class_, block)
+        block = self._blocks.get(class_.type_name)
+        self._parse_node_common(class_, block)
         self._parse_constructors(class_.constructors)
-        self._parse_methods(class_.methods)
-        self._parse_methods(class_.static_methods)
+        self._parse_methods(class_, class_.methods)
+        self._parse_vfuncs(class_, class_.virtual_methods)
+        self._parse_methods(class_, class_.static_methods)
         self._parse_properties(class_, class_.properties)
         self._parse_signals(class_, class_.signals)
         self._parse_fields(class_, class_.fields)
+        if block:
+            class_.doc = block.comment
 
     def _parse_interface(self, interface):
-        block = self._blocks.get(interface.name)
-        self._parse_version(interface, block)
-        self._parse_methods(interface.methods)
+        block = self._blocks.get(interface.type_name)
+        self._parse_node_common(interface, block)
+        self._parse_methods(interface, interface.methods)
+        self._parse_vfuncs(interface, interface.virtual_methods)
         self._parse_properties(interface, interface.properties)
         self._parse_signals(interface, interface.signals)
         self._parse_fields(interface, interface.fields)
+        if block:
+            interface.doc = block.comment
 
     def _parse_record(self, record):
         block = self._blocks.get(record.symbol)
-        self._parse_version(record, block)
+        self._parse_node_common(record, block)
         self._parse_constructors(record.constructors)
-        self._parse_fields(record, record.fields)
-        if isinstance(record, GLibBoxed):
-            self._parse_methods(record.methods)
+        self._parse_methods(record, record.methods)
+        self._parse_fields(record, record.fields, block)
+        if block:
+            record.doc = block.comment
 
     def _parse_boxed(self, boxed):
         block = self._blocks.get(boxed.name)
-        self._parse_version(boxed, block)
+        self._parse_node_common(boxed, block)
         self._parse_constructors(boxed.constructors)
-        self._parse_methods(boxed.methods)
+        self._parse_methods(boxed, boxed.methods)
+        if block:
+            boxed.doc = block.comment
 
     def _parse_union(self, union):
         block = self._blocks.get(union.name)
-        self._parse_fields(union, union.fields)
+        self._parse_node_common(union, block)
+        self._parse_fields(union, union.fields, block)
         self._parse_constructors(union.constructors)
-        if isinstance(union, GLibBoxed):
-            self._parse_methods(union.methods)
+        self._parse_methods(union, union.methods)
+        if block:
+            union.doc = block.comment
 
     def _parse_enum(self, enum):
         block = self._blocks.get(enum.symbol)
-        self._parse_version(enum, block)
+        self._parse_node_common(enum, block)
+        if block:
+            enum.doc = block.comment
+            type_opt = block.options.get(OPT_TYPE)
+            if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
+                # This is hack, but hey, it works :-)
+                enum.__class__ = Bitfield
+
+    def _parse_bitfield(self, bitfield):
+        block = self._blocks.get(bitfield.symbol)
+        self._parse_node_common(bitfield, block)
+        if block:
+            bitfield.doc = block.comment
 
     def _parse_constructors(self, constructors):
         for ctor in constructors:
             self._parse_function(ctor)
 
-    def _parse_fields(self, parent, fields):
+    def _parse_fields(self, parent, fields, block=None):
         for field in fields:
-            self._parse_field(parent, field)
+            self._parse_field(parent, field, block)
 
     def _parse_properties(self, parent, properties):
         for prop in properties:
             self._parse_property(parent, prop)
 
-    def _parse_methods(self, methods):
+    def _parse_methods(self, parent, methods):
         for method in methods:
-            self._parse_function(method)
+            self._parse_method(parent, method)
+
+    def _parse_vfuncs(self, parent, vfuncs):
+        for vfunc in vfuncs:
+            self._parse_vfunc(parent, vfunc)
 
     def _parse_signals(self, parent, signals):
         for signal in signals:
@@ -320,26 +402,44 @@ class AnnotationApplier(object):
 
     def _parse_property(self, parent, prop):
         block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
-        self._parse_version(prop, block)
-        self._parse_deprecated(prop, block)
+        self._parse_node_common(prop, block)
+        if block:
+            prop.doc = block.comment
 
     def _parse_callback(self, callback):
         block = self._blocks.get(callback.ctype)
-        self._parse_version(callback, block)
+        self._parse_node_common(callback, block)
         self._parse_params(callback, callback.parameters, block)
         self._parse_return(callback, callback.retval, block)
+        if block:
+            callback.doc = block.comment
+
+    def _parse_callable(self, callable, block):
+        self._parse_node_common(callable, block)
+        for i, param in enumerate(callable.parameters):
+            if (param.type.ctype != 'GDestroyNotify' and
+                param.type.name != 'GLib.DestroyNotify'):
+                continue
+            if i < 2:
+                break
+            callback_param = callable.parameters[i-2]
+            if callback_param.closure_index != -1:
+                callback_param.scope = OPT_SCOPE_NOTIFIED
+                callback_param.transfer = PARAM_TRANSFER_NONE
+
+        self._parse_params(callable, callable.parameters, block)
+        self._parse_return(callable, callable.retval, block)
+        if block:
+            callable.doc = block.comment
 
     def _parse_function(self, func):
         block = self._blocks.get(func.symbol)
-        self._parse_version(func, block)
-        self._parse_deprecated(func, block)
-        self._parse_params(func, func.parameters, block)
-        self._parse_return(func, func.retval, block)
+        self._parse_callable(func, block)
+        self._parse_rename_to_func(func, block)
 
     def _parse_signal(self, parent, signal):
         block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
-        self._parse_version(signal, block)
-        self._parse_deprecated(signal, block)
+        self._parse_node_common(signal, block)
         # We're only attempting to name the signal parameters if
         # the number of parameter tags (@foo) is the same or greater
         # than the number of signal parameters
@@ -354,15 +454,47 @@ class AnnotationApplier(object):
                 options = getattr(tag, 'options', {})
                 param_type = options.get(OPT_TYPE)
                 if param_type:
-                    param.type.name = param_type.one()
+                    param.type = self._resolve(param_type.one(), param.type)
             else:
                 tag = None
             self._parse_param(signal, param, tag)
         self._parse_return(signal, signal.retval, block)
-
-    def _parse_field(self, parent, field):
+        if block:
+            signal.doc = block.comment
+
+    def _parse_method(self, parent, meth):
+        block = self._blocks.get(meth.symbol)
+        self._parse_function(meth)
+        virtual = self._get_tag(block, TAG_VFUNC)
+        if virtual:
+            invoker_name = virtual.value
+            matched = False
+            for vfunc in parent.virtual_methods:
+                if vfunc.name == invoker_name:
+                    matched = True
+                    vfunc.invoker = meth.name
+                    break
+            if not matched:
+                print "warning: unmatched virtual invoker %r for method %r" % \
+                    (invoker_name, meth.symbol)
+
+    def _parse_vfunc(self, parent, vfunc):
+        key = '%s::%s' % (parent.type_name, vfunc.name)
+        self._parse_callable(vfunc, self._blocks.get(key))
+
+    def _parse_field(self, parent, field, block=None):
         if isinstance(field, Callback):
             self._parse_callback(field)
+        else:
+            if not block:
+                return
+            tag = block.get(field.name)
+            if not tag:
+                return
+            t = tag.options.get('type')
+            if not t:
+                return
+            field.type.name = self._transformer.resolve_type_name(t.one())
 
     def _parse_params(self, parent, params, block):
         for param in params:
@@ -371,20 +503,47 @@ class AnnotationApplier(object):
 
     def _parse_return(self, parent, return_, block):
         tag = self._get_tag(block, TAG_RETURNS)
-        options = getattr(tag, 'options', {})
-        self._parse_param_ret_common(parent, return_, options)
+        self._parse_param_ret_common(parent, return_, tag)
 
     def _parse_param(self, parent, param, tag):
         options = getattr(tag, 'options', {})
-
         if isinstance(parent, Function):
             scope = options.get(OPT_SCOPE)
             if scope:
                 param.scope = scope.one()
                 param.transfer = PARAM_TRANSFER_NONE
-        self._parse_param_ret_common(parent, param, options)
+            elif (param.type.ctype == 'GAsyncReadyCallback' or
+                  param.type.name == 'Gio.AsyncReadyCallback'):
+                param.scope = OPT_SCOPE_ASYNC
+                param.transfer = PARAM_TRANSFER_NONE
 
-    def _parse_param_ret_common(self, parent, node, options):
+            destroy = options.get(OPT_DESTROY)
+            if destroy:
+                param.destroy_index = parent.get_parameter_index(destroy.one())
+                self._fixup_param_destroy(parent, param)
+            closure = options.get(OPT_CLOSURE)
+            if closure:
+                param.closure_index = parent.get_parameter_index(closure.one())
+                self._fixup_param_closure(parent, param)
+        if isinstance(parent, Callback):
+            if OPT_CLOSURE in options:
+                param.closure_index = parent.get_parameter_index(param.name)
+                self._fixup_param_closure(parent, param)
+
+        self._parse_param_ret_common(parent, param, tag)
+
+    def _fixup_param_destroy(self, parent, param):
+        for p in parent.parameters:
+            if p is not param and p.destroy_index == param.destroy_index:
+                p.destroy_index = None
+
+    def _fixup_param_closure(self, parent, param):
+        for p in parent.parameters:
+            if p is not param and p.closure_index == param.closure_index:
+                p.closure_index = None
+
+    def _parse_param_ret_common(self, parent, node, tag):
+        options = getattr(tag, 'options', {})
         node.direction = self._extract_direction(node, options)
         container_type = self._extract_container_type(
             parent, node, options)
@@ -393,10 +552,17 @@ class AnnotationApplier(object):
         if node.direction is None:
             node.direction = self._guess_direction(node)
         node.transfer = self._extract_transfer(parent, node, options)
-        if OPT_ALLOW_NONE in options:
+        param_type = options.get(OPT_TYPE)
+        if param_type:
+            node.type = self._resolve(param_type.one(), node.type)
+
+        if (OPT_ALLOW_NONE in options or
+            node.type.ctype == 'GCancellable*'):
             node.allow_none = True
 
         assert node.transfer is not None
+        if tag is not None and tag.comment is not None:
+            node.doc = tag.comment
 
     def _extract_direction(self, node, options):
         if (OPT_INOUT in options or
@@ -428,6 +594,7 @@ class AnnotationApplier(object):
         if (not isinstance(node, Field) and
             (not has_element_type and
              (node.direction is None
+              or isinstance(node, Return)
               or node.direction == PARAM_DIRECTION_IN))):
             if self._guess_array(node):
                 has_array = True
@@ -450,12 +617,13 @@ class AnnotationApplier(object):
 
         element_type = options.get(OPT_ELEMENT_TYPE)
         if element_type is not None:
-            element_type_name = element_type.one()
+            element_type_node = self._resolve(element_type.one())
         else:
-            element_type_name = node.type.name
+            element_type_node = Type(node.type.name) # erase ctype
 
         container_type = Array(node.type.ctype,
-                               element_type_name)
+                               element_type_node)
+        container_type.is_const = node.type.is_const
         if OPT_ARRAY_ZERO_TERMINATED in array_values:
             container_type.zeroterminated = array_values.get(
                 OPT_ARRAY_ZERO_TERMINATED) == '1'
@@ -468,31 +636,72 @@ class AnnotationApplier(object):
             # is specified.
             if (isinstance(node, Parameter) and
                 node.type.name == 'utf8' and
-                self._guess_direction(node) == PARAM_DIRECTION_IN):
+                self._guess_direction(node) == PARAM_DIRECTION_IN and
+                element_type is None):
                 # FIXME: unsigned char/guchar should be uint8
-                container_type.element_type = 'int8'
+                container_type.element_type = Type('int8')
         container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
         return container_type
 
+    def _resolve(self, type_str, orig_node=None):
+        def grab_one(type_str, resolver, top_combiner, combiner):
+            """Return a complete type, and the trailing string part after it.
+            Use resolver() on each identifier, and combiner() on the parts of
+            each complete type. (top_combiner is used on the top-most type.)"""
+            bits = re.split(r'([,<>])', type_str, 1)
+            first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+            args = [resolver(first)]
+            if sep == '<':
+                while sep != '>':
+                    next, rest = grab_one(rest, resolver, combiner, combiner)
+                    args.append(next)
+                    sep, rest = rest[0], rest[1:]
+            else:
+                rest = sep + rest
+            return top_combiner(*args), rest
+        def resolver(ident):
+            return self._transformer.resolve_param_type(Type(ident))
+        def combiner(base, *rest):
+            if not rest:
+                return base
+            if (base.name in ['GLib.List', 'GLib.SList'] or
+                base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
+                return List(base.name, base.ctype, *rest)
+            if (base.name in ['GLib.HashTable'] or
+                base.ctype in ['GHashTable*']) and len(rest)==2:
+                return Map(base.name, base.ctype, *rest)
+            print "WARNING: throwing away type parameters:", type_str
+            return base
+        def top_combiner(base, *rest):
+            """For the top most type, recycle orig_node if possible."""
+            if orig_node is not None:
+                orig_node.name = base.name
+                base = orig_node # preserve other properties of orig_node
+            return combiner(base, *rest)
+
+        result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+        if rest:
+            print "WARNING: throwing away trailing part of type:", type_str
+        return result
+
     def _parse_element_type(self, parent, node, options):
         element_type_opt = options.get(OPT_ELEMENT_TYPE)
         element_type = element_type_opt.flat()
-        if node.type.name in ['GLib.List', 'GLib.SList']:
+        if (node.type.name in ['GLib.List', 'GLib.SList'] or
+            node.type.ctype in ['GList*', 'GSList*']):
             assert len(element_type) == 1
-            etype = Type(element_type[0])
             container_type = List(
                 node.type.name,
                 node.type.ctype,
-                self._transformer.resolve_param_type(etype))
-        elif node.type.name in ['GLib.HashTable']:
+                self._resolve(element_type[0]))
+        elif (node.type.name in ['GLib.HashTable'] or
+              node.type.ctype in ['GHashTable*']):
             assert len(element_type) == 2
-            key_type = Type(element_type[0])
-            value_type = Type(element_type[1])
             container_type = Map(
                 node.type.name,
                 node.type.ctype,
-                self._transformer.resolve_param_type(key_type),
-                self._transformer.resolve_param_type(value_type))
+                self._resolve(element_type[0]),
+                self._resolve(element_type[1]))
         else:
             print 'FIXME: unhandled element-type container:', node
         return container_type
@@ -513,6 +722,12 @@ class AnnotationApplier(object):
                     "none, container, full." % (node, parent.name, transfer))
         return transfer
 
+    def _parse_node_common(self, node, block):
+        self._parse_version(node, block)
+        self._parse_deprecated(node, block)
+        self._parse_attributes(node, block)
+        self._parse_skip(node, block)
+
     def _parse_version(self, node, block):
         since_tag = self._get_tag(block, TAG_SINCE)
         if since_tag is None:
@@ -533,6 +748,50 @@ class AnnotationApplier(object):
         if version is not None:
             node.deprecated_version = version
 
+    def _parse_attributes(self, node, block):
+        annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
+        if annos_tag is None:
+            return
+        for key, value in annos_tag.options.iteritems():
+            if value:
+                node.attributes.append((key, value.one()))
+
+    def _parse_skip(self, node, block):
+        if block is not None:
+            if OPT_SKIP in block.options:
+                node.skip = True
+
+    def _parse_rename_to_func(self, node, block):
+        rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
+        if rename_to_tag is None:
+            return
+        new_name = rename_to_tag.value
+
+        shadowed = []
+
+        def shadowed_filter(n):
+            if isinstance(n, Function) and n.symbol == new_name:
+                shadowed.append(n)
+                return False
+            return True
+
+        self._namespace.remove_matching(shadowed_filter)
+        if len(shadowed) == 1:
+            # method override; use the same (stripped) name as the overloaded
+            # method referenced.
+            # Note that 'g_timeout_add_full' may specify a new_name of
+            # 'g_timeout_add' but the *real* name desired is the stripped name
+            # of 'g_timeout_add' which is 'timeout_add' (for example).
+            node.name = shadowed[0].name
+        elif len(shadowed) == 0:
+            # literal rename, to force a particular prefix strip or whatever
+            # Example: the "nm-utils" package uses a "NM" prefix in most places
+            # but some functions have an "nm_utils_" prefix; the 'Rename To:'
+            # annotation in this case is used to strip the 'utils_' part off.
+            node.name = new_name
+        else:
+            assert False # more than two shadowed methods?  Shouldn't happen.
+
     def _guess_direction(self, node):
         if node.direction:
             return node.direction
@@ -549,12 +808,9 @@ class AnnotationApplier(object):
         if node.transfer is not None:
             return node.transfer
 
-        if isinstance(node.type, Array):
-            return PARAM_TRANSFER_NONE
         # Anything with 'const' gets none
         if node.type.is_const:
             return PARAM_TRANSFER_NONE
-
         elif node.type.name in [TYPE_NONE, TYPE_ANY]:
             return PARAM_TRANSFER_NONE
         elif isinstance(node.type, Varargs):