Add Skylark support for string_list_dict

Convert to Skylark values when destructuring a sequence or map.

--
MOS_MIGRATED_REVID=106591523
diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
index b633ea6..5d87753 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java
@@ -708,6 +708,42 @@
       };
 
   @SkylarkSignature(
+    name = "string_list_dict",
+    doc =
+        "Creates an attribute of type dictionary, mapping from string to list of string. "
+            + "Its default value is dict().",
+    objectType = SkylarkAttr.class,
+    returnType = Attribute.Builder.class,
+    optionalNamedOnly = {
+      @Param(name = DEFAULT_ARG, type = Map.class, defaultValue = "{}", doc = DEFAULT_DOC),
+      @Param(name = MANDATORY_ARG, type = Boolean.class, defaultValue = "False", doc = MANDATORY_DOC
+      ),
+      @Param(name = NON_EMPTY_ARG, type = Boolean.class, defaultValue = "False", doc = NON_EMPTY_DOC
+      )
+    },
+    useAst = true,
+    useEnvironment = true
+  )
+  private static BuiltinFunction stringListDict =
+      new BuiltinFunction("string_list_dict") {
+        public Attribute.Builder<?> invoke(
+            Map<?, ?> defaultO,
+            Boolean mandatory,
+            Boolean nonEmpty,
+            FuncallExpression ast,
+            Environment env)
+            throws EvalException {
+          env.checkLoadingPhase("attr.string_list_dict", ast.getLocation());
+          return createAttribute(
+              EvalUtils.optionMap(
+                  DEFAULT_ARG, defaultO, MANDATORY_ARG, mandatory, NON_EMPTY_ARG, nonEmpty),
+              Type.STRING_LIST_DICT,
+              ast,
+              env);
+        }
+      };
+
+  @SkylarkSignature(
     name = "license",
     doc = "Creates an attribute of type license. Its default value is NO_LICENSE.",
     // TODO(bazel-team): Implement proper license support for Skylark.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
index e14fbad..3cc7789 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
@@ -888,14 +888,14 @@
       mandatoryPositionals = {
         @Param(name = "self", type = Map.class, doc = "This object."),
         @Param(name = "key", type = Object.class, doc = "The index or key to access.")},
-      useLocation = true)
+      useLocation = true, useEnvironment = true)
   private static BuiltinFunction dictIndexOperator = new BuiltinFunction("$index") {
     public Object invoke(Map<?, ?> self, Object key,
-        Location loc) throws EvalException, ConversionException {
+        Location loc, Environment env) throws EvalException, ConversionException {
       if (!self.containsKey(key)) {
         throw new EvalException(loc, Printer.format("Key %r not found in dictionary", key));
       }
-      return self.get(key);
+      return SkylarkType.convertToSkylark(self.get(key), env);
     }
   };
 
@@ -905,15 +905,15 @@
       mandatoryPositionals = {
         @Param(name = "self", type = MutableList.class, doc = "This list."),
         @Param(name = "key", type = Object.class, doc = "The index or key to access.")},
-      useLocation = true)
+      useLocation = true, useEnvironment = true)
   private static BuiltinFunction listIndexOperator = new BuiltinFunction("$index") {
       public Object invoke(MutableList self, Object key,
-          Location loc) throws EvalException, ConversionException {
+          Location loc, Environment env) throws EvalException, ConversionException {
         if (self.isEmpty()) {
           throw new EvalException(loc, "List is empty");
         }
         int index = getListIndex(key, self.size(), loc);
-        return self.getList().get(index);
+        return SkylarkType.convertToSkylark(self.getList().get(index), env);
       }
     };
 
@@ -923,15 +923,15 @@
       mandatoryPositionals = {
         @Param(name = "self", type = Tuple.class, doc = "This tuple."),
         @Param(name = "key", type = Object.class, doc = "The index or key to access.")},
-      useLocation = true)
+      useLocation = true, useEnvironment = true)
   private static BuiltinFunction tupleIndexOperator = new BuiltinFunction("$index") {
       public Object invoke(Tuple self, Object key,
-          Location loc) throws EvalException, ConversionException {
+          Location loc, Environment env) throws EvalException, ConversionException {
         if (self.isEmpty()) {
           throw new EvalException(loc, "tuple is empty");
         }
         int index = getListIndex(key, self.size(), loc);
-        return self.getList().get(index);
+        return SkylarkType.convertToSkylark(self.getList().get(index), env);
       }
     };
 
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 67b7db6..74a3eae 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
@@ -227,7 +227,7 @@
      * @param contents the contents of the list
      * @return a Skylark list containing the specified arguments as elements.
      */
-    public static MutableList of(Environment env, Object... contents) {
+    public static MutableList of(@Nullable Environment env, Object... contents) {
       return new MutableList(ImmutableList.copyOf(contents), env);
     }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index 1c57974..23458c7 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -132,6 +132,13 @@
   }
 
   @Test
+  public void testStringListDictAttr() throws Exception {
+    Object result = evalRuleClassCode("attr.string_list_dict(default = {'a': ['b', 'c']})");
+    Attribute attr = ((Attribute.Builder<?>) result).build("a1");
+    assertEquals(Type.STRING_LIST_DICT, attr.getType());
+  }
+
+  @Test
   public void testAttrAllowedFileTypesAnyFile() throws Exception {
     Object result = evalRuleClassCode("attr.label_list(allow_files = True)");
     Attribute attr = ((Attribute.Builder<?>) result).build("a1");