Raise error if we find an unknown type in native.rule().

Handle more types:

* Boolean
* TriState
* SkylarkValue (eg. FileSetEntry)
* skip Licenses, Distribs.

--
MOS_MIGRATED_REVID=112690550
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index f738a0d..824ecc1 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -35,6 +35,7 @@
 import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import com.google.devtools.build.lib.syntax.AssignmentStatement;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.BuildFileAST;
@@ -842,7 +843,7 @@
   }
 
   @Nullable
-  private static Map<String, Object> targetDict(Target target) {
+  private static Map<String, Object> targetDict(Target target) throws NotRepresentableException {
     if (target == null && !(target instanceof Rule)) {
       return null;
     }
@@ -855,11 +856,23 @@
         continue;
       }
 
-      Object val = skylarkifyValue(cont.getAttr(attr.getName()), target.getPackage());
-      if (val == null) {
+      if (attr.getName().equals("distribs")) {
+        // attribute distribs: cannot represent type class java.util.Collections$SingletonSet
+        // in Skylark: [INTERNAL].
         continue;
       }
-      values.put(attr.getName(), val);
+
+      try {
+        Object val = skylarkifyValue(cont.getAttr(attr.getName()), target.getPackage());
+        if (val == null) {
+          continue;
+        }
+        values.put(attr.getName(), val);
+      } catch (NotRepresentableException e) {
+        throw new NotRepresentableException(
+            String.format(
+                "target %s, attribute %s: %s", target.getName(), attr.getName(), e.getMessage()));
+      }
     }
 
     values.put("name", rule.getName());
@@ -867,24 +880,47 @@
     return values;
   }
 
+  static class NotRepresentableException extends EvalException {
+    NotRepresentableException(String msg) {
+      super(null, msg);
+    }
+  };
+
   /**
    * Converts back to type that will work in BUILD and skylark,
    * such as string instead of label, SkylarkList instead of List,
    * Returns null if we don't want to export the value.
    *
    * <p>All of the types returned are immutable. If we want, we can change this to
-   * immutable in the future, but this is the safe choice for now.
+   * immutable in the future, but this is the safe choice for now.o
    */
-  private static Object skylarkifyValue(Object val, Package pkg) {
+  @Nullable
+  private static Object skylarkifyValue(Object val, Package pkg) throws NotRepresentableException {
     if (val == null) {
       return null;
     }
+    if (val instanceof Boolean) {
+      return val;
+    }
     if (val instanceof Integer) {
       return val;
     }
     if (val instanceof String) {
       return val;
     }
+
+    // Maybe we should have an interface for types so they can represent themselves to skylark?
+    if (val instanceof TriState) {
+      switch ((TriState) val) {
+        case AUTO:
+          return new Integer(-1);
+        case YES:
+          return new Integer(1);
+        case NO:
+          return new Integer(0);
+      }
+    }
+
     if (val instanceof Label) {
       Label l = (Label) val;
       if (l.getPackageName().equals(pkg.getName())) {
@@ -892,10 +928,16 @@
       }
       return l.getCanonicalForm();
     }
+
     if (val instanceof List) {
       List<Object> l = new ArrayList<>();
       for (Object o : (List) val) {
-        l.add(skylarkifyValue(o, pkg));
+        Object elt = skylarkifyValue(o, pkg);
+        if (elt == null) {
+          continue;
+        }
+
+        l.add(elt);
       }
 
       return SkylarkList.Tuple.copyOf(l);
@@ -903,7 +945,14 @@
     if (val instanceof Map) {
       Map<Object, Object> m = new TreeMap<>();
       for (Map.Entry<?, ?> e : ((Map<?, ?>) val).entrySet()) {
-        m.put(skylarkifyValue(e.getKey(), pkg), skylarkifyValue(e.getValue(), pkg));
+        Object key = skylarkifyValue(e.getKey(), pkg);
+        Object mapVal = skylarkifyValue(e.getValue(), pkg);
+
+        if (key == null || mapVal == null) {
+          continue;
+        }
+
+        m.put(key, mapVal);
       }
       return m;
     }
@@ -914,12 +963,24 @@
       return null;
     }
 
-    // Add any types we want to allow through here.
-    return null;
+    if (val instanceof SkylarkValue) {
+      return val;
+    }
+
+    if (val instanceof License) {
+      // TODO(bazel-team): convert License.getLicenseTypes() to a list of strings.
+      return null;
+    }
+
+    // We are explicit about types we don't understand so we minimize changes to existing callers
+    // if we add more types that we can represent.
+    throw new NotRepresentableException(
+        String.format(
+            "cannot represent %s (%s) in skylark", val.toString(), val.getClass().toString()));
   }
 
-  static Map callGetRulesFunction(FuncallExpression ast, Environment env) throws EvalException {
 
+  static Map callGetRulesFunction(FuncallExpression ast, Environment env) throws EvalException {
     PackageContext context = getContext(env, ast);
     Collection<Target> targets = context.pkgBuilder.getTargets();
 
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
index 6cbbf23..74a9f4c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
@@ -198,17 +198,31 @@
     }
   }
 
-  private IllegalStateException badCallException(Location loc, Throwable e, Object... args) {
-    // If this happens, it's a bug in our code.
-    return new IllegalStateException(String.format("%s%s (%s)\n"
-            + "while calling %s with args %s\nJava parameter types: %s\nSkylark type checks: %s",
-            (loc == null) ? "" : loc + ": ",
-            e.getClass().getName(), e.getMessage(), this,
-            Arrays.asList(args),
-            Arrays.asList(invokeMethod.getParameterTypes()),
-            signature.getTypes()), e);
+  private static String stacktraceToString(StackTraceElement[] elts) {
+    StringBuilder b = new StringBuilder();
+    for (StackTraceElement e : elts) {
+      b.append(e.toString());
+      b.append("\n");
+    }
+    return b.toString();
   }
 
+  private IllegalStateException badCallException(Location loc, Throwable e, Object... args) {
+    // If this happens, it's a bug in our code.
+    return new IllegalStateException(
+        String.format(
+            "%s%s (%s)\n"
+                + "while calling %s with args %s\n"
+                + "Java parameter types: %s\nSkylark type checks: %s",
+            (loc == null) ? "" : loc + ": ",
+            Arrays.asList(args),
+            e.getClass().getName(),
+            stacktraceToString(e.getStackTrace()),
+            this,
+            Arrays.asList(invokeMethod.getParameterTypes()),
+            signature.getTypes()),
+        e);
+  }
 
   /** Configure the reflection mechanism */
   @Override