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