Improve error message when requesting an invalid toolchain type from the context.

Fixes #3428.

Change-Id: Ib3f45bc6856651cfb29d338d0b4480ba1dd77cea
PiperOrigin-RevId: 163760940
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
index fc52bd6..23db96c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
@@ -51,31 +51,42 @@
 )
 public class ToolchainContext {
   public static ToolchainContext create(
-      List<Label> requiredToolchains, ImmutableBiMap<Label, Label> resolvedLabels) {
+      String targetDescription,
+      List<Label> requiredToolchains,
+      ImmutableBiMap<Label, Label> resolvedLabels) {
     ToolchainContext toolchainContext =
-        new ToolchainContext(requiredToolchains, new ResolvedToolchainLabels(resolvedLabels));
+        new ToolchainContext(
+            targetDescription, requiredToolchains, new ResolvedToolchainLabels(resolvedLabels));
     return toolchainContext;
   }
 
+  /** Description of the target the toolchain context applies to, for use in error messages. */
+  private final String targetDescription;
+
+  /** The toolchain types that are required by the target. */
   private final ImmutableList<Label> requiredToolchains;
 
-  // Map from toolchain type labels to actual resolved toolchain labels.
+  /** Map from toolchain type labels to actual resolved toolchain labels. */
   private final ResolvedToolchainLabels resolvedToolchainLabels;
 
-  // Stores the actual ToolchainInfo provider for each toolchain type.
-  private ResolvedToolchainProviders resolvedToolchainProviders =
-      ResolvedToolchainProviders.empty();
+  /** Stores the actual ToolchainInfo provider for each toolchain type. */
+  private ResolvedToolchainProviders resolvedToolchainProviders;
 
   private ToolchainContext(
-      List<Label> requiredToolchains, ResolvedToolchainLabels resolvedToolchainLabels) {
+      String targetDescription,
+      List<Label> requiredToolchains,
+      ResolvedToolchainLabels resolvedToolchainLabels) {
+    this.targetDescription = targetDescription;
     this.requiredToolchains = ImmutableList.copyOf(requiredToolchains);
     this.resolvedToolchainLabels = resolvedToolchainLabels;
+    this.resolvedToolchainProviders =
+        new ResolvedToolchainProviders(ImmutableMap.<Label, ToolchainInfo>of());
   }
 
   public void resolveToolchains(OrderedSetMultimap<Attribute, ConfiguredTarget> prerequisiteMap) {
     if (!this.requiredToolchains.isEmpty()) {
       this.resolvedToolchainProviders =
-          ResolvedToolchainProviders.create(resolvedToolchainLabels, prerequisiteMap);
+          new ResolvedToolchainProviders(findToolchains(resolvedToolchainLabels, prerequisiteMap));
     }
   }
 
@@ -109,38 +120,36 @@
     }
   }
 
