Supported "in" operator for all SkylarkIndexable objects.

--
MOS_MIGRATED_REVID=135483694
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
index 107e64b..cbe5056 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AbstractConfiguredTarget.java
@@ -150,6 +150,24 @@
   }
 
   @Override
+  public boolean containsKey(Object key, Location loc) throws EvalException {
+    if (!(key instanceof SkylarkClassObjectConstructor)) {
+      throw new EvalException(loc, String.format(
+          "Type Target only supports querying by object constructors, got %s instead",
+          EvalUtils.getDataTypeName(key)));
+    }
+    SkylarkClassObjectConstructor constructor = (SkylarkClassObjectConstructor) key;
+    SkylarkProviders provider = getProvider(SkylarkProviders.class);
+    if (provider != null) {
+      Object declaredProvider = provider.getDeclaredProvider(constructor.getKey());
+      if (declaredProvider != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
   public String errorMessage(String name) {
     return null;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
index 347c728..1c6eebd 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/AliasConfiguredTarget.java
@@ -96,6 +96,11 @@
   }
 
   @Override
+  public boolean containsKey(Object key, Location loc) throws EvalException {
+    return actual != null && actual.containsKey(key, loc);
+  }
+
+  @Override
   public Target getTarget() {
     return actual == null ? null : actual.getTarget();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
index 131ee54..15989ec 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
@@ -95,17 +95,8 @@
    * <p>Publicly accessible for reflection and compiled Skylark code.
    */
   public static boolean in(Object lval, Object rval, Location location) throws EvalException {
-    if (rval instanceof SkylarkList) {
-      for (Object obj : (SkylarkList) rval) {
-        if (obj.equals(lval)) {
-          return true;
-        }
-      }
-      return false;
-    } else if (rval instanceof SkylarkDict) {
-      return ((SkylarkDict<?, ?>) rval).containsKey(lval);
-    } else if (rval instanceof SkylarkNestedSet) {
-      return ((SkylarkNestedSet) rval).expandedSet().contains(lval);
+    if (rval instanceof SkylarkQueryable) {
+      return ((SkylarkQueryable) rval).containsKey(lval, location);
     } else if (rval instanceof String) {
       if (lval instanceof String) {
         return ((String) rval).contains((String) lval);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
index 49f6c58..fc5e9cf 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkDict.java
@@ -223,6 +223,11 @@
     return this.get(key);
   }
 
+  @Override
+  public final boolean containsKey(Object key, Location loc) throws EvalException {
+    return this.containsKey(key);
+  }
+
   public static <K, V> SkylarkDict<K, V> plus(
       SkylarkDict<? extends K, ? extends V> left,
       SkylarkDict<? extends K, ? extends V> right,
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkIndexable.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkIndexable.java
index 18293f4..b33fb45 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkIndexable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkIndexable.java
@@ -17,9 +17,9 @@
 import com.google.devtools.build.lib.events.Location;
 
 /**
- * Skylark values that support index access, i.e. object[key]
+ * Skylark values that support index access, i.e. `object[key]`
  */
-public interface SkylarkIndexable {
+public interface SkylarkIndexable extends SkylarkQueryable {
 
   /**
    * Returns the value associated with the given key.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
index 81cad2f..1c1c52b 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkList.java
@@ -136,6 +136,16 @@
     return list.get(index);
   }
 
+  @Override
+  public final boolean containsKey(Object key, Location loc) throws EvalException {
+    for (Object obj : this) {
+      if (obj.equals(key)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /**
    * Retrieve a sublist from a SkylarkList.
    * @param start start value
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
index 2a64d79..1657955 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkNestedSet.java
@@ -78,7 +78,7 @@
       + "nested sets."
 )
 @Immutable
-public final class SkylarkNestedSet implements Iterable<Object>, SkylarkValue {
+public final class SkylarkNestedSet implements Iterable<Object>, SkylarkValue, SkylarkQueryable {
 
   private final SkylarkType contentType;
   @Nullable private final List<Object> items;
@@ -272,4 +272,9 @@
     }
     Printer.append(buffer, ")");
   }
+
+  @Override
+  public final boolean containsKey(Object key, Location loc) throws EvalException {
+    return (this.expandedSet().contains(key));
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java
new file mode 100644
index 0000000..b278168
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkQueryable.java
@@ -0,0 +1,29 @@
+// Copyright 2015 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.syntax;
+
+import com.google.devtools.build.lib.events.Location;
+
+/**
+ * Skylark values that support querying by other objects, i.e. `foo in object`.
+ * Semantics of the operation may differ, i.e. dicts check for keys and lists for values.
+ */
+public interface SkylarkQueryable {
+
+  /**
+   * Returns whether the key is in the object.
+   */
+  boolean containsKey(Object key, Location loc) throws EvalException;
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
index 60b9643..719afd4 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleImplementationFunctionsTest.java
@@ -1041,6 +1041,87 @@
   }
 
   @Test
+  public void testDeclaredProvidersInOperator() throws Exception {
+    scratch.file(
+        "test/foo.bzl",
+        "foo_provider = provider()",
+        "bar_provider = provider()",
+        "",
+        "def _inner_impl(ctx):",
+        "    foo = foo_provider()",
+        "    return [foo]",
+        "inner_rule = rule(",
+        "    implementation = _inner_impl,",
+        ")",
+        "",
+        "def _outer_impl(ctx):",
+        "    dep = ctx.attr.deps[0]",
+        "    return struct(",
+        "        foo = (foo_provider in dep),",  // Should be true
+        "        bar = (bar_provider in dep),",  // Should be false
+        "    )",
+        "outer_rule = rule(",
+        "    implementation = _outer_impl,",
+        "    attrs = {",
+        "       'deps': attr.label_list(),",
+        "    }",
+        ")"
+    );
+    scratch.file(
+        "test/BUILD",
+        "load(':foo.bzl', 'inner_rule', 'outer_rule')",
+        "inner_rule(name = 'dep_rule')",
+        "outer_rule(name = 'my_rule', deps = [':dep_rule'])");
+
+    ConfiguredTarget configuredTarget = getConfiguredTarget("//test:my_rule");
+    Object foo = configuredTarget.getProvider(SkylarkProviders.class).getValue("foo");
+    assertThat(foo).isInstanceOf(Boolean.class);
+    assertThat((Boolean) foo).isTrue();
+    Object bar = configuredTarget.getProvider(SkylarkProviders.class).getValue("bar");
+    assertThat(bar).isInstanceOf(Boolean.class);
+    assertThat((Boolean) bar).isFalse();
+  }
+
+  @Test
+  public void testDeclaredProvidersInOperatorInvalidKey() throws Exception {
+    scratch.file(
+        "test/foo.bzl",
+        "foo_provider = provider()",
+        "bar_provider = provider()",
+        "",
+        "def _inner_impl(ctx):",
+        "    foo = foo_provider()",
+        "    return [foo]",
+        "inner_rule = rule(",
+        "    implementation = _inner_impl,",
+        ")",
+        "",
+        "def _outer_impl(ctx):",
+        "    dep = ctx.attr.deps[0]",
+        "    'foo_provider' in dep",  // Should throw an error here
+        "outer_rule = rule(",
+        "    implementation = _outer_impl,",
+        "    attrs = {",
+        "       'deps': attr.label_list(),",
+        "    }",
+        ")"
+    );
+    scratch.file(
+        "test/BUILD",
+        "load(':foo.bzl', 'inner_rule', 'outer_rule')",
+        "inner_rule(name = 'dep_rule')",
+        "outer_rule(name = 'my_rule', deps = [':dep_rule'])");
+
+    try {
+      getConfiguredTarget("//test:my_rule");
+      fail();
+    } catch (AssertionError expected) {
+      assertThat(expected.getMessage()).contains("Type Target only supports querying by object "
+          + "constructors, got string instead");
+    }
+  }
+
+  @Test
   public void testFilesForFileConfiguredTarget() throws Exception {
     Object result =
         evalRuleContextCode(createRuleContext("//foo:bar"), "ruleContext.attr.srcs[0].files");