Remove remaining undetailed BuildFailedException constructor

Some loading-phase-related failure modes have placeholder detailed codes
to be improved when loading-phase failures are detailed.

RELNOTES: None.
PiperOrigin-RevId: 318366815
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
index 8969094..effbe9a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
@@ -45,15 +45,6 @@
   private final boolean errorAlreadyShown;
   private final DetailedExitCode detailedExitCode;
 
-  public BuildFailedException(String message) {
-    this(
-        message,
-        /*catastrophic=*/ false,
-        NestedSetBuilder.emptySet(Order.STABLE_ORDER),
-        /*errorAlreadyShown=*/ false,
-        DetailedExitCode.justExitCode(ExitCode.BUILD_FAILURE));
-  }
-
   public BuildFailedException(String message, DetailedExitCode detailedExitCode) {
     this(
         message,
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisResult.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisResult.java
index ae03826..3c63b34 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisResult.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisResult.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.actions.ActionGraph;
 import com.google.devtools.build.lib.actions.PackageRoots;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
 import java.util.Collection;
 import javax.annotation.Nullable;
@@ -34,7 +35,7 @@
   private final ImmutableSet<ConfiguredTarget> targetsToBuild;
   @Nullable private final ImmutableList<ConfiguredTarget> targetsToTest;
   private final ImmutableSet<ConfiguredTarget> targetsToSkip;
-  @Nullable private final String error;
+  @Nullable private final FailureDetail failureDetail;
   private final ActionGraph actionGraph;
   private final ArtifactsToOwnerLabels topLevelArtifactsToOwnerLabels;
   private final ImmutableSet<ConfiguredTarget> parallelTests;
@@ -52,7 +53,7 @@
       ImmutableMap<AspectKey, ConfiguredAspect> aspects,
       @Nullable ImmutableList<ConfiguredTarget> targetsToTest,
       ImmutableSet<ConfiguredTarget> targetsToSkip,
-      @Nullable String error,
+      @Nullable FailureDetail failureDetail,
       ActionGraph actionGraph,
       ArtifactsToOwnerLabels topLevelArtifactsToOwnerLabels,
       ImmutableSet<ConfiguredTarget> parallelTests,
@@ -67,7 +68,7 @@
     this.aspects = aspects;
     this.targetsToTest = targetsToTest;
     this.targetsToSkip = targetsToSkip;
-    this.error = error;
+    this.failureDetail = failureDetail;
     this.actionGraph = actionGraph;
     this.topLevelArtifactsToOwnerLabels = topLevelArtifactsToOwnerLabels;
     this.parallelTests = parallelTests;
@@ -131,15 +132,14 @@
     return parallelTests;
   }
 
-  /**
-   * Returns an error description (if any).
-   */
-  @Nullable public String getError() {
-    return error;
+  /** Returns a {@link FailureDetail}, if any failures occurred. */
+  @Nullable
+  public FailureDetail getFailureDetail() {
+    return failureDetail;
   }
 
   public boolean hasError() {
-    return error != null;
+    return failureDetail != null;
   }
 
   /**
@@ -176,7 +176,7 @@
         aspects,
         targetsToTest,
         targetsToSkip,
-        error,
+        failureDetail,
         actionGraph,
         topLevelArtifactsToOwnerLabels,
         Sets.union(parallelTests, exclusiveTests).immutableCopy(),
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index b9598c2..afc5ee7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -620,6 +620,7 @@
         "//src/main/java/com/google/devtools/build/lib/util",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+        "//src/main/protobuf:failure_details_java_proto",
         "//third_party:flogger",
         "//third_party:guava",
         "//third_party:jsr305",
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 c042a1c..a8b8df5 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
@@ -66,6 +66,11 @@
 import com.google.devtools.build.lib.pkgcache.PackageManager.PackageManagerStatistics;
 import com.google.devtools.build.lib.profiler.Profiler;
 import com.google.devtools.build.lib.profiler.SilentCloseable;
+import com.google.devtools.build.lib.server.FailureDetails;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns;
+import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns.Code;
 import com.google.devtools.build.lib.skyframe.AspectValueKey;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
@@ -523,8 +528,8 @@
     ImmutableSet<ConfiguredTarget> parallelTests = testsPair.first;
     ImmutableSet<ConfiguredTarget> exclusiveTests = testsPair.second;
 
-    String error =
-        createErrorMessage(loadingResult, skyframeAnalysisResult, topLevelTargetsWithConfigs);
+    FailureDetail failureDetail =
+        createFailureDetail(loadingResult, skyframeAnalysisResult, topLevelTargetsWithConfigs);
 
     final WalkableGraph graph = skyframeAnalysisResult.getWalkableGraph();
     final ActionGraph actionGraph =
@@ -558,7 +563,7 @@
         aspects,
         allTargetsToTest == null ? null : ImmutableList.copyOf(allTargetsToTest),
         ImmutableSet.copyOf(targetsToSkip),
-        error,
+        failureDetail,
         actionGraph,
         artifactsToOwnerLabelsBuilder.build(),
         parallelTests,
@@ -575,22 +580,38 @@
    * interleaved, but sequential on the single target scale).
    */
   @Nullable
-  public static String createErrorMessage(
+  public static FailureDetail createFailureDetail(
       TargetPatternPhaseValue loadingResult,
       @Nullable SkyframeAnalysisResult skyframeAnalysisResult,
       @Nullable TopLevelTargetsAndConfigsResult topLevelTargetsAndConfigs) {
     if (loadingResult.hasError()) {
-      return "command succeeded, but there were errors parsing the target pattern";
+      return FailureDetail.newBuilder()
+          .setMessage("command succeeded, but there were errors parsing the target pattern")
+          .setTargetPatterns(TargetPatterns.newBuilder().setCode(Code.TARGET_PATTERN_PARSE_FAILURE))
+          .build();
     }
     if (loadingResult.hasPostExpansionError()
         || (skyframeAnalysisResult != null && skyframeAnalysisResult.hasLoadingError())) {
-      return "command succeeded, but there were loading phase errors";
+      return FailureDetail.newBuilder()
+          .setMessage("command succeeded, but there were loading phase errors")
+          .setAnalysis(Analysis.newBuilder().setCode(Analysis.Code.GENERIC_LOADING_PHASE_FAILURE))
+          .build();
     }
     if (topLevelTargetsAndConfigs != null && topLevelTargetsAndConfigs.hasError()) {
-      return "command succeeded, but top level configurations could not be created";
+      return FailureDetail.newBuilder()
+          .setMessage("command succeeded, but top level configurations could not be created")
+          .setBuildConfiguration(
+              FailureDetails.BuildConfiguration.newBuilder()
+                  .setCode(
+                      FailureDetails.BuildConfiguration.Code
+                          .TOP_LEVEL_CONFIGURATION_CREATION_FAILURE))
+          .build();
     }
     if (skyframeAnalysisResult != null && skyframeAnalysisResult.hasAnalysisError()) {
-      return "command succeeded, but not all targets were analyzed";
+      return FailureDetail.newBuilder()
+          .setMessage("command succeeded, but not all targets were analyzed")
+          .setAnalysis(Analysis.newBuilder().setCode(Analysis.Code.NOT_ALL_TARGETS_ANALYZED))
+          .build();
     }
     return null;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java b/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
index 4772624..5118fe4 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/AnalysisPhaseRunner.java
@@ -42,11 +42,13 @@
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.BuildInfoCollectionFunction;
 import com.google.devtools.build.lib.skyframe.PrecomputedValue;
 import com.google.devtools.build.lib.skyframe.TargetPatternPhaseValue;
 import com.google.devtools.build.lib.util.AbruptExitException;
+import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.util.RegexFilter;
 import com.google.devtools.common.options.OptionsParsingException;
@@ -148,9 +150,10 @@
       env.getReporter().handle(Event.progress("Loading complete."));
       env.getReporter().post(new NoAnalyzeEvent());
       logger.atInfo().log("No analysis requested, so finished");
-      String errorMessage = BuildView.createErrorMessage(loadingResult, null, null);
-      if (errorMessage != null) {
-        throw new BuildFailedException(errorMessage);
+      FailureDetail failureDetail = BuildView.createFailureDetail(loadingResult, null, null);
+      if (failureDetail != null) {
+        throw new BuildFailedException(
+            failureDetail.getMessage(), DetailedExitCode.of(failureDetail));
       }
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
index 5eaa996..9252143 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
@@ -45,6 +45,7 @@
 import com.google.devtools.build.lib.profiler.SilentCloseable;
 import com.google.devtools.build.lib.runtime.BlazeRuntime;
 import com.google.devtools.build.lib.runtime.CommandEnvironment;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.server.FailureDetails.Interrupted.Code;
 import com.google.devtools.build.lib.util.AbruptExitException;
 import com.google.devtools.build.lib.util.CrashFailureDetails;
@@ -189,9 +190,10 @@
         } else {
           env.getReporter().post(new NoExecutionEvent());
         }
-        String delayedErrorMsg = analysisResult.getError();
-        if (delayedErrorMsg != null) {
-          throw new BuildFailedException(delayedErrorMsg);
+        FailureDetail delayedFailureDetail = analysisResult.getFailureDetail();
+        if (delayedFailureDetail != null) {
+          throw new BuildFailedException(
+              delayedFailureDetail.getMessage(), DetailedExitCode.of(delayedFailureDetail));
         }
       }
       Profiler.instance().markPhase(ProfilePhase.FINISH);
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
index 3d99f98..c31c653 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java
@@ -15,6 +15,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Range;
@@ -44,6 +45,7 @@
 import com.google.devtools.build.lib.server.FailureDetails.Execution;
 import com.google.devtools.build.lib.server.FailureDetails.Execution.Code;
 import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.server.FailureDetails.IncludeScanning;
 import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
 import com.google.devtools.build.lib.skyframe.Builder;
@@ -330,22 +332,38 @@
           actionExecutionCause.getRootCauses(),
           /*errorAlreadyShown=*/ !actionExecutionCause.showError(),
           actionExecutionCause.getDetailedExitCode());
-    } else if (cause instanceof MissingInputFileException) {
-      throw new BuildFailedException(cause.getMessage());
-    } else if (cause instanceof BuildFileNotFoundException) {
+    }
+    if (cause instanceof MissingInputFileException) {
+      throw (MissingInputFileException) cause;
+    }
+    if (cause instanceof BuildFileNotFoundException) {
       // Sadly, this can happen because we may load new packages during input discovery. Any
       // failures reading those packages shouldn't terminate the build, but in Skyframe they do.
       LoggingUtil.logToRemote(Level.WARNING, "undesirable loading exception", cause);
-      throw new BuildFailedException(cause.getMessage());
-    } else {
-      // We encountered an exception we don't think we should have encountered. This can indicate
-      // an exception-processing bug in our code, such as lower level exceptions not being properly
-      // handled, or in our expectations in this method.
-      BugReport.sendBugReport(
-          new IllegalStateException("action terminated with unexpected exception", cause));
       throw new BuildFailedException(
-          "Unexpected exception, please file an issue with the Bazel team: " + cause.getMessage());
+          cause.getMessage(),
+          DetailedExitCode.of(
+              FailureDetail.newBuilder()
+                  .setMessage(Strings.nullToEmpty(cause.getMessage()))
+                  .setIncludeScanning(
+                      IncludeScanning.newBuilder()
+                          .setCode(IncludeScanning.Code.PACKAGE_LOAD_FAILURE))
+                  .build()));
     }
+    // We encountered an exception we don't think we should have encountered. This can indicate
+    // an exception-processing bug in our code, such as lower level exceptions not being properly
+    // handled, or in our expectations in this method.
+    BugReport.sendBugReport(
+        new IllegalStateException("action terminated with unexpected exception", cause));
+    String message =
+        "Unexpected exception, please file an issue with the Bazel team: " + cause.getMessage();
+    throw new BuildFailedException(
+        message,
+        DetailedExitCode.of(
+            FailureDetail.newBuilder()
+                .setMessage(message)
+                .setExecution(Execution.newBuilder().setCode(Code.UNEXPECTED_EXCEPTION))
+                .build()));
   }
 
   private static int countTestActions(Iterable<ConfiguredTarget> testTargets) {
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index 23fa1b7..7beafc6 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -141,6 +141,7 @@
     LtoAction lto_action = 169;
     TestAction test_action = 172;
     Worker worker = 173;
+    Analysis analysis = 174;
   }
 
   reserved 102; // For internal use
