# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for emboss.front_end.symbol_resolver.""" import unittest from compiler.front_end import glue from compiler.front_end import symbol_resolver from compiler.util import error from compiler.util import test_util _HAPPY_EMB = """ struct Foo: 0 [+4] UInt uint_field 4 [+4] Bar bar_field 8 [+16] UInt[4] array_field struct Bar: 0 [+4] Qux bar enum Qux: ABC = 1 DEF = 2 struct FieldRef: n-4 [+n] UInt:8[n] data offset-4 [+offset] UInt:8[offset] data2 0 [+4] UInt offset (n) struct VoidLength: 0 [+10] UInt:8[] ten_bytes enum Quux: ABC = 1 DEF = ABC struct UsesParameter(x: UInt:8): 0 [+x] UInt:8[] block """ class ResolveSymbolsTest(unittest.TestCase): """Tests for symbol_resolver.resolve_symbols().""" def _construct_ir_multiple(self, file_dict, primary_emb_name): ir, unused_debug_info, errors = glue.parse_emboss_file( primary_emb_name, test_util.dict_file_reader(file_dict), stop_before_step="resolve_symbols") assert not errors return ir def _construct_ir(self, emb_text, name="happy.emb"): return self._construct_ir_multiple({name: emb_text}, name) def test_struct_field_atomic_type_resolution(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[0].structure atomic_field1_reference = struct_ir.field[0].type.atomic_type.reference self.assertEqual(atomic_field1_reference.canonical_name.object_path, ["UInt" ]) self.assertEqual(atomic_field1_reference.canonical_name.module_file, "") atomic_field2_reference = struct_ir.field[1].type.atomic_type.reference self.assertEqual(atomic_field2_reference.canonical_name.object_path, ["Bar" ]) self.assertEqual(atomic_field2_reference.canonical_name.module_file, "happy.emb") def test_struct_field_enum_type_resolution(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[1].structure atomic_field_reference = struct_ir.field[0].type.atomic_type.reference self.assertEqual(atomic_field_reference.canonical_name.object_path, ["Qux"]) self.assertEqual(atomic_field_reference.canonical_name.module_file, "happy.emb") def test_struct_field_array_type_resolution(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) array_field_type = ir.module[0].type[0].structure.field[2].type.array_type array_field_reference = array_field_type.base_type.atomic_type.reference self.assertEqual(array_field_reference.canonical_name.object_path, ["UInt"]) self.assertEqual(array_field_reference.canonical_name.module_file, "") def test_inner_type_resolution(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) array_field_type = ir.module[0].type[0].structure.field[2].type.array_type array_field_reference = array_field_type.base_type.atomic_type.reference self.assertEqual(array_field_reference.canonical_name.object_path, ["UInt"]) self.assertEqual(array_field_reference.canonical_name.module_file, "") def test_struct_field_resolution_in_expression_in_location(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[3].structure field0_loc = struct_ir.field[0].location abbreviation_reference = field0_loc.size.field_reference.path[0] self.assertEqual(abbreviation_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(abbreviation_reference.canonical_name.module_file, "happy.emb") field0_start_left = field0_loc.start.function.args[0] nested_abbreviation_reference = field0_start_left.field_reference.path[0] self.assertEqual(nested_abbreviation_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(nested_abbreviation_reference.canonical_name.module_file, "happy.emb") field1_loc = struct_ir.field[1].location direct_reference = field1_loc.size.field_reference.path[0] self.assertEqual(direct_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(direct_reference.canonical_name.module_file, "happy.emb") field1_start_left = field1_loc.start.function.args[0] nested_direct_reference = field1_start_left.field_reference.path[0] self.assertEqual(nested_direct_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(nested_direct_reference.canonical_name.module_file, "happy.emb") def test_struct_field_resolution_in_expression_in_array_length(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[3].structure field0_array_type = struct_ir.field[0].type.array_type field0_array_element_count = field0_array_type.element_count abbreviation_reference = field0_array_element_count.field_reference.path[0] self.assertEqual(abbreviation_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(abbreviation_reference.canonical_name.module_file, "happy.emb") field1_array_type = struct_ir.field[1].type.array_type direct_reference = field1_array_type.element_count.field_reference.path[0] self.assertEqual(direct_reference.canonical_name.object_path, ["FieldRef", "offset"]) self.assertEqual(direct_reference.canonical_name.module_file, "happy.emb") def test_struct_parameter_resolution(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[6].structure size_ir = struct_ir.field[0].location.size self.assertTrue(size_ir.HasField("field_reference")) self.assertEqual(size_ir.field_reference.path[0].canonical_name.object_path, ["UsesParameter", "x"]) def test_enum_value_resolution_in_expression_in_enum_field(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) enum_ir = ir.module[0].type[5].enumeration value_reference = enum_ir.value[1].value.constant_reference self.assertEqual(value_reference.canonical_name.object_path, ["Quux", "ABC"]) self.assertEqual(value_reference.canonical_name.module_file, "happy.emb") def test_symbol_resolution_in_expression_in_void_array_length(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) struct_ir = ir.module[0].type[4].structure array_type = struct_ir.field[0].type.array_type # The symbol resolver should ignore void fields. self.assertEqual("automatic", array_type.WhichOneof("size")) def test_name_definitions_have_correct_canonical_names(self): ir = self._construct_ir(_HAPPY_EMB) self.assertEqual([], symbol_resolver.resolve_symbols(ir)) foo_name = ir.module[0].type[0].name self.assertEqual(foo_name.canonical_name.object_path, ["Foo"]) self.assertEqual(foo_name.canonical_name.module_file, "happy.emb") uint_field_name = ir.module[0].type[0].structure.field[0].name self.assertEqual(uint_field_name.canonical_name.object_path, ["Foo", "uint_field"]) self.assertEqual(uint_field_name.canonical_name.module_file, "happy.emb") foo_name = ir.module[0].type[2].name self.assertEqual(foo_name.canonical_name.object_path, ["Qux"]) self.assertEqual(foo_name.canonical_name.module_file, "happy.emb") def test_duplicate_type_name(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] UInt field\n" "struct Foo:\n" " 0 [+4] UInt bar\n", "duplicate_type.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) self.assertEqual([ [error.error("duplicate_type.emb", ir.module[0].type[1].name.source_location, "Duplicate name 'Foo'"), error.note("duplicate_type.emb", ir.module[0].type[0].name.source_location, "Original definition")] ], errors) def test_duplicate_field_name_in_struct(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] UInt field\n" " 4 [+4] UInt field\n", "duplicate_field.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) struct = ir.module[0].type[0].structure self.assertEqual([[ error.error("duplicate_field.emb", struct.field[1].name.source_location, "Duplicate name 'field'"), error.note("duplicate_field.emb", struct.field[0].name.source_location, "Original definition") ]], errors) def test_duplicate_abbreviation_in_struct(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] UInt field1 (f)\n" " 4 [+4] UInt field2 (f)\n", "duplicate_field.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) struct = ir.module[0].type[0].structure self.assertEqual([[ error.error("duplicate_field.emb", struct.field[1].abbreviation.source_location, "Duplicate name 'f'"), error.note("duplicate_field.emb", struct.field[0].abbreviation.source_location, "Original definition") ]], errors) def test_abbreviation_duplicates_field_name_in_struct(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] UInt field\n" " 4 [+4] UInt field2 (field)\n", "duplicate_field.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) struct = ir.module[0].type[0].structure self.assertEqual([[ error.error("duplicate_field.emb", struct.field[1].abbreviation.source_location, "Duplicate name 'field'"), error.note("duplicate_field.emb", struct.field[0].name.source_location, "Original definition") ]], errors) def test_field_name_duplicates_abbreviation_in_struct(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] UInt field (field2)\n" " 4 [+4] UInt field2\n", "duplicate_field.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) struct = ir.module[0].type[0].structure self.assertEqual([[ error.error("duplicate_field.emb", struct.field[1].name.source_location, "Duplicate name 'field2'"), error.note("duplicate_field.emb", struct.field[0].abbreviation.source_location, "Original definition") ]], errors) def test_duplicate_value_name_in_enum(self): ir = self._construct_ir("enum Foo:\n" " BAR = 1\n" " BAR = 1\n", "duplicate_enum.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) self.assertEqual([[ error.error( "duplicate_enum.emb", ir.module[0].type[0].enumeration.value[1].name.source_location, "Duplicate name 'BAR'"), error.note( "duplicate_enum.emb", ir.module[0].type[0].enumeration.value[0].name.source_location, "Original definition") ]], errors) def test_ambiguous_name(self): # struct UInt will be ambiguous with the external UInt in the prelude. ir = self._construct_ir("struct UInt:\n" " 0 [+4] Int:8[4] field\n" "struct Foo:\n" " 0 [+4] UInt bar\n", "ambiguous.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) # Find the UInt definition in the prelude. for type_ir in ir.module[1].type: if type_ir.name.name.text == "UInt": prelude_uint = type_ir break ambiguous_type_ir = ir.module[0].type[1].structure.field[0].type.atomic_type self.assertEqual([[ error.error("ambiguous.emb", ambiguous_type_ir.reference.source_name[0].source_location, "Ambiguous name 'UInt'"), error.note( "", prelude_uint.name.source_location, "Possible resolution"), error.note("ambiguous.emb", ir.module[0].type[0].name.source_location, "Possible resolution") ]], errors) def test_missing_name(self): ir = self._construct_ir("struct Foo:\n" " 0 [+4] Bar field\n", "missing.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) missing_type_ir = ir.module[0].type[0].structure.field[0].type.atomic_type self.assertEqual([ [error.error("missing.emb", missing_type_ir.reference.source_name[0].source_location, "No candidate for 'Bar'")] ], errors) def test_missing_leading_name(self): ir = self._construct_ir("struct Foo:\n" " 0 [+Num.FOUR] UInt field\n", "missing.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size self.assertEqual([ [error.error( "missing.emb", missing_expr_ir.constant_reference.source_name[0].source_location, "No candidate for 'Num'")] ], errors) def test_missing_trailing_name(self): ir = self._construct_ir("struct Foo:\n" " 0 [+Num.FOUR] UInt field\n" "enum Num:\n" " THREE = 3\n", "missing.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size self.assertEqual([ [error.error( "missing.emb", missing_expr_ir.constant_reference.source_name[1].source_location, "No candidate for 'FOUR'")] ], errors) def test_missing_middle_name(self): ir = self._construct_ir("struct Foo:\n" " 0 [+Num.NaN.FOUR] UInt field\n" "enum Num:\n" " FOUR = 4\n", "missing.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size self.assertEqual([ [error.error( "missing.emb", missing_expr_ir.constant_reference.source_name[1].source_location, "No candidate for 'NaN'")] ], errors) def test_inner_resolution(self): ir = self._construct_ir( "struct OuterStruct:\n" "\n" " struct InnerStruct2:\n" " 0 [+1] InnerStruct.InnerEnum inner_enum\n" "\n" " struct InnerStruct:\n" " enum InnerEnum:\n" " ONE = 1\n" "\n" " 0 [+1] InnerEnum inner_enum\n" "\n" " 0 [+InnerStruct.InnerEnum.ONE] InnerStruct.InnerEnum inner_enum\n", "nested.emb") errors = symbol_resolver.resolve_symbols(ir) self.assertFalse(errors) outer_struct = ir.module[0].type[0] inner_struct = outer_struct.subtype[1] inner_struct_2 = outer_struct.subtype[0] inner_enum = inner_struct.subtype[0] self.assertEqual(["OuterStruct", "InnerStruct"], list(inner_struct.name.canonical_name.object_path)) self.assertEqual(["OuterStruct", "InnerStruct", "InnerEnum"], list(inner_enum.name.canonical_name.object_path)) self.assertEqual(["OuterStruct", "InnerStruct2"], list(inner_struct_2.name.canonical_name.object_path)) outer_field = outer_struct.structure.field[0] outer_field_end_ref = outer_field.location.size.constant_reference self.assertEqual( ["OuterStruct", "InnerStruct", "InnerEnum", "ONE"], list( outer_field_end_ref.canonical_name.object_path)) self.assertEqual( ["OuterStruct", "InnerStruct", "InnerEnum"], list(outer_field.type.atomic_type.reference.canonical_name.object_path)) inner_field_2_type = inner_struct_2.structure.field[0].type.atomic_type self.assertEqual( ["OuterStruct", "InnerStruct", "InnerEnum" ], list(inner_field_2_type.reference.canonical_name.object_path)) def test_resolution_against_anonymous_bits(self): ir = self._construct_ir("struct Struct:\n" " 0 [+1] bits:\n" " 7 [+1] Flag last_packet\n" " 5 [+2] enum inline_inner_enum:\n" " AA = 0\n" " BB = 1\n" " CC = 2\n" " DD = 3\n" " 0 [+5] UInt header_size (h)\n" " 0 [+h] UInt:8[] header_bytes\n" "\n" "struct Struct2:\n" " 0 [+1] Struct.InlineInnerEnum value\n", "anonymity.emb") errors = symbol_resolver.resolve_symbols(ir) self.assertFalse(errors) struct1 = ir.module[0].type[0] struct1_bits_field = struct1.structure.field[0] struct1_bits_field_type = struct1_bits_field.type.atomic_type.reference struct1_byte_field = struct1.structure.field[4] inner_bits = struct1.subtype[0] inner_enum = struct1.subtype[1] self.assertTrue(inner_bits.HasField("structure")) self.assertTrue(inner_enum.HasField("enumeration")) self.assertTrue(inner_bits.name.is_anonymous) self.assertFalse(inner_enum.name.is_anonymous) self.assertEqual(["Struct", "InlineInnerEnum"], list(inner_enum.name.canonical_name.object_path)) self.assertEqual( ["Struct", "InlineInnerEnum", "AA"], list(inner_enum.enumeration.value[0].name.canonical_name.object_path)) self.assertEqual( list(inner_bits.name.canonical_name.object_path), list(struct1_bits_field_type.canonical_name.object_path)) self.assertEqual(2, len(inner_bits.name.canonical_name.object_path)) self.assertEqual( ["Struct", "header_size"], list(struct1_byte_field.location.size.field_reference.path[0]. canonical_name.object_path)) def test_duplicate_name_in_different_inline_bits(self): ir = self._construct_ir( "struct Struct:\n" " 0 [+1] bits:\n" " 7 [+1] Flag a\n" " 1 [+1] bits:\n" " 0 [+1] Flag a\n", "duplicate_in_anon.emb") errors = error.filter_errors(symbol_resolver.resolve_symbols(ir)) supertype = ir.module[0].type[0] self.assertEqual([[ error.error( "duplicate_in_anon.emb", supertype.structure.field[3].name.source_location, "Duplicate name 'a'"), error.note( "duplicate_in_anon.emb", supertype.structure.field[1].name.source_location, "Original definition") ]], errors) def test_duplicate_name_in_same_inline_bits(self): ir = self._construct_ir( "struct Struct:\n" " 0 [+1] bits:\n" " 7 [+1] Flag a\n" " 0 [+1] Flag a\n", "duplicate_in_anon.emb") errors = symbol_resolver.resolve_symbols(ir) supertype = ir.module[0].type[0] self.assertEqual([[ error.error( "duplicate_in_anon.emb", supertype.structure.field[2].name.source_location, "Duplicate name 'a'"), error.note( "duplicate_in_anon.emb", supertype.structure.field[1].name.source_location, "Original definition") ]], error.filter_errors(errors)) def test_import_type_resolution(self): importer = ('import "ed.emb" as ed\n' "struct Ff:\n" " 0 [+1] ed.Gg gg\n") imported = ("struct Gg:\n" " 0 [+1] UInt qq\n") ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer}, "er.emb") errors = symbol_resolver.resolve_symbols(ir) self.assertEqual([], errors) def test_duplicate_import_name(self): importer = ('import "ed.emb" as ed\n' 'import "ed.emb" as ed\n' "struct Ff:\n" " 0 [+1] ed.Gg gg\n") imported = ("struct Gg:\n" " 0 [+1] UInt qq\n") ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer}, "er.emb") errors = symbol_resolver.resolve_symbols(ir) # Note: the error is on import[2] duplicating import[1] because the implicit # prelude import is import[0]. self.assertEqual([ [error.error("er.emb", ir.module[0].foreign_import[2].local_name.source_location, "Duplicate name 'ed'"), error.note("er.emb", ir.module[0].foreign_import[1].local_name.source_location, "Original definition")] ], errors) def test_import_enum_resolution(self): importer = ('import "ed.emb" as ed\n' "struct Ff:\n" " if ed.Gg.GG == ed.Gg.GG:\n" " 0 [+1] UInt gg\n") imported = ("enum Gg:\n" " GG = 0\n") ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer}, "er.emb") errors = symbol_resolver.resolve_symbols(ir) self.assertEqual([], errors) def test_that_double_import_names_are_syntactically_invalid(self): # There are currently no checks in resolve_symbols that it is not possible # to get to symbols imported by another module, because it is syntactically # invalid. This may change in the future, in which case this test should be # fixed by adding an explicit check to resolve_symbols and checking the # error message here. importer = ('import "ed.emb" as ed\n' "struct Ff:\n" " 0 [+1] ed.ed2.Gg gg\n") imported = 'import "ed2.emb" as ed2\n' imported2 = ("struct Gg:\n" " 0 [+1] UInt qq\n") unused_ir, unused_debug_info, errors = glue.parse_emboss_file( "er.emb", test_util.dict_file_reader({"ed.emb": imported, "ed2.emb": imported2, "er.emb": importer}), stop_before_step="resolve_symbols") assert errors def test_no_error_when_inline_name_aliases_outer_name(self): # The inline enum's complete type should be Foo.Foo. During parsing, the # name is set to just "Foo", but symbol resolution should a) select the # correct Foo, and b) not complain that multiple Foos could match. ir = self._construct_ir( "struct Foo:\n" " 0 [+1] enum foo:\n" " BAR = 0\n") errors = symbol_resolver.resolve_symbols(ir) self.assertEqual([], errors) field = ir.module[0].type[0].structure.field[0] self.assertEqual( ["Foo", "Foo"], list(field.type.atomic_type.reference.canonical_name.object_path)) def test_no_error_when_inline_name_in_anonymous_bits_aliases_outer_name(self): # There is an extra layer of complexity when an inline type appears inside # of an inline bits. ir = self._construct_ir( "struct Foo:\n" " 0 [+1] bits:\n" " 0 [+4] enum foo:\n" " BAR = 0\n") errors = symbol_resolver.resolve_symbols(ir) self.assertEqual([], error.filter_errors(errors)) field = ir.module[0].type[0].subtype[0].structure.field[0] self.assertEqual( ["Foo", "Foo"], list(field.type.atomic_type.reference.canonical_name.object_path)) class ResolveFieldReferencesTest(unittest.TestCase): """Tests for symbol_resolver.resolve_field_references().""" def _construct_ir_multiple(self, file_dict, primary_emb_name): ir, unused_debug_info, errors = glue.parse_emboss_file( primary_emb_name, test_util.dict_file_reader(file_dict), stop_before_step="resolve_field_references") assert not errors return ir def _construct_ir(self, emb_text, name="happy.emb"): return self._construct_ir_multiple({name: emb_text}, name) def test_subfield_resolution(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg gg\n" " 1 [+gg.qq] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = symbol_resolver.resolve_field_references(ir) self.assertFalse(errors) ff = ir.module[0].type[0] location_end_path = ff.structure.field[1].location.size.field_reference.path self.assertEqual(["Ff", "gg"], list(location_end_path[0].canonical_name.object_path)) self.assertEqual(["Gg", "qq"], list(location_end_path[1].canonical_name.object_path)) def test_aliased_subfield_resolution(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg real_gg\n" " 1 [+gg.qq] UInt:8[] data\n" " let gg = real_gg\n" "struct Gg:\n" " 0 [+1] UInt real_qq\n" " let qq = real_qq", "subfield.emb") errors = symbol_resolver.resolve_field_references(ir) self.assertFalse(errors) ff = ir.module[0].type[0] location_end_path = ff.structure.field[1].location.size.field_reference.path self.assertEqual(["Ff", "gg"], list(location_end_path[0].canonical_name.object_path)) self.assertEqual(["Gg", "qq"], list(location_end_path[1].canonical_name.object_path)) def test_aliased_aliased_subfield_resolution(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg really_real_gg\n" " 1 [+gg.qq] UInt:8[] data\n" " let gg = real_gg\n" " let real_gg = really_real_gg\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = symbol_resolver.resolve_field_references(ir) self.assertFalse(errors) ff = ir.module[0].type[0] location_end_path = ff.structure.field[1].location.size.field_reference.path self.assertEqual(["Ff", "gg"], list(location_end_path[0].canonical_name.object_path)) self.assertEqual(["Gg", "qq"], list(location_end_path[1].canonical_name.object_path)) def test_subfield_resolution_fails(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg gg\n" " 1 [+gg.rr] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) self.assertEqual([ [error.error("subfield.emb", ir.module[0].type[0].structure.field[ 1].location.size.field_reference.path[1].source_name[ 0].source_location, "No candidate for 'rr'")] ], errors) def test_subfield_resolution_failure_shortcuts_further_resolution(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg gg\n" " 1 [+gg.rr.qq] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) self.assertEqual([ [error.error("subfield.emb", ir.module[0].type[0].structure.field[ 1].location.size.field_reference.path[1].source_name[ 0].source_location, "No candidate for 'rr'")] ], errors) def test_subfield_resolution_failure_with_aliased_name(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg gg\n" " 1 [+gg.gg] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) self.assertEqual([ [error.error("subfield.emb", ir.module[0].type[0].structure.field[ 1].location.size.field_reference.path[1].source_name[ 0].source_location, "No candidate for 'gg'")] ], errors) def test_subfield_resolution_failure_with_array(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg[1] gg\n" " 1 [+gg.qq] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) self.assertEqual([ [error.error("subfield.emb", ir.module[0].type[0].structure.field[ 1].location.size.field_reference.path[0].source_name[ 0].source_location, "Cannot access member of array 'gg'")] ], errors) def test_subfield_resolution_failure_with_int(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] UInt gg_source\n" " 1 [+gg.qq] UInt:8[] data\n" " let gg = gg_source + 1\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) error_field = ir.module[0].type[0].structure.field[1] error_reference = error_field.location.size.field_reference error_location = error_reference.path[0].source_name[0].source_location self.assertEqual([ [error.error("subfield.emb", error_location, "Cannot access member of noncomposite field 'gg'")] ], errors) def test_subfield_resolution_failure_with_int_no_cascade(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] UInt gg_source\n" " 1 [+qqx] UInt:8[] data\n" " let gg = gg_source + 1\n" " let yy = gg.no_field\n" " let qqx = yy.x\n" " let qqy = yy.y\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) error_field = ir.module[0].type[0].structure.field[3] error_reference = error_field.read_transform.field_reference error_location = error_reference.path[0].source_name[0].source_location self.assertEqual([ [error.error("subfield.emb", error_location, "Cannot access member of noncomposite field 'gg'")] ], errors) def test_subfield_resolution_failure_with_abbreviation(self): ir = self._construct_ir( "struct Ff:\n" " 0 [+1] Gg gg\n" " 1 [+gg.q] UInt:8[] data\n" "struct Gg:\n" " 0 [+1] UInt qq (q)\n", "subfield.emb") errors = error.filter_errors(symbol_resolver.resolve_field_references(ir)) self.assertEqual([ # TODO(bolms): Make the error message clearer, in this case. [error.error("subfield.emb", ir.module[0].type[0].structure.field[ 1].location.size.field_reference.path[1].source_name[ 0].source_location, "No candidate for 'q'")] ], errors) if __name__ == "__main__": unittest.main()