Disabled string.partition default parameter and introduce incompatible flag

Related: #8725

Closes #8894.

PiperOrigin-RevId: 259348686
diff --git a/site/docs/skylark/backward-compatibility.md b/site/docs/skylark/backward-compatibility.md
index 72de9de..8601bb5 100644
--- a/site/docs/skylark/backward-compatibility.md
+++ b/site/docs/skylark/backward-compatibility.md
@@ -18,6 +18,7 @@
 *   [Dictionary concatenation](#dictionary-concatenation)
 *   [String escapes](#string-escapes)
 *   [String.split empty separator](#string.split-empty-separator)
+*   [Disable default parameter of String.partition/rpartition](#string.split-empty-separator)
 *   [Load must appear at top of file](#load-must-appear-at-top-of-file)
 *   [Depset is no longer iterable](#depset-is-no-longer-iterable)
 *   [Depset union](#depset-union)
@@ -107,6 +108,15 @@
 *   Default: `false`
 *   Tracking issue: [#7355](https://github.com/bazelbuild/bazel/issues/7355)
 
+### Disable default parameter of String.partition/rpartition
+
+This flag disables the default value `' '` for `sep` parameter of `String.parition`
+and `String.rpartition`.
+
+*   Flag: `--incompatible_disable_partition_default_parameter`
+*   Default: `false`
+*   Tracking issue: [#8725](https://github.com/bazelbuild/bazel/issues/8725)
+
 ### Load must appear at top of file
 
 Previously, the `load` statement could appear anywhere in a `.bzl` file so long
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
index c4d3fe4..8461a68 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkSemanticsOptions.java
@@ -243,6 +243,20 @@
       help = "Unused. Will be removed in future versions of Bazel.")
   public boolean incompatibleDisableObjcProviderResources;
 
+  @Option(
+      name = "incompatible_disable_partition_default_parameter",
+      defaultValue = "false",
+      documentationCategory = OptionDocumentationCategory.STARLARK_SEMANTICS,
+      effectTags = {OptionEffectTag.BUILD_FILE_SEMANTICS},
+      metadataTags = {
+        OptionMetadataTag.INCOMPATIBLE_CHANGE,
+        OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
+      },
+      help =
+          "If set to true, the default value `' '` for the parameter `sep` of `string.partion` and"
+              + " `string.rpartition` will be disabled.")
+  public boolean incompatibleDisablePartitionDefaultParameter;
+
   // For Bazel, this flag is a no-op. Bazel doesn't support built-in third party license checking
   // (see https://github.com/bazelbuild/bazel/issues/7444).
   //
@@ -709,6 +723,8 @@
             .incompatibleDisallowSplitEmptySeparator(incompatibleDisallowSplitEmptySeparator)
             .incompatibleDisallowDictLookupUnhashableKeys(
                 incompatibleDisallowDictLookupUnhashableKeys)
+            .incompatibleDisablePartitionDefaultParameter(
+                incompatibleDisablePartitionDefaultParameter)
             .incompatibleAllowTagsPropagation(incompatibleAllowTagsPropagation)
             .build();
     return INTERNER.intern(semantics);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
index a1f7420..8f8567c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkSemantics.java
@@ -212,6 +212,8 @@
 
   public abstract boolean incompatibleDisallowDictLookupUnhashableKeys();
 
+  public abstract boolean incompatibleDisablePartitionDefaultParameter();
+
   public abstract boolean incompatibleAllowTagsPropagation();
 
   @Memoized
@@ -291,6 +293,7 @@
           .incompatibleRestrictStringEscapes(false)
           .incompatibleDisallowSplitEmptySeparator(false)
           .incompatibleDisallowDictLookupUnhashableKeys(false)
+          .incompatibleDisablePartitionDefaultParameter(false)
           .incompatibleAllowTagsPropagation(false)
           .build();
 
@@ -390,6 +393,8 @@
 
     public abstract Builder incompatibleDisallowDictLookupUnhashableKeys(boolean value);
 
+    public abstract Builder incompatibleDisablePartitionDefaultParameter(boolean value);
+
     public abstract StarlarkSemantics build();
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
index 4a6c6f9..13b194c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StringModule.java
@@ -432,17 +432,36 @@
         @Param(name = "self", type = String.class),
         @Param(
             name = "sep",
-            type = String.class,
+            type = Object.class,
             // TODO(cparsons): This parameter should be positional-only.
             legacyNamed = true,
-            defaultValue = "\" \"",
-            doc = "The string to split on, default is space (\" \").")
+            defaultValue = "unbound",
+            doc =
+                "The string to split on, has a default value, space (\" \"), "
+                    + "if the flag --incompatible_disable_partition_default_parameter is disabled. "
+                    + "Otherwise, a value must be provided.")
       },
       useEnvironment = true,
       useLocation = true)
-  public Tuple<String> partition(String self, String sep, Location loc, Environment env)
+  public Tuple<String> partition(String self, Object sep, Location loc, Environment env)
       throws EvalException {
-    return partitionWrapper(self, sep, true, loc);
+    if (sep == Runtime.UNBOUND) {
+      if (env.getSemantics().incompatibleDisablePartitionDefaultParameter()) {
+        throw new EvalException(
+            loc,
+            "parameter 'sep' has no default value, "
+                + "for call to method partition(sep) of 'string'");
+      } else {
+        sep = " ";
+      }
+    } else if (!(sep instanceof String)) {
+      throw new EvalException(
+          loc,
+          "expected value of type 'string' for parameter"
+              + " 'sep', for call to method partition(sep = unbound) of 'string'");
+    }
+
+    return partitionWrapper(self, (String) sep, true, loc);
   }
 
   @SkylarkCallable(
@@ -458,14 +477,32 @@
             type = String.class,
             // TODO(cparsons): This parameter should be positional-only.
             legacyNamed = true,
-            defaultValue = "\" \"",
-            doc = "The string to split on, default is space (\" \").")
+            defaultValue = "unbound",
+            doc =
+                "The string to split on, has a default value, space (\" \"), "
+                    + "if the flag --incompatible_disable_partition_default_parameter is disabled. "
+                    + "Otherwise, a value must be provided.")
       },
       useEnvironment = true,
       useLocation = true)
-  public Tuple<String> rpartition(String self, String sep, Location loc, Environment env)
+  public Tuple<String> rpartition(String self, Object sep, Location loc, Environment env)
       throws EvalException {
-    return partitionWrapper(self, sep, false, loc);
+    if (sep == Runtime.UNBOUND) {
+      if (env.getSemantics().incompatibleDisablePartitionDefaultParameter()) {
+        throw new EvalException(
+            loc,
+            "parameter 'sep' has no default value, "
+                + "for call to method partition(sep) of 'string'");
+      } else {
+        sep = " ";
+      }
+    } else if (!(sep instanceof String)) {
+      throw new EvalException(
+          loc,
+          "expected value of type 'string' for parameter"
+              + " 'sep', for call to method partition(sep = unbound) of 'string'");
+    }
+    return partitionWrapper(self, (String) sep, false, loc);
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index 995e0f7..da1f91c 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -168,6 +168,7 @@
         "--incompatible_string_join_requires_strings=" + rand.nextBoolean(),
         "--incompatible_restrict_string_escapes=" + rand.nextBoolean(),
         "--incompatible_disallow_dict_lookup_unhashable_keys=" + rand.nextBoolean(),
+        "--incompatible_disable_partition_default_parameter=" + rand.nextBoolean(),
         "--internal_skylark_flag_test_canary=" + rand.nextBoolean());
   }
 
@@ -224,6 +225,7 @@
         .incompatibleStringJoinRequiresStrings(rand.nextBoolean())
         .incompatibleRestrictStringEscapes(rand.nextBoolean())
         .incompatibleDisallowDictLookupUnhashableKeys(rand.nextBoolean())
+        .incompatibleDisablePartitionDefaultParameter(rand.nextBoolean())
         .internalSkylarkFlagTestCanary(rand.nextBoolean())
         .build();
   }
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index 5a913d9..baa8202 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -3076,6 +3076,43 @@
   }
 
   @Test