@@ -408,6 +409,7 @@
     NON_ACTION_EXECUTION_FAILURE = 34 [(metadata) = { exit_code: 1 }];
     CYCLE = 35 [(metadata) = { exit_code: 1 }];
     SOURCE_INPUT_MISSING = 36 [(metadata) = { exit_code: 1 }];
+    UNEXPECTED_EXCEPTION = 37 [(metadata) = { exit_code: 1 }];
   }
 
   Code code = 1;
@@ -544,6 +546,8 @@
     PLATFORM_MAPPING_EVALUATION_FAILURE = 1 [(metadata) = { exit_code: 2 }];
     PLATFORM_MAPPINGS_FILE_IS_DIRECTORY = 2 [(metadata) = { exit_code: 1 }];
     PLATFORM_MAPPINGS_FILE_NOT_FOUND = 3 [(metadata) = { exit_code: 1 }];
+    TOP_LEVEL_CONFIGURATION_CREATION_FAILURE = 4
+        [(metadata) = { exit_code: 1 }];
   }
 
   Code code = 1;
@@ -662,6 +666,9 @@
     INCLUDE_HINTS_FILE_NOT_IN_PACKAGE = 3 [(metadata) = { exit_code: 36 }];
     INCLUDE_HINTS_READ_FAILURE = 4 [(metadata) = { exit_code: 36 }];
     ILLEGAL_ABSOLUTE_PATH = 5 [(metadata) = { exit_code: 1 }];
