blob: ddf7783cbcde4453697bd7a156873d9dfcbf1ea9 [file] [log] [blame]
# 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.which_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()