Skylark: 'in' works on sets.

--
MOS_MIGRATED_REVID=89977206
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 39f220e..7b8c2df 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
@@ -36,9 +36,7 @@
 
   private final Operator operator;
 
-  public BinaryOperatorExpression(Operator operator,
-                                  Expression lhs,
-                                  Expression rhs) {
+  public BinaryOperatorExpression(Operator operator, Expression lhs, Expression rhs) {
     this.lhs = lhs;
     this.rhs = rhs;
     this.operator = operator;
@@ -250,6 +248,8 @@
           return ((Collection<?>) rval).contains(lval);
         } else if (rval instanceof Map<?, ?>) {
           return ((Map<?, ?>) rval).containsKey(lval);
+        } else if (rval instanceof SkylarkNestedSet) {
+          return ((SkylarkNestedSet) rval).expandedSet().contains(lval);
         } else if (rval instanceof String) {
           if (lval instanceof String) {
             return ((String) rval).contains((String) lval);
@@ -259,7 +259,7 @@
           }
         } else {
           throw new EvalException(getLocation(),
-              "in operator only works on lists, tuples, dictionaries and strings");
+              "in operator only works on lists, tuples, sets, dicts and strings");
         }
       }
 
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 233cfdd..39441b0 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
@@ -26,6 +26,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import javax.annotation.Nullable;
 
@@ -183,6 +184,10 @@
     return (NestedSet<T>) set;
   }
 
+  public Set<?> expandedSet() {
+    return set.toSet();
+  }
+
   // For some reason this cast is unsafe in Java
   @SuppressWarnings("unchecked")
   @Override
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index 1637525..cb52cc4 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -471,60 +471,24 @@
   }
 
   @Test
-  public void testInOnListContains() throws Exception {
+  public void testInOperator() throws Exception {
     assertEquals(Boolean.TRUE, eval("'b' in ['a', 'b']"));
-  }
-
-  @Test
-  public void testInOnListDoesNotContain() throws Exception {
     assertEquals(Boolean.FALSE, eval("'c' in ['a', 'b']"));
-  }
-
-  @Test
-  public void testInOnTupleContains() throws Exception {
     assertEquals(Boolean.TRUE, eval("'b' in ('a', 'b')"));
-  }
-
-  @Test
-  public void testInOnTupleDoesNotContain() throws Exception {
     assertEquals(Boolean.FALSE, eval("'c' in ('a', 'b')"));
-  }
-
-  @Test
-  public void testInOnDictContains() throws Exception {
     assertEquals(Boolean.TRUE, eval("'b' in {'a' : 1, 'b' : 2}"));
-  }
-
-  @Test
-  public void testInOnDictDoesNotContainKey() throws Exception {
     assertEquals(Boolean.FALSE, eval("'c' in {'a' : 1, 'b' : 2}"));
-  }
-
-  @Test
-  public void testInOnDictDoesNotContainVal() throws Exception {
     assertEquals(Boolean.FALSE, eval("1 in {'a' : 1, 'b' : 2}"));
-  }
-
-  @Test
-  public void testInOnStringContains() throws Exception {
     assertEquals(Boolean.TRUE, eval("'b' in 'abc'"));
-  }
-
-  @Test
-  public void testInOnStringDoesNotContain() throws Exception {
     assertEquals(Boolean.FALSE, eval("'d' in 'abc'"));
   }
 
   @Test
-  public void testInOnStringLeftNotString() throws Exception {
+  public void testInFail() throws Exception {
     checkEvalError("1 in '123'",
         "in operator only works on strings if the left operand is also a string");
-  }
-
-  @Test
-  public void testInFailsOnNonIterable() throws Exception {
     checkEvalError("'a' in 1",
-        "in operator only works on lists, tuples, dictionaries and strings");
+        "in operator only works on lists, tuples, sets, dicts and strings");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index da38aa0..b55f428 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -534,6 +534,13 @@
   }
 
   @Test
+  public void testInSet() throws Exception {
+    assertEquals(Boolean.TRUE, eval("'b' in set(['a', 'b'])"));
+    assertEquals(Boolean.FALSE, eval("'c' in set(['a', 'b'])"));
+    assertEquals(Boolean.FALSE, eval("1 in set(['a', 'b'])"));
+  }
+
+  @Test
   public void testClassObjectCannotAccessNestedSet() throws Exception {
     env.update("mock", new MockClassObject());
     checkEvalError(parseFileForSkylark("v = mock.nset", MOCK_TYPES), env,