-  /** Tracks the mapping from toolchain type label to {@link ToolchainInfo} provider. */
-  private static class ResolvedToolchainProviders implements SkylarkValue, SkylarkIndexable {
-    private static ResolvedToolchainProviders empty() {
-      return new ResolvedToolchainProviders(ImmutableMap.<Label, ToolchainInfo>of());
-    }
+  private static ImmutableMap<Label, ToolchainInfo> findToolchains(
+      ResolvedToolchainLabels resolvedToolchainLabels,
+      OrderedSetMultimap<Attribute, ConfiguredTarget> prerequisiteMap) {
+    // Find the prerequisites associated with the $toolchains attribute.
+    Optional<Attribute> toolchainAttribute =
+        prerequisiteMap
+            .keys()
+            .stream()
+            .filter(attribute -> attribute.getName().equals(PlatformSemantics.TOOLCHAINS_ATTR))
+            .findFirst();
+    Preconditions.checkState(
+        toolchainAttribute.isPresent(),
+        "No toolchains attribute found while loading resolved toolchains");
 
-    private static ResolvedToolchainProviders create(
-        ResolvedToolchainLabels resolvedToolchainLabels,
-        OrderedSetMultimap<Attribute, ConfiguredTarget> prerequisiteMap) {
-      // Find the prerequisites associated with the $toolchains attribute.
-      Optional<Attribute> toolchainAttribute =
-          prerequisiteMap
-              .keys()
-              .stream()
-              .filter(attribute -> attribute.getName().equals(PlatformSemantics.TOOLCHAINS_ATTR))
-              .findFirst();
-      Preconditions.checkState(
-          toolchainAttribute.isPresent(),
-          "No toolchains attribute found while loading resolved toolchains");
-
-      ImmutableMap.Builder<Label, ToolchainInfo> toolchains = new ImmutableMap.Builder<>();
-      for (ConfiguredTarget target : prerequisiteMap.get(toolchainAttribute.get())) {
-        Label discoveredLabel = target.getLabel();
-        Label toolchainType = resolvedToolchainLabels.getType(discoveredLabel);
-        if (toolchainType != null) {
-          ToolchainInfo toolchainInfo = PlatformProviderUtils.toolchain(target);
-          toolchains.put(toolchainType, toolchainInfo);
-        }
+    ImmutableMap.Builder<Label, ToolchainInfo> toolchains = new ImmutableMap.Builder<>();
+    for (ConfiguredTarget target : prerequisiteMap.get(toolchainAttribute.get())) {
+      Label discoveredLabel = target.getLabel();
+      Label toolchainType = resolvedToolchainLabels.getType(discoveredLabel);
+      if (toolchainType != null) {
+        ToolchainInfo toolchainInfo = PlatformProviderUtils.toolchain(target);
+        toolchains.put(toolchainType, toolchainInfo);
       }
-      return new ResolvedToolchainProviders(toolchains.build());
     }
 
+    return toolchains.build();
+  }
+
+  /** Tracks the mapping from toolchain type label to {@link ToolchainInfo} provider. */
+  private class ResolvedToolchainProviders implements SkylarkValue, SkylarkIndexable {
+
     private final ImmutableMap<Label, ToolchainInfo> toolchains;
 
     private ResolvedToolchainProviders(ImmutableMap<Label, ToolchainInfo> toolchains) {
@@ -187,6 +196,19 @@
     @Override
     public ToolchainInfo getIndex(Object key, Location loc) throws EvalException {
       Label toolchainType = transformKey(key, loc);
+
+      if (!requiredToolchains.contains(toolchainType)) {
+        throw new EvalException(
+            loc,
+            String.format(
+                "In %s, toolchain type %s was requested but only types [%s] are configured",
+                targetDescription,
+                toolchainType,
+                requiredToolchains
+                    .stream()
+                    .map(toolchain -> toolchain.toString())
+                    .collect(joining())));
+      }
       return toolchains.get(toolchainType);
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index 234ed0b..57b09e2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -278,7 +278,12 @@
         ImmutableList<Label> requiredToolchains = aspect.getDefinition().getRequiredToolchains();
         toolchainContext =
             ToolchainUtil.createToolchainContext(
-                env, requiredToolchains, key.getAspectConfiguration());
+                env,
+                String.format(
+                    "aspect %s applied to %s",
+                    aspect.getDescriptor().getDescription(), target.toString()),
+                requiredToolchains,
+                key.getAspectConfiguration());
       } catch (ToolchainContextException e) {
         // TODO(katre): better error handling
         throw new AspectCreationException(e.getMessage());
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index 4e3356d..1b783b9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -234,10 +234,11 @@
       // Determine what toolchains are needed by this target.
       ToolchainContext toolchainContext = null;
       if (target instanceof Rule) {
-        ImmutableList<Label> requiredToolchains =
-            ((Rule) target).getRuleClassObject().getRequiredToolchains();
+        Rule rule = ((Rule) target);
+        ImmutableList<Label> requiredToolchains = rule.getRuleClassObject().getRequiredToolchains();
         toolchainContext =
-            ToolchainUtil.createToolchainContext(env, requiredToolchains, configuration);
+            ToolchainUtil.createToolchainContext(
+                env, rule.toString(), requiredToolchains, configuration);
         if (env.valuesMissing()) {
           return null;
         }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java
index 3afe90a..4b1840a 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java
@@ -49,11 +49,15 @@
    * of the {@link ToolchainResolutionFunction}.
    */
   public static ToolchainContext createToolchainContext(
-      Environment env, List<Label> requiredToolchains, BuildConfiguration configuration)
+      Environment env,
+      String targetDescription,
+      List<Label> requiredToolchains,
+      BuildConfiguration configuration)
       throws ToolchainContextException, InterruptedException {
     ImmutableBiMap<Label, Label> resolvedLabels =
         resolveToolchainLabels(env, requiredToolchains, configuration);
-    ToolchainContext toolchainContext = ToolchainContext.create(requiredToolchains, resolvedLabels);
+    ToolchainContext toolchainContext =
+        ToolchainContext.create(targetDescription, requiredToolchains, resolvedLabels);
     return toolchainContext;
   }