Shortened the error log for unknown Starlark options.

PiperOrigin-RevId: 318526131
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
index 0e67901..7c1b53a 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeCommandDispatcher.java
@@ -13,6 +13,9 @@
 // limitations under the License.
 package com.google.devtools.build.lib.runtime;
 
+import static com.google.devtools.build.lib.runtime.BlazeOptionHandler.BAD_OPTION_TAG;
+import static com.google.devtools.build.lib.runtime.BlazeOptionHandler.ERROR_SEPARATOR;
+
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -91,16 +94,18 @@
   private String shutdownReason = null;
   private OutputStream logOutputStream = null;
   private final LoadingCache<BlazeCommand, OpaqueOptionsData> optionsDataCache =
-      CacheBuilder.newBuilder().build(
-          new CacheLoader<BlazeCommand, OpaqueOptionsData>() {
-            @Override
-            public OpaqueOptionsData load(BlazeCommand command) {
-              return OptionsParser.getOptionsData(BlazeCommandUtils.getOptions(
-                  command.getClass(),
-                  runtime.getBlazeModules(),
-                  runtime.getRuleClassProvider()));
-            }
-          });
+      CacheBuilder.newBuilder()
+          .build(
+              new CacheLoader<BlazeCommand, OpaqueOptionsData>() {
+                @Override
+                public OpaqueOptionsData load(BlazeCommand command) {
+                  return OptionsParser.getOptionsData(
+                      BlazeCommandUtils.getOptions(
+                          command.getClass(),
+                          runtime.getBlazeModules(),
+                          runtime.getRuleClassProvider()));
+                }
+              });
 
   @VisibleForTesting
   BlazeCommandDispatcher(BlazeRuntime runtime, BugReporter bugReporter, BlazeCommand... commands) {
@@ -118,9 +123,7 @@
     runtime.overrideCommands(ImmutableList.copyOf(commands));
   }
 
-  /**
-   * Create a Blaze dispatcher that uses the specified {@code BlazeRuntime} instance.
-   */
+  /** Create a Blaze dispatcher that uses the specified {@code BlazeRuntime} instance. */
   @VisibleForTesting
   public BlazeCommandDispatcher(BlazeRuntime runtime) {
     this(runtime, runtime.getBugReporter());
@@ -184,8 +187,11 @@
         switch (lockingMode) {
           case WAIT:
             if (!otherClientDescription.equals(currentClientDescription)) {
-              outErr.printErrLn("Another command (" + currentClientDescription + ") is running. "
-                  + " Waiting for it to complete on the server...");
+              outErr.printErrLn(
+                  "Another command ("
+                      + currentClientDescription
+                      + ") is running. "
+                      + " Waiting for it to complete on the server...");
               otherClientDescription = currentClientDescription;
             }
             commandLock.wait(500);
@@ -226,16 +232,17 @@
             ExitCode.LOCAL_ENVIRONMENTAL_ERROR,
             FailureDetails.Command.Code.PREVIOUSLY_SHUTDOWN);
       }
-      BlazeCommandResult result = execExclusively(
-          originalCommandLine,
-          invocationPolicy,
-          args,
-          outErr,
-          firstContactTimeMillis,
-          commandName,
-          command,
-          waitTimeInMs,
-          startupOptionsTaggedWithBazelRc);
+      BlazeCommandResult result =
+          execExclusively(
+              originalCommandLine,
+              invocationPolicy,
+              args,
+              outErr,
+              firstContactTimeMillis,
+              commandName,
+              command,
+              waitTimeInMs,
+              startupOptionsTaggedWithBazelRc);
       if (result.shutdown()) {
         // TODO(lberki): This also handles the case where we catch an uncaught Throwable in
         // execExclusively() which is not an explicit shutdown.
@@ -641,8 +648,15 @@
       NoBuildEvent noBuildEvent) {
     PrintingEventHandler printingEventHandler =
         new PrintingEventHandler(outErr, EventKind.ALL_EVENTS);
+
+    Optional<String> badOption = retrieveBadOption(storedEventHandler.getEvents());
+
     for (String note : optionHandler.getRcfileNotes()) {
-      printingEventHandler.handle(Event.info(note));
+      if (badOption.isPresent()) {
+        if (note.contains(badOption.get())) {
+          printingEventHandler.handle(Event.info(note));
+        }
+      }
     }
     for (Event event : storedEventHandler.getEvents()) {
       printingEventHandler.handle(event);
@@ -653,6 +667,15 @@
     env.getEventBus().post(noBuildEvent);
   }
 
+  private static Optional<String> retrieveBadOption(ImmutableList<Event> events) {
+    return events.stream()
+        .filter(e -> e.getTag() != null && e.getTag().equals(BAD_OPTION_TAG))
+        .map(Event::getMessage)
+        .filter(message -> message.contains(ERROR_SEPARATOR))
+        .map(message -> message.substring(0, message.indexOf(ERROR_SEPARATOR)))
+        .findFirst();
+  }
+
   private OutErr bufferOut(OutErr outErr) {
     OutputStream wrappedOut = new BufferedOutputStream(outErr.getOutputStream());
     return OutErr.create(wrappedOut, outErr.getErrorStream());
@@ -728,19 +751,15 @@
     return new UiEventHandler(outErr, eventOptions, runtime.getClock(), workspacePathFragment);
   }
 
-  /**
-   * Returns the runtime instance shared by the commands that this dispatcher
-   * dispatches to.
-   */
+  /** Returns the runtime instance shared by the commands that this dispatcher dispatches to. */
   @VisibleForTesting
   public BlazeRuntime getRuntime() {
     return runtime;
   }
 
   /**
-   * Shuts down all the registered commands to give them a chance to cleanup or
-   * close resources. Should be called by the owner of this command dispatcher
-   * in all termination cases.
+   * Shuts down all the registered commands to give them a chance to cleanup or close resources.
+   * Should be called by the owner of this command dispatcher in all termination cases.
    */
   public void shutdown() {
     closeSilently(logOutputStream);
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/BlazeOptionHandler.java b/src/main/java/com/google/devtools/build/lib/runtime/BlazeOptionHandler.java
index 7a83102..e14d704 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/BlazeOptionHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/BlazeOptionHandler.java
@@ -61,6 +61,11 @@
           "client_env",
           "client_cwd");
 
+  // Marks an event to indicate a parsing error.
+  static final String BAD_OPTION_TAG = "invalidOption";
+  // Separates the invalid tag from the full error message for easier parsing.
+  static final String ERROR_SEPARATOR = " :: ";
+
   private final BlazeRuntime runtime;
   private final OptionsParser optionsParser;
   private final BlazeWorkspace workspace;
@@ -247,10 +252,10 @@
     try {
       StarlarkOptionsParser.newStarlarkOptionsParser(env, optionsParser).parse(eventHandler);
     } catch (OptionsParsingException e) {
-      env.getReporter().handle(Event.error(e.getMessage()));
-      logger.atInfo().withCause(e).log("Error parsing Starlark options");
-      return createDetailedExitCode(
-          "Error parsing Starlark options: " + e.getMessage(), Code.STARLARK_OPTIONS_PARSE_FAILURE);
+      String logMessage = "Error parsing Starlark options";
+      logger.atInfo().withCause(e).log(logMessage);
+      return processOptionsParsingException(
+          eventHandler, e, logMessage, Code.STARLARK_OPTIONS_PARSE_FAILURE);
     }
     return DetailedExitCode.success();
   }