+    // TODO(b/138456686): this code should be deprecated in favor of more finely
+    //   resolved loading-phase codes.
+    PACKAGE_LOAD_FAILURE = 6 [(metadata) = { exit_code: 1 }];
   }
 
   Code code = 1;
@@ -712,6 +719,7 @@
     TARGET_PATTERN_FILE_WITH_COMMAND_LINE_PATTERN = 1
         [(metadata) = { exit_code: 2 }];
     TARGET_PATTERN_FILE_READ_FAILURE = 2 [(metadata) = { exit_code: 2 }];
+    TARGET_PATTERN_PARSE_FAILURE = 3 [(metadata) = { exit_code: 1 }];
   }
 
   Code code = 1;
@@ -1007,4 +1015,17 @@
   }
 
   Code code = 1;
-}
\ No newline at end of file
+}
+
+message Analysis {
+  enum Code {
+    ANALYSIS_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
+    LOAD_FAILURE = 1 [(metadata) = { exit_code: 1 }];
+    // TODO(b/138456686): this code should be deprecated in favor of more finely
+    //   resolved loading-phase codes.
+    GENERIC_LOADING_PHASE_FAILURE = 2 [(metadata) = { exit_code: 1 }];
+    NOT_ALL_TARGETS_ANALYZED = 3 [(metadata) = { exit_code: 1 }];
+  }
+
+  Code code = 1;
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
index 0e29f01..63f3e65 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/BuildViewTest.java
@@ -1208,7 +1208,8 @@
     reporter.removeHandler(failFastHandler);
     AnalysisResult result = update(defaultFlags().with(Flag.KEEP_GOING), "//a", "//b");
     assertThat(result.hasError()).isTrue();
-    assertThat(result.getError()).contains("command succeeded, but not all targets were analyzed");
+    assertThat(result.getFailureDetail().getMessage())
+        .contains("command succeeded, but not all targets were analyzed");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
index 46ee6d3..179e1bc 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
@@ -584,11 +584,6 @@
     assertThat(actual).containsExactlyEntriesIn(expected);
   }
 
-  protected String getAnalysisError() {
-    ensureUpdateWasCalled();
-    return analysisResult.getError();
-  }
-
   protected BuildViewForTesting getView() {
     return buildView;
   }