Add type information to symbolfile and ndkstubgen.

Test: mypy symbolfile
Test: pytest
Bug: None
Change-Id: I6b1045d315e5a10e699d31de9fafc084d82768b2
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 2f4326a..86bf6ff 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -20,13 +20,16 @@
 import logging
 import os
 import sys
+from typing import Iterable, TextIO
 
 import symbolfile
+from symbolfile import Arch, Version
 
 
 class Generator:
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file, version_script, arch, api, llndk, apex):
+    def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
+                 api: int, llndk: bool, apex: bool) -> None:
         self.src_file = src_file
         self.version_script = version_script
         self.arch = arch
@@ -34,12 +37,12 @@
         self.llndk = llndk
         self.apex = apex
 
-    def write(self, versions):
+    def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
         for version in versions:
             self.write_version(version)
 
-    def write_version(self, version):
+    def write_version(self, version: Version) -> None:
         """Writes a single version block's data to the output files."""
         if symbolfile.should_omit_version(version, self.arch, self.api,
                                           self.llndk, self.apex):
@@ -84,7 +87,7 @@
                 self.version_script.write('}' + base + ';\n')
 
 
-def parse_args():
+def parse_args() -> argparse.Namespace:
     """Parses and returns command line arguments."""
     parser = argparse.ArgumentParser()
 
@@ -100,23 +103,31 @@
     parser.add_argument(
         '--apex', action='store_true', help='Use the APEX variant.')
 
+    # https://github.com/python/mypy/issues/1317
+    # mypy has issues with using os.path.realpath as an argument here.
     parser.add_argument(
-        '--api-map', type=os.path.realpath, required=True,
+        '--api-map',
+        type=os.path.realpath,  # type: ignore
+        required=True,
         help='Path to the API level map JSON file.')
 
     parser.add_argument(
-        'symbol_file', type=os.path.realpath, help='Path to symbol file.')
+        'symbol_file',
+        type=os.path.realpath,  # type: ignore
+        help='Path to symbol file.')
     parser.add_argument(
-        'stub_src', type=os.path.realpath,
+        'stub_src',
+        type=os.path.realpath,  # type: ignore
         help='Path to output stub source file.')
     parser.add_argument(
-        'version_script', type=os.path.realpath,
+        'version_script',
+        type=os.path.realpath,  # type: ignore
         help='Path to output version script.')
 
     return parser.parse_args()
 
 
-def main():
+def main() -> None:
     """Program entry point."""
     args = parse_args()
 
diff --git a/cc/ndkstubgen/mypy.ini b/cc/ndkstubgen/mypy.ini
new file mode 100644
index 0000000..82aa7eb
--- /dev/null
+++ b/cc/ndkstubgen/mypy.ini
@@ -0,0 +1,2 @@
+[mypy]
+disallow_untyped_defs = True
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 70bcf78..6d2c9d6 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -21,19 +21,20 @@
 
 import ndkstubgen
 import symbolfile
+from symbolfile import Arch, Tag
 
 
 # pylint: disable=missing-docstring
 
 
 class GeneratorTest(unittest.TestCase):
