Use Object for what instead of String

The string is only used for error messaging on failure. Instead of constructing
lots of strings allow passing in a generic Object whos toString is used for the
error message. This allows us to bypass a lot of garbage string generation, at
th expense of a little indirection.

Chose Object over some specialized type, since we use inline strings frequently
as well.

--
MOS_MIGRATED_REVID=133741724
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
index 3a8c69c..825efb5 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
@@ -28,7 +28,6 @@
 import com.google.devtools.build.lib.syntax.Type.ConversionException;
 import com.google.devtools.build.lib.syntax.Type.DictType;
 import com.google.devtools.build.lib.syntax.Type.ListType;
-
 import java.util.Collection;
 import java.util.Collections;
 import java.util.LinkedHashMap;
@@ -36,7 +35,6 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-
 import javax.annotation.Nullable;
 
 /**
@@ -87,7 +85,7 @@
     }
 
     @Override
-    public DistributionType convert(Object x, String what, Object context) {
+    public DistributionType convert(Object x, Object what, Object context) {
       throw new UnsupportedOperationException();
     }
 
@@ -158,7 +156,7 @@
    * <p>The caller is responsible for casting the returned value appropriately.
    */
   public static <T> Object selectableConvert(
-      Type type, Object x, String what, @Nullable Label context)
+      Type type, Object x, Object what, @Nullable Label context)
       throws ConversionException {
     if (x instanceof com.google.devtools.build.lib.syntax.SelectorList) {
       return new SelectorList<T>(
@@ -177,7 +175,7 @@
     }
 
     @Override
-    public FilesetEntry convert(Object x, String what, Object context)
+    public FilesetEntry convert(Object x, Object what, Object context)
         throws ConversionException {
       if (!(x instanceof FilesetEntry)) {
         throw new ConversionException(this, x, what);
@@ -233,7 +231,7 @@
     }
 
     @Override
-    public Label convert(Object x, String what, Object context)
+    public Label convert(Object x, Object what, Object context)
         throws ConversionException {
       if (x instanceof Label) {
         return (Label) x;
@@ -259,7 +257,7 @@
     }
 
     @Override
-    public License convert(Object x, String what, Object context) throws ConversionException {
+    public License convert(Object x, Object what, Object context) throws ConversionException {
       try {
         List<String> licenseStrings = STRING_LIST.convert(x, what);
         return License.parseLicense(licenseStrings);
@@ -303,7 +301,7 @@
     }
 
     @Override
-    public Set<DistributionType> convert(Object x, String what, Object context)
+    public Set<DistributionType> convert(Object x, Object what, Object context)
         throws ConversionException {
       try {
         List<String> distribStrings = STRING_LIST.convert(x, what);
@@ -366,7 +364,7 @@
     }
 
     @Override
-    public Label convert(Object x, String what, Object context)
+    public Label convert(Object x, Object what, Object context)
         throws ConversionException {
 
       String value;
@@ -401,7 +399,7 @@
     private final List<Selector<T>> elements;
 
     @VisibleForTesting
-    SelectorList(List<Object> x, String what, @Nullable Label context,
+    SelectorList(List<Object> x, Object what, @Nullable Label context,
         Type<T> originalType) throws ConversionException {
       if (x.size() > 1 && originalType.concat(ImmutableList.<T>of()) == null) {
         throw new ConversionException(
@@ -411,7 +409,7 @@
       ImmutableList.Builder<Selector<T>> builder = ImmutableList.builder();
       for (Object elem : x) {
         if (elem instanceof SelectorValue) {
-          builder.add(new Selector<T>(((SelectorValue) elem).getDictionary(), what,
+          builder.add(new Selector<>(((SelectorValue) elem).getDictionary(), what,
               context, originalType, ((SelectorValue) elem).getNoMatchError()));
         } else {
           T directValue = originalType.convert(elem, what, context);
@@ -482,7 +480,7 @@
     /**
      * Creates a new Selector using the default error message when no conditions match.
      */
-    Selector(ImmutableMap<?, ?> x, String what, @Nullable Label context, Type<T> originalType)
+    Selector(ImmutableMap<?, ?> x, Object what, @Nullable Label context, Type<T> originalType)
         throws ConversionException {
       this(x, what, context, originalType, "");
     }
@@ -490,7 +488,7 @@
     /**
      * Creates a new Selector with a custom error message for when no conditions match.
      */
-    Selector(ImmutableMap<?, ?> x, String what, @Nullable Label context, Type<T> originalType,
+    Selector(ImmutableMap<?, ?> x, Object what, @Nullable Label context, Type<T> originalType,
         String noMatchError) throws ConversionException {
       this.originalType = originalType;
       LinkedHashMap<Label, T> result = new LinkedHashMap<>();
@@ -633,7 +631,7 @@
 
     // Like BooleanType, this must handle integers as well.
     @Override
-    public TriState convert(Object x, String what, Object context)
+    public TriState convert(Object x, Object what, Object context)
         throws ConversionException {
       if (x instanceof TriState) {
         return (TriState) x;
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index cba10a7..53aabfa 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -1727,9 +1727,11 @@
    */
   private static Object convertFromBuildLangType(Rule rule, Attribute attr, Object buildLangValue)
       throws ConversionException {
-    String what = "attribute '" + attr.getName() + "' in '" + rule.getRuleClass() + "' rule";
-    Object converted =
-        BuildType.selectableConvert(attr.getType(), buildLangValue, what, rule.getLabel());
+    Object converted = BuildType.selectableConvert(
+        attr.getType(),
+        buildLangValue,
+        new AttributeConversionContext(attr.getName(), rule.getRuleClass()),
+        rule.getLabel());
 
     if ((converted instanceof SelectorList<?>) && !attr.isConfigurable()) {
       throw new ConversionException(
@@ -1749,6 +1751,28 @@
   }
 
   /**
+   * Provides a {@link #toString()} description of the attribute being converted for
+   * {@link BuildType#selectableConvert}. This is preferred over a raw string to avoid uselessly
+   * constructing strings which are never used. A separate class instead of inline to avoid
+   * accidental memory leaks.
+   */
+  private static class AttributeConversionContext {
+    private final String attrName;
+    private final String ruleClass;
+
+    AttributeConversionContext(String attrName, String ruleClass) {
+      this.attrName = attrName;
+      this.ruleClass = ruleClass;
+    }
+
+    @Override
+    public String toString() {
+      return "attribute '" + attrName + "' in '" + ruleClass + "' rule";
+    }
+  }
+
+
+  /**
    * Verifies that the rule has a valid value for the attribute according to its allowed values.
    *
    * <p>If the value for the given attribute on the given rule is invalid, an error will be recorded