Introduce the 'siblings' query function.

RELNOTES[NEW]: There is now a 'siblings' query function. See the query documentation for more details.

PiperOrigin-RevId: 165010653
diff --git a/site/docs/query-how-to.md b/site/docs/query-how-to.md
index a27fb9d..6b5577e 100644
--- a/site/docs/query-how-to.md
+++ b/site/docs/query-how-to.md
@@ -86,6 +86,8 @@
             `//third_party/jpeg`?](#Why_does_library_photos_fronten)
     *   [What depends on ...](#What_depends_on_)
         *   [What rules under bar depend on Y?](#What_rules_under_bar_depend_o)
+        *   [What targets directly depend on T, in T's
+            package](#What_are_the_intra_package_direct_rdeps)
     *   [How do I break a dependency ...](#How_do_I_break_a_dependency_)
         *   [What dependency paths do I have to break to make `bar` no longer
             depend on X?](#What_dependency_paths_do_I_have_)
@@ -269,7 +271,7 @@
 ```
 
 <a name="What_build_rule_contains_file_ja"></a>
-#### What build rule contains file `src/main/java/com/example/cache/LRUCache.java` as a source?
+#### What rule target(s) contain file `src/main/java/com/example/cache/LRUCache.java` as a source?
 
 ```sh
 fullname=$(bazel query src/main/java/com/example/cache/LRUCache.java)
@@ -428,6 +430,13 @@
 depend on Y?" If expression X is non-trivial, it may be convenient to bind a
 name to it using `let` to avoid duplication.
 
+<a name="What_are_the_intra_package_direct_rdeps"></a>
+#### What targets directly depend on T, in T's package?
+
+```sh
+bazel query 'let t = T in rdeps(siblings($t), $t, 1)'
+```
+
 <a name="How_do_I_break_a_dependency_"></a>
 ### How do I break a dependency ...
 
diff --git a/site/docs/query.html b/site/docs/query.html
index d919091..c4c3359 100644
--- a/site/docs/query.html
+++ b/site/docs/query.html
@@ -486,6 +486,7 @@
 <a href="#labels">labels</a><br/>
 <a href="#loadfiles">loadfiles</a><br/>
 <a href="#rdeps">rdeps</a><br/>
+<a href="#siblings">siblings</a><br/>
 <a href="#some">some</a><br/>
 <a href="#path-operators">somepath</a><br/>
 <a href="#tests">tests</a><br/>
@@ -556,6 +557,13 @@
   <var>depth</var> parameter is omitted, the search is unbounded.
 </p>
 
+<h3 id="siblings">Dealing with a target's package: siblings</h3>
+<pre>expr ::= siblings(<var>expr</var>)</pre>
+<p>
+  The <code>siblings(<var>x</var>)</code> operator evalutes to the full set of targets that are in
+  the same package as a target in the argument set.
+</p>
+
 <h3 id="some">Arbitrary choice: some</h3>
 <pre>expr ::= some(<var>expr</var>)</pre>
 <p>
diff --git a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
index 2781b0f..fe267b9 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/BlazeQueryEnvironment.java
@@ -127,6 +127,14 @@
   }
 
   @Override
+  public Collection<Target> getSiblingTargetsInPackage(Target target) {
+    Collection<Target> siblings = target.getPackage().getTargets().values();
+    // Ensure that the sibling targets are in the graph being built-up.
+    siblings.forEach(this::getNode);
+    return siblings;
+  }
+
+  @Override
   public QueryTaskFuture<Void> getTargetsMatchingPattern(
       QueryExpression owner, String pattern, Callback<Target> callback) {
     try {
diff --git a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
index 84313ae..a9130f7 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
@@ -691,6 +691,12 @@
 
   @ThreadSafe
   @Override
+  public Collection<Target> getSiblingTargetsInPackage(Target target) {
+    return target.getPackage().getTargets().values();
+  }
+
+  @ThreadSafe
+  @Override
   public QueryTaskFuture<Void> getTargetsMatchingPattern(
       QueryExpression owner, String pattern, Callback<Target> callback) {
     TargetPatternKey targetPatternKey;
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java
index 453a049..1edc658 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/QueryEnvironment.java
@@ -16,6 +16,7 @@
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
+import java.util.Collection;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -142,6 +143,9 @@
     }
   }
 
+  /** Returns all of the targets in <code>target</code>'s package, in some stable order. */
+  Collection<T> getSiblingTargetsInPackage(T target);
+
   /**
    * Invokes {@code callback} with the set of target nodes in the graph for the specified target
    * pattern, in 'blaze build' syntax.
@@ -545,16 +549,17 @@
   ImmutableList<QueryFunction> DEFAULT_QUERY_FUNCTIONS =
       ImmutableList.of(
           new AllPathsFunction(),
-          new BuildFilesFunction(),
-          new LoadFilesFunction(),
           new AttrFunction(),
+          new BuildFilesFunction(),
+          new DepsFunction(),
           new FilterFunction(),
-          new LabelsFunction(),
           new KindFunction(),
+          new LabelsFunction(),
+          new LoadFilesFunction(),
+          new RdepsFunction(),
+          new SiblingsFunction(),
           new SomeFunction(),
           new SomePathFunction(),
           new TestsFunction(),
-          new DepsFunction(),
-          new RdepsFunction(),
           new VisibleFunction());
 }
diff --git a/src/main/java/com/google/devtools/build/lib/query2/engine/SiblingsFunction.java b/src/main/java/com/google/devtools/build/lib/query2/engine/SiblingsFunction.java
new file mode 100644
index 0000000..5d03bfd
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/query2/engine/SiblingsFunction.java
@@ -0,0 +1,72 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.query2.engine;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Argument;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.ArgumentType;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryTaskFuture;
+import com.google.devtools.build.lib.query2.engine.QueryEnvironment.TargetAccessor;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A "siblings" query expression, which computes all of the targets in all of the packages of all
+ * the targets to which the argument evaluates.
+ *
+ * <pre>expr ::= SIBLINGS '(' expr ')'</pre>
+ */
+public class SiblingsFunction implements QueryFunction {
+  @Override
+  public String getName() {
+    return "siblings";
+  }
+
+  @Override
+  public int getMandatoryArguments() {
+    return 1;
+  }
+
+  @Override
+  public Iterable<ArgumentType> getArgumentTypes() {
+    return ImmutableList.of(ArgumentType.EXPRESSION);
+  }
+
+  @Override
+  public <T> QueryTaskFuture<Void> eval(
+      QueryEnvironment<T> env,
+      VariableContext<T> context,
+      final QueryExpression expression,
+      List<Argument> args,
+      final Callback<T> callback) {
+    final TargetAccessor<T> targetAccessor = env.getAccessor();
+    Set<String> packageNames = Sets.newConcurrentHashSet();
+    return env.eval(
+        args.get(0).getExpression(),
+        context,
+        new Callback<T>() {
+          @Override
+          public void process(Iterable<T> partialResult)
+              throws QueryException, InterruptedException {
+            for (T target : partialResult) {
+              if (packageNames.add(targetAccessor.getPackage(target))) {
+                callback.process(env.getSiblingTargetsInPackage(target));
+              }
+            }
+          }
+        });
+  }
+}