@@ -311,10 +316,10 @@
         eventHandler.handle(Event.warn(warning));
       }
     } catch (OptionsParsingException e) {
-      eventHandler.handle(Event.error(e.getMessage()));
-      logger.atInfo().withCause(e).log("Error parsing options");
-      return createDetailedExitCode(
-          "Error parsing options" + e.getMessage(), Code.OPTIONS_PARSE_FAILURE);
+      String logMessage = "Error parsing options";
+      logger.atInfo().withCause(e).log(logMessage);
+      return processOptionsParsingException(
+          eventHandler, e, logMessage, Code.OPTIONS_PARSE_FAILURE);
     }
     return DetailedExitCode.success();
   }
@@ -349,6 +354,25 @@
     accumulator.add(commandAnnotation.name());
   }
 
+  private static DetailedExitCode processOptionsParsingException(
+      ExtendedEventHandler eventHandler,
+      OptionsParsingException e,
+      String logMessage,
+      Code failureCode) {
+    Event error;
+    // Differentiates errors stemming from an invalid argument and errors from different parts of
+    // the codebase.
+    if (e.getInvalidArgument() != null) {
+      error =
+          Event.error(e.getInvalidArgument() + ERROR_SEPARATOR + e.getMessage())
+              .withTag(BAD_OPTION_TAG);
+    } else {
+      error = Event.error(e.getMessage());
+    }
+    eventHandler.handle(error);
+    return createDetailedExitCode(logMessage + ": " + e.getMessage(), failureCode);
+  }
+
   private String getNotInRealWorkspaceError(Path doNotBuildFile) {
     String message =
         String.format(
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
index 7d6e937..f7e5f79 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/StarlarkOptionsParser.java
@@ -209,11 +209,11 @@
     } catch (InterruptedException | TargetParsingException e) {
       Thread.currentThread().interrupt();
       throw new OptionsParsingException(
-          "Error loading option " + targetToBuild + ": " + e.getMessage(), e);
+          "Error loading option " + targetToBuild + ": " + e.getMessage(), targetToBuild, e);
     }
     Rule associatedRule = buildSetting.getAssociatedRule();
     if (associatedRule == null || associatedRule.getRuleClassObject().getBuildSetting() == null) {
-      throw new OptionsParsingException("Unrecognized option: " + targetToBuild);
+      throw new OptionsParsingException("Unrecognized option: " + targetToBuild, targetToBuild);
     }
     return buildSetting;
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkOptionsParsingTest.java b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkOptionsParsingTest.java
index 48a58d7..e44bb2a 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/StarlarkOptionsParsingTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/StarlarkOptionsParsingTest.java
@@ -82,6 +82,7 @@
             () -> parseStarlarkOptions("--//fake_flag=blahblahblah"));
 
     assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
+    assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
   }
 
   // test --fake_flag value
@@ -96,6 +97,7 @@
             () -> parseStarlarkOptions("--//fake_flag blahblahblah"));
 
     assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
+    assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
   }
 
   // test --fake_flag
@@ -108,6 +110,7 @@
         assertThrows(OptionsParsingException.class, () -> parseStarlarkOptions("--//fake_flag"));
 
     assertThat(e).hasMessageThat().contains("Error loading option //fake_flag");
+    assertThat(e.getInvalidArgument()).isEqualTo("//fake_flag");
   }
 
   @Test