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
17import unittest
18from compiler.front_end import attribute_checker
19from compiler.front_end import glue
20from compiler.util import error
21from compiler.util import ir_data
22from compiler.util import ir_util
23from 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
33def _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
42class 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
696if __name__ == "__main__":
697  unittest.main()
698