Extend cquery's deps function to traverse aspects.

RELNOTES: None.
PiperOrigin-RevId: 314773050
diff --git a/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java
index 1a3749e..f2fd28f 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/PostAnalysisQueryEnvironment.java
@@ -391,7 +391,8 @@
                         .setConfiguration(getConfiguration(dependency))
                         .build());
         values.add(new ClassifiedDependency<>(dependency, implicit));
-      } else if (key.functionName().equals(SkyFunctions.TOOLCHAIN_RESOLUTION)) {
+      } else if (key.functionName().equals(SkyFunctions.TOOLCHAIN_RESOLUTION)
+          || key.functionName().equals(SkyFunctions.ASPECT)) {
         // Also fetch these dependencies.
         values.addAll(targetifyValues(null, graph.getDirectDeps(key)));
       }
diff --git a/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java b/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
index 6cb55f1..0463edf 100644
--- a/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/query2/testutil/AbstractQueryTest.java
@@ -124,6 +124,20 @@
     helper.overwriteFile(pathName, lines);
   }
 
+  protected final void overwriteFile(String pathName, ImmutableList<String> lines)
+      throws IOException {
+    helper.overwriteFile(pathName, lines.toArray(new String[lines.size()]));
+  }
+
+  protected final void appendToWorkspace(String... lines) throws IOException {
+    overwriteFile(
+        "WORKSPACE",
+        new ImmutableList.Builder<String>()
+            .addAll(analysisMock.getWorkspaceContents(mockToolsConfig))
+            .add(lines)
+            .build());
+  }
+
   protected void assertContainsEvent(String expectedMessage) {
     helper.assertContainsEvent(expectedMessage);
   }
@@ -576,6 +590,122 @@
         .containsNoneOf("//deps:BUILD", "//deps:build_def", "//deps:skylark.bzl", "//s:BUILD");
   }
 
+  protected void writeAspectDefinition(String aspectAttrs) throws Exception {
+    writeFile(
+        "test/aspect.bzl",
+        "def _aspect_impl(target, ctx):",
+        "   return struct()",
+        "def _rule_impl(ctx):",
+        "   return struct()",
+        "",
+        "MyAspect = aspect(",
+        "   implementation=_aspect_impl,",
+        "   attr_aspects=['deps'],",
+        "   attrs = ",
+        aspectAttrs,
+        ")",
+        "aspect_rule = rule(",
+        "   implementation=_rule_impl,",
+        "   attrs = { 'attr' : ",
+        "             attr.label_list(mandatory=True, allow_files=True, aspects = [MyAspect]),",
+        "             'param' : attr.string(),",
+        "           },",
+        ")",
+        "plain_rule = rule(",
+        "   implementation=_rule_impl,",
+        "   attrs = { 'attr' : ",
+        "             attr.label_list(mandatory=False, allow_files=True) ",
+        "           },",
+        ")");
+    writeFile(
+        "prod/BUILD",
+        "load('//test:aspect.bzl', 'plain_rule')",
+        "plain_rule(",
+        "     name = 'zzz'",
+        ")");
+  }
+
+  @Test
+  public void testAspectOnRuleWithoutDeclaredProviders() throws Exception {
+    writeAspectDefinition("{'_extra_deps' : attr.label(default = Label('//test:z'))}");
+    writeFile(
+        "test/BUILD",
+        "load('//test:aspect.bzl', 'aspect_rule', 'plain_rule')",
+        "aspect_rule(name='a', attr=[':b'])",
+        "plain_rule(name='b')",
+        "plain_rule(name='z')");
+
+    assertThat(eval("deps(//test:a)")).containsAtLeastElementsIn(eval("//test:b + //test:z"));
+  }
+
+  @Test
+  public void testQueryStarlarkAspects() throws Exception {
+    writeAspectDefinition("{'_extra_deps' : attr.label(default = Label('//prod:zzz'))}");
+    writeFile(
+        "test/BUILD",
+        "load('//test:aspect.bzl', 'aspect_rule', 'plain_rule')",
+        "plain_rule(",
+        "     name = 'yyy',",
+        ")",
+        "aspect_rule(",
+        "     name = 'xxx',",
+        "     attr = [':yyy'],",
+        ")",
+        "aspect_rule(",
+        "     name = 'qqq',",
+        "     attr = ['//external:yyy'],",
+        ")");
+    appendToWorkspace("bind(name = 'yyy', actual = '//test:yyy')");
+
+    assertThat(eval("deps(//test:xxx)")).containsAtLeastElementsIn(eval("//prod:zzz + //test:yyy"));
+    assertThat(eval("deps(//test:qqq)")).containsAtLeastElementsIn(eval("//prod:zzz + //test:yyy"));
+  }
+
+  @Test
+  public void testQueryStarlarkAspectWithParameters() throws Exception {
+    writeAspectDefinition(
+        "{'_extra_deps' : attr.label(default = Label('//prod:zzz')),"
+            + "'param' : attr.string(values=['a', 'b']) }");
+    writeFile(
+        "test/BUILD",
+        "load('//test:aspect.bzl', 'aspect_rule', 'plain_rule')",
+        "plain_rule(",
+        "     name = 'yyy',",
+        ")",
+        "aspect_rule(",
+        "     name = 'xxx',",
+        "     attr = [':yyy'],",
+        "     param = 'a',",
+        ")",
+        "aspect_rule(",
+        "     name = 'qqq',",
+        "     attr = ['//external:yyy'],",
+        "     param = 'b',",
+        ")");
+    appendToWorkspace("bind(name = 'yyy', actual = '//test:yyy')");
+
+    assertThat(eval("deps(//test:xxx)")).containsAtLeastElementsIn(eval("//prod:zzz + //test:yyy"));
+    assertThat(eval("deps(//test:qqq)")).containsAtLeastElementsIn(eval("//prod:zzz + //test:yyy"));
+  }
+
+  @Test
+  public void testQueryStarlarkAspectsNoImplicitDeps() throws Exception {
+    writeAspectDefinition("{'_extra_deps':attr.label(default = Label('//prod:zzz'))}");
+    writeFile(
+        "test/BUILD",
+        "load('//test:aspect.bzl', 'aspect_rule', 'plain_rule')",
+        "plain_rule(",
+        "     name = 'yyy',",
+        ")",
+        "aspect_rule(",
+        "     name = 'xxx',",
+        "     attr = [':yyy'],",
+        ")");
+    helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
+
+    assertThat(eval("deps(//test:xxx)")).containsNoneIn(eval("//prod:zzz"));
+  }
+
   @Test
   public void testStarlarkDiamondEquality() throws Exception {
     writeFile(