+  public void testPartitionDefaultParameter() throws Exception {
+    setSkylarkSemanticsOptions("--incompatible_disable_partition_default_parameter=false");
+
+    scratch.file("test/extension.bzl", "y = ['abc'.partition(), 'abc'.rpartition()]");
+
+    scratch.file("test/BUILD", "load('//test:extension.bzl', 'y')", "cc_library(name = 'r')");
+
+    getConfiguredTarget("//test:r");
+  }
+
+  @Test
+  public void testDisabledPartitionDefaultParameter() throws Exception {
+    setSkylarkSemanticsOptions("--incompatible_disable_partition_default_parameter=true");
+
+    scratch.file("test/extension.bzl", "y = 'abc'.partition()");
+
+    scratch.file("test/BUILD", "load('//test:extension.bzl', 'y')", "cc_library(name = 'r')");
+
+    reporter.removeHandler(failFastHandler);
+    getConfiguredTarget("//test:r");
+    assertContainsEvent("parameter 'sep' has no default value");
+  }
+
+  @Test
+  public void testDisabledPartitionDefaultParameter2() throws Exception {
+    setSkylarkSemanticsOptions("--incompatible_disable_partition_default_parameter=true");
+
+    scratch.file("test/extension.bzl", "y = 'abc'.rpartition()");
+
+    scratch.file("test/BUILD", "load('//test:extension.bzl', 'y')", "cc_library(name = 'r')");
+
+    reporter.removeHandler(failFastHandler);
+    getConfiguredTarget("//test:r");
+    assertContainsEvent("parameter 'sep' has no default value");
+  }
+
+  @Test
   public void testUnknownStringEscapesForbidden() throws Exception {
     setSkylarkSemanticsOptions("--incompatible_restrict_string_escapes=true");