2863e731c63608770443cbfab3eaaad4102562e2
[gnome.gobject-introspection] / giscanner / transformer.py
1 # -*- Mode: Python -*-
2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008  Johan Dahlin
4 #
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2 of the License, or (at your option) any later version.
9 #
10 # This library 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 GNU
13 # Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the
17 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 # Boston, MA 02111-1307, USA.
19 #
20
21 import os, sys
22
23 from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
24                   Parameter, Return, Struct, Field,
25                   Type, Array, Alias, Interface, Class, Node, Union,
26                   Varargs, Constant, type_name_from_ctype,
27                   type_names, TYPE_STRING, BASIC_GIR_TYPES)
28 from .config import DATADIR, GIR_DIR, GIR_SUFFIX
29 from .glibast import GLibBoxed
30 from .girparser import GIRParser
31 from .odict import odict
32 from .sourcescanner import (
33     SourceSymbol, ctype_name, CTYPE_POINTER,
34     CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
35     CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
36     CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
37     CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
38     CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
39     TYPE_QUALIFIER_CONST)
40 from .utils import to_underscores
41
42 _xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
43                       + [DATADIR, '/usr/share'] if x]
44
45
46 class SkipError(Exception):
47     pass
48
49
50 class Names(object):
51     names = property(lambda self: self._names)
52     aliases = property(lambda self: self._aliases)
53     type_names = property(lambda self: self._type_names)
54     ctypes = property(lambda self: self._ctypes)
55
56     def __init__(self):
57         super(Names, self).__init__()
58         self._names = odict() # Maps from GIName -> (namespace, node)
59         self._aliases = {} # Maps from GIName -> GIName
60         self._type_names = {} # Maps from GTName -> (namespace, node)
61         self._ctypes = {} # Maps from CType -> (namespace, node)
62
63
64 class Transformer(object):
65
66     def __init__(self, cachestore, namespace_name, namespace_version):
67         self._cachestore = cachestore
68         self.generator = None
69         self._namespace = Namespace(namespace_name, namespace_version)
70         self._names = Names()
71         self._pkg_config_packages = set()
72         self._typedefs_ns = {}
73         self._strip_prefix = ''
74         self._includes = set()
75         self._includepaths = []
76
77     def get_names(self):
78         return self._names
79
80     def get_includes(self):
81         return self._includes
82
83     def set_strip_prefix(self, strip_prefix):
84         self._strip_prefix = strip_prefix
85
86     def get_strip_prefix(self):
87         return self._strip_prefix
88
89     def get_pkgconfig_packages(self):
90         return self._pkg_config_packages
91
92     def set_source_ast(self, src_ast):
93         self.generator = src_ast
94
95     def parse(self):
96         nodes = []
97         for symbol in self.generator.get_symbols():
98             try:
99                 node = self._traverse_one(symbol)
100             except SkipError:
101                 continue
102             self._add_node(node)
103         return self._namespace
104
105     def set_include_paths(self, paths):
106         self._includepaths = list(paths)
107
108     def register_include(self, include):
109         if include in self._includes:
110             return
111         filename = self._find_include(include)
112         self._parse_include(filename)
113         self._includes.add(include)
114
115     # Private
116
117     def _find_include(self, include):
118         searchdirs = self._includepaths[:]
119         for path in _xdg_data_dirs:
120             searchdirs.append(os.path.join(path, GIR_SUFFIX))
121         searchdirs.append(GIR_DIR)
122
123         girname = '%s-%s.gir' % (include.name, include.version)
124         for d in searchdirs:
125             path = os.path.join(d, girname)
126             if os.path.exists(path):
127                 return path
128         sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
129                          % (girname, searchdirs))
130         sys.exit(1)
131
132     def _parse_include(self, filename):
133         parser = self._cachestore.load(filename)
134         if parser is None:
135             parser = GIRParser()
136             parser.set_include_parsing(True)
137             parser.parse(filename)
138             self._cachestore.store(filename, parser)
139
140         for include in parser.get_includes():
141             self.register_include(include)
142
143         for pkg in parser.get_pkgconfig_packages():
144             self._pkg_config_packages.add(pkg)
145         namespace = parser.get_namespace()
146         nsname = namespace.name
147         for node in namespace.nodes:
148             if isinstance(node, Alias):
149                 self._names.aliases[node.name] = (nsname, node)
150             elif isinstance(node, (GLibBoxed, Interface, Class)):
151                 self._names.type_names[node.type_name] = (nsname, node)
152             giname = '%s.%s' % (nsname, node.name)
153             self._names.names[giname] = (nsname, node)
154             if hasattr(node, 'ctype'):
155                 self._names.ctypes[node.ctype] = (nsname, node)
156             elif hasattr(node, 'symbol'):
157                 self._names.ctypes[node.symbol] = (nsname, node)
158
159     def _add_node(self, node):
160         if node is None:
161             return
162         if node.name.startswith('_'):
163             return
164         self._namespace.nodes.append(node)
165         self._names.names[node.name] = (None, node)
166
167     def _strip_namespace_func(self, name):
168         prefix = self._namespace.name.lower() + '_'
169         if name.lower().startswith(prefix):
170             name = name[len(prefix):]
171         else:
172             prefix = to_underscores(self._namespace.name).lower() + '_'
173             if name.lower().startswith(prefix):
174                 name = name[len(prefix):]
175         return self.remove_prefix(name, isfunction=True)
176
177     def remove_prefix(self, name, isfunction=False):
178         # when --strip-prefix=g:
179         #   GHashTable -> HashTable
180         #   g_hash_table_new -> hash_table_new
181         prefix = self._strip_prefix.lower()
182         if isfunction:
183             prefix += '_'
184         if len(name) > len(prefix) and name.lower().startswith(prefix):
185             name = name[len(prefix):]
186
187         while name.startswith('_'):
188             name = name[1:]
189         return name
190
191     def _traverse_one(self, symbol, stype=None):
192         assert isinstance(symbol, SourceSymbol), symbol
193
194         if stype is None:
195             stype = symbol.type
196         if stype == CSYMBOL_TYPE_FUNCTION:
197             return self._create_function(symbol)
198         elif stype == CSYMBOL_TYPE_TYPEDEF:
199             return self._create_typedef(symbol)
200         elif stype == CSYMBOL_TYPE_STRUCT:
201             return self._create_struct(symbol)
202         elif stype == CSYMBOL_TYPE_ENUM:
203             return self._create_enum(symbol)
204         elif stype == CSYMBOL_TYPE_OBJECT:
205             return self._create_object(symbol)
206         elif stype == CSYMBOL_TYPE_MEMBER:
207             return self._create_member(symbol)
208         elif stype == CSYMBOL_TYPE_UNION:
209             return self._create_union(symbol)
210         elif stype == CSYMBOL_TYPE_CONST:
211             return self._create_const(symbol)
212         else:
213             raise NotImplementedError(
214                 'Transformer: unhandled symbol: %r' % (symbol, ))
215
216     def _enum_common_prefix(self, symbol):
217         def common_prefix(a, b):
218             commonparts = []
219             for aword, bword in zip(a.split('_'), b.split('_')):
220                 if aword != bword:
221                     return '_'.join(commonparts) + '_'
222                 commonparts.append(aword)
223             return min(a, b)
224
225         # Nothing less than 2 has a common prefix
226         if len(list(symbol.base_type.child_list)) < 2:
227             return None
228         prefix = None
229         for child in symbol.base_type.child_list:
230             if prefix is None:
231                 prefix = child.ident
232             else:
233                 prefix = common_prefix(prefix, child.ident)
234                 if prefix == '':
235                     return None
236         return prefix
237
238     def _create_enum(self, symbol):
239         prefix = self._enum_common_prefix(symbol)
240         if prefix:
241             prefixlen = len(prefix)
242         else:
243             prefixlen = 0
244         members = []
245         for child in symbol.base_type.child_list:
246             if prefixlen > 0:
247                 name = child.ident[prefixlen:]
248             else:
249                 # Ok, the enum members don't have a consistent prefix
250                 # among them, so let's just remove the global namespace
251                 # prefix.
252                 name = self.remove_prefix(child.ident)
253             members.append(Member(name.lower(),
254                                   child.const_int,
255                                   child.ident))
256
257         enum_name = self.remove_prefix(symbol.ident)
258         if symbol.base_type.is_bitfield:
259             klass = Bitfield
260         else:
261             klass = Enum
262         node = klass(enum_name, symbol.ident, members)
263         self._names.type_names[symbol.ident] = (None, node)
264         return node
265
266     def _create_object(self, symbol):
267         return Member(symbol.ident, symbol.base_type.name,
268                       symbol.ident)
269
270     def _type_is_callback(self, type):
271         if isinstance(type, Callback):
272             return True
273         node = self._names.names.get(type.name)
274         if node and isinstance(node[1], Callback):
275             return True
276         return False
277
278     def _handle_closure(self, param, closure_idx, closure_param):
279         if (closure_param.type.name == 'any' and
280             closure_param.name.endswith('data')):
281             param.closure_name = closure_param.name
282             param.closure_index = closure_idx
283             return True
284         return False
285
286     def _handle_destroy(self, param, destroy_idx, destroy_param):
287         if (destroy_param.type.name == 'GLib.DestroyNotify' or
288             destroy_param.type.ctype == 'GDestroyNotify'):
289             param.destroy_name = destroy_param.name
290             param.destroy_index = destroy_idx
291             return True
292         return False
293
294     def _augment_callback_params(self, params):
295         for i, param in enumerate(params):
296             if not self._type_is_callback(param.type):
297                 continue
298
299             # set a default scope
300             if param.scope is None:
301                 param.scope = 'call'
302
303             # j is the index where we look for closure/destroy to
304             # group with the callback param
305             j = i + 1
306             if j == len(params):
307                 continue # no more args -> nothing to group
308             # look at the param directly following for either a
309             # closure or a destroy; only one of these will fire
310             had_closure = self._handle_closure(param, j, params[j])
311             had_destroy = self._handle_destroy(param, j, params[j])
312             j += 1
313             # are we out of params, or did we find neither?
314             if j == len(params) or (not had_closure and not had_destroy):
315                 continue
316             # we found either a closure or a destroy; check the
317             # parameter following for the other
318             if not had_closure:
319                 self._handle_closure(param, j, params[j])
320             if not had_destroy:
321                 self._handle_destroy(param, j, params[j])
322
323     def _create_function(self, symbol):
324         parameters = list(self._create_parameters(symbol.base_type))
325         return_ = self._create_return(symbol.base_type.base_type)
326         self._augment_callback_params(parameters)
327         name = self._strip_namespace_func(symbol.ident)
328         func = Function(name, return_, parameters, symbol.ident)
329         return func
330
331     def _create_source_type(self, source_type):
332         if source_type is None:
333             return 'None'
334         if source_type.type == CTYPE_VOID:
335             value = 'void'
336         elif source_type.type == CTYPE_BASIC_TYPE:
337             value = source_type.name
338         elif source_type.type == CTYPE_TYPEDEF:
339             value = source_type.name
340         elif source_type.type == CTYPE_ARRAY:
341             return self._create_source_type(source_type.base_type)
342         elif source_type.type == CTYPE_POINTER:
343             value = self._create_source_type(source_type.base_type) + '*'
344         else:
345             value = 'any'
346         return value
347
348     def _create_parameters(self, base_type):
349
350         # warn if we see annotations for unknown parameters
351         param_names = set(child.ident for child in base_type.child_list)
352         for child in base_type.child_list:
353             yield self._create_parameter(child)
354
355     def _create_member(self, symbol):
356         source_type = symbol.base_type
357         if (source_type.type == CTYPE_POINTER and
358             symbol.base_type.base_type.type == CTYPE_FUNCTION):
359             node = self._create_callback(symbol)
360         elif source_type.type == CTYPE_STRUCT and source_type.name is None:
361             node = self._create_struct(symbol, anonymous=True)
362         elif source_type.type == CTYPE_UNION and source_type.name is None:
363             node = self._create_union(symbol, anonymous=True)
364         else:
365             # Special handling for fields; we don't have annotations on them
366             # to apply later, yet.
367             if source_type.type == CTYPE_ARRAY:
368                 ctype = self._create_source_type(source_type)
369                 canonical_ctype = self._canonicalize_ctype(ctype)
370                 if canonical_ctype[-1] == '*':
371                     derefed_name = canonical_ctype[:-1]
372                 else:
373                     derefed_name = canonical_ctype
374                 derefed_name = self.resolve_param_type(derefed_name)
375                 ftype = Array(ctype, self.parse_ctype(derefed_name))
376                 child_list = list(symbol.base_type.child_list)
377                 ftype.zeroterminated = False
378                 if child_list:
379                     ftype.size = '%d' % (child_list[0].const_int, )
380             else:
381                 ftype = self._create_type(symbol.base_type,
382                                           is_param=False, is_retval=False)
383             ftype = self.resolve_param_type(ftype)
384             # Fields are assumed to be read-write
385             # (except for Objects, see also glibtransformer.py)
386             node = Field(symbol.ident, ftype, ftype.name,
387                          readable=True, writable=True, bits=symbol.const_int)
388         return node
389
390     def _create_typedef(self, symbol):
391         ctype = symbol.base_type.type
392         if (ctype == CTYPE_POINTER and
393             symbol.base_type.base_type.type == CTYPE_FUNCTION):
394             node = self._create_typedef_callback(symbol)
395         elif (ctype == CTYPE_POINTER and
396             symbol.base_type.base_type.type == CTYPE_STRUCT):
397             node = self._create_typedef_struct(symbol, disguised=True)
398         elif ctype == CTYPE_STRUCT:
399             node = self._create_typedef_struct(symbol)
400         elif ctype == CTYPE_UNION:
401             node = self._create_typedef_union(symbol)
402         elif ctype == CTYPE_ENUM:
403             return self._create_enum(symbol)
404         elif ctype in (CTYPE_TYPEDEF,
405                        CTYPE_POINTER,
406                        CTYPE_BASIC_TYPE,
407                        CTYPE_VOID):
408             name = self.remove_prefix(symbol.ident)
409             if symbol.base_type.name:
410                 target = self.remove_prefix(symbol.base_type.name)
411             else:
412                 target = 'none'
413             if name in type_names:
414                 return None
415             return Alias(name, target, ctype=symbol.ident)
416         else:
417             raise NotImplementedError(
418                 "symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
419         return node
420
421     def _canonicalize_ctype(self, ctype):
422         # First look up the ctype including any pointers;
423         # a few type names like 'char*' have their own aliases
424         # and we need pointer information for those.
425         firstpass = type_name_from_ctype(ctype)
426
427         # If we have a particular alias for this, skip deep
428         # canonicalization to prevent changing
429         # e.g. char* -> int8*
430         if firstpass != ctype:
431             return firstpass
432
433         # We're also done if the type is already a fundamental
434         # known type, or there are no pointers.
435         if ctype in type_names or not firstpass.endswith('*'):
436             return firstpass
437
438         # We have a pointer type.
439         # Strip the end pointer, canonicalize our base type
440         base = firstpass[:-1]
441         canonical_base = self._canonicalize_ctype(base)
442
443         # Append the pointer again
444         canonical = canonical_base + '*'
445
446         return canonical
447
448     def parse_ctype(self, ctype, is_member=False):
449         canonical = self._canonicalize_ctype(ctype)
450
451         # Remove all pointers - we require standard calling
452         # conventions.  For example, an 'int' is always passed by
453         # value (unless it's out or inout).
454         derefed_typename = canonical.replace('*', '')
455
456         # Preserve "pointerness" of struct/union members
457         if (is_member and canonical.endswith('*') and
458             derefed_typename in BASIC_GIR_TYPES):
459             return 'any'
460         else:
461             return derefed_typename
462
463     def _create_type(self, source_type, is_param, is_retval):
464         ctype = self._create_source_type(source_type)
465         if ctype.startswith('va_list'):
466             raise SkipError()
467         # FIXME: FILE* should not be skipped, it should be handled
468         #        properly instead
469         elif ctype == 'FILE*':
470             raise SkipError
471
472         is_member = not (is_param or is_retval)
473         # Here we handle basic type parsing; most of the heavy lifting
474         # and inference comes in annotationparser.py when we merge
475         # in annotation data.
476         derefed_name = self.parse_ctype(ctype, is_member)
477         rettype = Type(derefed_name, ctype)
478         rettype.canonical = self._canonicalize_ctype(ctype)
479         derefed_ctype = ctype.replace('*', '')
480         rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
481
482         canontype = type_name_from_ctype(ctype)
483         # Is it a const char * or a const gpointer?
484         if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
485             (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
486             rettype.is_const = True
487         return rettype
488
489     def _create_parameter(self, symbol):
490         if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
491             ptype = Varargs()
492         else:
493             ptype = self._create_type(symbol.base_type,
494                                       is_param=True, is_retval=False)
495             ptype = self.resolve_param_type(ptype)
496         return Parameter(symbol.ident, ptype)
497
498     def _create_return(self, source_type):
499         rtype = self._create_type(source_type,
500                                   is_param=False, is_retval=True)
501         rtype = self.resolve_param_type(rtype)
502         return_ = Return(rtype)
503         return return_
504
505     def _create_const(self, symbol):
506         # Don't create constants for non-public things
507         # http://bugzilla.gnome.org/show_bug.cgi?id=572790
508         if (symbol.source_filename is None or
509             not symbol.source_filename.endswith('.h')):
510             return None
511         name = self.remove_prefix(symbol.ident)
512         if symbol.const_string is not None:
513             type_name = 'utf8'
514             value = symbol.const_string
515         elif symbol.const_int is not None:
516             type_name = 'int'
517             value = symbol.const_int
518         elif symbol.const_double is not None:
519             type_name = 'double'
520             value = symbol.const_double
521         else:
522             raise AssertionError()
523
524         const = Constant(name, type_name, value)
525         return const
526
527     def _create_typedef_struct(self, symbol, disguised=False):
528         name = self.remove_prefix(symbol.ident)
529         struct = Struct(name, symbol.ident, disguised)
530         self._typedefs_ns[symbol.ident] = struct
531         self._create_struct(symbol)
532         return struct
533
534     def _create_typedef_union(self, symbol):
535         name = self.remove_prefix(symbol.ident)
536         union = Union(name, symbol.ident)
537         self._typedefs_ns[symbol.ident] = union
538         self._create_union(symbol)
539         return union
540
541     def _create_typedef_callback(self, symbol):
542         callback = self._create_callback(symbol)
543         self._typedefs_ns[callback.name] = callback
544         return callback
545
546     def _create_compound(self, klass, symbol, anonymous):
547         if symbol.ident is None:
548             # the compound is an anonymous member of another union or a struct
549             assert anonymous
550             compound = klass(None, None)
551         else:
552             compound = self._typedefs_ns.get(symbol.ident, None)
553
554         if compound is None:
555             # This is a bit of a hack; really we should try
556             # to resolve through the typedefs to find the real
557             # name
558             if symbol.ident.startswith('_'):
559                 name = symbol.ident[1:]
560                 compound = self._typedefs_ns.get(name, None)
561             else:
562                 name = symbol.ident
563             if compound is None:
564                 name = self.remove_prefix(name)
565                 compound = klass(name, symbol.ident)
566
567         for child in symbol.base_type.child_list:
568             field = self._traverse_one(child)
569             if field:
570                 compound.fields.append(field)
571
572         return compound
573
574     def _create_struct(self, symbol, anonymous=False):
575         return self._create_compound(Struct, symbol, anonymous)
576
577     def _create_union(self, symbol, anonymous=False):
578         return self._create_compound(Union, symbol, anonymous)
579
580     def _create_callback(self, symbol):
581         parameters = list(self._create_parameters(symbol.base_type.base_type))
582         retval = self._create_return(symbol.base_type.base_type.base_type)
583
584         # Mark the 'user_data' arguments
585         for i, param in enumerate(parameters):
586             if (param.type.name == 'any' and
587                 param.name == 'user_data'):
588                 param.closure_index = i
589
590         if symbol.ident.find('_') > 0:
591             name = self.remove_prefix(symbol.ident, True)
592         else:
593             name = self.remove_prefix(symbol.ident)
594         callback = Callback(name, retval, parameters, symbol.ident)
595
596         return callback
597
598     def _typepair_to_str(self, item):
599         nsname, item = item
600         if nsname is None:
601             return item.name
602         return '%s.%s' % (nsname, item.name)
603
604     def _resolve_type_name_1(self, type_name, ctype, names):
605         # First look using the built-in names
606         if ctype:
607             try:
608                 return type_names[ctype]
609             except KeyError, e:
610                 pass
611         try:
612             return type_names[type_name]
613         except KeyError, e:
614             pass
615
616         if ctype:
617             ctype = ctype.replace('*', '')
618             resolved = names.ctypes.get(ctype)
619             if resolved:
620                 return self._typepair_to_str(resolved)
621         type_name = self.remove_prefix(type_name)
622         resolved = names.aliases.get(type_name)
623         if resolved:
624             return self._typepair_to_str(resolved)
625         resolved = names.names.get(type_name)
626         if resolved:
627             return self._typepair_to_str(resolved)
628         resolved = names.type_names.get(type_name)
629         if resolved:
630             return self._typepair_to_str(resolved)
631         raise KeyError("failed to find %r" % (type_name, ))
632
633     def resolve_type_name_full(self, type_name, ctype,
634                                names, allow_invalid=True):
635         try:
636             return self._resolve_type_name_1(type_name, ctype, names)
637         except KeyError, e:
638             try:
639                 return self._resolve_type_name_1(type_name, ctype, self._names)
640             except KeyError, e:
641                 if not allow_invalid:
642                     raise
643                 return type_name
644
645     def resolve_type_name(self, type_name, ctype=None):
646         try:
647             return self.resolve_type_name_full(type_name, ctype, self._names)
648         except KeyError, e:
649             return type_name
650
651     def gtypename_to_giname(self, gtname, names):
652         resolved = names.type_names.get(gtname)
653         if resolved:
654             return self._typepair_to_str(resolved)
655         resolved = self._names.type_names.get(gtname)
656         if resolved:
657             return self._typepair_to_str(resolved)
658         raise KeyError("Failed to resolve GType name: %r" % (gtname, ))
659
660     def ctype_of(self, obj):
661         if hasattr(obj, 'ctype'):
662             return obj.ctype
663         elif hasattr(obj, 'symbol'):
664             return obj.symbol
665         else:
666             return None
667
668     def resolve_param_type_full(self, ptype, names, **kwargs):
669         if isinstance(ptype, Node):
670             ptype.name = self.resolve_type_name_full(ptype.name,
671                                                      self.ctype_of(ptype),
672                                                      names, **kwargs)
673         elif isinstance(ptype, basestring):
674             return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
675         else:
676             raise AssertionError("Unhandled param: %r" % (ptype, ))
677         return ptype
678
679     def resolve_param_type(self, ptype):
680         try:
681             return self.resolve_param_type_full(ptype, self._names)
682         except KeyError, e:
683             return ptype
684
685     def follow_aliases(self, type_name, names):
686         while True:
687             resolved = names.aliases.get(type_name)
688             if resolved:
689                 (ns, alias) = resolved
690                 type_name = alias.target
691             else:
692                 break
693         return type_name