-    def test_omit_version(self):
+    def test_omit_version(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         version = symbolfile.Version('VERSION_PRIVATE', None, [], [
             symbolfile.Symbol('foo', []),
@@ -42,74 +43,75 @@
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, ['x86'], [
+        version = symbolfile.Version('VERSION', None, [Tag('x86')], [
             symbolfile.Symbol('foo', []),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, ['introduced=14'], [
+        version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
             symbolfile.Symbol('foo', []),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-    def test_omit_symbol(self):
+    def test_omit_symbol(self) -> None:
         # Thorough testing of the cases involved here is handled by
         # SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['x86']),
+            symbolfile.Symbol('foo', [Tag('x86')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['introduced=14']),
+            symbolfile.Symbol('foo', [Tag('introduced=14')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['llndk']),
+            symbolfile.Symbol('foo', [Tag('llndk')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
         version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', ['apex']),
+            symbolfile.Symbol('foo', [Tag('apex')]),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-    def test_write(self):
+    def test_write(self) -> None:
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
 
         versions = [
             symbolfile.Version('VERSION_1', None, [], [
                 symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', ['var']),
-                symbolfile.Symbol('woodly', ['weak']),
-                symbolfile.Symbol('doodly', ['weak', 'var']),
+                symbolfile.Symbol('bar', [Tag('var')]),
+                symbolfile.Symbol('woodly', [Tag('weak')]),
+                symbolfile.Symbol('doodly',
+                                  [Tag('weak'), Tag('var')]),
             ]),
             symbolfile.Version('VERSION_2', 'VERSION_1', [], [
                 symbolfile.Symbol('baz', []),
             ]),
             symbolfile.Version('VERSION_3', 'VERSION_1', [], [
-                symbolfile.Symbol('qux', ['versioned=14']),
+                symbolfile.Symbol('qux', [Tag('versioned=14')]),
             ]),
         ]
 
@@ -141,7 +143,7 @@
 
 
 class IntegrationTest(unittest.TestCase):
-    def test_integration(self):
+    def test_integration(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -178,14 +180,14 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9, False, False)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -213,7 +215,7 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-    def test_integration_future_api(self):
+    def test_integration_future_api(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -230,14 +232,14 @@
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9001,
-                                             False, False)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9001, False, False)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9001,
-                                         False, False)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9001, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -255,7 +257,7 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-    def test_multiple_definition(self):
+    def test_multiple_definition(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 global:
@@ -280,8 +282,8 @@
             } VERSION_2;
 
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False,
-                                             False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         with self.assertRaises(
                 symbolfile.MultiplyDefinedSymbolError) as ex_context:
@@ -289,7 +291,7 @@
         self.assertEqual(['bar', 'foo'],
                          ex_context.exception.multiply_defined_symbols)
 
-    def test_integration_with_apex(self):
+    def test_integration_with_apex(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
@@ -328,14 +330,14 @@
                 wobble;
             } VERSION_4;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, api_map, 'arm', 9,
-                                             False, True)
+        parser = symbolfile.SymbolFileParser(input_file, api_map, Arch('arm'),
+                                             9, False, True)
         versions = parser.parse()
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, 'arm', 9,
-                                         False, True)
+        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
+                                         9, False, True)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -369,7 +371,8 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
-def main():
+
+def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
     unittest.TextTestRunner(verbosity=3).run(suite)
 
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index faa3823..5678e7d 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -14,15 +14,31 @@
 # limitations under the License.
 #
 """Parser for Android's version script information."""
+from dataclasses import dataclass
 import logging
 import re
+from typing import (
+    Dict,
+    Iterable,
+    List,
+    Mapping,
+    NewType,
+    Optional,
+    TextIO,
+    Tuple,
+)
+
+
+ApiMap = Mapping[str, int]
+Arch = NewType('Arch', str)
+Tag = NewType('Tag', str)
 
 
 ALL_ARCHITECTURES = (
-    'arm',
-    'arm64',
-    'x86',
-    'x86_64',
+    Arch('arm'),
+    Arch('arm64'),
+    Arch('x86'),
+    Arch('x86_64'),
 )
 
 
@@ -30,18 +46,36 @@
 FUTURE_API_LEVEL = 10000
 
 
-def logger():
+def logger() -> logging.Logger:
     """Return the main logger for this module."""
     return logging.getLogger(__name__)
 
 
-def get_tags(line):
+@dataclass
+class Symbol:
+    """A symbol definition from a symbol file."""
+
+    name: str
+    tags: List[Tag]
+
+
+@dataclass
+class Version:
+    """A version block of a symbol file."""
+
+    name: str
+    base: Optional[str]
+    tags: List[Tag]
+    symbols: List[Symbol]
+
+
+def get_tags(line: str) -> List[Tag]:
     """Returns a list of all tags on this line."""
     _, _, all_tags = line.strip().partition('#')
-    return [e for e in re.split(r'\s+', all_tags) if e.strip()]
+    return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
 
 
-def is_api_level_tag(tag):
+def is_api_level_tag(tag: Tag) -> bool:
     """Returns true if this tag has an API level that may need decoding."""
     if tag.startswith('introduced='):
         return True
@@ -52,7 +86,7 @@
     return False
 
 
-def decode_api_level(api, api_map):
+def decode_api_level(api: str, api_map: ApiMap) -> int:
     """Decodes the API level argument into the API level number.
 
     For the average case, this just decodes the integer value from the string,
@@ -70,12 +104,13 @@
     return api_map[api]
 
 
-def decode_api_level_tags(tags, api_map):
+def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
     """Decodes API level code names in a list of tags.
 
     Raises:
         ParseError: An unknown version name was found in a tag.
     """
+    decoded_tags = list(tags)
     for idx, tag in enumerate(tags):
         if not is_api_level_tag(tag):
             continue
@@ -83,13 +118,13 @@
 
         try:
             decoded = str(decode_api_level(value, api_map))
-            tags[idx] = '='.join([name, decoded])
+            decoded_tags[idx] = Tag('='.join([name, decoded]))
         except KeyError:
-            raise ParseError('Unknown version name in tag: {}'.format(tag))
-    return tags
+            raise ParseError(f'Unknown version name in tag: {tag}')
+    return decoded_tags
 
 
-def split_tag(tag):
+def split_tag(tag: Tag) -> Tuple[str, str]:
     """Returns a key/value tuple of the tag.
 
     Raises:
@@ -103,7 +138,7 @@
     return key, value
 
 
-def get_tag_value(tag):
+def get_tag_value(tag: Tag) -> str:
     """Returns the value of a key/value tag.
 
     Raises:
@@ -114,12 +149,13 @@
     return split_tag(tag)[1]
 
 
-def version_is_private(version):
+def version_is_private(version: str) -> bool:
     """Returns True if the version name should be treated as private."""
     return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
 
 
-def should_omit_version(version, arch, api, llndk, apex):
+def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
+                        apex: bool) -> bool:
     """Returns True if the version section should be ommitted.
 
     We want to omit any sections that do not have any symbols we'll have in the
@@ -145,7 +181,8 @@
     return False
 
 
-def should_omit_symbol(symbol, arch, api, llndk, apex):
+def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
+                       apex: bool) -> bool:
     """Returns True if the symbol should be omitted."""
     no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
     keep = no_llndk_no_apex or \
@@ -160,7 +197,7 @@
     return False
 
 
-def symbol_in_arch(tags, arch):
+def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
     has_arch_tags = False
     for tag in tags:
@@ -175,7 +212,7 @@
     return not has_arch_tags
 
 
-def symbol_in_api(tags, arch, api):
+def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool:
     """Returns true if the symbol is present for the given API level."""
     introduced_tag = None
     arch_specific = False
@@ -197,7 +234,7 @@
     return api >= int(get_tag_value(introduced_tag))
 
 
-def symbol_versioned_in_api(tags, api):
+def symbol_versioned_in_api(tags: Iterable[Tag], api: int) -> bool:
     """Returns true if the symbol should be versioned for the given API.
 
     This models the `versioned=API` tag. This should be a very uncommonly
@@ -223,68 +260,40 @@
 
 class MultiplyDefinedSymbolError(RuntimeError):
     """A symbol name was multiply defined."""
-    def __init__(self, multiply_defined_symbols):
-        super(MultiplyDefinedSymbolError, self).__init__(
+    def __init__(self, multiply_defined_symbols: Iterable[str]) -> None:
+        super().__init__(
             'Version script contains multiple definitions for: {}'.format(
                 ', '.join(multiply_defined_symbols)))
         self.multiply_defined_symbols = multiply_defined_symbols
 
 
-class Version:
-    """A version block of a symbol file."""
-    def __init__(self, name, base, tags, symbols):
-        self.name = name
-        self.base = base
-        self.tags = tags
-        self.symbols = symbols
-
-    def __eq__(self, other):
-        if self.name != other.name:
-            return False
-        if self.base != other.base:
-            return False
-        if self.tags != other.tags:
-            return False
-        if self.symbols != other.symbols:
-            return False
-        return True
-
-
-class Symbol:
-    """A symbol definition from a symbol file."""
-    def __init__(self, name, tags):
-        self.name = name
-        self.tags = tags
-
-    def __eq__(self, other):
-        return self.name == other.name and set(self.tags) == set(other.tags)
-
-
 class SymbolFileParser:
     """Parses NDK symbol files."""
-    def __init__(self, input_file, api_map, arch, api, llndk, apex):
+    def __init__(self, input_file: TextIO, api_map: ApiMap, arch: Arch,
+                 api: int, llndk: bool, apex: bool) -> None:
         self.input_file = input_file
         self.api_map = api_map
         self.arch = arch
         self.api = api
         self.llndk = llndk
         self.apex = apex
-        self.current_line = None
+        self.current_line: Optional[str] = None
 
-    def parse(self):
+    def parse(self) -> List[Version]:
         """Parses the symbol file and returns a list of Version objects."""
         versions = []
         while self.next_line() != '':
+            assert self.current_line is not None
             if '{' in self.current_line:
                 versions.append(self.parse_version())
             else:
                 raise ParseError(
-                    'Unexpected contents at top level: ' + self.current_line)
+                    f'Unexpected contents at top level: {self.current_line}')
 
         self.check_no_duplicate_symbols(versions)
         return versions
 
-    def check_no_duplicate_symbols(self, versions):
+    def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None:
         """Raises errors for multiply defined symbols.
 
         This situation is the normal case when symbol versioning is actually
@@ -312,12 +321,13 @@
             raise MultiplyDefinedSymbolError(
                 sorted(list(multiply_defined_symbols)))
 
-    def parse_version(self):
+    def parse_version(self) -> Version:
         """Parses a single version section and returns a Version object."""
+        assert self.current_line is not None
         name = self.current_line.split('{')[0].strip()
         tags = get_tags(self.current_line)
         tags = decode_api_level_tags(tags, self.api_map)
-        symbols = []
+        symbols: List[Symbol] = []
         global_scope = True
         cpp_symbols = False
         while self.next_line() != '':
@@ -333,9 +343,7 @@
                     cpp_symbols = False
                 else:
                     base = base.rstrip(';').rstrip()
-                    if base == '':
-                        base = None
-                    return Version(name, base, tags, symbols)
+                    return Version(name, base or None, tags, symbols)
             elif 'extern "C++" {' in self.current_line:
                 cpp_symbols = True
             elif not cpp_symbols and ':' in self.current_line:
@@ -354,8 +362,9 @@
                 pass
         raise ParseError('Unexpected EOF in version block.')
 
-    def parse_symbol(self):
+    def parse_symbol(self) -> Symbol:
         """Parses a single symbol line and returns a Symbol object."""
+        assert self.current_line is not None
         if ';' not in self.current_line:
             raise ParseError(
                 'Expected ; to terminate symbol: ' + self.current_line)
@@ -368,7 +377,7 @@
         tags = decode_api_level_tags(tags, self.api_map)
         return Symbol(name, tags)
 
-    def next_line(self):
+    def next_line(self) -> str:
         """Returns the next non-empty non-comment line.
 
         A return value of '' indicates EOF.
diff --git a/cc/symbolfile/mypy.ini b/cc/symbolfile/mypy.ini
new file mode 100644
index 0000000..82aa7eb
--- /dev/null
+++ b/cc/symbolfile/mypy.ini
@@ -0,0 +1,2 @@
+[mypy]
+disallow_untyped_defs = True
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index c91131f..92b1399 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,12 +19,13 @@
 import unittest
 
 import symbolfile
+from symbolfile import Arch, Tag
 
 # pylint: disable=missing-docstring
 
 
 class DecodeApiLevelTest(unittest.TestCase):
-    def test_decode_api_level(self):
+    def test_decode_api_level(self) -> None:
         self.assertEqual(9, symbolfile.decode_api_level('9', {}))
         self.assertEqual(9000, symbolfile.decode_api_level('O', {'O': 9000}))
 
@@ -33,70 +34,73 @@
 
 
 class TagsTest(unittest.TestCase):
-    def test_get_tags_no_tags(self):
+    def test_get_tags_no_tags(self) -> None:
         self.assertEqual([], symbolfile.get_tags(''))
         self.assertEqual([], symbolfile.get_tags('foo bar baz'))
 
-    def test_get_tags(self):
+    def test_get_tags(self) -> None:
         self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
         self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
 
-    def test_split_tag(self):
-        self.assertTupleEqual(('foo', 'bar'), symbolfile.split_tag('foo=bar'))
-        self.assertTupleEqual(('foo', 'bar=baz'), symbolfile.split_tag('foo=bar=baz'))
+    def test_split_tag(self) -> None:
+        self.assertTupleEqual(('foo', 'bar'),
+                              symbolfile.split_tag(Tag('foo=bar')))
+        self.assertTupleEqual(('foo', 'bar=baz'),
+                              symbolfile.split_tag(Tag('foo=bar=baz')))
         with self.assertRaises(ValueError):
-            symbolfile.split_tag('foo')
+            symbolfile.split_tag(Tag('foo'))
 
-    def test_get_tag_value(self):
-        self.assertEqual('bar', symbolfile.get_tag_value('foo=bar'))
-        self.assertEqual('bar=baz', symbolfile.get_tag_value('foo=bar=baz'))
+    def test_get_tag_value(self) -> None:
+        self.assertEqual('bar', symbolfile.get_tag_value(Tag('foo=bar')))
+        self.assertEqual('bar=baz',
+                         symbolfile.get_tag_value(Tag('foo=bar=baz')))
         with self.assertRaises(ValueError):
-            symbolfile.get_tag_value('foo')
+            symbolfile.get_tag_value(Tag('foo'))
 
-    def test_is_api_level_tag(self):
-        self.assertTrue(symbolfile.is_api_level_tag('introduced=24'))
-        self.assertTrue(symbolfile.is_api_level_tag('introduced-arm=24'))
-        self.assertTrue(symbolfile.is_api_level_tag('versioned=24'))
+    def test_is_api_level_tag(self) -> None:
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced=24')))
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('introduced-arm=24')))
+        self.assertTrue(symbolfile.is_api_level_tag(Tag('versioned=24')))
 
         # Shouldn't try to process things that aren't a key/value tag.
-        self.assertFalse(symbolfile.is_api_level_tag('arm'))
-        self.assertFalse(symbolfile.is_api_level_tag('introduced'))
-        self.assertFalse(symbolfile.is_api_level_tag('versioned'))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('arm')))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('introduced')))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned')))
 
         # We don't support arch specific `versioned` tags.
-        self.assertFalse(symbolfile.is_api_level_tag('versioned-arm=24'))
+        self.assertFalse(symbolfile.is_api_level_tag(Tag('versioned-arm=24')))
 
-    def test_decode_api_level_tags(self):
+    def test_decode_api_level_tags(self) -> None:
         api_map = {
             'O': 9000,
             'P': 9001,
         }
 
         tags = [
-            'introduced=9',
-            'introduced-arm=14',
-            'versioned=16',
-            'arm',
-            'introduced=O',
-            'introduced=P',
+            Tag('introduced=9'),
+            Tag('introduced-arm=14'),
+            Tag('versioned=16'),
+            Tag('arm'),
+            Tag('introduced=O'),
+            Tag('introduced=P'),
         ]
         expected_tags = [
-            'introduced=9',
-            'introduced-arm=14',
-            'versioned=16',
-            'arm',
-            'introduced=9000',
-            'introduced=9001',
+            Tag('introduced=9'),
+            Tag('introduced-arm=14'),
+            Tag('versioned=16'),
+            Tag('arm'),
+            Tag('introduced=9000'),
+            Tag('introduced=9001'),
         ]
         self.assertListEqual(
             expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
 
         with self.assertRaises(symbolfile.ParseError):
-            symbolfile.decode_api_level_tags(['introduced=O'], {})
+            symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
 
 
 class PrivateVersionTest(unittest.TestCase):
-    def test_version_is_private(self):
+    def test_version_is_private(self) -> None:
         self.assertFalse(symbolfile.version_is_private('foo'))
         self.assertFalse(symbolfile.version_is_private('PRIVATE'))
         self.assertFalse(symbolfile.version_is_private('PLATFORM'))
@@ -110,191 +114,227 @@
 
 
 class SymbolPresenceTest(unittest.TestCase):
-    def test_symbol_in_arch(self):
-        self.assertTrue(symbolfile.symbol_in_arch([], 'arm'))
-        self.assertTrue(symbolfile.symbol_in_arch(['arm'], 'arm'))
+    def test_symbol_in_arch(self) -> None:
+        self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
+        self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
 
-        self.assertFalse(symbolfile.symbol_in_arch(['x86'], 'arm'))
+        self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
 
-    def test_symbol_in_api(self):
-        self.assertTrue(symbolfile.symbol_in_api([], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(['introduced-x86=14'], 'arm', 9))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced-arm=9', 'introduced-x86=21'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced=9', 'introduced-x86=21'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['introduced=21', 'introduced-arm=9'], 'arm', 14))
-        self.assertTrue(symbolfile.symbol_in_api(
-            ['future'], 'arm', symbolfile.FUTURE_API_LEVEL))
+    def test_symbol_in_api(self) -> None:
+        self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 9))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced=9')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
+                                     14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9')], Arch('arm'),
+                                     14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('introduced-x86=14')], Arch('arm'),
+                                     9))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced-arm=9'),
+                 Tag('introduced-x86=21')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'),
+                 Tag('introduced-x86=21')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=21'),
+                 Tag('introduced-arm=9')], Arch('arm'), 14))
+        self.assertTrue(
+            symbolfile.symbol_in_api([Tag('future')], Arch('arm'),
+                                     symbolfile.FUTURE_API_LEVEL))
 
-        self.assertFalse(symbolfile.symbol_in_api(['introduced=14'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(['introduced-arm=14'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(['future'], 'arm', 9))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=9', 'future'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced-arm=9', 'future'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced-arm=21', 'introduced-x86=9'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=9', 'introduced-arm=21'], 'arm', 14))
-        self.assertFalse(symbolfile.symbol_in_api(
-            ['introduced=21', 'introduced-x86=9'], 'arm', 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced=14')], Arch('arm'), 9))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced-arm=14')], Arch('arm'),
+                                     9))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('future')], Arch('arm'), 9))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'), Tag('future')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api([Tag('introduced-arm=9'),
+                                      Tag('future')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced-arm=21'),
+                 Tag('introduced-x86=9')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=9'),
+                 Tag('introduced-arm=21')], Arch('arm'), 14))
+        self.assertFalse(
+            symbolfile.symbol_in_api(
+                [Tag('introduced=21'),
+                 Tag('introduced-x86=9')], Arch('arm'), 14))
 
         # Interesting edge case: this symbol should be omitted from the
         # library, but this call should still return true because none of the
         # tags indiciate that it's not present in this API level.
-        self.assertTrue(symbolfile.symbol_in_api(['x86'], 'arm', 9))
+        self.assertTrue(symbolfile.symbol_in_api([Tag('x86')], Arch('arm'), 9))
 
-    def test_verioned_in_api(self):
+    def test_verioned_in_api(self) -> None:
         self.assertTrue(symbolfile.symbol_versioned_in_api([], 9))
-        self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 9))
-        self.assertTrue(symbolfile.symbol_versioned_in_api(['versioned=9'], 14))
+        self.assertTrue(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 9))
+        self.assertTrue(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=9')], 14))
 
-        self.assertFalse(symbolfile.symbol_versioned_in_api(['versioned=14'], 9))
+        self.assertFalse(
+            symbolfile.symbol_versioned_in_api([Tag('versioned=14')], 9))
 
 
 class OmitVersionTest(unittest.TestCase):
-    def test_omit_private(self):
+    def test_omit_private(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PRIVATE', None, [], []), 'arm', 9,
-                False, False))
+                symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
+                9, False, False))
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PLATFORM', None, [], []), 'arm', 9,
-                False, False))
-
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['platform-only'], []), 'arm',
+                symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
                 9, False, False))
 
-    def test_omit_llndk(self):
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9,
-                False, False))
+                symbolfile.Version('foo', None, [Tag('platform-only')], []),
+                Arch('arm'), 9, False, False))
 
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, True,
-                False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['llndk'], []), 'arm', 9, True,
-                False))
-
-    def test_omit_apex(self):
+    def test_omit_llndk(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
-                False))
+                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                Arch('arm'), 9, False, False))
 
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
+                False))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                Arch('arm'), 9, True, False))
+
+    def test_omit_apex(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('apex')], []),
+                Arch('arm'), 9, False, False))
+
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 True))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['apex'], []), 'arm', 9, False,
-                True))
+                symbolfile.Version('foo', None, [Tag('apex')], []),
+                Arch('arm'), 9, False, True))
 
-    def test_omit_arch(self):
+    def test_omit_arch(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
                 False))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['arm'], []), 'arm', 9, False,
-                False))
-
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['x86'], []), 'arm', 9, False,
-                False))
-
-    def test_omit_api(self):
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), 'arm', 9, False,
-                False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['introduced=9'], []), 'arm',
+                symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
                 9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, ['introduced=14'], []), 'arm',
+                symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
                 9, False, False))
 
+    def test_omit_api(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
+                False))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('introduced=9')], []),
+                Arch('arm'), 9, False, False))
+
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, [Tag('introduced=14')], []),
+                Arch('arm'), 9, False, False))
+
 
 class OmitSymbolTest(unittest.TestCase):
-    def test_omit_llndk(self):
+    def test_omit_llndk(self) -> None:
         self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
-                                          'arm', 9, False, False))
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
+                False, False))
 
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, True, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['llndk']),
-                                          'arm', 9, True, False))
-
-    def test_omit_apex(self):
-        self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
-                                          'arm', 9, False, False))
-
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, True))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['apex']),
-                                          'arm', 9, False, True))
-
-    def test_omit_arch(self):
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, False))
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['arm']),
-                                          'arm', 9, False, False))
-
-        self.assertTrue(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', ['x86']),
-                                          'arm', 9, False, False))
-
-    def test_omit_api(self):
-        self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []), 'arm',
-                                          9, False, False))
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, True, False))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', ['introduced=9']), 'arm', 9, False,
+                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
+                False))
+
+    def test_omit_apex(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
+                False))
+
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, True))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
+                True))
+
+    def test_omit_arch(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, False))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
                 False))
 
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', ['introduced=14']), 'arm', 9, False,
+                symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
                 False))
 
+    def test_omit_api(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+                                          Arch('arm'), 9, False, False))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
+                9, False, False))
+
+        self.assertTrue(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
+                9, False, False))
+
 
 class SymbolFileParseTest(unittest.TestCase):
-    def test_next_line(self):
+    def test_next_line(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo
 
@@ -302,10 +342,12 @@
             # baz
             qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         self.assertIsNone(parser.current_line)
 
         self.assertEqual('foo', parser.next_line().strip())
+        assert parser.current_line is not None
         self.assertEqual('foo', parser.current_line.strip())
 
         self.assertEqual('bar', parser.next_line().strip())
@@ -317,7 +359,7 @@
         self.assertEqual('', parser.next_line())
         self.assertEqual('', parser.current_line)
 
-    def test_parse_version(self):
+    def test_parse_version(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 { # foo bar
                 baz;
@@ -327,7 +369,8 @@
             VERSION_2 {
             } VERSION_1; # asdf
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         parser.next_line()
         version = parser.parse_version()
@@ -337,7 +380,7 @@
 
         expected_symbols = [
             symbolfile.Symbol('baz', []),
-            symbolfile.Symbol('qux', ['woodly', 'doodly']),
+            symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -347,32 +390,35 @@
         self.assertEqual('VERSION_1', version.base)
         self.assertEqual([], version.tags)
 
-    def test_parse_version_eof(self):
+    def test_parse_version_eof(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_unknown_scope_label(self):
+    def test_unknown_scope_label(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo:
             }
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_parse_symbol(self):
+    def test_parse_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             foo;
             bar; # baz qux
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
 
         parser.next_line()
         symbol = parser.parse_symbol()
@@ -384,48 +430,51 @@
         self.assertEqual('bar', symbol.name)
         self.assertEqual(['baz', 'qux'], symbol.tags)
 
-    def test_wildcard_symbol_global(self):
+    def test_wildcard_symbol_global(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_wildcard_symbol_local(self):
+    def test_wildcard_symbol_local(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 local:
                     *;
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         version = parser.parse_version()
         self.assertEqual([], version.symbols)
 
-    def test_missing_semicolon(self):
+    def test_missing_semicolon(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         parser.next_line()
         with self.assertRaises(symbolfile.ParseError):
             parser.parse_version()
 
-    def test_parse_fails_invalid_input(self):
+    def test_parse_fails_invalid_input(self) -> None:
         with self.assertRaises(symbolfile.ParseError):
             input_file = io.StringIO('foo')
-            parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16,
-                                                 False, False)
+            parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
+                                                 16, False, False)
             parser.parse()
 
-    def test_parse(self):
+    def test_parse(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 local:
@@ -443,23 +492,24 @@
                     qwerty;
             } VERSION_1;
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, False)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, False)
         versions = parser.parse()
 
         expected = [
             symbolfile.Version('VERSION_1', None, [], [
                 symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', ['baz']),
+                symbolfile.Symbol('bar', [Tag('baz')]),
             ]),
-            symbolfile.Version('VERSION_2', 'VERSION_1', ['wasd'], [
+            symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
                 symbolfile.Symbol('woodly', []),
-                symbolfile.Symbol('doodly', ['asdf']),
+                symbolfile.Symbol('doodly', [Tag('asdf')]),
             ]),
         ]
 
         self.assertEqual(expected, versions)
 
-    def test_parse_llndk_apex_symbol(self):
+    def test_parse_llndk_apex_symbol(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
                 foo;
@@ -468,7 +518,8 @@
                 qux; # apex
             };
         """))
-        parser = symbolfile.SymbolFileParser(input_file, {}, 'arm', 16, False, True)
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'), 16,
+                                             False, True)
 
         parser.next_line()
         version = parser.parse_version()
@@ -477,14 +528,14 @@
 
         expected_symbols = [
             symbolfile.Symbol('foo', []),
-            symbolfile.Symbol('bar', ['llndk']),
-            symbolfile.Symbol('baz', ['llndk', 'apex']),
-            symbolfile.Symbol('qux', ['apex']),
+            symbolfile.Symbol('bar', [Tag('llndk')]),
+            symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
+            symbolfile.Symbol('qux', [Tag('apex')]),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
 
-def main():
+def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
     unittest.TextTestRunner(verbosity=3).run(suite)