xref: /aosp_15_r20/external/emboss/compiler/front_end/symbol_resolver_test.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for emboss.front_end.symbol_resolver."""
16
17import unittest
18from compiler.front_end import glue
19from compiler.front_end import symbol_resolver
20from compiler.util import error
21from compiler.util import test_util
22
23_HAPPY_EMB = """
24struct Foo:
25  0 [+4]  UInt    uint_field
26  4 [+4]  Bar     bar_field
27  8 [+16] UInt[4] array_field
28
29struct Bar:
30  0 [+4]  Qux     bar
31
32enum Qux:
33  ABC = 1
34  DEF = 2
35
36struct FieldRef:
37  n-4      [+n]       UInt:8[n]       data
38  offset-4 [+offset]  UInt:8[offset]  data2
39  0        [+4]       UInt            offset (n)
40
41struct VoidLength:
42  0 [+10]  UInt:8[]  ten_bytes
43
44enum Quux:
45  ABC = 1
46  DEF = ABC
47
48struct UsesParameter(x: UInt:8):
49  0 [+x]   UInt:8[]  block
50"""
51
52
53class ResolveSymbolsTest(unittest.TestCase):
54  """Tests for symbol_resolver.resolve_symbols()."""
55
56  def _construct_ir_multiple(self, file_dict, primary_emb_name):
57    ir, unused_debug_info, errors = glue.parse_emboss_file(
58        primary_emb_name,
59        test_util.dict_file_reader(file_dict),
60        stop_before_step="resolve_symbols")
61    assert not errors
62    return ir
63
64  def _construct_ir(self, emb_text, name="happy.emb"):
65    return self._construct_ir_multiple({name: emb_text}, name)
66
67  def test_struct_field_atomic_type_resolution(self):
68    ir = self._construct_ir(_HAPPY_EMB)
69    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
70    struct_ir = ir.module[0].type[0].structure
71    atomic_field1_reference = struct_ir.field[0].type.atomic_type.reference
72    self.assertEqual(atomic_field1_reference.canonical_name.object_path, ["UInt"
73                                                                         ])
74    self.assertEqual(atomic_field1_reference.canonical_name.module_file, "")
75    atomic_field2_reference = struct_ir.field[1].type.atomic_type.reference
76    self.assertEqual(atomic_field2_reference.canonical_name.object_path, ["Bar"
77                                                                         ])
78    self.assertEqual(atomic_field2_reference.canonical_name.module_file,
79                     "happy.emb")
80
81  def test_struct_field_enum_type_resolution(self):
82    ir = self._construct_ir(_HAPPY_EMB)
83    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
84    struct_ir = ir.module[0].type[1].structure
85    atomic_field_reference = struct_ir.field[0].type.atomic_type.reference
86    self.assertEqual(atomic_field_reference.canonical_name.object_path, ["Qux"])
87    self.assertEqual(atomic_field_reference.canonical_name.module_file,
88                     "happy.emb")
89
90  def test_struct_field_array_type_resolution(self):
91    ir = self._construct_ir(_HAPPY_EMB)
92    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
93    array_field_type = ir.module[0].type[0].structure.field[2].type.array_type
94    array_field_reference = array_field_type.base_type.atomic_type.reference
95    self.assertEqual(array_field_reference.canonical_name.object_path, ["UInt"])
96    self.assertEqual(array_field_reference.canonical_name.module_file, "")
97
98  def test_inner_type_resolution(self):
99    ir = self._construct_ir(_HAPPY_EMB)
100    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
101    array_field_type = ir.module[0].type[0].structure.field[2].type.array_type
102    array_field_reference = array_field_type.base_type.atomic_type.reference
103    self.assertEqual(array_field_reference.canonical_name.object_path, ["UInt"])
104    self.assertEqual(array_field_reference.canonical_name.module_file, "")
105
106  def test_struct_field_resolution_in_expression_in_location(self):
107    ir = self._construct_ir(_HAPPY_EMB)
108    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
109    struct_ir = ir.module[0].type[3].structure
110    field0_loc = struct_ir.field[0].location
111    abbreviation_reference = field0_loc.size.field_reference.path[0]
112    self.assertEqual(abbreviation_reference.canonical_name.object_path,
113                     ["FieldRef", "offset"])
114    self.assertEqual(abbreviation_reference.canonical_name.module_file,
115                     "happy.emb")
116    field0_start_left = field0_loc.start.function.args[0]
117    nested_abbreviation_reference = field0_start_left.field_reference.path[0]
118    self.assertEqual(nested_abbreviation_reference.canonical_name.object_path,
119                     ["FieldRef", "offset"])
120    self.assertEqual(nested_abbreviation_reference.canonical_name.module_file,
121                     "happy.emb")
122    field1_loc = struct_ir.field[1].location
123    direct_reference = field1_loc.size.field_reference.path[0]
124    self.assertEqual(direct_reference.canonical_name.object_path, ["FieldRef",
125                                                                   "offset"])
126    self.assertEqual(direct_reference.canonical_name.module_file, "happy.emb")
127    field1_start_left = field1_loc.start.function.args[0]
128    nested_direct_reference = field1_start_left.field_reference.path[0]
129    self.assertEqual(nested_direct_reference.canonical_name.object_path,
130                     ["FieldRef", "offset"])
131    self.assertEqual(nested_direct_reference.canonical_name.module_file,
132                     "happy.emb")
133
134  def test_struct_field_resolution_in_expression_in_array_length(self):
135    ir = self._construct_ir(_HAPPY_EMB)
136    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
137    struct_ir = ir.module[0].type[3].structure
138    field0_array_type = struct_ir.field[0].type.array_type
139    field0_array_element_count = field0_array_type.element_count
140    abbreviation_reference = field0_array_element_count.field_reference.path[0]
141    self.assertEqual(abbreviation_reference.canonical_name.object_path,
142                     ["FieldRef", "offset"])
143    self.assertEqual(abbreviation_reference.canonical_name.module_file,
144                     "happy.emb")
145    field1_array_type = struct_ir.field[1].type.array_type
146    direct_reference = field1_array_type.element_count.field_reference.path[0]
147    self.assertEqual(direct_reference.canonical_name.object_path, ["FieldRef",
148                                                                   "offset"])
149    self.assertEqual(direct_reference.canonical_name.module_file, "happy.emb")
150
151  def test_struct_parameter_resolution(self):
152    ir = self._construct_ir(_HAPPY_EMB)
153    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
154    struct_ir = ir.module[0].type[6].structure
155    size_ir = struct_ir.field[0].location.size
156    self.assertTrue(size_ir.HasField("field_reference"))
157    self.assertEqual(size_ir.field_reference.path[0].canonical_name.object_path,
158                     ["UsesParameter", "x"])
159
160  def test_enum_value_resolution_in_expression_in_enum_field(self):
161    ir = self._construct_ir(_HAPPY_EMB)
162    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
163    enum_ir = ir.module[0].type[5].enumeration
164    value_reference = enum_ir.value[1].value.constant_reference
165    self.assertEqual(value_reference.canonical_name.object_path,
166                     ["Quux", "ABC"])
167    self.assertEqual(value_reference.canonical_name.module_file, "happy.emb")
168
169  def test_symbol_resolution_in_expression_in_void_array_length(self):
170    ir = self._construct_ir(_HAPPY_EMB)
171    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
172    struct_ir = ir.module[0].type[4].structure
173    array_type = struct_ir.field[0].type.array_type
174    # The symbol resolver should ignore void fields.
175    self.assertEqual("automatic", array_type.WhichOneof("size"))
176
177  def test_name_definitions_have_correct_canonical_names(self):
178    ir = self._construct_ir(_HAPPY_EMB)
179    self.assertEqual([], symbol_resolver.resolve_symbols(ir))
180    foo_name = ir.module[0].type[0].name
181    self.assertEqual(foo_name.canonical_name.object_path, ["Foo"])
182    self.assertEqual(foo_name.canonical_name.module_file, "happy.emb")
183    uint_field_name = ir.module[0].type[0].structure.field[0].name
184    self.assertEqual(uint_field_name.canonical_name.object_path, ["Foo",
185                                                                  "uint_field"])
186    self.assertEqual(uint_field_name.canonical_name.module_file, "happy.emb")
187    foo_name = ir.module[0].type[2].name
188    self.assertEqual(foo_name.canonical_name.object_path, ["Qux"])
189    self.assertEqual(foo_name.canonical_name.module_file, "happy.emb")
190
191  def test_duplicate_type_name(self):
192    ir = self._construct_ir("struct Foo:\n"
193                            "  0 [+4]  UInt  field\n"
194                            "struct Foo:\n"
195                            "  0 [+4]  UInt  bar\n", "duplicate_type.emb")
196    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
197    self.assertEqual([
198        [error.error("duplicate_type.emb",
199                     ir.module[0].type[1].name.source_location,
200                     "Duplicate name 'Foo'"),
201         error.note("duplicate_type.emb",
202                    ir.module[0].type[0].name.source_location,
203                    "Original definition")]
204    ], errors)
205
206  def test_duplicate_field_name_in_struct(self):
207    ir = self._construct_ir("struct Foo:\n"
208                            "  0 [+4]  UInt  field\n"
209                            "  4 [+4]  UInt  field\n", "duplicate_field.emb")
210    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
211    struct = ir.module[0].type[0].structure
212    self.assertEqual([[
213        error.error("duplicate_field.emb",
214                    struct.field[1].name.source_location,
215                    "Duplicate name 'field'"),
216        error.note("duplicate_field.emb",
217                   struct.field[0].name.source_location,
218                   "Original definition")
219    ]], errors)
220
221  def test_duplicate_abbreviation_in_struct(self):
222    ir = self._construct_ir("struct Foo:\n"
223                            "  0 [+4]  UInt  field1 (f)\n"
224                            "  4 [+4]  UInt  field2 (f)\n",
225                            "duplicate_field.emb")
226    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
227    struct = ir.module[0].type[0].structure
228    self.assertEqual([[
229        error.error("duplicate_field.emb",
230                    struct.field[1].abbreviation.source_location,
231                    "Duplicate name 'f'"),
232        error.note("duplicate_field.emb",
233                   struct.field[0].abbreviation.source_location,
234                   "Original definition")
235    ]], errors)
236
237  def test_abbreviation_duplicates_field_name_in_struct(self):
238    ir = self._construct_ir("struct Foo:\n"
239                            "  0 [+4]  UInt  field\n"
240                            "  4 [+4]  UInt  field2 (field)\n",
241                            "duplicate_field.emb")
242    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
243    struct = ir.module[0].type[0].structure
244    self.assertEqual([[
245        error.error("duplicate_field.emb",
246                    struct.field[1].abbreviation.source_location,
247                    "Duplicate name 'field'"),
248        error.note("duplicate_field.emb",
249                   struct.field[0].name.source_location,
250                   "Original definition")
251    ]], errors)
252
253  def test_field_name_duplicates_abbreviation_in_struct(self):
254    ir = self._construct_ir("struct Foo:\n"
255                            "  0 [+4]  UInt  field (field2)\n"
256                            "  4 [+4]  UInt  field2\n", "duplicate_field.emb")
257    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
258    struct = ir.module[0].type[0].structure
259    self.assertEqual([[
260        error.error("duplicate_field.emb",
261                    struct.field[1].name.source_location,
262                    "Duplicate name 'field2'"),
263        error.note("duplicate_field.emb",
264                   struct.field[0].abbreviation.source_location,
265                   "Original definition")
266    ]], errors)
267
268  def test_duplicate_value_name_in_enum(self):
269    ir = self._construct_ir("enum Foo:\n"
270                            "  BAR = 1\n"
271                            "  BAR = 1\n", "duplicate_enum.emb")
272    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
273    self.assertEqual([[
274        error.error(
275            "duplicate_enum.emb",
276            ir.module[0].type[0].enumeration.value[1].name.source_location,
277            "Duplicate name 'BAR'"),
278        error.note(
279            "duplicate_enum.emb",
280            ir.module[0].type[0].enumeration.value[0].name.source_location,
281            "Original definition")
282    ]], errors)
283
284  def test_ambiguous_name(self):
285    # struct UInt will be ambiguous with the external UInt in the prelude.
286    ir = self._construct_ir("struct UInt:\n"
287                            "  0 [+4]  Int:8[4]  field\n"
288                            "struct Foo:\n"
289                            "  0 [+4]  UInt  bar\n", "ambiguous.emb")
290    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
291    # Find the UInt definition in the prelude.
292    for type_ir in ir.module[1].type:
293      if type_ir.name.name.text == "UInt":
294        prelude_uint = type_ir
295        break
296    ambiguous_type_ir = ir.module[0].type[1].structure.field[0].type.atomic_type
297    self.assertEqual([[
298        error.error("ambiguous.emb",
299                    ambiguous_type_ir.reference.source_name[0].source_location,
300                    "Ambiguous name 'UInt'"), error.note(
301                        "", prelude_uint.name.source_location,
302                        "Possible resolution"),
303        error.note("ambiguous.emb", ir.module[0].type[0].name.source_location,
304                   "Possible resolution")
305    ]], errors)
306
307  def test_missing_name(self):
308    ir = self._construct_ir("struct Foo:\n"
309                            "  0 [+4]  Bar  field\n",
310                            "missing.emb")
311    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
312    missing_type_ir = ir.module[0].type[0].structure.field[0].type.atomic_type
313    self.assertEqual([
314        [error.error("missing.emb",
315                     missing_type_ir.reference.source_name[0].source_location,
316                     "No candidate for 'Bar'")]
317    ], errors)
318
319  def test_missing_leading_name(self):
320    ir = self._construct_ir("struct Foo:\n"
321                            "  0 [+Num.FOUR]  UInt  field\n", "missing.emb")
322    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
323    missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size
324    self.assertEqual([
325        [error.error(
326            "missing.emb",
327            missing_expr_ir.constant_reference.source_name[0].source_location,
328            "No candidate for 'Num'")]
329    ], errors)
330
331  def test_missing_trailing_name(self):
332    ir = self._construct_ir("struct Foo:\n"
333                            "  0 [+Num.FOUR]  UInt  field\n"
334                            "enum Num:\n"
335                            "  THREE = 3\n", "missing.emb")
336    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
337    missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size
338    self.assertEqual([
339        [error.error(
340            "missing.emb",
341            missing_expr_ir.constant_reference.source_name[1].source_location,
342            "No candidate for 'FOUR'")]
343    ], errors)
344
345  def test_missing_middle_name(self):
346    ir = self._construct_ir("struct Foo:\n"
347                            "  0 [+Num.NaN.FOUR]  UInt  field\n"
348                            "enum Num:\n"
349                            "  FOUR = 4\n", "missing.emb")
350    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
351    missing_expr_ir = ir.module[0].type[0].structure.field[0].location.size
352    self.assertEqual([
353        [error.error(
354            "missing.emb",
355            missing_expr_ir.constant_reference.source_name[1].source_location,
356            "No candidate for 'NaN'")]
357    ], errors)
358
359  def test_inner_resolution(self):
360    ir = self._construct_ir(
361        "struct OuterStruct:\n"
362        "\n"
363        "  struct InnerStruct2:\n"
364        "    0 [+1]  InnerStruct.InnerEnum  inner_enum\n"
365        "\n"
366        "  struct InnerStruct:\n"
367        "    enum InnerEnum:\n"
368        "      ONE = 1\n"
369        "\n"
370        "    0 [+1]  InnerEnum  inner_enum\n"
371        "\n"
372        "  0 [+InnerStruct.InnerEnum.ONE]  InnerStruct.InnerEnum  inner_enum\n",
373        "nested.emb")
374    errors = symbol_resolver.resolve_symbols(ir)
375    self.assertFalse(errors)
376    outer_struct = ir.module[0].type[0]
377    inner_struct = outer_struct.subtype[1]
378    inner_struct_2 = outer_struct.subtype[0]
379    inner_enum = inner_struct.subtype[0]
380    self.assertEqual(["OuterStruct", "InnerStruct"],
381                     list(inner_struct.name.canonical_name.object_path))
382    self.assertEqual(["OuterStruct", "InnerStruct", "InnerEnum"],
383                     list(inner_enum.name.canonical_name.object_path))
384    self.assertEqual(["OuterStruct", "InnerStruct2"],
385                     list(inner_struct_2.name.canonical_name.object_path))
386    outer_field = outer_struct.structure.field[0]
387    outer_field_end_ref = outer_field.location.size.constant_reference
388    self.assertEqual(
389        ["OuterStruct", "InnerStruct", "InnerEnum", "ONE"], list(
390            outer_field_end_ref.canonical_name.object_path))
391    self.assertEqual(
392        ["OuterStruct", "InnerStruct", "InnerEnum"],
393        list(outer_field.type.atomic_type.reference.canonical_name.object_path))
394    inner_field_2_type = inner_struct_2.structure.field[0].type.atomic_type
395    self.assertEqual(
396        ["OuterStruct", "InnerStruct", "InnerEnum"
397        ], list(inner_field_2_type.reference.canonical_name.object_path))
398
399  def test_resolution_against_anonymous_bits(self):
400    ir = self._construct_ir("struct Struct:\n"
401                            "  0 [+1]  bits:\n"
402                            "    7 [+1]  Flag  last_packet\n"
403                            "    5 [+2]  enum  inline_inner_enum:\n"
404                            "      AA = 0\n"
405                            "      BB = 1\n"
406                            "      CC = 2\n"
407                            "      DD = 3\n"
408                            "    0 [+5]  UInt  header_size (h)\n"
409                            "  0 [+h]  UInt:8[]  header_bytes\n"
410                            "\n"
411                            "struct Struct2:\n"
412                            "  0 [+1]  Struct.InlineInnerEnum  value\n",
413                            "anonymity.emb")
414    errors = symbol_resolver.resolve_symbols(ir)
415    self.assertFalse(errors)
416    struct1 = ir.module[0].type[0]
417    struct1_bits_field = struct1.structure.field[0]
418    struct1_bits_field_type = struct1_bits_field.type.atomic_type.reference
419    struct1_byte_field = struct1.structure.field[4]
420    inner_bits = struct1.subtype[0]
421    inner_enum = struct1.subtype[1]
422    self.assertTrue(inner_bits.HasField("structure"))
423    self.assertTrue(inner_enum.HasField("enumeration"))
424    self.assertTrue(inner_bits.name.is_anonymous)
425    self.assertFalse(inner_enum.name.is_anonymous)
426    self.assertEqual(["Struct", "InlineInnerEnum"],
427                     list(inner_enum.name.canonical_name.object_path))
428    self.assertEqual(
429        ["Struct", "InlineInnerEnum", "AA"],
430        list(inner_enum.enumeration.value[0].name.canonical_name.object_path))
431    self.assertEqual(
432        list(inner_bits.name.canonical_name.object_path),
433        list(struct1_bits_field_type.canonical_name.object_path))
434    self.assertEqual(2, len(inner_bits.name.canonical_name.object_path))
435    self.assertEqual(
436        ["Struct", "header_size"],
437        list(struct1_byte_field.location.size.field_reference.path[0].
438             canonical_name.object_path))
439
440  def test_duplicate_name_in_different_inline_bits(self):
441    ir = self._construct_ir(
442        "struct Struct:\n"
443        "  0 [+1]  bits:\n"
444        "    7 [+1]  Flag  a\n"
445        "  1 [+1]  bits:\n"
446        "    0 [+1]  Flag  a\n", "duplicate_in_anon.emb")
447    errors = error.filter_errors(symbol_resolver.resolve_symbols(ir))
448    supertype = ir.module[0].type[0]
449    self.assertEqual([[
450        error.error(
451            "duplicate_in_anon.emb",
452            supertype.structure.field[3].name.source_location,
453            "Duplicate name 'a'"),
454        error.note(
455            "duplicate_in_anon.emb",
456            supertype.structure.field[1].name.source_location,
457            "Original definition")
458    ]], errors)
459
460  def test_duplicate_name_in_same_inline_bits(self):
461    ir = self._construct_ir(
462        "struct Struct:\n"
463        "  0 [+1]  bits:\n"
464        "    7 [+1]  Flag  a\n"
465        "    0 [+1]  Flag  a\n", "duplicate_in_anon.emb")
466    errors = symbol_resolver.resolve_symbols(ir)
467    supertype = ir.module[0].type[0]
468    self.assertEqual([[
469        error.error(
470            "duplicate_in_anon.emb",
471            supertype.structure.field[2].name.source_location,
472            "Duplicate name 'a'"),
473        error.note(
474            "duplicate_in_anon.emb",
475            supertype.structure.field[1].name.source_location,
476            "Original definition")
477    ]], error.filter_errors(errors))
478
479  def test_import_type_resolution(self):
480    importer = ('import "ed.emb" as ed\n'
481                "struct Ff:\n"
482                "  0 [+1]  ed.Gg  gg\n")
483    imported = ("struct Gg:\n"
484                "  0 [+1]  UInt  qq\n")
485    ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer},
486                                     "er.emb")
487    errors = symbol_resolver.resolve_symbols(ir)
488    self.assertEqual([], errors)
489
490  def test_duplicate_import_name(self):
491    importer = ('import "ed.emb" as ed\n'
492                'import "ed.emb" as ed\n'
493                "struct Ff:\n"
494                "  0 [+1]  ed.Gg  gg\n")
495    imported = ("struct Gg:\n"
496                "  0 [+1]  UInt  qq\n")
497    ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer},
498                                     "er.emb")
499    errors = symbol_resolver.resolve_symbols(ir)
500    # Note: the error is on import[2] duplicating import[1] because the implicit
501    # prelude import is import[0].
502    self.assertEqual([
503        [error.error("er.emb",
504                     ir.module[0].foreign_import[2].local_name.source_location,
505                     "Duplicate name 'ed'"),
506         error.note("er.emb",
507                    ir.module[0].foreign_import[1].local_name.source_location,
508                    "Original definition")]
509    ], errors)
510
511  def test_import_enum_resolution(self):
512    importer = ('import "ed.emb" as ed\n'
513                "struct Ff:\n"
514                "  if ed.Gg.GG == ed.Gg.GG:\n"
515                "    0 [+1]  UInt  gg\n")
516    imported = ("enum Gg:\n"
517                "  GG = 0\n")
518    ir = self._construct_ir_multiple({"ed.emb": imported, "er.emb": importer},
519                                     "er.emb")
520    errors = symbol_resolver.resolve_symbols(ir)
521    self.assertEqual([], errors)
522
523  def test_that_double_import_names_are_syntactically_invalid(self):
524    # There are currently no checks in resolve_symbols that it is not possible
525    # to get to symbols imported by another module, because it is syntactically
526    # invalid.  This may change in the future, in which case this test should be
527    # fixed by adding an explicit check to resolve_symbols and checking the
528    # error message here.
529    importer = ('import "ed.emb" as ed\n'
530                "struct Ff:\n"
531                "  0 [+1]  ed.ed2.Gg  gg\n")
532    imported = 'import "ed2.emb" as ed2\n'
533    imported2 = ("struct Gg:\n"
534                 "  0 [+1]  UInt  qq\n")
535    unused_ir, unused_debug_info, errors = glue.parse_emboss_file(
536        "er.emb",
537        test_util.dict_file_reader({"ed.emb": imported,
538                                    "ed2.emb": imported2,
539                                    "er.emb": importer}),
540        stop_before_step="resolve_symbols")
541    assert errors
542
543  def test_no_error_when_inline_name_aliases_outer_name(self):
544    # The inline enum's complete type should be Foo.Foo.  During parsing, the
545    # name is set to just "Foo", but symbol resolution should a) select the
546    # correct Foo, and b) not complain that multiple Foos could match.
547    ir = self._construct_ir(
548        "struct Foo:\n"
549        "  0 [+1]  enum  foo:\n"
550        "    BAR = 0\n")
551    errors = symbol_resolver.resolve_symbols(ir)
552    self.assertEqual([], errors)
553    field = ir.module[0].type[0].structure.field[0]
554    self.assertEqual(
555        ["Foo", "Foo"],
556        list(field.type.atomic_type.reference.canonical_name.object_path))
557
558  def test_no_error_when_inline_name_in_anonymous_bits_aliases_outer_name(self):
559    # There is an extra layer of complexity when an inline type appears inside
560    # of an inline bits.
561    ir = self._construct_ir(
562        "struct Foo:\n"
563        "  0 [+1]  bits:\n"
564        "    0 [+4]  enum  foo:\n"
565        "      BAR = 0\n")
566    errors = symbol_resolver.resolve_symbols(ir)
567    self.assertEqual([], error.filter_errors(errors))
568    field = ir.module[0].type[0].subtype[0].structure.field[0]
569    self.assertEqual(
570        ["Foo", "Foo"],
571        list(field.type.atomic_type.reference.canonical_name.object_path))
572
573
574class ResolveFieldReferencesTest(unittest.TestCase):
575  """Tests for symbol_resolver.resolve_field_references()."""
576
577  def _construct_ir_multiple(self, file_dict, primary_emb_name):
578    ir, unused_debug_info, errors = glue.parse_emboss_file(
579        primary_emb_name,
580        test_util.dict_file_reader(file_dict),
581        stop_before_step="resolve_field_references")
582    assert not errors
583    return ir
584
585  def _construct_ir(self, emb_text, name="happy.emb"):
586    return self._construct_ir_multiple({name: emb_text}, name)
587
588  def test_subfield_resolution(self):
589    ir = self._construct_ir(
590        "struct Ff:\n"
591        "  0 [+1]      Gg        gg\n"
592        "  1 [+gg.qq]  UInt:8[]  data\n"
593        "struct Gg:\n"
594        "  0 [+1]      UInt    qq\n", "subfield.emb")
595    errors = symbol_resolver.resolve_field_references(ir)
596    self.assertFalse(errors)
597    ff = ir.module[0].type[0]
598    location_end_path = ff.structure.field[1].location.size.field_reference.path
599    self.assertEqual(["Ff", "gg"],
600                     list(location_end_path[0].canonical_name.object_path))
601    self.assertEqual(["Gg", "qq"],
602                     list(location_end_path[1].canonical_name.object_path))
603
604  def test_aliased_subfield_resolution(self):
605    ir = self._construct_ir(
606        "struct Ff:\n"
607        "  0 [+1]      Gg        real_gg\n"
608        "  1 [+gg.qq]  UInt:8[]  data\n"
609        "  let gg = real_gg\n"
610        "struct Gg:\n"
611        "  0 [+1]      UInt    real_qq\n"
612        "  let qq = real_qq", "subfield.emb")
613    errors = symbol_resolver.resolve_field_references(ir)
614    self.assertFalse(errors)
615    ff = ir.module[0].type[0]
616    location_end_path = ff.structure.field[1].location.size.field_reference.path
617    self.assertEqual(["Ff", "gg"],
618                     list(location_end_path[0].canonical_name.object_path))
619    self.assertEqual(["Gg", "qq"],
620                     list(location_end_path[1].canonical_name.object_path))
621
622  def test_aliased_aliased_subfield_resolution(self):
623    ir = self._construct_ir(
624        "struct Ff:\n"
625        "  0 [+1]      Gg        really_real_gg\n"
626        "  1 [+gg.qq]  UInt:8[]  data\n"
627        "  let gg = real_gg\n"
628        "  let real_gg = really_real_gg\n"
629        "struct Gg:\n"
630        "  0 [+1]      UInt    qq\n", "subfield.emb")
631    errors = symbol_resolver.resolve_field_references(ir)
632    self.assertFalse(errors)
633    ff = ir.module[0].type[0]
634    location_end_path = ff.structure.field[1].location.size.field_reference.path
635    self.assertEqual(["Ff", "gg"],
636                     list(location_end_path[0].canonical_name.object_path))
637    self.assertEqual(["Gg", "qq"],
638                     list(location_end_path[1].canonical_name.object_path))
639
640  def test_subfield_resolution_fails(self):
641    ir = self._construct_ir(
642        "struct Ff:\n"
643        "  0 [+1]      Gg        gg\n"
644        "  1 [+gg.rr]  UInt:8[]  data\n"
645        "struct Gg:\n"
646        "  0 [+1]      UInt    qq\n", "subfield.emb")
647    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
648    self.assertEqual([
649        [error.error("subfield.emb", ir.module[0].type[0].structure.field[
650            1].location.size.field_reference.path[1].source_name[
651                0].source_location, "No candidate for 'rr'")]
652    ], errors)
653
654  def test_subfield_resolution_failure_shortcuts_further_resolution(self):
655    ir = self._construct_ir(
656        "struct Ff:\n"
657        "  0 [+1]         Gg        gg\n"
658        "  1 [+gg.rr.qq]  UInt:8[]  data\n"
659        "struct Gg:\n"
660        "  0 [+1]         UInt    qq\n", "subfield.emb")
661    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
662    self.assertEqual([
663        [error.error("subfield.emb", ir.module[0].type[0].structure.field[
664            1].location.size.field_reference.path[1].source_name[
665                0].source_location, "No candidate for 'rr'")]
666    ], errors)
667
668  def test_subfield_resolution_failure_with_aliased_name(self):
669    ir = self._construct_ir(
670        "struct Ff:\n"
671        "  0 [+1]      Gg        gg\n"
672        "  1 [+gg.gg]  UInt:8[]  data\n"
673        "struct Gg:\n"
674        "  0 [+1]      UInt    qq\n", "subfield.emb")
675    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
676    self.assertEqual([
677        [error.error("subfield.emb", ir.module[0].type[0].structure.field[
678            1].location.size.field_reference.path[1].source_name[
679                0].source_location, "No candidate for 'gg'")]
680    ], errors)
681
682  def test_subfield_resolution_failure_with_array(self):
683    ir = self._construct_ir(
684        "struct Ff:\n"
685        "  0 [+1]      Gg[1]     gg\n"
686        "  1 [+gg.qq]  UInt:8[]  data\n"
687        "struct Gg:\n"
688        "  0 [+1]      UInt    qq\n", "subfield.emb")
689    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
690    self.assertEqual([
691        [error.error("subfield.emb", ir.module[0].type[0].structure.field[
692            1].location.size.field_reference.path[0].source_name[
693                0].source_location, "Cannot access member of array 'gg'")]
694    ], errors)
695
696  def test_subfield_resolution_failure_with_int(self):
697    ir = self._construct_ir(
698        "struct Ff:\n"
699        "  0 [+1]      UInt      gg_source\n"
700        "  1 [+gg.qq]  UInt:8[]  data\n"
701        "  let gg = gg_source + 1\n",
702        "subfield.emb")
703    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
704    error_field = ir.module[0].type[0].structure.field[1]
705    error_reference = error_field.location.size.field_reference
706    error_location = error_reference.path[0].source_name[0].source_location
707    self.assertEqual([
708        [error.error("subfield.emb", error_location,
709                     "Cannot access member of noncomposite field 'gg'")]
710    ], errors)
711
712  def test_subfield_resolution_failure_with_int_no_cascade(self):
713    ir = self._construct_ir(
714        "struct Ff:\n"
715        "  0 [+1]    UInt      gg_source\n"
716        "  1 [+qqx]  UInt:8[]  data\n"
717        "  let gg = gg_source + 1\n"
718        "  let yy = gg.no_field\n"
719        "  let qqx = yy.x\n"
720        "  let qqy = yy.y\n",
721        "subfield.emb")
722    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
723    error_field = ir.module[0].type[0].structure.field[3]
724    error_reference = error_field.read_transform.field_reference
725    error_location = error_reference.path[0].source_name[0].source_location
726    self.assertEqual([
727        [error.error("subfield.emb", error_location,
728                     "Cannot access member of noncomposite field 'gg'")]
729    ], errors)
730
731  def test_subfield_resolution_failure_with_abbreviation(self):
732    ir = self._construct_ir(
733        "struct Ff:\n"
734        "  0 [+1]     Gg        gg\n"
735        "  1 [+gg.q]  UInt:8[]  data\n"
736        "struct Gg:\n"
737        "  0 [+1]     UInt    qq (q)\n", "subfield.emb")
738    errors = error.filter_errors(symbol_resolver.resolve_field_references(ir))
739    self.assertEqual([
740        # TODO(bolms): Make the error message clearer, in this case.
741        [error.error("subfield.emb", ir.module[0].type[0].structure.field[
742            1].location.size.field_reference.path[1].source_name[
743                0].source_location, "No candidate for 'q'")]
744    ], errors)
745
746
747if __name__ == "__main__":
748  unittest.main()
749