Support attribute lists for mojom imports.

Update the definition of the Import class in
//mojo/public/tools/bindings/pylib/mojom/parse/ast.py to have
an attribute list.

Update the grammar in
//mojo/public/tools/bindings/pylib/mojo/parse/parser.py so that
imports are now preceded by an attribute_section.

Update existing tests in parser_unittests.py and add
a unit test for Imports with attributes to
conditional_features_unittest.py

Bug: 676224
Change-Id: If347ef41f9149d3587db1f79abba84ccf552dbc2
Reviewed-on: https://chromium-review.googlesource.com/934004
Commit-Queue: Eve Martin-Jones <evem@chromium.org>
Reviewed-by: Sam McNally <sammc@chromium.org>
Reviewed-by: Ken Rockot <rockot@chromium.org>
Cr-Commit-Position: refs/heads/master@{#538937}

CrOS-Libchrome-Original-Commit: 55b8e2e73a7448a10a8b751c450eb636b2abb7d3
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
index 3108411..e9d7844 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/ast.py
@@ -180,13 +180,16 @@
 class Import(NodeBase):
   """Represents an import statement."""
 
-  def __init__(self, import_filename, **kwargs):
+  def __init__(self, attribute_list, import_filename, **kwargs):
+    assert attribute_list is None or isinstance(attribute_list, AttributeList)
     assert isinstance(import_filename, str)
     super(Import, self).__init__(**kwargs)
+    self.attribute_list = attribute_list
     self.import_filename = import_filename
 
   def __eq__(self, other):
     return super(Import, self).__eq__(other) and \
+           self.attribute_list == other.attribute_list and \
            self.import_filename == other.import_filename
 
 
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py b/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py
index 5df4160..c2279cf 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/conditional_features.py
@@ -68,6 +68,10 @@
 
 def RemoveDisabledDefinitions(mojom, enabled_features):
   """Removes conditionally disabled definitions from a Mojom node."""
+  mojom.import_list = ast.ImportList([
+    imported_file for imported_file in mojom.import_list
+      if _IsEnabled(imported_file, enabled_features)
+  ])
   mojom.definition_list = [
       definition for definition in mojom.definition_list
           if _IsEnabled(definition, enabled_features)
diff --git a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
index 01c69f1..916d69d 100644
--- a/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
+++ b/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -103,10 +103,11 @@
     p[0].definition_list.append(p[2])
 
   def p_import(self, p):
-    """import : IMPORT STRING_LITERAL SEMI"""
+    """import : attribute_section IMPORT STRING_LITERAL SEMI"""
     # 'eval' the literal to strip the quotes.
     # TODO(vtl): This eval is dubious. We should unquote/unescape ourselves.
-    p[0] = ast.Import(eval(p[2]), filename=self.filename, lineno=p.lineno(2))
+    p[0] = ast.Import(p[1], eval(p[3]), filename=self.filename,
+                      lineno=p.lineno(2))
 
   def p_module(self, p):
     """module : attribute_section MODULE identifier_wrapped SEMI"""
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/conditional_features_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/conditional_features_unittest.py
index c95990a..4d58388 100644
--- a/mojo/public/tools/bindings/pylib/mojom_tests/parse/conditional_features_unittest.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/conditional_features_unittest.py
@@ -75,6 +75,22 @@
     """
     self.parseAndAssertEqual(enum_source, expected_source)
 
+  def testFilterImport(self):
+    """Test that imports are correctly filtered from a Mojom."""
+    import_source = """
+      [EnableIf=blue]
+      import "foo.mojom";
+      import "bar.mojom";
+      [EnableIf=purple]
+      import "baz.mojom";
+    """
+    expected_source = """
+      [EnableIf=blue]
+      import "foo.mojom";
+      import "bar.mojom";
+    """
+    self.parseAndAssertEqual(import_source, expected_source)
+
   def testFilterInterface(self):
     """Test that definitions are correctly filtered from an Interface."""
     interface_source = """
diff --git a/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
index 0aadefb..b156bb7 100644
--- a/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
+++ b/mojo/public/tools/bindings/pylib/mojom_tests/parse/parser_unittest.py
@@ -990,65 +990,70 @@
     source4 = """\
         [Attr0=0] module my_module;
 
-        [Attr1=1] struct MyStruct {
-          [Attr2=2] int32 a;
+        [Attr1=1] import "my_import";
+
+        [Attr2=2] struct MyStruct {
+          [Attr3=3] int32 a;
         };
-        [Attr3=3] union MyUnion {
-          [Attr4=4] int32 a;
+        [Attr4=4] union MyUnion {
+          [Attr5=5] int32 a;
         };
-        [Attr5=5] enum MyEnum {
-          [Attr6=6] a
+        [Attr6=6] enum MyEnum {
+          [Attr7=7] a
         };
-        [Attr7=7] interface MyInterface {
-          [Attr8=8] MyMethod([Attr9=9] int32 a) => ([Attr10=10] bool b);
+        [Attr8=8] interface MyInterface {
+          [Attr9=9] MyMethod([Attr10=10] int32 a) => ([Attr11=11] bool b);
         };
-        [Attr11=11] const double kMyConst = 1.23;
+        [Attr12=12] const double kMyConst = 1.23;
         """
     expected4 = ast.Mojom(
         ast.Module(('IDENTIFIER', 'my_module'),
                    ast.AttributeList([ast.Attribute("Attr0", 0)])),
-        ast.ImportList(),
+        ast.ImportList(ast.Import(
+            ast.AttributeList([ast.Attribute("Attr1", 1)]),
+            "my_import")),
         [ast.Struct(
              'MyStruct',
-             ast.AttributeList(ast.Attribute("Attr1", 1)),
+             ast.AttributeList(ast.Attribute("Attr2", 2)),
              ast.StructBody(
                  ast.StructField(
-                     'a', ast.AttributeList([ast.Attribute("Attr2", 2)]),
+                     'a', ast.AttributeList([ast.Attribute("Attr3", 3)]),
                      None, 'int32', None))),
          ast.Union(
              'MyUnion',
-             ast.AttributeList(ast.Attribute("Attr3", 3)),
+             ast.AttributeList(ast.Attribute("Attr4", 4)),
              ast.UnionBody(
                  ast.UnionField(
-                     'a', ast.AttributeList([ast.Attribute("Attr4", 4)]), None,
+                     'a', ast.AttributeList([ast.Attribute("Attr5", 5)]), None,
                      'int32'))),
          ast.Enum(
              'MyEnum',
-             ast.AttributeList(ast.Attribute("Attr5", 5)),
+             ast.AttributeList(ast.Attribute("Attr6", 6)),
              ast.EnumValueList(
                  ast.EnumValue(
-                     'VALUE', ast.AttributeList([ast.Attribute("Attr6", 6)]),
+                     'VALUE', ast.AttributeList([ast.Attribute("Attr7", 7)]),
                      None))),
          ast.Interface(
             'MyInterface',
-            ast.AttributeList(ast.Attribute("Attr7", 7)),
+            ast.AttributeList(ast.Attribute("Attr8", 8)),
             ast.InterfaceBody(
                 ast.Method(
                     'MyMethod',
-                    ast.AttributeList(ast.Attribute("Attr8", 8)),
+                    ast.AttributeList(ast.Attribute("Attr9", 9)),
                     None,
                     ast.ParameterList(
                         ast.Parameter(
-                            'a', ast.AttributeList([ast.Attribute("Attr9", 9)]),
+                            'a',
+                            ast.AttributeList([ast.Attribute("Attr10", 10)]),
                             None, 'int32')),
                     ast.ParameterList(
                         ast.Parameter(
                             'b',
-                            ast.AttributeList([ast.Attribute("Attr10", 10)]),
+                            ast.AttributeList([ast.Attribute("Attr11", 11)]),
                             None, 'bool'))))),
          ast.Const(
             'kMyConst',
-            ast.AttributeList(ast.Attribute("Attr11", 11)),
+            ast.AttributeList(ast.Attribute("Attr12", 12)),
             'double', '1.23')])
     self.assertEquals(parser.Parse(source4, "my_file.mojom"), expected4)
 
@@ -1090,7 +1095,7 @@
     source1 = "import \"somedir/my.mojom\";"
     expected1 = ast.Mojom(
         None,
-        ast.ImportList(ast.Import("somedir/my.mojom")),
+        ast.ImportList(ast.Import(None, "somedir/my.mojom")),
         [])
     self.assertEquals(parser.Parse(source1, "my_file.mojom"), expected1)
 
@@ -1101,8 +1106,8 @@
         """
     expected2 = ast.Mojom(
         None,
-        ast.ImportList([ast.Import("somedir/my1.mojom"),
-                        ast.Import("somedir/my2.mojom")]),
+        ast.ImportList([ast.Import(None, "somedir/my1.mojom"),
+                        ast.Import(None, "somedir/my2.mojom")]),
         [])
     self.assertEquals(parser.Parse(source2, "my_file.mojom"), expected2)
 
@@ -1114,8 +1119,8 @@
         """
     expected3 = ast.Mojom(
         ast.Module(('IDENTIFIER', 'my_module'), None),
-        ast.ImportList([ast.Import("somedir/my1.mojom"),
-                        ast.Import("somedir/my2.mojom")]),
+        ast.ImportList([ast.Import(None, "somedir/my1.mojom"),
+                        ast.Import(None, "somedir/my2.mojom")]),
         [])
     self.assertEquals(parser.Parse(source3, "my_file.mojom"), expected3)