Encode ViewCreation failures with FailureDetails

Bazel's analysis phase can fail with a ViewCreationFailedException,
encompassing a variety of loading, analysis, and post-analysis evaluation
problems. This change equips that exception with FailureDetails and
eliminates the final use of (non-success) DetailedExitCode without
FailureDetails.

Before this change, a ViewCreationFailedException (VCFE) always led to a
numerical exit code of 1. To ensure backwards compatibility, this change
uses the DetailedExitCode factory method which overrides the numerical
exit code metadata from failure_detail.proto. Most of the detailed failure
subcategories that can end up in a VCFE do have metadata specifying 1 as
their numerical exit code, but not all; e.g., post-analysis query
evaluation failures can cause a VCFE and, as query failures, have 2 or 7
for their metadata.

Ensuring the propagation of failure details from analysis to the VCFE
required the following work.

SkyframeBuildView translated analysis exceptions, designated via the
marker interface SaneAnalysisException, into VCFEs. Likewise, it
translated the loading-phase exceptions NoSuch{Package,Target}Exception
into VCFEs. By making SaneAnalysisException extend DetailedException,
that conversion now propagates any detailed failures described by those
three exception types.

SaneAnalysisException has six implementations. This change implements
DetailedException across them. Doing so with AspectCreationException and
ConfiguredValueCreationException involved moderately complex work. In
AspectFunction and ConfiguredTargetFunction, these two exception types
get instantiated when a nested set of "Cause"s is non-empty (and also in
several other more straightforward contexts).

"Cause" had already been equipped with a DetailedExitCode property, but
AnalysisFailedCause didn't support it (Cause's other implementations
did). This change implements the property for AnalysisFailedCause.

The nested set traversal in
ConfiguredTargetFunction.getPrioritizedDetailedExitCode does not involve
any deep traversals, though that's not obvious. If the nested set
builder ever gets another nested set added to it via addTransitive, then
it does so while handling a dependency's exception, which results in a
DependencyEvaluationException, whose handling avoids the call to
getPrioritizedDetailedExitCode.

This change does *not* detail analysis failures in maximum resolution. To
do so, a future change would need to enhance the error-event-sensitive
failure detection in AspectFunction and ConfiguredTargetFunction, using
e.g. the events-with-properties strategy used by
ErrorSensingEventHandler. It would also need to replace the non-detailed
AspectCreationException and ConfiguredValueCreationException
constructors.

RELNOTES: None.
PiperOrigin-RevId: 333827282
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index 74f0b84..7dd75d0 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -333,16 +333,21 @@
               Label.parseAbsolute(
                   bzlFileLoadLikeString, /* repositoryMapping= */ ImmutableMap.of());
         } catch (LabelSyntaxException e) {
+          String errorMessage = String.format("Invalid aspect '%s': %s", aspect, e.getMessage());
           throw new ViewCreationFailedException(
-              String.format("Invalid aspect '%s': %s", aspect, e.getMessage()), e);
+              errorMessage,
+              createFailureDetail(errorMessage, Analysis.Code.ASPECT_LABEL_SYNTAX_ERROR),
+              e);
         }
 
         String starlarkFunctionName = aspect.substring(delimiterPosition + 1);
         for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
           if (targetSpec.getConfiguration() != null
               && targetSpec.getConfiguration().trimConfigurationsRetroactively()) {
+            String errorMessage =
+                "Aspects were requested, but are not supported in retroactive trimming mode.";
             throw new ViewCreationFailedException(
-                "Aspects were requested, but are not supported in retroactive trimming mode.");
+                errorMessage, createFailureDetail(errorMessage, Analysis.Code.ASPECT_PREREQ_UNMET));
           }
           aspectConfigurations.put(
               Pair.of(targetSpec.getLabel(), aspect), targetSpec.getConfiguration());
@@ -364,8 +369,11 @@
           for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
             if (targetSpec.getConfiguration() != null
                 && targetSpec.getConfiguration().trimConfigurationsRetroactively()) {
+              String errorMessage =
+                  "Aspects were requested, but are not supported in retroactive trimming mode.";
               throw new ViewCreationFailedException(
-                  "Aspects were requested, but are not supported in retroactive trimming mode.");
+                  errorMessage,
+                  createFailureDetail(errorMessage, Analysis.Code.ASPECT_PREREQ_UNMET));
             }
             // For invoking top-level aspects, use the top-level configuration for both the
             // aspect and the base target while the top-level configuration is untrimmed.
@@ -379,7 +387,9 @@
                     configuration));
           }
         } else {
-          throw new ViewCreationFailedException("Aspect '" + aspect + "' is unknown");
+          String errorMessage = "Aspect '" + aspect + "' is unknown";
+          throw new ViewCreationFailedException(
+              errorMessage, createFailureDetail(errorMessage, Analysis.Code.ASPECT_NOT_FOUND));
         }
       }
     }
@@ -617,6 +627,13 @@
     return null;
   }
 
+  private static FailureDetail createFailureDetail(String errorMessage, Analysis.Code code) {
+    return FailureDetail.newBuilder()
+        .setMessage(errorMessage)
+        .setAnalysis(Analysis.newBuilder().setCode(code))
+        .build();
+  }
+
   private static NestedSet<Artifact> getBaselineCoverageArtifacts(
       Collection<ConfiguredTarget> configuredTargets,
       ArtifactsToOwnerLabels.Builder topLevelArtifactsToOwnerLabels) {