| # 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 front_end import glue |
| from front_end import symbol_resolver |
| from front_end import test_util |
| from util import error |
| |
| _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() |