xref: /aosp_15_r20/external/emboss/compiler/front_end/attribute_checker_test.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1 # Copyright 2019 Google LLC
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     https://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """Tests for attribute_checker.py."""
16 
17 import unittest
18 from compiler.front_end import attribute_checker
19 from compiler.front_end import glue
20 from compiler.util import error
21 from compiler.util import ir_data
22 from compiler.util import ir_util
23 from compiler.util import test_util
24 
25 # These are not shared with attribute_checker.py because their values are part
26 # of the contract with back ends.
27 _BYTE_ORDER = "byte_order"
28 _FIXED_SIZE = "fixed_size_in_bits"
29 _IS_SIGNED = "is_signed"
30 _MAX_BITS = "maximum_bits"
31 
32 
33 def _make_ir_from_emb(emb_text, name="m.emb"):
34   ir, unused_debug_info, errors = glue.parse_emboss_file(
35       name,
36       test_util.dict_file_reader({name: emb_text}),
37       stop_before_step="normalize_and_verify")
38   assert not errors
39   return ir
40 
41 
42 class NormalizeIrTest(unittest.TestCase):
43 
44   def test_rejects_may_be_used_as_integer(self):
45     enum_ir = _make_ir_from_emb("enum Foo:\n"
46                                 "  [may_be_used_as_integer: false]\n"
47                                 "  VALUE = 1\n")
48     enum_type_ir = enum_ir.module[0].type[0]
49     self.assertEqual([[
50         error.error(
51             "m.emb", enum_type_ir.attribute[0].name.source_location,
52             "Unknown attribute 'may_be_used_as_integer' on enum 'Foo'.")
53     ]], attribute_checker.normalize_and_verify(enum_ir))
54 
55   def test_adds_fixed_size_attribute_to_struct(self):
56     # field2 is intentionally after field3, in order to trigger certain code
57     # paths in attribute_checker.py.
58     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
59                                   "struct Foo:\n"
60                                   "  0 [+2]  UInt  field1\n"
61                                   "  4 [+4]  UInt  field2\n"
62                                   "  2 [+2]  UInt  field3\n")
63     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
64     size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
65                                       _FIXED_SIZE)
66     self.assertEqual(64, ir_util.constant_value(size_attr.expression))
67     self.assertEqual(struct_ir.module[0].type[0].source_location,
68                      size_attr.source_location)
69 
70   def test_adds_fixed_size_attribute_to_struct_with_virtual_field(self):
71     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
72                                   "struct Foo:\n"
73                                   "  0 [+2]  UInt  field1\n"
74                                   "  let field2 = field1\n"
75                                   "  2 [+2]  UInt  field3\n")
76     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
77     size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
78                                       _FIXED_SIZE)
79     self.assertEqual(32, ir_util.constant_value(size_attr.expression))
80     self.assertEqual(struct_ir.module[0].type[0].source_location,
81                      size_attr.source_location)
82 
83   def test_adds_fixed_size_attribute_to_anonymous_bits(self):
84     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
85                                   "struct Foo:\n"
86                                   "  0 [+4]  bits:\n"
87                                   "    0 [+8]  UInt  field\n")
88     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
89     size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
90                                       _FIXED_SIZE)
91     self.assertEqual(32, ir_util.constant_value(size_attr.expression))
92     bits_size_attr = ir_util.get_attribute(
93         struct_ir.module[0].type[0].subtype[0].attribute, _FIXED_SIZE)
94     self.assertEqual(8, ir_util.constant_value(bits_size_attr.expression))
95     self.assertEqual(struct_ir.module[0].type[0].source_location,
96                      size_attr.source_location)
97 
98   def test_does_not_add_fixed_size_attribute_to_variable_size_struct(self):
99     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
100                                   "struct Foo:\n"
101                                   "  0 [+4]  UInt      n\n"
102                                   "  4 [+n]  UInt:8[]  payload\n")
103     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
104     self.assertIsNone(ir_util.get_attribute(
105         struct_ir.module[0].type[0].attribute, _FIXED_SIZE))
106 
107   def test_accepts_correct_fixed_size_and_size_attributes_on_struct(self):
108     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
109                                   "struct Foo:\n"
110                                   "  [fixed_size_in_bits: 64]\n"
111                                   "  0 [+2]  UInt  field1\n"
112                                   "  2 [+2]  UInt  field2\n"
113                                   "  4 [+4]  UInt  field3\n")
114     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
115     size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
116                                       _FIXED_SIZE)
117     self.assertTrue(size_attr)
118     self.assertEqual(64, ir_util.constant_value(size_attr.expression))
119 
120   def test_accepts_correct_size_attribute_on_struct(self):
121     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
122                                   "struct Foo:\n"
123                                   "  [fixed_size_in_bits: 64]\n"
124                                   "  0 [+2]  UInt  field1\n"
125                                   "  4 [+4]  UInt  field3\n")
126     self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
127     size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
128                                       _FIXED_SIZE)
129     self.assertTrue(size_attr.expression)
130     self.assertEqual(64, ir_util.constant_value(size_attr.expression))
131 
132   def test_rejects_incorrect_fixed_size_attribute_on_variable_size_struct(self):
133     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
134                                   "struct Foo:\n"
135                                   "  [fixed_size_in_bits: 8]\n"
136                                   "  0 [+4]  UInt      n\n"
137                                   "  4 [+n]  UInt:8[]  payload\n")
138     struct_type_ir = struct_ir.module[0].type[0]
139     self.assertEqual([[error.error(
140         "m.emb", struct_type_ir.attribute[0].value.source_location,
141         "Struct is marked as fixed size, but contains variable-location "
142         "fields.")]], attribute_checker.normalize_and_verify(struct_ir))
143 
144   def test_rejects_size_attribute_with_wrong_large_value_on_struct(self):
145     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
146                                   "struct Foo:\n"
147                                   "  [fixed_size_in_bits: 80]\n"
148                                   "  0 [+2]  UInt  field1\n"
149                                   "  2 [+2]  UInt  field2\n"
150                                   "  4 [+4]  UInt  field3\n")
151     struct_type_ir = struct_ir.module[0].type[0]
152     self.assertEqual([
153         [error.error("m.emb", struct_type_ir.attribute[0].value.source_location,
154                      "Struct is 64 bits, but is marked as 80 bits.")]
155     ], attribute_checker.normalize_and_verify(struct_ir))
156 
157   def test_rejects_size_attribute_with_wrong_small_value_on_struct(self):
158     struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
159                                   "struct Foo:\n"
160                                   "  [fixed_size_in_bits: 40]\n"
161                                   "  0 [+2]  UInt  field1\n"
162                                   "  2 [+2]  UInt  field2\n"
163                                   "  4 [+4]  UInt  field3\n")
164     struct_type_ir = struct_ir.module[0].type[0]
165     self.assertEqual([
166         [error.error("m.emb", struct_type_ir.attribute[0].value.source_location,
167                      "Struct is 64 bits, but is marked as 40 bits.")]
168     ], attribute_checker.normalize_and_verify(struct_ir))
169 
170   def test_accepts_variable_size_external(self):
171     external_ir = _make_ir_from_emb("external Foo:\n"
172                                     "  [addressable_unit_size: 1]\n")
173     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
174 
175   def test_accepts_fixed_size_external(self):
176     external_ir = _make_ir_from_emb("external Foo:\n"
177                                     "  [fixed_size_in_bits: 32]\n"
178                                     "  [addressable_unit_size: 1]\n")
179     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
180 
181   def test_rejects_external_with_no_addressable_unit_size_attribute(self):
182     external_ir = _make_ir_from_emb("external Foo:\n"
183                                     "  [is_integer: false]\n")
184     external_type_ir = external_ir.module[0].type[0]
185     self.assertEqual([
186         [error.error(
187             "m.emb", external_type_ir.source_location,
188             "Expected 'addressable_unit_size' attribute for external type.")]
189     ], attribute_checker.normalize_and_verify(external_ir))
190 
191   def test_rejects_is_integer_with_non_constant_value(self):
192     external_ir = _make_ir_from_emb(
193         "external Foo:\n"
194         "  [is_integer: $static_size_in_bits == 1]\n"
195         "  [addressable_unit_size: 1]\n")
196     external_type_ir = external_ir.module[0].type[0]
197     self.assertEqual([
198         [error.error(
199             "m.emb", external_type_ir.attribute[0].value.source_location,
200             "Attribute 'is_integer' must have a constant boolean value.")]
201     ], attribute_checker.normalize_and_verify(external_ir))
202 
203   def test_rejects_addressable_unit_size_with_non_constant_value(self):
204     external_ir = _make_ir_from_emb(
205         "external Foo:\n"
206         "  [is_integer: true]\n"
207         "  [addressable_unit_size: $static_size_in_bits]\n")
208     external_type_ir = external_ir.module[0].type[0]
209     self.assertEqual([
210         [error.error(
211             "m.emb", external_type_ir.attribute[1].value.source_location,
212             "Attribute 'addressable_unit_size' must have a constant value.")]
213     ], attribute_checker.normalize_and_verify(external_ir))
214 
215   def test_rejects_external_with_wrong_addressable_unit_size_attribute(self):
216     external_ir = _make_ir_from_emb("external Foo:\n"
217                                     "  [addressable_unit_size: 4]\n")
218     external_type_ir = external_ir.module[0].type[0]
219     self.assertEqual([
220         [error.error(
221             "m.emb", external_type_ir.source_location,
222             "Only values '1' (bit) and '8' (byte) are allowed for the "
223             "'addressable_unit_size' attribute")]
224     ], attribute_checker.normalize_and_verify(external_ir))
225 
226   def test_rejects_duplicate_attribute(self):
227     ir = _make_ir_from_emb("external Foo:\n"
228                            "  [is_integer: true]\n"
229                            "  [is_integer: true]\n")
230     self.assertEqual([[
231         error.error("m.emb", ir.module[0].type[0].attribute[1].source_location,
232                     "Duplicate attribute 'is_integer'."),
233         error.note("m.emb", ir.module[0].type[0].attribute[0].source_location,
234                    "Original attribute"),
235     ]], attribute_checker.normalize_and_verify(ir))
236 
237   def test_rejects_duplicate_default_attribute(self):
238     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
239                            '[$default byte_order: "LittleEndian"]\n')
240     self.assertEqual(
241         [[
242             error.error("m.emb", ir.module[0].attribute[1].source_location,
243                         "Duplicate attribute 'byte_order'."),
244             error.note("m.emb", ir.module[0].attribute[0].source_location,
245                        "Original attribute"),
246         ]], attribute_checker.normalize_and_verify(ir))
247 
248   def test_rejects_unknown_attribute(self):
249     ir = _make_ir_from_emb("[gibberish: true]\n")
250     attr = ir.module[0].attribute[0]
251     self.assertEqual([[
252         error.error("m.emb", attr.name.source_location,
253                     "Unknown attribute 'gibberish' on module 'm.emb'.")
254     ]], attribute_checker.normalize_and_verify(ir))
255 
256   def test_rejects_non_constant_attribute(self):
257     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
258                            "struct Foo:\n"
259                            "  [fixed_size_in_bits: field1]\n"
260                            "  0 [+2]  UInt  field1\n")
261     attr = ir.module[0].type[0].attribute[0]
262     self.assertEqual(
263         [[
264             error.error(
265                 "m.emb", attr.value.source_location,
266                 "Attribute 'fixed_size_in_bits' must have a constant value.")
267         ]],
268         attribute_checker.normalize_and_verify(ir))
269 
270   def test_rejects_attribute_missing_required_back_end_specifier(self):
271     ir = _make_ir_from_emb('[namespace: "abc"]\n')
272     attr = ir.module[0].attribute[0]
273     self.assertEqual([[
274         error.error("m.emb", attr.name.source_location,
275                     "Unknown attribute 'namespace' on module 'm.emb'.")
276     ]], attribute_checker.normalize_and_verify(ir))
277 
278   def test_accepts_attribute_with_default_known_back_end_specifier(self):
279     ir = _make_ir_from_emb('[(cpp) namespace: "abc"]\n')
280     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
281 
282   def test_rejects_attribute_with_specified_back_end_specifier(self):
283     ir = _make_ir_from_emb('[(c) namespace: "abc"]\n'
284                            '[expected_back_ends: "c, cpp"]\n')
285     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
286 
287   def test_rejects_cpp_backend_attribute_when_not_in_expected_back_ends(self):
288     ir = _make_ir_from_emb('[(cpp) namespace: "abc"]\n'
289                            '[expected_back_ends: "c"]\n')
290     attr = ir.module[0].attribute[0]
291     self.maxDiff = 200000
292     self.assertEqual([[
293         error.error(
294             "m.emb", attr.back_end.source_location,
295             "Back end specifier 'cpp' does not match any expected back end "
296             "specifier for this file: 'c'.  Add or update the "
297             "'[expected_back_ends: \"c, cpp\"]' attribute at the file level if "
298             "this back end specifier is intentional.")
299     ]], attribute_checker.normalize_and_verify(ir))
300 
301   def test_rejects_expected_back_ends_with_bad_back_end(self):
302     ir = _make_ir_from_emb('[expected_back_ends: "c++"]\n')
303     attr = ir.module[0].attribute[0]
304     self.assertEqual([[
305         error.error(
306             "m.emb", attr.value.source_location,
307             "Attribute 'expected_back_ends' must be a comma-delimited list of "
308             "back end specifiers (like \"cpp, proto\")), not \"c++\".")
309     ]], attribute_checker.normalize_and_verify(ir))
310 
311   def test_rejects_expected_back_ends_with_no_comma(self):
312     ir = _make_ir_from_emb('[expected_back_ends: "cpp z"]\n')
313     attr = ir.module[0].attribute[0]
314     self.assertEqual([[
315         error.error(
316             "m.emb", attr.value.source_location,
317             "Attribute 'expected_back_ends' must be a comma-delimited list of "
318             "back end specifiers (like \"cpp, proto\")), not \"cpp z\".")
319     ]], attribute_checker.normalize_and_verify(ir))
320 
321   def test_rejects_expected_back_ends_with_extra_commas(self):
322     ir = _make_ir_from_emb('[expected_back_ends: "cpp,,z"]\n')
323     attr = ir.module[0].attribute[0]
324     self.assertEqual([[
325         error.error(
326             "m.emb", attr.value.source_location,
327             "Attribute 'expected_back_ends' must be a comma-delimited list of "
328             "back end specifiers (like \"cpp, proto\")), not \"cpp,,z\".")
329     ]], attribute_checker.normalize_and_verify(ir))
330 
331   def test_accepts_empty_expected_back_ends(self):
332     ir = _make_ir_from_emb('[expected_back_ends: ""]\n')
333     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
334 
335   def test_adds_byte_order_attributes_from_default(self):
336     ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
337                            "struct Foo:\n"
338                            "  0 [+2]  UInt  bar\n"
339                            "  2 [+2]  UInt  baz\n"
340                            '    [byte_order: "LittleEndian"]\n')
341     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
342     byte_order_attr = ir_util.get_attribute(
343         ir.module[0].type[0].structure.field[0].attribute, _BYTE_ORDER)
344     self.assertTrue(byte_order_attr.HasField("string_constant"))
345     self.assertEqual("BigEndian", byte_order_attr.string_constant.text)
346     byte_order_attr = ir_util.get_attribute(
347         ir.module[0].type[0].structure.field[1].attribute, _BYTE_ORDER)
348     self.assertTrue(byte_order_attr.HasField("string_constant"))
349     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
350 
351   def test_adds_null_byte_order_attributes(self):
352     ir = _make_ir_from_emb("struct Foo:\n"
353                            "  0 [+1]  UInt      bar\n"
354                            "  1 [+1]  UInt      baz\n"
355                            '    [byte_order: "LittleEndian"]\n'
356                            "  2 [+2]  UInt:8[]  baseball\n"
357                            "  4 [+2]  UInt:8[]  bat\n"
358                            '    [byte_order: "LittleEndian"]\n')
359     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
360     structure = ir.module[0].type[0].structure
361     byte_order_attr = ir_util.get_attribute(
362         structure.field[0].attribute, _BYTE_ORDER)
363     self.assertTrue(byte_order_attr.HasField("string_constant"))
364     self.assertEqual("Null", byte_order_attr.string_constant.text)
365     self.assertEqual(structure.field[0].source_location,
366                      byte_order_attr.source_location)
367     byte_order_attr = ir_util.get_attribute(structure.field[1].attribute,
368                                             _BYTE_ORDER)
369     self.assertTrue(byte_order_attr.HasField("string_constant"))
370     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
371     byte_order_attr = ir_util.get_attribute(structure.field[2].attribute,
372                                             _BYTE_ORDER)
373     self.assertTrue(byte_order_attr.HasField("string_constant"))
374     self.assertEqual("Null", byte_order_attr.string_constant.text)
375     self.assertEqual(structure.field[2].source_location,
376                      byte_order_attr.source_location)
377     byte_order_attr = ir_util.get_attribute(structure.field[3].attribute,
378                                             _BYTE_ORDER)
379     self.assertTrue(byte_order_attr.HasField("string_constant"))
380     self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
381 
382   def test_disallows_default_byte_order_on_field(self):
383     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
384                            "struct Foo:\n"
385                            "  0 [+2]  UInt  bar\n"
386                            '    [$default byte_order: "LittleEndian"]\n')
387     default_byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
388     self.assertEqual(
389         [[error.error(
390             "m.emb", default_byte_order.name.source_location,
391             "Attribute 'byte_order' may not be defaulted on struct field 'bar'."
392         )]],
393         attribute_checker.normalize_and_verify(ir))
394 
395   def test_disallows_default_byte_order_on_bits(self):
396     ir = _make_ir_from_emb("bits Foo:\n"
397                            '  [$default byte_order: "LittleEndian"]\n'
398                            "  0 [+2]  UInt  bar\n")
399     default_byte_order = ir.module[0].type[0].attribute[0]
400     self.assertEqual(
401         [[error.error(
402             "m.emb", default_byte_order.name.source_location,
403             "Attribute 'byte_order' may not be defaulted on bits 'Foo'.")]],
404         attribute_checker.normalize_and_verify(ir))
405 
406   def test_disallows_default_byte_order_on_enum(self):
407     ir = _make_ir_from_emb("enum Foo:\n"
408                            '  [$default byte_order: "LittleEndian"]\n'
409                            "  BAR = 1\n")
410     default_byte_order = ir.module[0].type[0].attribute[0]
411     self.assertEqual(
412         [[error.error(
413             "m.emb", default_byte_order.name.source_location,
414             "Attribute 'byte_order' may not be defaulted on enum 'Foo'.")]],
415         attribute_checker.normalize_and_verify(ir))
416 
417   def test_adds_byte_order_from_scoped_default(self):
418     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
419                            "struct Foo:\n"
420                            '  [$default byte_order: "BigEndian"]\n'
421                            "  0 [+2]  UInt  bar\n")
422     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
423     byte_order_attr = ir_util.get_attribute(
424         ir.module[0].type[0].structure.field[0].attribute, _BYTE_ORDER)
425     self.assertTrue(byte_order_attr.HasField("string_constant"))
426     self.assertEqual("BigEndian", byte_order_attr.string_constant.text)
427 
428   def test_disallows_unknown_byte_order(self):
429     ir = _make_ir_from_emb("struct Foo:\n"
430                            "  0 [+2]  UInt  bar\n"
431                            '    [byte_order: "NoEndian"]\n')
432     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
433     self.assertEqual(
434         [[error.error(
435             "m.emb", byte_order.value.source_location,
436             "Attribute 'byte_order' must be 'BigEndian' or 'LittleEndian' or "
437             "'Null'.")]],
438         attribute_checker.normalize_and_verify(ir))
439 
440   def test_disallows_unknown_default_byte_order(self):
441     ir = _make_ir_from_emb('[$default byte_order: "NoEndian"]\n')
442     default_byte_order = ir.module[0].attribute[0]
443     self.assertEqual(
444         [[error.error(
445             "m.emb", default_byte_order.value.source_location,
446             "Attribute 'byte_order' must be 'BigEndian' or 'LittleEndian' or "
447             "'Null'.")]],
448         attribute_checker.normalize_and_verify(ir))
449 
450   def test_disallows_byte_order_on_non_byte_order_dependent_fields(self):
451     ir = _make_ir_from_emb("struct Foo:\n"
452                            '  [$default byte_order: "LittleEndian"]\n'
453                            "  0 [+2]  UInt  uint\n"
454                            "struct Bar:\n"
455                            "  0 [+2]  Foo  foo\n"
456                            '    [byte_order: "LittleEndian"]\n')
457     byte_order = ir.module[0].type[1].structure.field[0].attribute[0]
458     self.assertEqual(
459         [[error.error(
460             "m.emb", byte_order.value.source_location,
461             "Attribute 'byte_order' not allowed on field which is not byte "
462             "order dependent.")]],
463         attribute_checker.normalize_and_verify(ir))
464 
465   def test_disallows_byte_order_on_virtual_field(self):
466     ir = _make_ir_from_emb("struct Foo:\n"
467                            "  let x = 10\n"
468                            '    [byte_order: "LittleEndian"]\n')
469     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
470     self.assertEqual(
471         [[error.error(
472             "m.emb", byte_order.name.source_location,
473             "Unknown attribute 'byte_order' on virtual struct field 'x'.")]],
474         attribute_checker.normalize_and_verify(ir))
475 
476   def test_disallows_null_byte_order_on_multibyte_fields(self):
477     ir = _make_ir_from_emb("struct Foo:\n"
478                            "  0 [+2]  UInt  uint\n"
479                            '    [byte_order: "Null"]\n')
480     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
481     self.assertEqual(
482         [[error.error(
483             "m.emb", byte_order.value.source_location,
484             "Attribute 'byte_order' may only be 'Null' for one-byte fields.")]],
485         attribute_checker.normalize_and_verify(ir))
486 
487   def test_disallows_null_byte_order_on_multibyte_array_elements(self):
488     ir = _make_ir_from_emb("struct Foo:\n"
489                            "  0 [+4]  UInt:16[]  uint\n"
490                            '    [byte_order: "Null"]\n')
491     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
492     self.assertEqual(
493         [[error.error(
494             "m.emb", byte_order.value.source_location,
495             "Attribute 'byte_order' may only be 'Null' for one-byte fields.")]],
496         attribute_checker.normalize_and_verify(ir))
497 
498   def test_requires_byte_order_on_byte_order_dependent_fields(self):
499     ir = _make_ir_from_emb("struct Foo:\n"
500                            "  0 [+2]  UInt  uint\n")
501     field = ir.module[0].type[0].structure.field[0]
502     self.assertEqual(
503         [[error.error(
504             "m.emb", field.source_location,
505             "Attribute 'byte_order' required on field which is byte order "
506             "dependent.")]],
507         attribute_checker.normalize_and_verify(ir))
508 
509   def test_disallows_unknown_text_output_attribute(self):
510     ir = _make_ir_from_emb("struct Foo:\n"
511                            "  0 [+2]  UInt  bar\n"
512                            '    [text_output: "None"]\n')
513     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
514     self.assertEqual(
515         [[error.error(
516             "m.emb", byte_order.value.source_location,
517             "Attribute 'text_output' must be 'Emit' or 'Skip'.")]],
518         attribute_checker.normalize_and_verify(ir))
519 
520   def test_disallows_non_string_text_output_attribute(self):
521     ir = _make_ir_from_emb("struct Foo:\n"
522                            "  0 [+2]  UInt  bar\n"
523                            "    [text_output: 0]\n")
524     byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
525     self.assertEqual(
526         [[error.error(
527             "m.emb", byte_order.value.source_location,
528             "Attribute 'text_output' must be 'Emit' or 'Skip'.")]],
529         attribute_checker.normalize_and_verify(ir))
530 
531   def test_allows_skip_text_output_attribute_on_physical_field(self):
532     ir = _make_ir_from_emb("struct Foo:\n"
533                            "  0 [+1]  UInt  bar\n"
534                            '    [text_output: "Skip"]\n')
535     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
536 
537   def test_allows_skip_text_output_attribute_on_virtual_field(self):
538     ir = _make_ir_from_emb("struct Foo:\n"
539                            "  let x = 10\n"
540                            '    [text_output: "Skip"]\n')
541     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
542 
543   def test_allows_emit_text_output_attribute_on_physical_field(self):
544     ir = _make_ir_from_emb("struct Foo:\n"
545                            "  0 [+1]  UInt  bar\n"
546                            '    [text_output: "Emit"]\n')
547     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
548 
549   def test_adds_bit_addressable_unit_to_external(self):
550     external_ir = _make_ir_from_emb("external Foo:\n"
551                                     "  [addressable_unit_size: 1]\n")
552     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
553     self.assertEqual(ir_data.AddressableUnit.BIT,
554                      external_ir.module[0].type[0].addressable_unit)
555 
556   def test_adds_byte_addressable_unit_to_external(self):
557     external_ir = _make_ir_from_emb("external Foo:\n"
558                                     "  [addressable_unit_size: 8]\n")
559     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
560     self.assertEqual(ir_data.AddressableUnit.BYTE,
561                      external_ir.module[0].type[0].addressable_unit)
562 
563   def test_rejects_requires_using_array(self):
564     ir = _make_ir_from_emb("struct Foo:\n"
565                            "  0 [+4]  UInt:8[]  array\n"
566                            "    [requires: this]\n")
567     field_ir = ir.module[0].type[0].structure.field[0]
568     self.assertEqual(
569         [[error.error("m.emb", field_ir.attribute[0].value.source_location,
570                       "Attribute 'requires' must have a boolean value.")]],
571         attribute_checker.normalize_and_verify(ir))
572 
573   def test_rejects_requires_on_array(self):
574     ir = _make_ir_from_emb("struct Foo:\n"
575                            "  0 [+4]  UInt:8[]  array\n"
576                            "    [requires: false]\n")
577     field_ir = ir.module[0].type[0].structure.field[0]
578     self.assertEqual(
579         [[
580             error.error("m.emb", field_ir.attribute[0].value.source_location,
581                         "Attribute 'requires' is only allowed on integer, "
582                         "enumeration, or boolean fields, not arrays."),
583             error.note("m.emb", field_ir.type.source_location,
584                        "Field type."),
585         ]],
586         error.filter_errors(attribute_checker.normalize_and_verify(ir)))
587 
588   def test_rejects_requires_on_struct(self):
589     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
590                            "struct Foo:\n"
591                            "  0 [+4]  Bar  bar\n"
592                            "    [requires: false]\n"
593                            "struct Bar:\n"
594                            "  0 [+4]  UInt  uint\n")
595     field_ir = ir.module[0].type[0].structure.field[0]
596     self.assertEqual(
597         [[error.error("m.emb", field_ir.attribute[0].value.source_location,
598                       "Attribute 'requires' is only allowed on integer, "
599                       "enumeration, or boolean fields.")]],
600         error.filter_errors(attribute_checker.normalize_and_verify(ir)))
601 
602   def test_rejects_requires_on_float(self):
603     ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
604                            "struct Foo:\n"
605                            "  0 [+4]  Float  float\n"
606                            "    [requires: false]\n")
607     field_ir = ir.module[0].type[0].structure.field[0]
608     self.assertEqual(
609         [[error.error("m.emb", field_ir.attribute[0].value.source_location,
610                       "Attribute 'requires' is only allowed on integer, "
611                       "enumeration, or boolean fields.")]],
612         error.filter_errors(attribute_checker.normalize_and_verify(ir)))
613 
614   def test_adds_false_is_signed_attribute(self):
615     ir = _make_ir_from_emb("enum Foo:\n"
616                            "  ZERO = 0\n")
617     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
618     enum = ir.module[0].type[0]
619     is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
620     self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
621     self.assertFalse(is_signed_attr.expression.boolean_constant.value)
622 
623   def test_leaves_is_signed_attribute(self):
624     ir = _make_ir_from_emb("enum Foo:\n"
625                            "  [is_signed: true]\n"
626                            "  ZERO = 0\n")
627     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
628     enum = ir.module[0].type[0]
629     is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
630     self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
631     self.assertTrue(is_signed_attr.expression.boolean_constant.value)
632 
633   def test_adds_true_is_signed_attribute(self):
634     ir = _make_ir_from_emb("enum Foo:\n"
635                            "  NEGATIVE_ONE = -1\n")
636     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
637     enum = ir.module[0].type[0]
638     is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
639     self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
640     self.assertTrue(is_signed_attr.expression.boolean_constant.value)
641 
642   def test_adds_max_bits_attribute(self):
643     ir = _make_ir_from_emb("enum Foo:\n"
644                            "  ZERO = 0\n")
645     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
646     enum = ir.module[0].type[0]
647     max_bits_attr = ir_util.get_attribute(enum.attribute, _MAX_BITS)
648     self.assertTrue(max_bits_attr.expression.HasField("constant"))
649     self.assertEqual("64", max_bits_attr.expression.constant.value)
650 
651   def test_leaves_max_bits_attribute(self):
652     ir = _make_ir_from_emb("enum Foo:\n"
653                            "  [maximum_bits: 32]\n"
654                            "  ZERO = 0\n")
655     self.assertEqual([], attribute_checker.normalize_and_verify(ir))
656     enum = ir.module[0].type[0]
657     max_bits_attr = ir_util.get_attribute(enum.attribute, _MAX_BITS)
658     self.assertTrue(max_bits_attr.expression.HasField("constant"))
659     self.assertEqual("32", max_bits_attr.expression.constant.value)
660 
661   def test_rejects_too_small_max_bits(self):
662     ir = _make_ir_from_emb("enum Foo:\n"
663                            "  [maximum_bits: 0]\n"
664                            "  ZERO = 0\n")
665     attribute_ir = ir.module[0].type[0].attribute[0]
666     self.assertEqual(
667         [[error.error(
668             "m.emb", attribute_ir.value.source_location,
669             "'maximum_bits' on an 'enum' must be between 1 and 64.")]],
670         error.filter_errors(attribute_checker.normalize_and_verify(ir)))
671 
672   def test_rejects_too_large_max_bits(self):
673     ir = _make_ir_from_emb("enum Foo:\n"
674                            "  [maximum_bits: 65]\n"
675                            "  ZERO = 0\n")
676     attribute_ir = ir.module[0].type[0].attribute[0]
677     self.assertEqual(
678         [[error.error(
679             "m.emb", attribute_ir.value.source_location,
680             "'maximum_bits' on an 'enum' must be between 1 and 64.")]],
681         error.filter_errors(attribute_checker.normalize_and_verify(ir)))
682 
683   def test_rejects_unknown_enum_value_attribute(self):
684       ir = _make_ir_from_emb("enum Foo:\n"
685                              "  BAR = 0  \n"
686                              "    [bad_attr: true]\n")
687       attribute_ir = ir.module[0].type[0].enumeration.value[0].attribute[0]
688       self.assertNotEqual([], attribute_checker.normalize_and_verify(ir))
689       self.assertEqual(
690           [[error.error(
691               "m.emb", attribute_ir.name.source_location,
692               "Unknown attribute 'bad_attr' on enum value 'BAR'.")]],
693           error.filter_errors(attribute_checker.normalize_and_verify(ir)))
694 
695 
696 if __name__ == "__main__":
697   unittest.main()
698