qga: fix guest-get-disks regression
[qemu/ar7.git] / scripts / qapi / expr.py
blob540b3982b10470e0a7dc15f230c483fb160b3749
1 # -*- coding: utf-8 -*-
3 # Check (context-free) QAPI schema expression structure
5 # Copyright IBM, Corp. 2011
6 # Copyright (c) 2013-2019 Red Hat Inc.
8 # Authors:
9 # Anthony Liguori <aliguori@us.ibm.com>
10 # Markus Armbruster <armbru@redhat.com>
11 # Eric Blake <eblake@redhat.com>
12 # Marc-André Lureau <marcandre.lureau@redhat.com>
14 # This work is licensed under the terms of the GNU GPL, version 2.
15 # See the COPYING file in the top-level directory.
17 from collections import OrderedDict
18 import re
20 from .common import c_name
21 from .error import QAPISemError
24 # Names consist of letters, digits, -, and _, starting with a letter.
25 # An experimental name is prefixed with x-. A name of a downstream
26 # extension is prefixed with __RFQDN_. The latter prefix goes first.
27 valid_name = re.compile(r'(__[a-z0-9.-]+_)?'
28 r'(x-)?'
29 r'([a-z][a-z0-9_-]*)$', re.IGNORECASE)
32 def check_name_is_str(name, info, source):
33 if not isinstance(name, str):
34 raise QAPISemError(info, "%s requires a string name" % source)
37 def check_name_str(name, info, source):
38 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
39 # and 'q_obj_*' implicit type names.
40 match = valid_name.match(name)
41 if not match or c_name(name, False).startswith('q_'):
42 raise QAPISemError(info, "%s has an invalid name" % source)
43 return match.group(3)
46 def check_name_upper(name, info, source):
47 stem = check_name_str(name, info, source)
48 if re.search(r'[a-z-]', stem):
49 raise QAPISemError(
50 info, "name of %s must not use lowercase or '-'" % source)
53 def check_name_lower(name, info, source,
54 permit_upper=False,
55 permit_underscore=False):
56 stem = check_name_str(name, info, source)
57 if ((not permit_upper and re.search(r'[A-Z]', stem))
58 or (not permit_underscore and '_' in stem)):
59 raise QAPISemError(
60 info, "name of %s must not use uppercase or '_'" % source)
63 def check_name_camel(name, info, source):
64 stem = check_name_str(name, info, source)
65 if not re.match(r'[A-Z][A-Za-z0-9]*[a-z][A-Za-z0-9]*$', stem):
66 raise QAPISemError(info, "name of %s must use CamelCase" % source)
69 def check_defn_name_str(name, info, meta):
70 if meta == 'event':
71 check_name_upper(name, info, meta)
72 elif meta == 'command':
73 check_name_lower(
74 name, info, meta,
75 permit_underscore=name in info.pragma.command_name_exceptions)
76 else:
77 check_name_camel(name, info, meta)
78 if name.endswith('Kind') or name.endswith('List'):
79 raise QAPISemError(
80 info, "%s name should not end in '%s'" % (meta, name[-4:]))
83 def check_keys(value, info, source, required, optional):
85 def pprint(elems):
86 return ', '.join("'" + e + "'" for e in sorted(elems))
88 missing = set(required) - set(value)
89 if missing:
90 raise QAPISemError(
91 info,
92 "%s misses key%s %s"
93 % (source, 's' if len(missing) > 1 else '',
94 pprint(missing)))
95 allowed = set(required + optional)
96 unknown = set(value) - allowed
97 if unknown:
98 raise QAPISemError(
99 info,
100 "%s has unknown key%s %s\nValid keys are %s."
101 % (source, 's' if len(unknown) > 1 else '',
102 pprint(unknown), pprint(allowed)))
105 def check_flags(expr, info):
106 for key in ['gen', 'success-response']:
107 if key in expr and expr[key] is not False:
108 raise QAPISemError(
109 info, "flag '%s' may only use false value" % key)
110 for key in ['boxed', 'allow-oob', 'allow-preconfig', 'coroutine']:
111 if key in expr and expr[key] is not True:
112 raise QAPISemError(
113 info, "flag '%s' may only use true value" % key)
114 if 'allow-oob' in expr and 'coroutine' in expr:
115 # This is not necessarily a fundamental incompatibility, but
116 # we don't have a use case and the desired semantics isn't
117 # obvious. The simplest solution is to forbid it until we get
118 # a use case for it.
119 raise QAPISemError(info, "flags 'allow-oob' and 'coroutine' "
120 "are incompatible")
123 def check_if(expr, info, source):
125 def check_if_str(ifcond, info):
126 if not isinstance(ifcond, str):
127 raise QAPISemError(
128 info,
129 "'if' condition of %s must be a string or a list of strings"
130 % source)
131 if ifcond.strip() == '':
132 raise QAPISemError(
133 info,
134 "'if' condition '%s' of %s makes no sense"
135 % (ifcond, source))
137 ifcond = expr.get('if')
138 if ifcond is None:
139 return
140 if isinstance(ifcond, list):
141 if ifcond == []:
142 raise QAPISemError(
143 info, "'if' condition [] of %s is useless" % source)
144 for elt in ifcond:
145 check_if_str(elt, info)
146 else:
147 check_if_str(ifcond, info)
148 expr['if'] = [ifcond]
151 def normalize_members(members):
152 if isinstance(members, OrderedDict):
153 for key, arg in members.items():
154 if isinstance(arg, dict):
155 continue
156 members[key] = {'type': arg}
159 def check_type(value, info, source,
160 allow_array=False, allow_dict=False):
161 if value is None:
162 return
164 # Array type
165 if isinstance(value, list):
166 if not allow_array:
167 raise QAPISemError(info, "%s cannot be an array" % source)
168 if len(value) != 1 or not isinstance(value[0], str):
169 raise QAPISemError(info,
170 "%s: array type must contain single type name" %
171 source)
172 return
174 # Type name
175 if isinstance(value, str):
176 return
178 # Anonymous type
180 if not allow_dict:
181 raise QAPISemError(info, "%s should be a type name" % source)
183 if not isinstance(value, OrderedDict):
184 raise QAPISemError(info,
185 "%s should be an object or type name" % source)
187 permissive = allow_dict in info.pragma.member_name_exceptions
189 # value is a dictionary, check that each member is okay
190 for (key, arg) in value.items():
191 key_source = "%s member '%s'" % (source, key)
192 if key.startswith('*'):
193 key = key[1:]
194 check_name_lower(key, info, key_source,
195 permit_upper=permissive,
196 permit_underscore=permissive)
197 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
198 raise QAPISemError(info, "%s uses reserved name" % key_source)
199 check_keys(arg, info, key_source, ['type'], ['if', 'features'])
200 check_if(arg, info, key_source)
201 check_features(arg.get('features'), info)
202 check_type(arg['type'], info, key_source, allow_array=True)
205 def check_features(features, info):
206 if features is None:
207 return
208 if not isinstance(features, list):
209 raise QAPISemError(info, "'features' must be an array")
210 features[:] = [f if isinstance(f, dict) else {'name': f}
211 for f in features]
212 for f in features:
213 source = "'features' member"
214 assert isinstance(f, dict)
215 check_keys(f, info, source, ['name'], ['if'])
216 check_name_is_str(f['name'], info, source)
217 source = "%s '%s'" % (source, f['name'])
218 check_name_lower(f['name'], info, source)
219 check_if(f, info, source)
222 def check_enum(expr, info):
223 name = expr['enum']
224 members = expr['data']
225 prefix = expr.get('prefix')
227 if not isinstance(members, list):
228 raise QAPISemError(info, "'data' must be an array")
229 if prefix is not None and not isinstance(prefix, str):
230 raise QAPISemError(info, "'prefix' must be a string")
232 permissive = name in info.pragma.member_name_exceptions
234 members[:] = [m if isinstance(m, dict) else {'name': m}
235 for m in members]
236 for member in members:
237 source = "'data' member"
238 member_name = member['name']
239 check_keys(member, info, source, ['name'], ['if'])
240 check_name_is_str(member_name, info, source)
241 source = "%s '%s'" % (source, member_name)
242 # Enum members may start with a digit
243 if member_name[0].isdigit():
244 member_name = 'd' + member_name # Hack: hide the digit
245 check_name_lower(member_name, info, source,
246 permit_upper=permissive,
247 permit_underscore=permissive)
248 check_if(member, info, source)
251 def check_struct(expr, info):
252 name = expr['struct']
253 members = expr['data']
255 check_type(members, info, "'data'", allow_dict=name)
256 check_type(expr.get('base'), info, "'base'")
259 def check_union(expr, info):
260 name = expr['union']
261 base = expr.get('base')
262 discriminator = expr.get('discriminator')
263 members = expr['data']
265 if discriminator is None: # simple union
266 if base is not None:
267 raise QAPISemError(info, "'base' requires 'discriminator'")
268 else: # flat union
269 check_type(base, info, "'base'", allow_dict=name)
270 if not base:
271 raise QAPISemError(info, "'discriminator' requires 'base'")
272 check_name_is_str(discriminator, info, "'discriminator'")
274 for (key, value) in members.items():
275 source = "'data' member '%s'" % key
276 if discriminator is None:
277 check_name_lower(key, info, source)
278 # else: name is in discriminator enum, which gets checked
279 check_keys(value, info, source, ['type'], ['if'])
280 check_if(value, info, source)
281 check_type(value['type'], info, source, allow_array=not base)
284 def check_alternate(expr, info):
285 members = expr['data']
287 if not members:
288 raise QAPISemError(info, "'data' must not be empty")
289 for (key, value) in members.items():
290 source = "'data' member '%s'" % key
291 check_name_lower(key, info, source)
292 check_keys(value, info, source, ['type'], ['if'])
293 check_if(value, info, source)
294 check_type(value['type'], info, source)
297 def check_command(expr, info):
298 args = expr.get('data')
299 rets = expr.get('returns')
300 boxed = expr.get('boxed', False)
302 if boxed and args is None:
303 raise QAPISemError(info, "'boxed': true requires 'data'")
304 check_type(args, info, "'data'", allow_dict=not boxed)
305 check_type(rets, info, "'returns'", allow_array=True)
308 def check_event(expr, info):
309 args = expr.get('data')
310 boxed = expr.get('boxed', False)
312 if boxed and args is None:
313 raise QAPISemError(info, "'boxed': true requires 'data'")
314 check_type(args, info, "'data'", allow_dict=not boxed)
317 def check_exprs(exprs):
318 for expr_elem in exprs:
319 expr = expr_elem['expr']
320 info = expr_elem['info']
321 doc = expr_elem.get('doc')
323 if 'include' in expr:
324 continue
326 if 'enum' in expr:
327 meta = 'enum'
328 elif 'union' in expr:
329 meta = 'union'
330 elif 'alternate' in expr:
331 meta = 'alternate'
332 elif 'struct' in expr:
333 meta = 'struct'
334 elif 'command' in expr:
335 meta = 'command'
336 elif 'event' in expr:
337 meta = 'event'
338 else:
339 raise QAPISemError(info, "expression is missing metatype")
341 name = expr[meta]
342 check_name_is_str(name, info, "'%s'" % meta)
343 info.set_defn(meta, name)
344 check_defn_name_str(name, info, meta)
346 if doc:
347 if doc.symbol != name:
348 raise QAPISemError(
349 info, "documentation comment is for '%s'" % doc.symbol)
350 doc.check_expr(expr)
351 elif info.pragma.doc_required:
352 raise QAPISemError(info,
353 "documentation comment required")
355 if meta == 'enum':
356 check_keys(expr, info, meta,
357 ['enum', 'data'], ['if', 'features', 'prefix'])
358 check_enum(expr, info)
359 elif meta == 'union':
360 check_keys(expr, info, meta,
361 ['union', 'data'],
362 ['base', 'discriminator', 'if', 'features'])
363 normalize_members(expr.get('base'))
364 normalize_members(expr['data'])
365 check_union(expr, info)
366 elif meta == 'alternate':
367 check_keys(expr, info, meta,
368 ['alternate', 'data'], ['if', 'features'])
369 normalize_members(expr['data'])
370 check_alternate(expr, info)
371 elif meta == 'struct':
372 check_keys(expr, info, meta,
373 ['struct', 'data'], ['base', 'if', 'features'])
374 normalize_members(expr['data'])
375 check_struct(expr, info)
376 elif meta == 'command':
377 check_keys(expr, info, meta,
378 ['command'],
379 ['data', 'returns', 'boxed', 'if', 'features',
380 'gen', 'success-response', 'allow-oob',
381 'allow-preconfig', 'coroutine'])
382 normalize_members(expr.get('data'))
383 check_command(expr, info)
384 elif meta == 'event':
385 check_keys(expr, info, meta,
386 ['event'], ['data', 'boxed', 'if', 'features'])
387 normalize_members(expr.get('data'))
388 check_event(expr, info)
389 else:
390 assert False, 'unexpected meta type'
392 check_if(expr, info, meta)
393 check_features(expr.get('features'), info)
394 check_flags(expr, info)
396 return exprs