qapi/schema: add type hints
[qemu/kevin.git] / scripts / qapi / schema.py
blobef4d6a7def32976fadc7a8a6eecc79086c834968
1 # -*- coding: utf-8 -*-
3 # QAPI schema internal representation
5 # Copyright (c) 2015-2019 Red Hat Inc.
7 # Authors:
8 # Markus Armbruster <armbru@redhat.com>
9 # Eric Blake <eblake@redhat.com>
10 # Marc-André Lureau <marcandre.lureau@redhat.com>
12 # This work is licensed under the terms of the GNU GPL, version 2.
13 # See the COPYING file in the top-level directory.
15 # pylint: disable=too-many-lines
17 # TODO catching name collisions in generated code would be nice
19 from __future__ import annotations
21 from abc import ABC, abstractmethod
22 from collections import OrderedDict
23 import os
24 import re
25 from typing import (
26 Any,
27 Callable,
28 Dict,
29 List,
30 Optional,
31 Union,
32 cast,
35 from .common import (
36 POINTER_SUFFIX,
37 c_name,
38 cgen_ifcond,
39 docgen_ifcond,
40 gen_endif,
41 gen_if,
43 from .error import QAPIError, QAPISemError, QAPISourceError
44 from .expr import check_exprs
45 from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser
46 from .source import QAPISourceInfo
49 class QAPISchemaIfCond:
50 def __init__(
51 self,
52 ifcond: Optional[Union[str, Dict[str, object]]] = None,
53 ) -> None:
54 self.ifcond = ifcond
56 def _cgen(self) -> str:
57 return cgen_ifcond(self.ifcond)
59 def gen_if(self) -> str:
60 return gen_if(self._cgen())
62 def gen_endif(self) -> str:
63 return gen_endif(self._cgen())
65 def docgen(self) -> str:
66 return docgen_ifcond(self.ifcond)
68 def is_present(self) -> bool:
69 return bool(self.ifcond)
72 class QAPISchemaEntity:
73 """
74 A schema entity.
76 This is either a directive, such as include, or a definition.
77 The latter uses sub-class `QAPISchemaDefinition`.
78 """
79 def __init__(self, info: Optional[QAPISourceInfo]):
80 self._module: Optional[QAPISchemaModule] = None
81 # For explicitly defined entities, info points to the (explicit)
82 # definition. For builtins (and their arrays), info is None.
83 # For implicitly defined entities, info points to a place that
84 # triggered the implicit definition (there may be more than one
85 # such place).
86 self.info = info
87 self._checked = False
89 def __repr__(self) -> str:
90 return "<%s at 0x%x>" % (type(self).__name__, id(self))
92 def check(self, schema: QAPISchema) -> None:
93 # pylint: disable=unused-argument
94 self._checked = True
96 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
97 pass
99 def _set_module(
100 self, schema: QAPISchema, info: Optional[QAPISourceInfo]
101 ) -> None:
102 assert self._checked
103 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
104 self._module = schema.module_by_fname(fname)
105 self._module.add_entity(self)
107 def set_module(self, schema: QAPISchema) -> None:
108 self._set_module(schema, self.info)
110 def visit(self, visitor: QAPISchemaVisitor) -> None:
111 # pylint: disable=unused-argument
112 assert self._checked
115 class QAPISchemaDefinition(QAPISchemaEntity):
116 meta: str
118 def __init__(
119 self,
120 name: str,
121 info: Optional[QAPISourceInfo],
122 doc: Optional[QAPIDoc],
123 ifcond: Optional[QAPISchemaIfCond] = None,
124 features: Optional[List[QAPISchemaFeature]] = None,
126 assert isinstance(name, str)
127 super().__init__(info)
128 for f in features or []:
129 assert isinstance(f, QAPISchemaFeature)
130 f.set_defined_in(name)
131 self.name = name
132 self.doc = doc
133 self._ifcond = ifcond or QAPISchemaIfCond()
134 self.features = features or []
136 def __repr__(self) -> str:
137 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
138 id(self))
140 def c_name(self) -> str:
141 return c_name(self.name)
143 def check(self, schema: QAPISchema) -> None:
144 assert not self._checked
145 super().check(schema)
146 seen: Dict[str, QAPISchemaMember] = {}
147 for f in self.features:
148 f.check_clash(self.info, seen)
150 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
151 super().connect_doc(doc)
152 doc = doc or self.doc
153 if doc:
154 for f in self.features:
155 doc.connect_feature(f)
157 @property
158 def ifcond(self) -> QAPISchemaIfCond:
159 assert self._checked
160 return self._ifcond
162 def is_implicit(self) -> bool:
163 return not self.info
165 def describe(self) -> str:
166 assert self.meta
167 return "%s '%s'" % (self.meta, self.name)
170 class QAPISchemaVisitor:
171 def visit_begin(self, schema: QAPISchema) -> None:
172 pass
174 def visit_end(self) -> None:
175 pass
177 def visit_module(self, name: str) -> None:
178 pass
180 def visit_needed(self, entity: QAPISchemaEntity) -> bool:
181 # pylint: disable=unused-argument
182 # Default to visiting everything
183 return True
185 def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None:
186 pass
188 def visit_builtin_type(
189 self, name: str, info: Optional[QAPISourceInfo], json_type: str
190 ) -> None:
191 pass
193 def visit_enum_type(
194 self,
195 name: str,
196 info: Optional[QAPISourceInfo],
197 ifcond: QAPISchemaIfCond,
198 features: List[QAPISchemaFeature],
199 members: List[QAPISchemaEnumMember],
200 prefix: Optional[str],
201 ) -> None:
202 pass
204 def visit_array_type(
205 self,
206 name: str,
207 info: Optional[QAPISourceInfo],
208 ifcond: QAPISchemaIfCond,
209 element_type: QAPISchemaType,
210 ) -> None:
211 pass
213 def visit_object_type(
214 self,
215 name: str,
216 info: Optional[QAPISourceInfo],
217 ifcond: QAPISchemaIfCond,
218 features: List[QAPISchemaFeature],
219 base: Optional[QAPISchemaObjectType],
220 members: List[QAPISchemaObjectTypeMember],
221 variants: Optional[QAPISchemaVariants],
222 ) -> None:
223 pass
225 def visit_object_type_flat(
226 self,
227 name: str,
228 info: Optional[QAPISourceInfo],
229 ifcond: QAPISchemaIfCond,
230 features: List[QAPISchemaFeature],
231 members: List[QAPISchemaObjectTypeMember],
232 variants: Optional[QAPISchemaVariants],
233 ) -> None:
234 pass
236 def visit_alternate_type(
237 self,
238 name: str,
239 info: Optional[QAPISourceInfo],
240 ifcond: QAPISchemaIfCond,
241 features: List[QAPISchemaFeature],
242 variants: QAPISchemaVariants,
243 ) -> None:
244 pass
246 def visit_command(
247 self,
248 name: str,
249 info: Optional[QAPISourceInfo],
250 ifcond: QAPISchemaIfCond,
251 features: List[QAPISchemaFeature],
252 arg_type: Optional[QAPISchemaObjectType],
253 ret_type: Optional[QAPISchemaType],
254 gen: bool,
255 success_response: bool,
256 boxed: bool,
257 allow_oob: bool,
258 allow_preconfig: bool,
259 coroutine: bool,
260 ) -> None:
261 pass
263 def visit_event(
264 self,
265 name: str,
266 info: Optional[QAPISourceInfo],
267 ifcond: QAPISchemaIfCond,
268 features: List[QAPISchemaFeature],
269 arg_type: Optional[QAPISchemaObjectType],
270 boxed: bool,
271 ) -> None:
272 pass
275 class QAPISchemaModule:
277 BUILTIN_MODULE_NAME = './builtin'
279 def __init__(self, name: str):
280 self.name = name
281 self._entity_list: List[QAPISchemaEntity] = []
283 @staticmethod
284 def is_system_module(name: str) -> bool:
286 System modules are internally defined modules.
288 Their names start with the "./" prefix.
290 return name.startswith('./')
292 @classmethod
293 def is_user_module(cls, name: str) -> bool:
295 User modules are those defined by the user in qapi JSON files.
297 They do not start with the "./" prefix.
299 return not cls.is_system_module(name)
301 @classmethod
302 def is_builtin_module(cls, name: str) -> bool:
304 The built-in module is a single System module for the built-in types.
306 It is always "./builtin".
308 return name == cls.BUILTIN_MODULE_NAME
310 def add_entity(self, ent: QAPISchemaEntity) -> None:
311 self._entity_list.append(ent)
313 def visit(self, visitor: QAPISchemaVisitor) -> None:
314 visitor.visit_module(self.name)
315 for entity in self._entity_list:
316 if visitor.visit_needed(entity):
317 entity.visit(visitor)
320 class QAPISchemaInclude(QAPISchemaEntity):
321 def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo):
322 super().__init__(info)
323 self._sub_module = sub_module
325 def visit(self, visitor: QAPISchemaVisitor) -> None:
326 super().visit(visitor)
327 visitor.visit_include(self._sub_module.name, self.info)
330 class QAPISchemaType(QAPISchemaDefinition, ABC):
331 # Return the C type for common use.
332 # For the types we commonly box, this is a pointer type.
333 @abstractmethod
334 def c_type(self) -> str:
335 pass
337 # Return the C type to be used in a parameter list.
338 def c_param_type(self) -> str:
339 return self.c_type()
341 # Return the C type to be used where we suppress boxing.
342 def c_unboxed_type(self) -> str:
343 return self.c_type()
345 @abstractmethod
346 def json_type(self) -> str:
347 pass
349 def alternate_qtype(self) -> Optional[str]:
350 json2qtype = {
351 'null': 'QTYPE_QNULL',
352 'string': 'QTYPE_QSTRING',
353 'number': 'QTYPE_QNUM',
354 'int': 'QTYPE_QNUM',
355 'boolean': 'QTYPE_QBOOL',
356 'array': 'QTYPE_QLIST',
357 'object': 'QTYPE_QDICT'
359 return json2qtype.get(self.json_type())
361 def doc_type(self) -> Optional[str]:
362 if self.is_implicit():
363 return None
364 return self.name
366 def need_has_if_optional(self) -> bool:
367 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
368 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
369 return not self.c_type().endswith(POINTER_SUFFIX)
371 def check(self, schema: QAPISchema) -> None:
372 super().check(schema)
373 for feat in self.features:
374 if feat.is_special():
375 raise QAPISemError(
376 self.info,
377 f"feature '{feat.name}' is not supported for types")
379 def describe(self) -> str:
380 assert self.meta
381 return "%s type '%s'" % (self.meta, self.name)
384 class QAPISchemaBuiltinType(QAPISchemaType):
385 meta = 'built-in'
387 def __init__(self, name: str, json_type: str, c_type: str):
388 super().__init__(name, None, None)
389 assert not c_type or isinstance(c_type, str)
390 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
391 'value')
392 self._json_type_name = json_type
393 self._c_type_name = c_type
395 def c_name(self) -> str:
396 return self.name
398 def c_type(self) -> str:
399 return self._c_type_name
401 def c_param_type(self) -> str:
402 if self.name == 'str':
403 return 'const ' + self._c_type_name
404 return self._c_type_name
406 def json_type(self) -> str:
407 return self._json_type_name
409 def doc_type(self) -> str:
410 return self.json_type()
412 def visit(self, visitor: QAPISchemaVisitor) -> None:
413 super().visit(visitor)
414 visitor.visit_builtin_type(self.name, self.info, self.json_type())
417 class QAPISchemaEnumType(QAPISchemaType):
418 meta = 'enum'
420 def __init__(
421 self,
422 name: str,
423 info: Optional[QAPISourceInfo],
424 doc: Optional[QAPIDoc],
425 ifcond: Optional[QAPISchemaIfCond],
426 features: Optional[List[QAPISchemaFeature]],
427 members: List[QAPISchemaEnumMember],
428 prefix: Optional[str],
430 super().__init__(name, info, doc, ifcond, features)
431 for m in members:
432 assert isinstance(m, QAPISchemaEnumMember)
433 m.set_defined_in(name)
434 assert prefix is None or isinstance(prefix, str)
435 self.members = members
436 self.prefix = prefix
438 def check(self, schema: QAPISchema) -> None:
439 super().check(schema)
440 seen: Dict[str, QAPISchemaMember] = {}
441 for m in self.members:
442 m.check_clash(self.info, seen)
444 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
445 super().connect_doc(doc)
446 doc = doc or self.doc
447 for m in self.members:
448 m.connect_doc(doc)
450 def is_implicit(self) -> bool:
451 # See QAPISchema._def_predefineds()
452 return self.name == 'QType'
454 def c_type(self) -> str:
455 return c_name(self.name)
457 def member_names(self) -> List[str]:
458 return [m.name for m in self.members]
460 def json_type(self) -> str:
461 return 'string'
463 def visit(self, visitor: QAPISchemaVisitor) -> None:
464 super().visit(visitor)
465 visitor.visit_enum_type(
466 self.name, self.info, self.ifcond, self.features,
467 self.members, self.prefix)
470 class QAPISchemaArrayType(QAPISchemaType):
471 meta = 'array'
473 def __init__(
474 self, name: str, info: Optional[QAPISourceInfo], element_type: str
476 super().__init__(name, info, None)
477 assert isinstance(element_type, str)
478 self._element_type_name = element_type
479 self.element_type: QAPISchemaType
481 def need_has_if_optional(self) -> bool:
482 # When FOO is an array, we still need has_FOO to distinguish
483 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
484 return True
486 def check(self, schema: QAPISchema) -> None:
487 super().check(schema)
488 self.element_type = schema.resolve_type(
489 self._element_type_name, self.info,
490 self.info.defn_meta if self.info else None)
491 assert not isinstance(self.element_type, QAPISchemaArrayType)
493 def set_module(self, schema: QAPISchema) -> None:
494 self._set_module(schema, self.element_type.info)
496 @property
497 def ifcond(self) -> QAPISchemaIfCond:
498 assert self._checked
499 return self.element_type.ifcond
501 def is_implicit(self) -> bool:
502 return True
504 def c_type(self) -> str:
505 return c_name(self.name) + POINTER_SUFFIX
507 def json_type(self) -> str:
508 return 'array'
510 def doc_type(self) -> Optional[str]:
511 elt_doc_type = self.element_type.doc_type()
512 if not elt_doc_type:
513 return None
514 return 'array of ' + elt_doc_type
516 def visit(self, visitor: QAPISchemaVisitor) -> None:
517 super().visit(visitor)
518 visitor.visit_array_type(self.name, self.info, self.ifcond,
519 self.element_type)
521 def describe(self) -> str:
522 assert self.meta
523 return "%s type ['%s']" % (self.meta, self._element_type_name)
526 class QAPISchemaObjectType(QAPISchemaType):
527 def __init__(
528 self,
529 name: str,
530 info: Optional[QAPISourceInfo],
531 doc: Optional[QAPIDoc],
532 ifcond: Optional[QAPISchemaIfCond],
533 features: Optional[List[QAPISchemaFeature]],
534 base: Optional[str],
535 local_members: List[QAPISchemaObjectTypeMember],
536 variants: Optional[QAPISchemaVariants],
538 # struct has local_members, optional base, and no variants
539 # union has base, variants, and no local_members
540 super().__init__(name, info, doc, ifcond, features)
541 self.meta = 'union' if variants else 'struct'
542 assert base is None or isinstance(base, str)
543 for m in local_members:
544 assert isinstance(m, QAPISchemaObjectTypeMember)
545 m.set_defined_in(name)
546 if variants is not None:
547 assert isinstance(variants, QAPISchemaVariants)
548 variants.set_defined_in(name)
549 self._base_name = base
550 self.base = None
551 self.local_members = local_members
552 self.variants = variants
553 self.members: List[QAPISchemaObjectTypeMember]
554 self._check_complete = False
556 def check(self, schema: QAPISchema) -> None:
557 # This calls another type T's .check() exactly when the C
558 # struct emitted by gen_object() contains that T's C struct
559 # (pointers don't count).
560 if self._check_complete:
561 # A previous .check() completed: nothing to do
562 return
563 if self._checked:
564 # Recursed: C struct contains itself
565 raise QAPISemError(self.info,
566 "object %s contains itself" % self.name)
568 super().check(schema)
569 assert self._checked and not self._check_complete
571 seen = OrderedDict()
572 if self._base_name:
573 self.base = schema.resolve_type(self._base_name, self.info,
574 "'base'")
575 if (not isinstance(self.base, QAPISchemaObjectType)
576 or self.base.variants):
577 raise QAPISemError(
578 self.info,
579 "'base' requires a struct type, %s isn't"
580 % self.base.describe())
581 self.base.check(schema)
582 self.base.check_clash(self.info, seen)
583 for m in self.local_members:
584 m.check(schema)
585 m.check_clash(self.info, seen)
587 # self.check_clash() works in terms of the supertype, but
588 # self.members is declared List[QAPISchemaObjectTypeMember].
589 # Cast down to the subtype.
590 members = cast(List[QAPISchemaObjectTypeMember], list(seen.values()))
592 if self.variants:
593 self.variants.check(schema, seen)
594 self.variants.check_clash(self.info, seen)
596 self.members = members
597 self._check_complete = True # mark completed
599 # Check that the members of this type do not cause duplicate JSON members,
600 # and update seen to track the members seen so far. Report any errors
601 # on behalf of info, which is not necessarily self.info
602 def check_clash(
603 self,
604 info: Optional[QAPISourceInfo],
605 seen: Dict[str, QAPISchemaMember],
606 ) -> None:
607 assert self._checked
608 for m in self.members:
609 m.check_clash(info, seen)
610 if self.variants:
611 self.variants.check_clash(info, seen)
613 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
614 super().connect_doc(doc)
615 doc = doc or self.doc
616 if self.base and self.base.is_implicit():
617 self.base.connect_doc(doc)
618 for m in self.local_members:
619 m.connect_doc(doc)
621 def is_implicit(self) -> bool:
622 # See QAPISchema._make_implicit_object_type(), as well as
623 # _def_predefineds()
624 return self.name.startswith('q_')
626 def is_empty(self) -> bool:
627 return not self.members and not self.variants
629 def has_conditional_members(self) -> bool:
630 return any(m.ifcond.is_present() for m in self.members)
632 def c_name(self) -> str:
633 assert self.name != 'q_empty'
634 return super().c_name()
636 def c_type(self) -> str:
637 assert not self.is_implicit()
638 return c_name(self.name) + POINTER_SUFFIX
640 def c_unboxed_type(self) -> str:
641 return c_name(self.name)
643 def json_type(self) -> str:
644 return 'object'
646 def visit(self, visitor: QAPISchemaVisitor) -> None:
647 super().visit(visitor)
648 visitor.visit_object_type(
649 self.name, self.info, self.ifcond, self.features,
650 self.base, self.local_members, self.variants)
651 visitor.visit_object_type_flat(
652 self.name, self.info, self.ifcond, self.features,
653 self.members, self.variants)
656 class QAPISchemaAlternateType(QAPISchemaType):
657 meta = 'alternate'
659 def __init__(
660 self,
661 name: str,
662 info: QAPISourceInfo,
663 doc: Optional[QAPIDoc],
664 ifcond: Optional[QAPISchemaIfCond],
665 features: List[QAPISchemaFeature],
666 variants: QAPISchemaVariants,
668 super().__init__(name, info, doc, ifcond, features)
669 assert isinstance(variants, QAPISchemaVariants)
670 assert variants.tag_member
671 variants.set_defined_in(name)
672 variants.tag_member.set_defined_in(self.name)
673 self.variants = variants
675 def check(self, schema: QAPISchema) -> None:
676 super().check(schema)
677 self.variants.tag_member.check(schema)
678 # Not calling self.variants.check_clash(), because there's nothing
679 # to clash with
680 self.variants.check(schema, {})
681 # Alternate branch names have no relation to the tag enum values;
682 # so we have to check for potential name collisions ourselves.
683 seen: Dict[str, QAPISchemaMember] = {}
684 types_seen: Dict[str, str] = {}
685 for v in self.variants.variants:
686 v.check_clash(self.info, seen)
687 qtype = v.type.alternate_qtype()
688 if not qtype:
689 raise QAPISemError(
690 self.info,
691 "%s cannot use %s"
692 % (v.describe(self.info), v.type.describe()))
693 conflicting = set([qtype])
694 if qtype == 'QTYPE_QSTRING':
695 if isinstance(v.type, QAPISchemaEnumType):
696 for m in v.type.members:
697 if m.name in ['on', 'off']:
698 conflicting.add('QTYPE_QBOOL')
699 if re.match(r'[-+0-9.]', m.name):
700 # lazy, could be tightened
701 conflicting.add('QTYPE_QNUM')
702 else:
703 conflicting.add('QTYPE_QNUM')
704 conflicting.add('QTYPE_QBOOL')
705 for qt in conflicting:
706 if qt in types_seen:
707 raise QAPISemError(
708 self.info,
709 "%s can't be distinguished from '%s'"
710 % (v.describe(self.info), types_seen[qt]))
711 types_seen[qt] = v.name
713 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
714 super().connect_doc(doc)
715 doc = doc or self.doc
716 for v in self.variants.variants:
717 v.connect_doc(doc)
719 def c_type(self) -> str:
720 return c_name(self.name) + POINTER_SUFFIX
722 def json_type(self) -> str:
723 return 'value'
725 def visit(self, visitor: QAPISchemaVisitor) -> None:
726 super().visit(visitor)
727 visitor.visit_alternate_type(
728 self.name, self.info, self.ifcond, self.features, self.variants)
731 class QAPISchemaVariants:
732 def __init__(
733 self,
734 tag_name: Optional[str],
735 info: QAPISourceInfo,
736 tag_member: Optional[QAPISchemaObjectTypeMember],
737 variants: List[QAPISchemaVariant],
739 # Unions pass tag_name but not tag_member.
740 # Alternates pass tag_member but not tag_name.
741 # After check(), tag_member is always set.
742 assert bool(tag_member) != bool(tag_name)
743 assert (isinstance(tag_name, str) or
744 isinstance(tag_member, QAPISchemaObjectTypeMember))
745 for v in variants:
746 assert isinstance(v, QAPISchemaVariant)
747 self._tag_name = tag_name
748 self.info = info
749 self._tag_member = tag_member
750 self.variants = variants
752 @property
753 def tag_member(self) -> QAPISchemaObjectTypeMember:
754 if self._tag_member is None:
755 raise RuntimeError(
756 "QAPISchemaVariants has no tag_member property until "
757 "after check() has been run."
759 return self._tag_member
761 def set_defined_in(self, name: str) -> None:
762 for v in self.variants:
763 v.set_defined_in(name)
765 def check(
766 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember]
767 ) -> None:
768 if self._tag_name: # union
769 # We need to narrow the member type:
770 tmp = seen.get(c_name(self._tag_name))
771 assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember)
772 self._tag_member = tmp
774 base = "'base'"
775 # Pointing to the base type when not implicit would be
776 # nice, but we don't know it here
777 if not self._tag_member or self._tag_name != self._tag_member.name:
778 raise QAPISemError(
779 self.info,
780 "discriminator '%s' is not a member of %s"
781 % (self._tag_name, base))
782 # Here we do:
783 assert self.tag_member.defined_in
784 base_type = schema.lookup_type(self.tag_member.defined_in)
785 assert base_type
786 if not base_type.is_implicit():
787 base = "base type '%s'" % self.tag_member.defined_in
788 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
789 raise QAPISemError(
790 self.info,
791 "discriminator member '%s' of %s must be of enum type"
792 % (self._tag_name, base))
793 if self.tag_member.optional:
794 raise QAPISemError(
795 self.info,
796 "discriminator member '%s' of %s must not be optional"
797 % (self._tag_name, base))
798 if self.tag_member.ifcond.is_present():
799 raise QAPISemError(
800 self.info,
801 "discriminator member '%s' of %s must not be conditional"
802 % (self._tag_name, base))
803 else: # alternate
804 assert self._tag_member
805 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
806 assert not self.tag_member.optional
807 assert not self.tag_member.ifcond.is_present()
808 if self._tag_name: # union
809 # branches that are not explicitly covered get an empty type
810 assert self.tag_member.defined_in
811 cases = {v.name for v in self.variants}
812 for m in self.tag_member.type.members:
813 if m.name not in cases:
814 v = QAPISchemaVariant(m.name, self.info,
815 'q_empty', m.ifcond)
816 v.set_defined_in(self.tag_member.defined_in)
817 self.variants.append(v)
818 if not self.variants:
819 raise QAPISemError(self.info, "union has no branches")
820 for v in self.variants:
821 v.check(schema)
822 # Union names must match enum values; alternate names are
823 # checked separately. Use 'seen' to tell the two apart.
824 if seen:
825 if v.name not in self.tag_member.type.member_names():
826 raise QAPISemError(
827 self.info,
828 "branch '%s' is not a value of %s"
829 % (v.name, self.tag_member.type.describe()))
830 if not isinstance(v.type, QAPISchemaObjectType):
831 raise QAPISemError(
832 self.info,
833 "%s cannot use %s"
834 % (v.describe(self.info), v.type.describe()))
835 v.type.check(schema)
837 def check_clash(
838 self,
839 info: Optional[QAPISourceInfo],
840 seen: Dict[str, QAPISchemaMember],
841 ) -> None:
842 for v in self.variants:
843 # Reset seen map for each variant, since qapi names from one
844 # branch do not affect another branch.
846 # v.type's typing is enforced in check() above.
847 assert isinstance(v.type, QAPISchemaObjectType)
848 v.type.check_clash(info, dict(seen))
851 class QAPISchemaMember:
852 """ Represents object members, enum members and features """
853 role = 'member'
855 def __init__(
856 self,
857 name: str,
858 info: Optional[QAPISourceInfo],
859 ifcond: Optional[QAPISchemaIfCond] = None,
861 assert isinstance(name, str)
862 self.name = name
863 self.info = info
864 self.ifcond = ifcond or QAPISchemaIfCond()
865 self.defined_in: Optional[str] = None
867 def set_defined_in(self, name: str) -> None:
868 assert not self.defined_in
869 self.defined_in = name
871 def check_clash(
872 self,
873 info: Optional[QAPISourceInfo],
874 seen: Dict[str, QAPISchemaMember],
875 ) -> None:
876 cname = c_name(self.name)
877 if cname in seen:
878 raise QAPISemError(
879 info,
880 "%s collides with %s"
881 % (self.describe(info), seen[cname].describe(info)))
882 seen[cname] = self
884 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
885 if doc:
886 doc.connect_member(self)
888 def describe(self, info: Optional[QAPISourceInfo]) -> str:
889 role = self.role
890 meta = 'type'
891 defined_in = self.defined_in
892 assert defined_in
894 if defined_in.startswith('q_obj_'):
895 # See QAPISchema._make_implicit_object_type() - reverse the
896 # mapping there to create a nice human-readable description
897 defined_in = defined_in[6:]
898 if defined_in.endswith('-arg'):
899 # Implicit type created for a command's dict 'data'
900 assert role == 'member'
901 role = 'parameter'
902 meta = 'command'
903 defined_in = defined_in[:-4]
904 elif defined_in.endswith('-base'):
905 # Implicit type created for a union's dict 'base'
906 role = 'base ' + role
907 defined_in = defined_in[:-5]
908 else:
909 assert False
911 assert info is not None
912 if defined_in != info.defn_name:
913 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
914 return "%s '%s'" % (role, self.name)
917 class QAPISchemaEnumMember(QAPISchemaMember):
918 role = 'value'
920 def __init__(
921 self,
922 name: str,
923 info: Optional[QAPISourceInfo],
924 ifcond: Optional[QAPISchemaIfCond] = None,
925 features: Optional[List[QAPISchemaFeature]] = None,
927 super().__init__(name, info, ifcond)
928 for f in features or []:
929 assert isinstance(f, QAPISchemaFeature)
930 f.set_defined_in(name)
931 self.features = features or []
933 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
934 super().connect_doc(doc)
935 if doc:
936 for f in self.features:
937 doc.connect_feature(f)
940 class QAPISchemaFeature(QAPISchemaMember):
941 role = 'feature'
943 def is_special(self) -> bool:
944 return self.name in ('deprecated', 'unstable')
947 class QAPISchemaObjectTypeMember(QAPISchemaMember):
948 def __init__(
949 self,
950 name: str,
951 info: QAPISourceInfo,
952 typ: str,
953 optional: bool,
954 ifcond: Optional[QAPISchemaIfCond] = None,
955 features: Optional[List[QAPISchemaFeature]] = None,
957 super().__init__(name, info, ifcond)
958 assert isinstance(typ, str)
959 assert isinstance(optional, bool)
960 for f in features or []:
961 assert isinstance(f, QAPISchemaFeature)
962 f.set_defined_in(name)
963 self._type_name = typ
964 self.type: QAPISchemaType # set during check()
965 self.optional = optional
966 self.features = features or []
968 def need_has(self) -> bool:
969 assert self.type
970 return self.optional and self.type.need_has_if_optional()
972 def check(self, schema: QAPISchema) -> None:
973 assert self.defined_in
974 self.type = schema.resolve_type(self._type_name, self.info,
975 self.describe)
976 seen: Dict[str, QAPISchemaMember] = {}
977 for f in self.features:
978 f.check_clash(self.info, seen)
980 def connect_doc(self, doc: Optional[QAPIDoc]) -> None:
981 super().connect_doc(doc)
982 if doc:
983 for f in self.features:
984 doc.connect_feature(f)
987 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
988 role = 'branch'
990 def __init__(
991 self,
992 name: str,
993 info: QAPISourceInfo,
994 typ: str,
995 ifcond: QAPISchemaIfCond,
997 super().__init__(name, info, typ, False, ifcond)
1000 class QAPISchemaCommand(QAPISchemaDefinition):
1001 meta = 'command'
1003 def __init__(
1004 self,
1005 name: str,
1006 info: QAPISourceInfo,
1007 doc: Optional[QAPIDoc],
1008 ifcond: QAPISchemaIfCond,
1009 features: List[QAPISchemaFeature],
1010 arg_type: Optional[str],
1011 ret_type: Optional[str],
1012 gen: bool,
1013 success_response: bool,
1014 boxed: bool,
1015 allow_oob: bool,
1016 allow_preconfig: bool,
1017 coroutine: bool,
1019 super().__init__(name, info, doc, ifcond, features)
1020 assert not arg_type or isinstance(arg_type, str)
1021 assert not ret_type or isinstance(ret_type, str)
1022 self._arg_type_name = arg_type
1023 self.arg_type: Optional[QAPISchemaObjectType] = None
1024 self._ret_type_name = ret_type
1025 self.ret_type: Optional[QAPISchemaType] = None
1026 self.gen = gen
1027 self.success_response = success_response
1028 self.boxed = boxed
1029 self.allow_oob = allow_oob
1030 self.allow_preconfig = allow_preconfig
1031 self.coroutine = coroutine
1033 def check(self, schema: QAPISchema) -> None:
1034 assert self.info is not None
1035 super().check(schema)
1036 if self._arg_type_name:
1037 arg_type = schema.resolve_type(
1038 self._arg_type_name, self.info, "command's 'data'")
1039 if not isinstance(arg_type, QAPISchemaObjectType):
1040 raise QAPISemError(
1041 self.info,
1042 "command's 'data' cannot take %s"
1043 % arg_type.describe())
1044 self.arg_type = arg_type
1045 if self.arg_type.variants and not self.boxed:
1046 raise QAPISemError(
1047 self.info,
1048 "command's 'data' can take %s only with 'boxed': true"
1049 % self.arg_type.describe())
1050 self.arg_type.check(schema)
1051 if self.arg_type.has_conditional_members() and not self.boxed:
1052 raise QAPISemError(
1053 self.info,
1054 "conditional command arguments require 'boxed': true")
1055 if self._ret_type_name:
1056 self.ret_type = schema.resolve_type(
1057 self._ret_type_name, self.info, "command's 'returns'")
1058 if self.name not in self.info.pragma.command_returns_exceptions:
1059 typ = self.ret_type
1060 if isinstance(typ, QAPISchemaArrayType):
1061 assert typ
1062 typ = typ.element_type
1063 if not isinstance(typ, QAPISchemaObjectType):
1064 raise QAPISemError(
1065 self.info,
1066 "command's 'returns' cannot take %s"
1067 % self.ret_type.describe())
1069 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
1070 super().connect_doc(doc)
1071 doc = doc or self.doc
1072 if doc:
1073 if self.arg_type and self.arg_type.is_implicit():
1074 self.arg_type.connect_doc(doc)
1076 def visit(self, visitor: QAPISchemaVisitor) -> None:
1077 super().visit(visitor)
1078 visitor.visit_command(
1079 self.name, self.info, self.ifcond, self.features,
1080 self.arg_type, self.ret_type, self.gen, self.success_response,
1081 self.boxed, self.allow_oob, self.allow_preconfig,
1082 self.coroutine)
1085 class QAPISchemaEvent(QAPISchemaDefinition):
1086 meta = 'event'
1088 def __init__(
1089 self,
1090 name: str,
1091 info: QAPISourceInfo,
1092 doc: Optional[QAPIDoc],
1093 ifcond: QAPISchemaIfCond,
1094 features: List[QAPISchemaFeature],
1095 arg_type: Optional[str],
1096 boxed: bool,
1098 super().__init__(name, info, doc, ifcond, features)
1099 assert not arg_type or isinstance(arg_type, str)
1100 self._arg_type_name = arg_type
1101 self.arg_type: Optional[QAPISchemaObjectType] = None
1102 self.boxed = boxed
1104 def check(self, schema: QAPISchema) -> None:
1105 super().check(schema)
1106 if self._arg_type_name:
1107 typ = schema.resolve_type(
1108 self._arg_type_name, self.info, "event's 'data'")
1109 if not isinstance(typ, QAPISchemaObjectType):
1110 raise QAPISemError(
1111 self.info,
1112 "event's 'data' cannot take %s"
1113 % typ.describe())
1114 self.arg_type = typ
1115 if self.arg_type.variants and not self.boxed:
1116 raise QAPISemError(
1117 self.info,
1118 "event's 'data' can take %s only with 'boxed': true"
1119 % self.arg_type.describe())
1120 self.arg_type.check(schema)
1121 if self.arg_type.has_conditional_members() and not self.boxed:
1122 raise QAPISemError(
1123 self.info,
1124 "conditional event arguments require 'boxed': true")
1126 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None:
1127 super().connect_doc(doc)
1128 doc = doc or self.doc
1129 if doc:
1130 if self.arg_type and self.arg_type.is_implicit():
1131 self.arg_type.connect_doc(doc)
1133 def visit(self, visitor: QAPISchemaVisitor) -> None:
1134 super().visit(visitor)
1135 visitor.visit_event(
1136 self.name, self.info, self.ifcond, self.features,
1137 self.arg_type, self.boxed)
1140 class QAPISchema:
1141 def __init__(self, fname: str):
1142 self.fname = fname
1144 try:
1145 parser = QAPISchemaParser(fname)
1146 except OSError as err:
1147 raise QAPIError(
1148 f"can't read schema file '{fname}': {err.strerror}"
1149 ) from err
1151 exprs = check_exprs(parser.exprs)
1152 self.docs = parser.docs
1153 self._entity_list: List[QAPISchemaEntity] = []
1154 self._entity_dict: Dict[str, QAPISchemaDefinition] = {}
1155 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict()
1156 self._schema_dir = os.path.dirname(fname)
1157 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
1158 self._make_module(fname)
1159 self._predefining = True
1160 self._def_predefineds()
1161 self._predefining = False
1162 self._def_exprs(exprs)
1163 self.check()
1165 def _def_entity(self, ent: QAPISchemaEntity) -> None:
1166 self._entity_list.append(ent)
1168 def _def_definition(self, defn: QAPISchemaDefinition) -> None:
1169 # Only the predefined types are allowed to not have info
1170 assert defn.info or self._predefining
1171 self._def_entity(defn)
1172 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1173 # because they're liable to clash in generated C.
1174 other_defn = self._entity_dict.get(defn.name)
1175 if other_defn:
1176 if other_defn.info:
1177 where = QAPISourceError(other_defn.info, "previous definition")
1178 raise QAPISemError(
1179 defn.info,
1180 "'%s' is already defined\n%s" % (defn.name, where))
1181 raise QAPISemError(
1182 defn.info, "%s is already defined" % other_defn.describe())
1183 self._entity_dict[defn.name] = defn
1185 def lookup_entity(
1186 self,
1187 name: str,
1188 typ: Optional[type] = None,
1189 ) -> Optional[QAPISchemaDefinition]:
1190 ent = self._entity_dict.get(name)
1191 if typ and not isinstance(ent, typ):
1192 return None
1193 return ent
1195 def lookup_type(self, name: str) -> Optional[QAPISchemaType]:
1196 typ = self.lookup_entity(name, QAPISchemaType)
1197 assert typ is None or isinstance(typ, QAPISchemaType)
1198 return typ
1200 def resolve_type(
1201 self,
1202 name: str,
1203 info: Optional[QAPISourceInfo],
1204 what: Union[None, str, Callable[[QAPISourceInfo], str]],
1205 ) -> QAPISchemaType:
1206 typ = self.lookup_type(name)
1207 if not typ:
1208 assert info and what # built-in types must not fail lookup
1209 if callable(what):
1210 what = what(info)
1211 raise QAPISemError(
1212 info, "%s uses unknown type '%s'" % (what, name))
1213 return typ
1215 def _module_name(self, fname: str) -> str:
1216 if QAPISchemaModule.is_system_module(fname):
1217 return fname
1218 return os.path.relpath(fname, self._schema_dir)
1220 def _make_module(self, fname: str) -> QAPISchemaModule:
1221 name = self._module_name(fname)
1222 if name not in self._module_dict:
1223 self._module_dict[name] = QAPISchemaModule(name)
1224 return self._module_dict[name]
1226 def module_by_fname(self, fname: str) -> QAPISchemaModule:
1227 name = self._module_name(fname)
1228 return self._module_dict[name]
1230 def _def_include(self, expr: QAPIExpression) -> None:
1231 include = expr['include']
1232 assert expr.doc is None
1233 self._def_entity(
1234 QAPISchemaInclude(self._make_module(include), expr.info))
1236 def _def_builtin_type(
1237 self, name: str, json_type: str, c_type: str
1238 ) -> None:
1239 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type))
1240 # Instantiating only the arrays that are actually used would
1241 # be nice, but we can't as long as their generated code
1242 # (qapi-builtin-types.[ch]) may be shared by some other
1243 # schema.
1244 self._make_array_type(name, None)
1246 def _def_predefineds(self) -> None:
1247 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
1248 ('number', 'number', 'double'),
1249 ('int', 'int', 'int64_t'),
1250 ('int8', 'int', 'int8_t'),
1251 ('int16', 'int', 'int16_t'),
1252 ('int32', 'int', 'int32_t'),
1253 ('int64', 'int', 'int64_t'),
1254 ('uint8', 'int', 'uint8_t'),
1255 ('uint16', 'int', 'uint16_t'),
1256 ('uint32', 'int', 'uint32_t'),
1257 ('uint64', 'int', 'uint64_t'),
1258 ('size', 'int', 'uint64_t'),
1259 ('bool', 'boolean', 'bool'),
1260 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1261 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
1262 self._def_builtin_type(*t)
1263 self.the_empty_object_type = QAPISchemaObjectType(
1264 'q_empty', None, None, None, None, None, [], None)
1265 self._def_definition(self.the_empty_object_type)
1267 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1268 'qbool']
1269 qtype_values = self._make_enum_members(
1270 [{'name': n} for n in qtypes], None)
1272 self._def_definition(QAPISchemaEnumType(
1273 'QType', None, None, None, None, qtype_values, 'QTYPE'))
1275 def _make_features(
1276 self,
1277 features: Optional[List[Dict[str, Any]]],
1278 info: Optional[QAPISourceInfo],
1279 ) -> List[QAPISchemaFeature]:
1280 if features is None:
1281 return []
1282 return [QAPISchemaFeature(f['name'], info,
1283 QAPISchemaIfCond(f.get('if')))
1284 for f in features]
1286 def _make_enum_member(
1287 self,
1288 name: str,
1289 ifcond: Optional[Union[str, Dict[str, Any]]],
1290 features: Optional[List[Dict[str, Any]]],
1291 info: Optional[QAPISourceInfo],
1292 ) -> QAPISchemaEnumMember:
1293 return QAPISchemaEnumMember(name, info,
1294 QAPISchemaIfCond(ifcond),
1295 self._make_features(features, info))
1297 def _make_enum_members(
1298 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo]
1299 ) -> List[QAPISchemaEnumMember]:
1300 return [self._make_enum_member(v['name'], v.get('if'),
1301 v.get('features'), info)
1302 for v in values]
1304 def _make_array_type(
1305 self, element_type: str, info: Optional[QAPISourceInfo]
1306 ) -> str:
1307 name = element_type + 'List' # reserved by check_defn_name_str()
1308 if not self.lookup_type(name):
1309 self._def_definition(QAPISchemaArrayType(
1310 name, info, element_type))
1311 return name
1313 def _make_implicit_object_type(
1314 self,
1315 name: str,
1316 info: QAPISourceInfo,
1317 ifcond: QAPISchemaIfCond,
1318 role: str,
1319 members: List[QAPISchemaObjectTypeMember],
1320 ) -> Optional[str]:
1321 if not members:
1322 return None
1323 # See also QAPISchemaObjectTypeMember.describe()
1324 name = 'q_obj_%s-%s' % (name, role)
1325 typ = self.lookup_entity(name, QAPISchemaObjectType)
1326 if typ:
1327 # The implicit object type has multiple users. This can
1328 # only be a duplicate definition, which will be flagged
1329 # later.
1330 pass
1331 else:
1332 self._def_definition(QAPISchemaObjectType(
1333 name, info, None, ifcond, None, None, members, None))
1334 return name
1336 def _def_enum_type(self, expr: QAPIExpression) -> None:
1337 name = expr['enum']
1338 data = expr['data']
1339 prefix = expr.get('prefix')
1340 ifcond = QAPISchemaIfCond(expr.get('if'))
1341 info = expr.info
1342 features = self._make_features(expr.get('features'), info)
1343 self._def_definition(QAPISchemaEnumType(
1344 name, info, expr.doc, ifcond, features,
1345 self._make_enum_members(data, info), prefix))
1347 def _make_member(
1348 self,
1349 name: str,
1350 typ: Union[List[str], str],
1351 ifcond: QAPISchemaIfCond,
1352 features: Optional[List[Dict[str, Any]]],
1353 info: QAPISourceInfo,
1354 ) -> QAPISchemaObjectTypeMember:
1355 optional = False
1356 if name.startswith('*'):
1357 name = name[1:]
1358 optional = True
1359 if isinstance(typ, list):
1360 assert len(typ) == 1
1361 typ = self._make_array_type(typ[0], info)
1362 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1363 self._make_features(features, info))
1365 def _make_members(
1366 self,
1367 data: Dict[str, Any],
1368 info: QAPISourceInfo,
1369 ) -> List[QAPISchemaObjectTypeMember]:
1370 return [self._make_member(key, value['type'],
1371 QAPISchemaIfCond(value.get('if')),
1372 value.get('features'), info)
1373 for (key, value) in data.items()]
1375 def _def_struct_type(self, expr: QAPIExpression) -> None:
1376 name = expr['struct']
1377 base = expr.get('base')
1378 data = expr['data']
1379 info = expr.info
1380 ifcond = QAPISchemaIfCond(expr.get('if'))
1381 features = self._make_features(expr.get('features'), info)
1382 self._def_definition(QAPISchemaObjectType(
1383 name, info, expr.doc, ifcond, features, base,
1384 self._make_members(data, info),
1385 None))
1387 def _make_variant(
1388 self,
1389 case: str,
1390 typ: str,
1391 ifcond: QAPISchemaIfCond,
1392 info: QAPISourceInfo,
1393 ) -> QAPISchemaVariant:
1394 if isinstance(typ, list):
1395 assert len(typ) == 1
1396 typ = self._make_array_type(typ[0], info)
1397 return QAPISchemaVariant(case, info, typ, ifcond)
1399 def _def_union_type(self, expr: QAPIExpression) -> None:
1400 name = expr['union']
1401 base = expr['base']
1402 tag_name = expr['discriminator']
1403 data = expr['data']
1404 assert isinstance(data, dict)
1405 info = expr.info
1406 ifcond = QAPISchemaIfCond(expr.get('if'))
1407 features = self._make_features(expr.get('features'), info)
1408 if isinstance(base, dict):
1409 base = self._make_implicit_object_type(
1410 name, info, ifcond,
1411 'base', self._make_members(base, info))
1412 variants = [
1413 self._make_variant(key, value['type'],
1414 QAPISchemaIfCond(value.get('if')),
1415 info)
1416 for (key, value) in data.items()]
1417 members: List[QAPISchemaObjectTypeMember] = []
1418 self._def_definition(
1419 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
1420 base, members,
1421 QAPISchemaVariants(
1422 tag_name, info, None, variants)))
1424 def _def_alternate_type(self, expr: QAPIExpression) -> None:
1425 name = expr['alternate']
1426 data = expr['data']
1427 assert isinstance(data, dict)
1428 ifcond = QAPISchemaIfCond(expr.get('if'))
1429 info = expr.info
1430 features = self._make_features(expr.get('features'), info)
1431 variants = [
1432 self._make_variant(key, value['type'],
1433 QAPISchemaIfCond(value.get('if')),
1434 info)
1435 for (key, value) in data.items()]
1436 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1437 self._def_definition(
1438 QAPISchemaAlternateType(
1439 name, info, expr.doc, ifcond, features,
1440 QAPISchemaVariants(None, info, tag_member, variants)))
1442 def _def_command(self, expr: QAPIExpression) -> None:
1443 name = expr['command']
1444 data = expr.get('data')
1445 rets = expr.get('returns')
1446 gen = expr.get('gen', True)
1447 success_response = expr.get('success-response', True)
1448 boxed = expr.get('boxed', False)
1449 allow_oob = expr.get('allow-oob', False)
1450 allow_preconfig = expr.get('allow-preconfig', False)
1451 coroutine = expr.get('coroutine', False)
1452 ifcond = QAPISchemaIfCond(expr.get('if'))
1453 info = expr.info
1454 features = self._make_features(expr.get('features'), info)
1455 if isinstance(data, OrderedDict):
1456 data = self._make_implicit_object_type(
1457 name, info, ifcond,
1458 'arg', self._make_members(data, info))
1459 if isinstance(rets, list):
1460 assert len(rets) == 1
1461 rets = self._make_array_type(rets[0], info)
1462 self._def_definition(
1463 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data,
1464 rets, gen, success_response, boxed, allow_oob,
1465 allow_preconfig, coroutine))
1467 def _def_event(self, expr: QAPIExpression) -> None:
1468 name = expr['event']
1469 data = expr.get('data')
1470 boxed = expr.get('boxed', False)
1471 ifcond = QAPISchemaIfCond(expr.get('if'))
1472 info = expr.info
1473 features = self._make_features(expr.get('features'), info)
1474 if isinstance(data, OrderedDict):
1475 data = self._make_implicit_object_type(
1476 name, info, ifcond,
1477 'arg', self._make_members(data, info))
1478 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond,
1479 features, data, boxed))
1481 def _def_exprs(self, exprs: List[QAPIExpression]) -> None:
1482 for expr in exprs:
1483 if 'enum' in expr:
1484 self._def_enum_type(expr)
1485 elif 'struct' in expr:
1486 self._def_struct_type(expr)
1487 elif 'union' in expr:
1488 self._def_union_type(expr)
1489 elif 'alternate' in expr:
1490 self._def_alternate_type(expr)
1491 elif 'command' in expr:
1492 self._def_command(expr)
1493 elif 'event' in expr:
1494 self._def_event(expr)
1495 elif 'include' in expr:
1496 self._def_include(expr)
1497 else:
1498 assert False
1500 def check(self) -> None:
1501 for ent in self._entity_list:
1502 ent.check(self)
1503 ent.connect_doc()
1504 for ent in self._entity_list:
1505 ent.set_module(self)
1506 for doc in self.docs:
1507 doc.check()
1509 def visit(self, visitor: QAPISchemaVisitor) -> None:
1510 visitor.visit_begin(self)
1511 for mod in self._module_dict.values():
1512 mod.visit(visitor)
1513 visitor.visit_end()