]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qapi/schema.py
qapi/schema: add type narrowing to lookup_type()
[mirror_qemu.git] / scripts / qapi / schema.py
CommitLineData
e6c42b96
MA
1# -*- coding: utf-8 -*-
2#
3# QAPI schema internal representation
4#
5# Copyright (c) 2015-2019 Red Hat Inc.
6#
7# Authors:
8# Markus Armbruster <armbru@redhat.com>
9# Eric Blake <eblake@redhat.com>
10# Marc-André Lureau <marcandre.lureau@redhat.com>
11#
12# This work is licensed under the terms of the GNU GPL, version 2.
13# See the COPYING file in the top-level directory.
14
ce7fde06
JS
15# pylint: disable=too-many-lines
16
e6c42b96
MA
17# TODO catching name collisions in generated code would be nice
18
d150be3d 19from abc import ABC, abstractmethod
67fea575 20from collections import OrderedDict
e6c42b96
MA
21import os
22import re
42011059 23from typing import List, Optional
e6c42b96 24
d806f89f
MAL
25from .common import (
26 POINTER_SUFFIX,
27 c_name,
28 cgen_ifcond,
29 docgen_ifcond,
1889e57a
MA
30 gen_endif,
31 gen_if,
d806f89f 32)
3404e574 33from .error import QAPIError, QAPISemError, QAPISourceError
7137a960 34from .expr import check_exprs
42011059 35from .parser import QAPIExpression, QAPISchemaParser
e6c42b96
MA
36
37
f17539c8
MAL
38class QAPISchemaIfCond:
39 def __init__(self, ifcond=None):
e46c930c 40 self.ifcond = ifcond
f17539c8 41
1889e57a 42 def _cgen(self):
6cc2e481
MAL
43 return cgen_ifcond(self.ifcond)
44
1889e57a
MA
45 def gen_if(self):
46 return gen_if(self._cgen())
47
48 def gen_endif(self):
49 return gen_endif(self._cgen())
50
d806f89f
MAL
51 def docgen(self):
52 return docgen_ifcond(self.ifcond)
53
33aa3267
MAL
54 def is_present(self):
55 return bool(self.ifcond)
56
f17539c8 57
baa310f1 58class QAPISchemaEntity:
2418d1c4
JS
59 """
60 A schema entity.
e6c42b96 61
2418d1c4
JS
62 This is either a directive, such as include, or a definition.
63 The latter uses sub-class `QAPISchemaDefinition`.
64 """
65 def __init__(self, info):
e6c42b96
MA
66 self._module = None
67 # For explicitly defined entities, info points to the (explicit)
68 # definition. For builtins (and their arrays), info is None.
69 # For implicitly defined entities, info points to a place that
70 # triggered the implicit definition (there may be more than one
71 # such place).
72 self.info = info
2418d1c4
JS
73 self._checked = False
74
75 def __repr__(self):
76 return "<%s at 0x%x>" % (type(self).__name__, id(self))
77
78 def check(self, schema):
79 # pylint: disable=unused-argument
80 self._checked = True
81
82 def connect_doc(self, doc=None):
83 pass
84
85 def _set_module(self, schema, info):
86 assert self._checked
87 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
88 self._module = schema.module_by_fname(fname)
89 self._module.add_entity(self)
90
91 def set_module(self, schema):
92 self._set_module(schema, self.info)
93
94 def visit(self, visitor):
95 # pylint: disable=unused-argument
96 assert self._checked
97
98
99class QAPISchemaDefinition(QAPISchemaEntity):
100 meta: Optional[str] = None
101
102 def __init__(self, name: str, info, doc, ifcond=None, features=None):
103 assert isinstance(name, str)
104 super().__init__(info)
105 for f in features or []:
106 assert isinstance(f, QAPISchemaFeature)
107 f.set_defined_in(name)
108 self.name = name
e6c42b96 109 self.doc = doc
f17539c8 110 self._ifcond = ifcond or QAPISchemaIfCond()
b3cdff10 111 self.features = features or []
e6c42b96 112
e307a817 113 def __repr__(self):
6d133eef
MA
114 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
115 id(self))
e307a817 116
e6c42b96
MA
117 def c_name(self):
118 return c_name(self.name)
119
120 def check(self, schema):
121 assert not self._checked
2418d1c4 122 super().check(schema)
b3cdff10
MA
123 seen = {}
124 for f in self.features:
125 f.check_clash(self.info, seen)
e6c42b96 126
7faefad1 127 def connect_doc(self, doc=None):
2418d1c4 128 super().connect_doc(doc)
e4405b30
MA
129 doc = doc or self.doc
130 if doc:
131 for f in self.features:
132 doc.connect_feature(f)
ee1e6a1f 133
e6c42b96
MA
134 @property
135 def ifcond(self):
136 assert self._checked
137 return self._ifcond
138
e6c42b96
MA
139 def is_implicit(self):
140 return not self.info
141
e6c42b96
MA
142 def describe(self):
143 assert self.meta
144 return "%s '%s'" % (self.meta, self.name)
145
146
baa310f1 147class QAPISchemaVisitor:
e6c42b96
MA
148 def visit_begin(self, schema):
149 pass
150
151 def visit_end(self):
152 pass
153
8ec0e1a4 154 def visit_module(self, name):
e6c42b96
MA
155 pass
156
157 def visit_needed(self, entity):
ce7fde06 158 # pylint: disable=unused-argument
e6c42b96
MA
159 # Default to visiting everything
160 return True
161
8ec0e1a4 162 def visit_include(self, name, info):
e6c42b96
MA
163 pass
164
165 def visit_builtin_type(self, name, info, json_type):
166 pass
167
013b4efc 168 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
e6c42b96
MA
169 pass
170
171 def visit_array_type(self, name, info, ifcond, element_type):
172 pass
173
7b3bc9e2
MA
174 def visit_object_type(self, name, info, ifcond, features,
175 base, members, variants):
e6c42b96
MA
176 pass
177
7b3bc9e2
MA
178 def visit_object_type_flat(self, name, info, ifcond, features,
179 members, variants):
e6c42b96
MA
180 pass
181
013b4efc 182 def visit_alternate_type(self, name, info, ifcond, features, variants):
e6c42b96
MA
183 pass
184
7b3bc9e2
MA
185 def visit_command(self, name, info, ifcond, features,
186 arg_type, ret_type, gen, success_response, boxed,
04f22362 187 allow_oob, allow_preconfig, coroutine):
e6c42b96
MA
188 pass
189
013b4efc 190 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
e6c42b96
MA
191 pass
192
193
baa310f1 194class QAPISchemaModule:
39b2d838
JS
195
196 BUILTIN_MODULE_NAME = './builtin'
197
a9f1dd7e
MA
198 def __init__(self, name):
199 self.name = name
3e7fb581
MA
200 self._entity_list = []
201
98967c24 202 @staticmethod
e2bbc4ea 203 def is_system_module(name: str) -> bool:
98967c24
JS
204 """
205 System modules are internally defined modules.
206
207 Their names start with the "./" prefix.
208 """
e2bbc4ea 209 return name.startswith('./')
98967c24
JS
210
211 @classmethod
e2bbc4ea 212 def is_user_module(cls, name: str) -> bool:
98967c24
JS
213 """
214 User modules are those defined by the user in qapi JSON files.
215
216 They do not start with the "./" prefix.
217 """
218 return not cls.is_system_module(name)
219
39b2d838
JS
220 @classmethod
221 def is_builtin_module(cls, name: str) -> bool:
98967c24
JS
222 """
223 The built-in module is a single System module for the built-in types.
224
e2bbc4ea 225 It is always "./builtin".
98967c24 226 """
39b2d838 227 return name == cls.BUILTIN_MODULE_NAME
98967c24 228
3e7fb581
MA
229 def add_entity(self, ent):
230 self._entity_list.append(ent)
231
232 def visit(self, visitor):
233 visitor.visit_module(self.name)
234 for entity in self._entity_list:
235 if visitor.visit_needed(entity):
236 entity.visit(visitor)
a9f1dd7e 237
e6c42b96 238
a9f1dd7e
MA
239class QAPISchemaInclude(QAPISchemaEntity):
240 def __init__(self, sub_module, info):
2418d1c4 241 super().__init__(info)
a9f1dd7e 242 self._sub_module = sub_module
e6c42b96
MA
243
244 def visit(self, visitor):
2cae67bc 245 super().visit(visitor)
a9f1dd7e 246 visitor.visit_include(self._sub_module.name, self.info)
e6c42b96
MA
247
248
d150be3d 249class QAPISchemaType(QAPISchemaDefinition, ABC):
e6c42b96
MA
250 # Return the C type for common use.
251 # For the types we commonly box, this is a pointer type.
d150be3d 252 @abstractmethod
e6c42b96
MA
253 def c_type(self):
254 pass
255
256 # Return the C type to be used in a parameter list.
257 def c_param_type(self):
258 return self.c_type()
259
260 # Return the C type to be used where we suppress boxing.
261 def c_unboxed_type(self):
262 return self.c_type()
263
d150be3d 264 @abstractmethod
e6c42b96
MA
265 def json_type(self):
266 pass
267
268 def alternate_qtype(self):
269 json2qtype = {
270 'null': 'QTYPE_QNULL',
271 'string': 'QTYPE_QSTRING',
272 'number': 'QTYPE_QNUM',
273 'int': 'QTYPE_QNUM',
274 'boolean': 'QTYPE_QBOOL',
a5806949 275 'array': 'QTYPE_QLIST',
e6c42b96
MA
276 'object': 'QTYPE_QDICT'
277 }
278 return json2qtype.get(self.json_type())
279
280 def doc_type(self):
281 if self.is_implicit():
282 return None
283 return self.name
284
44ea9d9b
MA
285 def need_has_if_optional(self):
286 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
287 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
288 return not self.c_type().endswith(POINTER_SUFFIX)
289
f965e8fe 290 def check(self, schema):
ecee568e 291 super().check(schema)
57df0dff
MA
292 for feat in self.features:
293 if feat.is_special():
294 raise QAPISemError(
295 self.info,
296 f"feature '{feat.name}' is not supported for types")
f965e8fe 297
e6c42b96
MA
298 def describe(self):
299 assert self.meta
300 return "%s type '%s'" % (self.meta, self.name)
301
302
303class QAPISchemaBuiltinType(QAPISchemaType):
304 meta = 'built-in'
305
306 def __init__(self, name, json_type, c_type):
2cae67bc 307 super().__init__(name, None, None)
e6c42b96
MA
308 assert not c_type or isinstance(c_type, str)
309 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
310 'value')
311 self._json_type_name = json_type
312 self._c_type_name = c_type
313
314 def c_name(self):
315 return self.name
316
317 def c_type(self):
318 return self._c_type_name
319
320 def c_param_type(self):
321 if self.name == 'str':
322 return 'const ' + self._c_type_name
323 return self._c_type_name
324
325 def json_type(self):
326 return self._json_type_name
327
328 def doc_type(self):
329 return self.json_type()
330
331 def visit(self, visitor):
2cae67bc 332 super().visit(visitor)
e6c42b96
MA
333 visitor.visit_builtin_type(self.name, self.info, self.json_type())
334
335
336class QAPISchemaEnumType(QAPISchemaType):
337 meta = 'enum'
338
013b4efc
MA
339 def __init__(self, name, info, doc, ifcond, features, members, prefix):
340 super().__init__(name, info, doc, ifcond, features)
e6c42b96
MA
341 for m in members:
342 assert isinstance(m, QAPISchemaEnumMember)
343 m.set_defined_in(name)
344 assert prefix is None or isinstance(prefix, str)
345 self.members = members
346 self.prefix = prefix
347
348 def check(self, schema):
2cae67bc 349 super().check(schema)
e6c42b96
MA
350 seen = {}
351 for m in self.members:
352 m.check_clash(self.info, seen)
ee1e6a1f 353
7faefad1 354 def connect_doc(self, doc=None):
e4405b30 355 super().connect_doc(doc)
7faefad1 356 doc = doc or self.doc
645178c0
MA
357 for m in self.members:
358 m.connect_doc(doc)
e6c42b96
MA
359
360 def is_implicit(self):
4e99f4b1
MA
361 # See QAPISchema._def_predefineds()
362 return self.name == 'QType'
e6c42b96
MA
363
364 def c_type(self):
365 return c_name(self.name)
366
367 def member_names(self):
368 return [m.name for m in self.members]
369
370 def json_type(self):
371 return 'string'
372
373 def visit(self, visitor):
2cae67bc 374 super().visit(visitor)
013b4efc
MA
375 visitor.visit_enum_type(
376 self.name, self.info, self.ifcond, self.features,
377 self.members, self.prefix)
e6c42b96
MA
378
379
380class QAPISchemaArrayType(QAPISchemaType):
381 meta = 'array'
382
383 def __init__(self, name, info, element_type):
013b4efc 384 super().__init__(name, info, None)
e6c42b96
MA
385 assert isinstance(element_type, str)
386 self._element_type_name = element_type
578cd932 387 self.element_type: QAPISchemaType
e6c42b96 388
44ea9d9b
MA
389 def need_has_if_optional(self):
390 # When FOO is an array, we still need has_FOO to distinguish
391 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
392 return True
393
e6c42b96 394 def check(self, schema):
2cae67bc 395 super().check(schema)
e6c42b96
MA
396 self.element_type = schema.resolve_type(
397 self._element_type_name, self.info,
398 self.info and self.info.defn_meta)
399 assert not isinstance(self.element_type, QAPISchemaArrayType)
400
a9f1dd7e
MA
401 def set_module(self, schema):
402 self._set_module(schema, self.element_type.info)
403
e6c42b96
MA
404 @property
405 def ifcond(self):
406 assert self._checked
407 return self.element_type.ifcond
408
e6c42b96
MA
409 def is_implicit(self):
410 return True
411
412 def c_type(self):
a7aa64a6 413 return c_name(self.name) + POINTER_SUFFIX
e6c42b96
MA
414
415 def json_type(self):
416 return 'array'
417
418 def doc_type(self):
419 elt_doc_type = self.element_type.doc_type()
420 if not elt_doc_type:
421 return None
422 return 'array of ' + elt_doc_type
423
424 def visit(self, visitor):
2cae67bc 425 super().visit(visitor)
e6c42b96
MA
426 visitor.visit_array_type(self.name, self.info, self.ifcond,
427 self.element_type)
428
429 def describe(self):
430 assert self.meta
431 return "%s type ['%s']" % (self.meta, self._element_type_name)
432
433
434class QAPISchemaObjectType(QAPISchemaType):
013b4efc
MA
435 def __init__(self, name, info, doc, ifcond, features,
436 base, local_members, variants):
e6c42b96 437 # struct has local_members, optional base, and no variants
4e99f4b1 438 # union has base, variants, and no local_members
2cae67bc 439 super().__init__(name, info, doc, ifcond, features)
e6c42b96
MA
440 self.meta = 'union' if variants else 'struct'
441 assert base is None or isinstance(base, str)
442 for m in local_members:
443 assert isinstance(m, QAPISchemaObjectTypeMember)
444 m.set_defined_in(name)
445 if variants is not None:
5858fd1a 446 assert isinstance(variants, QAPISchemaVariants)
e6c42b96 447 variants.set_defined_in(name)
e6c42b96
MA
448 self._base_name = base
449 self.base = None
450 self.local_members = local_members
451 self.variants = variants
452 self.members = None
e6c42b96
MA
453
454 def check(self, schema):
455 # This calls another type T's .check() exactly when the C
456 # struct emitted by gen_object() contains that T's C struct
457 # (pointers don't count).
458 if self.members is not None:
459 # A previous .check() completed: nothing to do
460 return
461 if self._checked:
462 # Recursed: C struct contains itself
463 raise QAPISemError(self.info,
464 "object %s contains itself" % self.name)
465
2cae67bc 466 super().check(schema)
e6c42b96
MA
467 assert self._checked and self.members is None
468
469 seen = OrderedDict()
470 if self._base_name:
471 self.base = schema.resolve_type(self._base_name, self.info,
472 "'base'")
473 if (not isinstance(self.base, QAPISchemaObjectType)
474 or self.base.variants):
475 raise QAPISemError(
476 self.info,
477 "'base' requires a struct type, %s isn't"
478 % self.base.describe())
479 self.base.check(schema)
480 self.base.check_clash(self.info, seen)
481 for m in self.local_members:
482 m.check(schema)
483 m.check_clash(self.info, seen)
e6c42b96
MA
484 members = seen.values()
485
486 if self.variants:
487 self.variants.check(schema, seen)
488 self.variants.check_clash(self.info, seen)
489
e6c42b96
MA
490 self.members = members # mark completed
491
492 # Check that the members of this type do not cause duplicate JSON members,
493 # and update seen to track the members seen so far. Report any errors
494 # on behalf of info, which is not necessarily self.info
495 def check_clash(self, info, seen):
496 assert self._checked
e6c42b96
MA
497 for m in self.members:
498 m.check_clash(info, seen)
a17dbc4b
DB
499 if self.variants:
500 self.variants.check_clash(info, seen)
e6c42b96 501
7faefad1 502 def connect_doc(self, doc=None):
e4405b30 503 super().connect_doc(doc)
7faefad1 504 doc = doc or self.doc
645178c0
MA
505 if self.base and self.base.is_implicit():
506 self.base.connect_doc(doc)
507 for m in self.local_members:
508 m.connect_doc(doc)
ee1e6a1f 509
e6c42b96
MA
510 def is_implicit(self):
511 # See QAPISchema._make_implicit_object_type(), as well as
512 # _def_predefineds()
513 return self.name.startswith('q_')
514
515 def is_empty(self):
516 assert self.members is not None
517 return not self.members and not self.variants
518
de3b3f52
MA
519 def has_conditional_members(self):
520 assert self.members is not None
521 return any(m.ifcond.is_present() for m in self.members)
522
e6c42b96
MA
523 def c_name(self):
524 assert self.name != 'q_empty'
2cae67bc 525 return super().c_name()
e6c42b96
MA
526
527 def c_type(self):
528 assert not self.is_implicit()
a7aa64a6 529 return c_name(self.name) + POINTER_SUFFIX
e6c42b96
MA
530
531 def c_unboxed_type(self):
532 return c_name(self.name)
533
534 def json_type(self):
535 return 'object'
536
537 def visit(self, visitor):
2cae67bc 538 super().visit(visitor)
7b3bc9e2
MA
539 visitor.visit_object_type(
540 self.name, self.info, self.ifcond, self.features,
541 self.base, self.local_members, self.variants)
542 visitor.visit_object_type_flat(
543 self.name, self.info, self.ifcond, self.features,
544 self.members, self.variants)
e6c42b96
MA
545
546
226b5be6
MA
547class QAPISchemaAlternateType(QAPISchemaType):
548 meta = 'alternate'
e6c42b96 549
226b5be6
MA
550 def __init__(self, name, info, doc, ifcond, features, variants):
551 super().__init__(name, info, doc, ifcond, features)
5858fd1a 552 assert isinstance(variants, QAPISchemaVariants)
226b5be6
MA
553 assert variants.tag_member
554 variants.set_defined_in(name)
555 variants.tag_member.set_defined_in(self.name)
556 self.variants = variants
e6c42b96 557
226b5be6
MA
558 def check(self, schema):
559 super().check(schema)
560 self.variants.tag_member.check(schema)
561 # Not calling self.variants.check_clash(), because there's nothing
562 # to clash with
563 self.variants.check(schema, {})
564 # Alternate branch names have no relation to the tag enum values;
565 # so we have to check for potential name collisions ourselves.
566 seen = {}
567 types_seen = {}
568 for v in self.variants.variants:
569 v.check_clash(self.info, seen)
570 qtype = v.type.alternate_qtype()
571 if not qtype:
572 raise QAPISemError(
573 self.info,
574 "%s cannot use %s"
575 % (v.describe(self.info), v.type.describe()))
576 conflicting = set([qtype])
577 if qtype == 'QTYPE_QSTRING':
578 if isinstance(v.type, QAPISchemaEnumType):
579 for m in v.type.members:
580 if m.name in ['on', 'off']:
581 conflicting.add('QTYPE_QBOOL')
582 if re.match(r'[-+0-9.]', m.name):
583 # lazy, could be tightened
584 conflicting.add('QTYPE_QNUM')
585 else:
586 conflicting.add('QTYPE_QNUM')
587 conflicting.add('QTYPE_QBOOL')
588 for qt in conflicting:
589 if qt in types_seen:
590 raise QAPISemError(
591 self.info,
592 "%s can't be distinguished from '%s'"
593 % (v.describe(self.info), types_seen[qt]))
594 types_seen[qt] = v.name
e6c42b96 595
226b5be6
MA
596 def connect_doc(self, doc=None):
597 super().connect_doc(doc)
598 doc = doc or self.doc
645178c0
MA
599 for v in self.variants.variants:
600 v.connect_doc(doc)
e6c42b96 601
226b5be6 602 def c_type(self):
a7aa64a6 603 return c_name(self.name) + POINTER_SUFFIX
e6c42b96 604
226b5be6
MA
605 def json_type(self):
606 return 'value'
e6c42b96 607
226b5be6
MA
608 def visit(self, visitor):
609 super().visit(visitor)
610 visitor.visit_alternate_type(
611 self.name, self.info, self.ifcond, self.features, self.variants)
e6c42b96
MA
612
613
5858fd1a 614class QAPISchemaVariants:
e6c42b96 615 def __init__(self, tag_name, info, tag_member, variants):
4e99f4b1
MA
616 # Unions pass tag_name but not tag_member.
617 # Alternates pass tag_member but not tag_name.
618 # After check(), tag_member is always set.
e6c42b96
MA
619 assert bool(tag_member) != bool(tag_name)
620 assert (isinstance(tag_name, str) or
621 isinstance(tag_member, QAPISchemaObjectTypeMember))
622 for v in variants:
5858fd1a 623 assert isinstance(v, QAPISchemaVariant)
e6c42b96
MA
624 self._tag_name = tag_name
625 self.info = info
626 self.tag_member = tag_member
627 self.variants = variants
628
629 def set_defined_in(self, name):
630 for v in self.variants:
631 v.set_defined_in(name)
632
633 def check(self, schema, seen):
4e99f4b1 634 if self._tag_name: # union
e6c42b96
MA
635 self.tag_member = seen.get(c_name(self._tag_name))
636 base = "'base'"
637 # Pointing to the base type when not implicit would be
638 # nice, but we don't know it here
639 if not self.tag_member or self._tag_name != self.tag_member.name:
640 raise QAPISemError(
641 self.info,
642 "discriminator '%s' is not a member of %s"
643 % (self._tag_name, base))
644 # Here we do:
645 base_type = schema.lookup_type(self.tag_member.defined_in)
646 assert base_type
647 if not base_type.is_implicit():
648 base = "base type '%s'" % self.tag_member.defined_in
649 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
650 raise QAPISemError(
651 self.info,
652 "discriminator member '%s' of %s must be of enum type"
653 % (self._tag_name, base))
654 if self.tag_member.optional:
655 raise QAPISemError(
656 self.info,
657 "discriminator member '%s' of %s must not be optional"
658 % (self._tag_name, base))
33aa3267 659 if self.tag_member.ifcond.is_present():
e6c42b96
MA
660 raise QAPISemError(
661 self.info,
662 "discriminator member '%s' of %s must not be conditional"
663 % (self._tag_name, base))
4e99f4b1 664 else: # alternate
e6c42b96
MA
665 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
666 assert not self.tag_member.optional
33aa3267 667 assert not self.tag_member.ifcond.is_present()
4e99f4b1 668 if self._tag_name: # union
e6c42b96 669 # branches that are not explicitly covered get an empty type
8ec0e1a4 670 cases = {v.name for v in self.variants}
e6c42b96
MA
671 for m in self.tag_member.type.members:
672 if m.name not in cases:
5858fd1a
MA
673 v = QAPISchemaVariant(m.name, self.info,
674 'q_empty', m.ifcond)
e6c42b96
MA
675 v.set_defined_in(self.tag_member.defined_in)
676 self.variants.append(v)
677 if not self.variants:
678 raise QAPISemError(self.info, "union has no branches")
679 for v in self.variants:
680 v.check(schema)
681 # Union names must match enum values; alternate names are
682 # checked separately. Use 'seen' to tell the two apart.
683 if seen:
684 if v.name not in self.tag_member.type.member_names():
685 raise QAPISemError(
686 self.info,
687 "branch '%s' is not a value of %s"
688 % (v.name, self.tag_member.type.describe()))
a17dbc4b 689 if not isinstance(v.type, QAPISchemaObjectType):
e6c42b96
MA
690 raise QAPISemError(
691 self.info,
692 "%s cannot use %s"
693 % (v.describe(self.info), v.type.describe()))
694 v.type.check(schema)
695
696 def check_clash(self, info, seen):
697 for v in self.variants:
698 # Reset seen map for each variant, since qapi names from one
699 # branch do not affect another branch
700 v.type.check_clash(info, dict(seen))
701
702
226b5be6
MA
703class QAPISchemaMember:
704 """ Represents object members, enum members and features """
705 role = 'member'
e6c42b96 706
226b5be6
MA
707 def __init__(self, name, info, ifcond=None):
708 assert isinstance(name, str)
709 self.name = name
710 self.info = info
f17539c8 711 self.ifcond = ifcond or QAPISchemaIfCond()
226b5be6 712 self.defined_in = None
e6c42b96 713
226b5be6
MA
714 def set_defined_in(self, name):
715 assert not self.defined_in
716 self.defined_in = name
e6c42b96 717
226b5be6
MA
718 def check_clash(self, info, seen):
719 cname = c_name(self.name)
720 if cname in seen:
721 raise QAPISemError(
722 info,
723 "%s collides with %s"
724 % (self.describe(info), seen[cname].describe(info)))
725 seen[cname] = self
e6c42b96 726
645178c0
MA
727 def connect_doc(self, doc):
728 if doc:
729 doc.connect_member(self)
730
226b5be6
MA
731 def describe(self, info):
732 role = self.role
1e148b54 733 meta = 'type'
226b5be6
MA
734 defined_in = self.defined_in
735 assert defined_in
e6c42b96 736
226b5be6
MA
737 if defined_in.startswith('q_obj_'):
738 # See QAPISchema._make_implicit_object_type() - reverse the
739 # mapping there to create a nice human-readable description
740 defined_in = defined_in[6:]
741 if defined_in.endswith('-arg'):
742 # Implicit type created for a command's dict 'data'
743 assert role == 'member'
744 role = 'parameter'
1e148b54
MA
745 meta = 'command'
746 defined_in = defined_in[:-4]
226b5be6 747 elif defined_in.endswith('-base'):
4e99f4b1 748 # Implicit type created for a union's dict 'base'
226b5be6 749 role = 'base ' + role
1e148b54 750 defined_in = defined_in[:-5]
226b5be6 751 else:
226b5be6 752 assert False
1e148b54
MA
753
754 if defined_in != info.defn_name:
755 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
226b5be6 756 return "%s '%s'" % (role, self.name)
ee1e6a1f 757
ee1e6a1f 758
226b5be6
MA
759class QAPISchemaEnumMember(QAPISchemaMember):
760 role = 'value'
e6c42b96 761
b6c18755
MA
762 def __init__(self, name, info, ifcond=None, features=None):
763 super().__init__(name, info, ifcond)
764 for f in features or []:
765 assert isinstance(f, QAPISchemaFeature)
766 f.set_defined_in(name)
767 self.features = features or []
768
769 def connect_doc(self, doc):
770 super().connect_doc(doc)
771 if doc:
772 for f in self.features:
773 doc.connect_feature(f)
774
e6c42b96 775
226b5be6
MA
776class QAPISchemaFeature(QAPISchemaMember):
777 role = 'feature'
778
c67db1ed 779 def is_special(self):
57df0dff 780 return self.name in ('deprecated', 'unstable')
c67db1ed 781
226b5be6
MA
782
783class QAPISchemaObjectTypeMember(QAPISchemaMember):
84ab0086 784 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
226b5be6
MA
785 super().__init__(name, info, ifcond)
786 assert isinstance(typ, str)
787 assert isinstance(optional, bool)
84ab0086
MA
788 for f in features or []:
789 assert isinstance(f, QAPISchemaFeature)
790 f.set_defined_in(name)
226b5be6 791 self._type_name = typ
ec103961 792 self.type: QAPISchemaType # set during check()
226b5be6 793 self.optional = optional
84ab0086 794 self.features = features or []
226b5be6 795
44ea9d9b
MA
796 def need_has(self):
797 assert self.type
44ea9d9b
MA
798 return self.optional and self.type.need_has_if_optional()
799
226b5be6
MA
800 def check(self, schema):
801 assert self.defined_in
802 self.type = schema.resolve_type(self._type_name, self.info,
803 self.describe)
84ab0086
MA
804 seen = {}
805 for f in self.features:
806 f.check_clash(self.info, seen)
807
808 def connect_doc(self, doc):
809 super().connect_doc(doc)
810 if doc:
811 for f in self.features:
812 doc.connect_feature(f)
226b5be6
MA
813
814
5858fd1a 815class QAPISchemaVariant(QAPISchemaObjectTypeMember):
226b5be6
MA
816 role = 'branch'
817
818 def __init__(self, name, info, typ, ifcond=None):
819 super().__init__(name, info, typ, False, ifcond)
e6c42b96
MA
820
821
2418d1c4 822class QAPISchemaCommand(QAPISchemaDefinition):
e6c42b96
MA
823 meta = 'command'
824
013b4efc
MA
825 def __init__(self, name, info, doc, ifcond, features,
826 arg_type, ret_type,
04f22362
KW
827 gen, success_response, boxed, allow_oob, allow_preconfig,
828 coroutine):
2cae67bc 829 super().__init__(name, info, doc, ifcond, features)
e6c42b96
MA
830 assert not arg_type or isinstance(arg_type, str)
831 assert not ret_type or isinstance(ret_type, str)
832 self._arg_type_name = arg_type
833 self.arg_type = None
834 self._ret_type_name = ret_type
835 self.ret_type = None
836 self.gen = gen
837 self.success_response = success_response
838 self.boxed = boxed
839 self.allow_oob = allow_oob
840 self.allow_preconfig = allow_preconfig
04f22362 841 self.coroutine = coroutine
e6c42b96
MA
842
843 def check(self, schema):
2cae67bc 844 super().check(schema)
e6c42b96 845 if self._arg_type_name:
9bda6c7d 846 arg_type = schema.resolve_type(
e6c42b96 847 self._arg_type_name, self.info, "command's 'data'")
9bda6c7d 848 if not isinstance(arg_type, QAPISchemaObjectType):
e6c42b96
MA
849 raise QAPISemError(
850 self.info,
851 "command's 'data' cannot take %s"
9bda6c7d
JS
852 % arg_type.describe())
853 self.arg_type = arg_type
e6c42b96
MA
854 if self.arg_type.variants and not self.boxed:
855 raise QAPISemError(
856 self.info,
857 "command's 'data' can take %s only with 'boxed': true"
858 % self.arg_type.describe())
de3b3f52
MA
859 self.arg_type.check(schema)
860 if self.arg_type.has_conditional_members() and not self.boxed:
861 raise QAPISemError(
862 self.info,
863 "conditional command arguments require 'boxed': true")
e6c42b96
MA
864 if self._ret_type_name:
865 self.ret_type = schema.resolve_type(
866 self._ret_type_name, self.info, "command's 'returns'")
b86df374 867 if self.name not in self.info.pragma.command_returns_exceptions:
7e9c1707
MA
868 typ = self.ret_type
869 if isinstance(typ, QAPISchemaArrayType):
7e9c1707 870 assert typ
9bda6c7d 871 typ = typ.element_type
7e9c1707 872 if not isinstance(typ, QAPISchemaObjectType):
e6c42b96
MA
873 raise QAPISemError(
874 self.info,
875 "command's 'returns' cannot take %s"
876 % self.ret_type.describe())
877
bf83f04e 878 def connect_doc(self, doc=None):
e4405b30 879 super().connect_doc(doc)
bf83f04e
MA
880 doc = doc or self.doc
881 if doc:
882 if self.arg_type and self.arg_type.is_implicit():
883 self.arg_type.connect_doc(doc)
884
e6c42b96 885 def visit(self, visitor):
2cae67bc 886 super().visit(visitor)
7b3bc9e2
MA
887 visitor.visit_command(
888 self.name, self.info, self.ifcond, self.features,
889 self.arg_type, self.ret_type, self.gen, self.success_response,
04f22362
KW
890 self.boxed, self.allow_oob, self.allow_preconfig,
891 self.coroutine)
e6c42b96
MA
892
893
2418d1c4 894class QAPISchemaEvent(QAPISchemaDefinition):
e6c42b96
MA
895 meta = 'event'
896
013b4efc
MA
897 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
898 super().__init__(name, info, doc, ifcond, features)
e6c42b96
MA
899 assert not arg_type or isinstance(arg_type, str)
900 self._arg_type_name = arg_type
901 self.arg_type = None
902 self.boxed = boxed
903
904 def check(self, schema):
2cae67bc 905 super().check(schema)
e6c42b96 906 if self._arg_type_name:
9bda6c7d 907 typ = schema.resolve_type(
e6c42b96 908 self._arg_type_name, self.info, "event's 'data'")
9bda6c7d 909 if not isinstance(typ, QAPISchemaObjectType):
e6c42b96
MA
910 raise QAPISemError(
911 self.info,
912 "event's 'data' cannot take %s"
9bda6c7d
JS
913 % typ.describe())
914 self.arg_type = typ
e6c42b96
MA
915 if self.arg_type.variants and not self.boxed:
916 raise QAPISemError(
917 self.info,
918 "event's 'data' can take %s only with 'boxed': true"
919 % self.arg_type.describe())
de3b3f52
MA
920 self.arg_type.check(schema)
921 if self.arg_type.has_conditional_members() and not self.boxed:
922 raise QAPISemError(
923 self.info,
924 "conditional event arguments require 'boxed': true")
e6c42b96 925
bf83f04e 926 def connect_doc(self, doc=None):
e4405b30 927 super().connect_doc(doc)
bf83f04e
MA
928 doc = doc or self.doc
929 if doc:
930 if self.arg_type and self.arg_type.is_implicit():
931 self.arg_type.connect_doc(doc)
932
e6c42b96 933 def visit(self, visitor):
2cae67bc 934 super().visit(visitor)
013b4efc
MA
935 visitor.visit_event(
936 self.name, self.info, self.ifcond, self.features,
937 self.arg_type, self.boxed)
e6c42b96
MA
938
939
baa310f1 940class QAPISchema:
e6c42b96
MA
941 def __init__(self, fname):
942 self.fname = fname
3404e574
JS
943
944 try:
945 parser = QAPISchemaParser(fname)
946 except OSError as err:
947 raise QAPIError(
948 f"can't read schema file '{fname}': {err.strerror}"
949 ) from err
950
e6c42b96
MA
951 exprs = check_exprs(parser.exprs)
952 self.docs = parser.docs
953 self._entity_list = []
954 self._entity_dict = {}
43d1455c 955 self._module_dict = OrderedDict()
a9f1dd7e 956 self._schema_dir = os.path.dirname(fname)
39b2d838 957 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
a9f1dd7e 958 self._make_module(fname)
e6c42b96
MA
959 self._predefining = True
960 self._def_predefineds()
961 self._predefining = False
962 self._def_exprs(exprs)
963 self.check()
964
965 def _def_entity(self, ent):
e6c42b96 966 self._entity_list.append(ent)
2418d1c4
JS
967
968 def _def_definition(self, defn):
969 # Only the predefined types are allowed to not have info
970 assert defn.info or self._predefining
971 self._def_entity(defn)
e6c42b96
MA
972 # TODO reject names that differ only in '_' vs. '.' vs. '-',
973 # because they're liable to clash in generated C.
2418d1c4
JS
974 other_defn = self._entity_dict.get(defn.name)
975 if other_defn:
976 if other_defn.info:
977 where = QAPISourceError(other_defn.info, "previous definition")
e6c42b96 978 raise QAPISemError(
2418d1c4
JS
979 defn.info,
980 "'%s' is already defined\n%s" % (defn.name, where))
e6c42b96 981 raise QAPISemError(
2418d1c4
JS
982 defn.info, "%s is already defined" % other_defn.describe())
983 self._entity_dict[defn.name] = defn
e6c42b96
MA
984
985 def lookup_entity(self, name, typ=None):
986 ent = self._entity_dict.get(name)
987 if typ and not isinstance(ent, typ):
988 return None
989 return ent
990
991 def lookup_type(self, name):
10755a95
JS
992 typ = self.lookup_entity(name, QAPISchemaType)
993 assert typ is None or isinstance(typ, QAPISchemaType)
994 return typ
e6c42b96
MA
995
996 def resolve_type(self, name, info, what):
997 typ = self.lookup_type(name)
998 if not typ:
999 if callable(what):
1000 what = what(info)
1001 raise QAPISemError(
1002 info, "%s uses unknown type '%s'" % (what, name))
1003 return typ
1004
e2bbc4ea 1005 def _module_name(self, fname: str) -> str:
98967c24
JS
1006 if QAPISchemaModule.is_system_module(fname):
1007 return fname
a9f1dd7e
MA
1008 return os.path.relpath(fname, self._schema_dir)
1009
1010 def _make_module(self, fname):
1011 name = self._module_name(fname)
8ec0e1a4 1012 if name not in self._module_dict:
a9f1dd7e
MA
1013 self._module_dict[name] = QAPISchemaModule(name)
1014 return self._module_dict[name]
1015
1016 def module_by_fname(self, fname):
1017 name = self._module_name(fname)
a9f1dd7e
MA
1018 return self._module_dict[name]
1019
42011059 1020 def _def_include(self, expr: QAPIExpression):
e6c42b96 1021 include = expr['include']
42011059
JS
1022 assert expr.doc is None
1023 self._def_entity(
1024 QAPISchemaInclude(self._make_module(include), expr.info))
e6c42b96
MA
1025
1026 def _def_builtin_type(self, name, json_type, c_type):
2418d1c4 1027 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type))
e6c42b96
MA
1028 # Instantiating only the arrays that are actually used would
1029 # be nice, but we can't as long as their generated code
1030 # (qapi-builtin-types.[ch]) may be shared by some other
1031 # schema.
1032 self._make_array_type(name, None)
1033
1034 def _def_predefineds(self):
a7aa64a6 1035 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
e6c42b96
MA
1036 ('number', 'number', 'double'),
1037 ('int', 'int', 'int64_t'),
1038 ('int8', 'int', 'int8_t'),
1039 ('int16', 'int', 'int16_t'),
1040 ('int32', 'int', 'int32_t'),
1041 ('int64', 'int', 'int64_t'),
1042 ('uint8', 'int', 'uint8_t'),
1043 ('uint16', 'int', 'uint16_t'),
1044 ('uint32', 'int', 'uint32_t'),
1045 ('uint64', 'int', 'uint64_t'),
1046 ('size', 'int', 'uint64_t'),
1047 ('bool', 'boolean', 'bool'),
a7aa64a6
JS
1048 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1049 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
e6c42b96
MA
1050 self._def_builtin_type(*t)
1051 self.the_empty_object_type = QAPISchemaObjectType(
013b4efc 1052 'q_empty', None, None, None, None, None, [], None)
2418d1c4 1053 self._def_definition(self.the_empty_object_type)
e6c42b96
MA
1054
1055 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1056 'qbool']
1057 qtype_values = self._make_enum_members(
1058 [{'name': n} for n in qtypes], None)
1059
2418d1c4
JS
1060 self._def_definition(QAPISchemaEnumType(
1061 'QType', None, None, None, None, qtype_values, 'QTYPE'))
e6c42b96 1062
ed30f58d
MA
1063 def _make_features(self, features, info):
1064 if features is None:
1065 return []
f17539c8
MAL
1066 return [QAPISchemaFeature(f['name'], info,
1067 QAPISchemaIfCond(f.get('if')))
e6c42b96
MA
1068 for f in features]
1069
b6c18755
MA
1070 def _make_enum_member(self, name, ifcond, features, info):
1071 return QAPISchemaEnumMember(name, info,
1072 QAPISchemaIfCond(ifcond),
1073 self._make_features(features, info))
1074
e6c42b96 1075 def _make_enum_members(self, values, info):
b6c18755
MA
1076 return [self._make_enum_member(v['name'], v.get('if'),
1077 v.get('features'), info)
e6c42b96
MA
1078 for v in values]
1079
e6c42b96
MA
1080 def _make_array_type(self, element_type, info):
1081 name = element_type + 'List' # reserved by check_defn_name_str()
1082 if not self.lookup_type(name):
2418d1c4
JS
1083 self._def_definition(QAPISchemaArrayType(
1084 name, info, element_type))
e6c42b96
MA
1085 return name
1086
a710e1c8 1087 def _make_implicit_object_type(self, name, info, ifcond, role, members):
e6c42b96
MA
1088 if not members:
1089 return None
1090 # See also QAPISchemaObjectTypeMember.describe()
1091 name = 'q_obj_%s-%s' % (name, role)
1092 typ = self.lookup_entity(name, QAPISchemaObjectType)
1093 if typ:
4e99f4b1
MA
1094 # The implicit object type has multiple users. This can
1095 # only be a duplicate definition, which will be flagged
1096 # later.
b32abbb2 1097 pass
e6c42b96 1098 else:
2418d1c4 1099 self._def_definition(QAPISchemaObjectType(
013b4efc 1100 name, info, None, ifcond, None, None, members, None))
e6c42b96
MA
1101 return name
1102
42011059 1103 def _def_enum_type(self, expr: QAPIExpression):
e6c42b96
MA
1104 name = expr['enum']
1105 data = expr['data']
1106 prefix = expr.get('prefix')
f17539c8 1107 ifcond = QAPISchemaIfCond(expr.get('if'))
42011059 1108 info = expr.info
ed30f58d 1109 features = self._make_features(expr.get('features'), info)
2418d1c4 1110 self._def_definition(QAPISchemaEnumType(
42011059 1111 name, info, expr.doc, ifcond, features,
e6c42b96
MA
1112 self._make_enum_members(data, info), prefix))
1113
84ab0086 1114 def _make_member(self, name, typ, ifcond, features, info):
e6c42b96
MA
1115 optional = False
1116 if name.startswith('*'):
1117 name = name[1:]
1118 optional = True
1119 if isinstance(typ, list):
1120 assert len(typ) == 1
1121 typ = self._make_array_type(typ[0], info)
84ab0086
MA
1122 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1123 self._make_features(features, info))
e6c42b96
MA
1124
1125 def _make_members(self, data, info):
f17539c8
MAL
1126 return [self._make_member(key, value['type'],
1127 QAPISchemaIfCond(value.get('if')),
84ab0086 1128 value.get('features'), info)
e6c42b96
MA
1129 for (key, value) in data.items()]
1130
42011059 1131 def _def_struct_type(self, expr: QAPIExpression):
e6c42b96
MA
1132 name = expr['struct']
1133 base = expr.get('base')
1134 data = expr['data']
42011059 1135 info = expr.info
f17539c8 1136 ifcond = QAPISchemaIfCond(expr.get('if'))
ed30f58d 1137 features = self._make_features(expr.get('features'), info)
2418d1c4 1138 self._def_definition(QAPISchemaObjectType(
42011059 1139 name, info, expr.doc, ifcond, features, base,
e6c42b96 1140 self._make_members(data, info),
013b4efc 1141 None))
e6c42b96
MA
1142
1143 def _make_variant(self, case, typ, ifcond, info):
a5806949
PB
1144 if isinstance(typ, list):
1145 assert len(typ) == 1
1146 typ = self._make_array_type(typ[0], info)
5858fd1a 1147 return QAPISchemaVariant(case, info, typ, ifcond)
e6c42b96 1148
42011059 1149 def _def_union_type(self, expr: QAPIExpression):
e6c42b96 1150 name = expr['union']
4e99f4b1
MA
1151 base = expr['base']
1152 tag_name = expr['discriminator']
e6c42b96 1153 data = expr['data']
42011059
JS
1154 assert isinstance(data, dict)
1155 info = expr.info
f17539c8 1156 ifcond = QAPISchemaIfCond(expr.get('if'))
ed30f58d 1157 features = self._make_features(expr.get('features'), info)
e6c42b96
MA
1158 if isinstance(base, dict):
1159 base = self._make_implicit_object_type(
a710e1c8 1160 name, info, ifcond,
e6c42b96 1161 'base', self._make_members(base, info))
4e99f4b1
MA
1162 variants = [
1163 self._make_variant(key, value['type'],
1164 QAPISchemaIfCond(value.get('if')),
1165 info)
1166 for (key, value) in data.items()]
42011059 1167 members: List[QAPISchemaObjectTypeMember] = []
2418d1c4 1168 self._def_definition(
42011059 1169 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
013b4efc 1170 base, members,
5858fd1a 1171 QAPISchemaVariants(
4e99f4b1 1172 tag_name, info, None, variants)))
e6c42b96 1173
42011059 1174 def _def_alternate_type(self, expr: QAPIExpression):
e6c42b96
MA
1175 name = expr['alternate']
1176 data = expr['data']
42011059 1177 assert isinstance(data, dict)
f17539c8 1178 ifcond = QAPISchemaIfCond(expr.get('if'))
42011059 1179 info = expr.info
ed30f58d 1180 features = self._make_features(expr.get('features'), info)
f17539c8
MAL
1181 variants = [
1182 self._make_variant(key, value['type'],
1183 QAPISchemaIfCond(value.get('if')),
1184 info)
1185 for (key, value) in data.items()]
e6c42b96 1186 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
2418d1c4 1187 self._def_definition(
42011059
JS
1188 QAPISchemaAlternateType(
1189 name, info, expr.doc, ifcond, features,
1190 QAPISchemaVariants(None, info, tag_member, variants)))
e6c42b96 1191
42011059 1192 def _def_command(self, expr: QAPIExpression):
e6c42b96
MA
1193 name = expr['command']
1194 data = expr.get('data')
1195 rets = expr.get('returns')
1196 gen = expr.get('gen', True)
1197 success_response = expr.get('success-response', True)
1198 boxed = expr.get('boxed', False)
1199 allow_oob = expr.get('allow-oob', False)
1200 allow_preconfig = expr.get('allow-preconfig', False)
04f22362 1201 coroutine = expr.get('coroutine', False)
f17539c8 1202 ifcond = QAPISchemaIfCond(expr.get('if'))
42011059 1203 info = expr.info
ed30f58d 1204 features = self._make_features(expr.get('features'), info)
e6c42b96
MA
1205 if isinstance(data, OrderedDict):
1206 data = self._make_implicit_object_type(
013b4efc
MA
1207 name, info, ifcond,
1208 'arg', self._make_members(data, info))
e6c42b96
MA
1209 if isinstance(rets, list):
1210 assert len(rets) == 1
1211 rets = self._make_array_type(rets[0], info)
2418d1c4
JS
1212 self._def_definition(
1213 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
1214 rets, gen, success_response, boxed, allow_oob,
1215 allow_preconfig, coroutine))
e6c42b96 1216
42011059 1217 def _def_event(self, expr: QAPIExpression):
e6c42b96
MA
1218 name = expr['event']
1219 data = expr.get('data')
1220 boxed = expr.get('boxed', False)
f17539c8 1221 ifcond = QAPISchemaIfCond(expr.get('if'))
42011059 1222 info = expr.info
ed30f58d 1223 features = self._make_features(expr.get('features'), info)
e6c42b96
MA
1224 if isinstance(data, OrderedDict):
1225 data = self._make_implicit_object_type(
013b4efc
MA
1226 name, info, ifcond,
1227 'arg', self._make_members(data, info))
2418d1c4
JS
1228 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
1229 features, data, boxed))
e6c42b96
MA
1230
1231 def _def_exprs(self, exprs):
42011059 1232 for expr in exprs:
e6c42b96 1233 if 'enum' in expr:
42011059 1234 self._def_enum_type(expr)
e6c42b96 1235 elif 'struct' in expr:
42011059 1236 self._def_struct_type(expr)
e6c42b96 1237 elif 'union' in expr:
42011059 1238 self._def_union_type(expr)
e6c42b96 1239 elif 'alternate' in expr:
42011059 1240 self._def_alternate_type(expr)
e6c42b96 1241 elif 'command' in expr:
42011059 1242 self._def_command(expr)
e6c42b96 1243 elif 'event' in expr:
42011059 1244 self._def_event(expr)
e6c42b96 1245 elif 'include' in expr:
42011059 1246 self._def_include(expr)
e6c42b96
MA
1247 else:
1248 assert False
1249
1250 def check(self):
1251 for ent in self._entity_list:
1252 ent.check(self)
ee1e6a1f 1253 ent.connect_doc()
a9f1dd7e
MA
1254 for ent in self._entity_list:
1255 ent.set_module(self)
fedc04c9
MA
1256 for doc in self.docs:
1257 doc.check()
e6c42b96
MA
1258
1259 def visit(self, visitor):
1260 visitor.visit_begin(self)
3e7fb581
MA
1261 for mod in self._module_dict.values():
1262 mod.visit(visitor)
e6c42b96 1263 visitor.visit_end()