Don't perform crate renaming within rules_rust workspace; minor fixes. (#1143)

Co-authored-by: cfredric <cfredric@chromium.org>
Co-authored-by: Marcel Hlopko <hlopko@google.com>
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index def840e..897cd18 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -253,7 +253,7 @@
     else:
         output_hash = None
 
-    crate_name = compute_crate_name(ctx.label, toolchain, ctx.attr.crate_name)
+    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
     rust_lib_name = _determine_lib_name(
         crate_name,
         crate_type,
@@ -297,7 +297,7 @@
         list: A list of providers. See `rustc_compile_action`
     """
     toolchain = find_toolchain(ctx)
-    crate_name = compute_crate_name(ctx.label, toolchain, ctx.attr.crate_name)
+    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
     _assert_correct_dep_mapping(ctx)
 
     output = ctx.actions.declare_file(ctx.label.name + toolchain.binary_ext)
@@ -340,7 +340,7 @@
     _assert_no_deprecated_attributes(ctx)
     _assert_correct_dep_mapping(ctx)
 
-    crate_name = compute_crate_name(ctx.label, toolchain, ctx.attr.crate_name)
+    crate_name = compute_crate_name(ctx.workspace_name, ctx.label, toolchain, ctx.attr.crate_name)
     crate_type = "bin"
 
     deps = transform_deps(ctx.attr.deps)
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index 0433d69..04f09bc 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -265,10 +265,11 @@
 
     return dict([(c, ()) for c in name.elems() if not (c.isalnum() or c == "_")]).keys()
 
-def compute_crate_name(label, toolchain, name_override = None):
+def compute_crate_name(workspace_name, label, toolchain, name_override = None):
     """Returns the crate name to use for the current target.
 
     Args:
+        workspace_name (string): The current workspace name.
         label (struct): The label of the current target.
         toolchain (struct): The toolchain in use for the target.
         name_override (String): An optional name to use (as an override of label.name).
@@ -285,11 +286,9 @@
             ))
         return name_override
 
-    if toolchain and label and toolchain._rename_first_party_crates:
-        if should_encode_label_in_crate_name(label.package, toolchain._third_party_dir):
-            crate_name = label.name
-        else:
-            crate_name = encode_label_as_crate_name(label.package, label.name)
+    if (toolchain and label and toolchain._rename_first_party_crates and
+        should_encode_label_in_crate_name(workspace_name, label, toolchain._third_party_dir)):
+        crate_name = encode_label_as_crate_name(label.package, label.name)
     else:
         crate_name = name_to_crate_name(label.name)
 
@@ -400,13 +399,16 @@
         cc_info = dep[CcInfo] if CcInfo in dep else None,
     ) for dep in deps]
 
-def should_encode_label_in_crate_name(package, third_party_dir):
+def should_encode_label_in_crate_name(workspace_name, label, third_party_dir):
     """Determines if the crate's name should include the Bazel label, encoded.
 
-    Names of third-party crates do not encode the full label.
+    Crate names may only encode the label if the target is in the current repo,
+    the target is not in the third_party_dir, and the current repo is not
+    rules_rust.
 
     Args:
-        package (string): The package in question.
+        workspace_name (string): The name of the current workspace.
+        label (Label): The package in question.
         third_party_dir (string): The directory in which third-party packages are kept.
 
     Returns:
@@ -415,7 +417,11 @@
 
     # TODO(hlopko): This code assumes a monorepo; make it work with external
     # repositories as well.
-    return ("//" + package + "/").startswith(third_party_dir + "/")
+    return (
+        workspace_name != "rules_rust" and
+        not label.workspace_root and
+        not ("//" + label.package + "/").startswith(third_party_dir + "/")
+    )
 
 # This is a list of pairs, where the first element of the pair is a character
 # that is allowed in Bazel package or target names but not in crate names; and
@@ -522,25 +528,21 @@
         A string with the appropriate substitutions performed.
     """
 
-    # Find the highest-priority pattern matches.
+    # Find the highest-priority pattern matches for each string index, going
+    # left-to-right and skipping indices that are already involved in a
+    # pattern match.
     plan = {}
-    for pattern, replacement in substitutions:
-        for pattern_start in range(len(string)):
-            if not pattern_start in plan and string.startswith(pattern, pattern_start):
-                plan[pattern_start] = (len(pattern), replacement)
-
-    # Drop replacements that overlap with a replacement earlier in the string.
-    replaced_indices_set = {}
-    leftmost_plan = {}
-    for pattern_start in sorted(plan.keys()):
-        length, _ = plan[pattern_start]
-        pattern_indices = list(range(pattern_start, pattern_start + length))
-        if any([i in replaced_indices_set for i in pattern_indices]):
+    matched_indices_set = {}
+    for pattern_start in range(len(string)):
+        if pattern_start in matched_indices_set:
             continue
-        replaced_indices_set.update([(i, True) for i in pattern_indices])
-        leftmost_plan[pattern_start] = plan[pattern_start]
-
-    plan = leftmost_plan
+        for (pattern, replacement) in substitutions:
+            if not string.startswith(pattern, pattern_start):
+                continue
+            length = len(pattern)
+            plan[pattern_start] = (length, replacement)
+            matched_indices_set.update([(pattern_start + i, True) for i in range(length)])
+            break
 
     # Execute the replacement plan, working from right to left.
     for pattern_start in sorted(plan.keys(), reverse = True):
diff --git a/rust/settings/BUILD.bazel b/rust/settings/BUILD.bazel
index 3ff0ef6..638f1a7 100644
--- a/rust/settings/BUILD.bazel
+++ b/rust/settings/BUILD.bazel
@@ -14,7 +14,7 @@
 )
 
 # A flag specifying the location of vendored third-party rust crates within this
-# repository that must not be renamed when `rename_1p_crates` is enabled.
+# repository that must not be renamed when `rename_first_party_crates` is enabled.
 #
 # Must be specified as a Bazel package, e.g. "//some/location/in/repo".
 string_flag(
diff --git a/test/unit/rename_first_party_crates/BUILD.bazel b/test/unit/rename_first_party_crates/BUILD.bazel
deleted file mode 100644
index 8849fd0..0000000
--- a/test/unit/rename_first_party_crates/BUILD.bazel
+++ /dev/null
@@ -1,4 +0,0 @@
-load(":rename_first_party_crates_test.bzl", "rename_first_party_crates_test_suite")
-
-############################ UNIT TESTS #############################
-rename_first_party_crates_test_suite(name = "rename_first_party_crates_test_suite")
diff --git a/test/unit/rename_first_party_crates/lib.rs b/test/unit/rename_first_party_crates/lib.rs
deleted file mode 100644
index 8b13789..0000000
--- a/test/unit/rename_first_party_crates/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/test/unit/rename_first_party_crates/main.rs b/test/unit/rename_first_party_crates/main.rs
deleted file mode 100644
index f328e4d..0000000
--- a/test/unit/rename_first_party_crates/main.rs
+++ /dev/null
@@ -1 +0,0 @@
-fn main() {}
diff --git a/test/unit/rename_first_party_crates/my_third_party_dir/BUILD.bazel b/test/unit/rename_first_party_crates/my_third_party_dir/BUILD.bazel
deleted file mode 100644
index ae2ece1..0000000
--- a/test/unit/rename_first_party_crates/my_third_party_dir/BUILD.bazel
+++ /dev/null
@@ -1,7 +0,0 @@
-load("//rust:defs.bzl", "rust_library")
-
-rust_library(
-    name = "third_party_lib",
-    srcs = ["lib.rs"],
-    visibility = ["//visibility:public"],
-)
diff --git a/test/unit/rename_first_party_crates/my_third_party_dir/lib.rs b/test/unit/rename_first_party_crates/my_third_party_dir/lib.rs
deleted file mode 100644
index 8b13789..0000000
--- a/test/unit/rename_first_party_crates/my_third_party_dir/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/test/unit/rename_first_party_crates/rename_first_party_crates_test.bzl b/test/unit/rename_first_party_crates/rename_first_party_crates_test.bzl
deleted file mode 100644
index 3bf5700..0000000
--- a/test/unit/rename_first_party_crates/rename_first_party_crates_test.bzl
+++ /dev/null
@@ -1,206 +0,0 @@
-"""Unit tests for renaming first-party crates."""
-
-load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
-load("//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
-load("//test/unit:common.bzl", "assert_argv_contains")
-
-def _default_crate_name_library_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-
-    # Note: crate name encodes entire label.
-    assert_argv_contains(env, tut.actions[0], "--crate-name=test_slash_unit_slash_rename_first_party_crates_colon_default_dash_crate_dash_name_dash_library")
-    return analysistest.end(env)
-
-def _custom_crate_name_library_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-    assert_argv_contains(env, tut.actions[0], "--crate-name=custom_name")
-    return analysistest.end(env)
-
-def _default_crate_name_binary_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-
-    # Note: crate name encodes entire label.
-    assert_argv_contains(env, tut.actions[0], "--crate-name=test_slash_unit_slash_rename_first_party_crates_colon_default_dash_crate_dash_name_dash_binary")
-    return analysistest.end(env)
-
-def _custom_crate_name_binary_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-    assert_argv_contains(env, tut.actions[0], "--crate-name=custom_name")
-    return analysistest.end(env)
-
-def _default_crate_name_test_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-
-    # Note: crate name encodes entire label.
-    assert_argv_contains(env, tut.actions[0], "--crate-name=test_slash_unit_slash_rename_first_party_crates_colon_default_dash_crate_dash_name_dash_test")
-    return analysistest.end(env)
-
-def _custom_crate_name_test_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-    assert_argv_contains(env, tut.actions[0], "--crate-name=custom_name")
-    return analysistest.end(env)
-
-def _invalid_custom_crate_name_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    asserts.expect_failure(env, "contains invalid character(s): -")
-    return analysistest.end(env)
-
-def _third_party_lib_test_impl(ctx):
-    env = analysistest.begin(ctx)
-    tut = analysistest.target_under_test(env)
-
-    # Note: not renamed.
-    assert_argv_contains(env, tut.actions[0], "--crate-name=third_party_lib")
-    return analysistest.end(env)
-
-config_settings = {
-    "@//rust/settings:rename_first_party_crates": True,
-}
-third_party_config_settings = dict(
-    config_settings,
-    **{"@//rust/settings:third_party_dir": "//test/unit/rename_first_party_crates/my_third_party_dir"}
-)
-
-default_crate_name_library_test = analysistest.make(
-    _default_crate_name_library_test_impl,
-    config_settings = config_settings,
-)
-custom_crate_name_library_test = analysistest.make(
-    _custom_crate_name_library_test_impl,
-    config_settings = config_settings,
-)
-default_crate_name_binary_test = analysistest.make(
-    _default_crate_name_binary_test_impl,
-    config_settings = config_settings,
-)
-custom_crate_name_binary_test = analysistest.make(
-    _custom_crate_name_binary_test_impl,
-    config_settings = config_settings,
-)
-default_crate_name_test_test = analysistest.make(
-    _default_crate_name_test_test_impl,
-    config_settings = config_settings,
-)
-custom_crate_name_test_test = analysistest.make(
-    _custom_crate_name_test_test_impl,
-    config_settings = config_settings,
-)
-invalid_custom_crate_name_test = analysistest.make(
-    _invalid_custom_crate_name_test_impl,
-    config_settings = config_settings,
-    expect_failure = True,
-)
-third_party_lib_test = analysistest.make(
-    _third_party_lib_test_impl,
-    config_settings = third_party_config_settings,
-)
-
-def _rename_first_party_crates_test():
-    rust_library(
-        name = "default-crate-name-library",
-        srcs = ["lib.rs"],
-    )
-
-    rust_library(
-        name = "custom-crate-name-library",
-        crate_name = "custom_name",
-        srcs = ["lib.rs"],
-    )
-
-    rust_binary(
-        name = "default-crate-name-binary",
-        srcs = ["main.rs"],
-    )
-
-    rust_binary(
-        name = "custom-crate-name-binary",
-        crate_name = "custom_name",
-        srcs = ["main.rs"],
-    )
-
-    rust_test(
-        name = "default-crate-name-test",
-        srcs = ["main.rs"],
-    )
-
-    rust_test(
-        name = "custom-crate-name-test",
-        crate_name = "custom_name",
-        srcs = ["main.rs"],
-    )
-
-    rust_library(
-        name = "invalid-custom-crate-name",
-        crate_name = "hyphens-not-allowed",
-        srcs = ["lib.rs"],
-        tags = ["manual", "norustfmt"],
-    )
-
-    default_crate_name_library_test(
-        name = "default_crate_name_library_test",
-        target_under_test = ":default-crate-name-library",
-    )
-
-    custom_crate_name_library_test(
-        name = "custom_crate_name_library_test",
-        target_under_test = ":custom-crate-name-library",
-    )
-
-    default_crate_name_binary_test(
-        name = "default_crate_name_binary_test",
-        target_under_test = ":default-crate-name-binary",
-    )
-
-    custom_crate_name_binary_test(
-        name = "custom_crate_name_binary_test",
-        target_under_test = ":custom-crate-name-binary",
-    )
-
-    default_crate_name_test_test(
-        name = "default_crate_name_test_test",
-        target_under_test = ":default-crate-name-test",
-    )
-
-    custom_crate_name_test_test(
-        name = "custom_crate_name_test_test",
-        target_under_test = ":custom-crate-name-test",
-    )
-
-    invalid_custom_crate_name_test(
-        name = "invalid_custom_crate_name_test",
-        target_under_test = ":invalid-custom-crate-name",
-    )
-
-    third_party_lib_test(
-        name = "third_party_lib_test",
-        target_under_test = "//test/unit/rename_first_party_crates/my_third_party_dir:third_party_lib",
-    )
-
-def rename_first_party_crates_test_suite(name):
-    """Entry-point macro called from the BUILD file.
-
-    Args:
-        name: Name of the macro.
-    """
-
-    _rename_first_party_crates_test()
-
-    native.test_suite(
-        name = name,
-        tests = [
-            ":default_crate_name_library_test",
-            ":custom_crate_name_library_test",
-            ":default_crate_name_binary_test",
-            ":custom_crate_name_binary_test",
-            ":default_crate_name_test_test",
-            ":custom_crate_name_test_test",
-            ":invalid_custom_crate_name_test",
-            ":third_party_lib_test",
-        ],
-    )
diff --git a/test/unit/utils/utils_test.bzl b/test/unit/utils/utils_test.bzl
index 293887f..dd7bbe5 100644
--- a/test/unit/utils/utils_test.bzl
+++ b/test/unit/utils/utils_test.bzl
@@ -58,19 +58,23 @@
 def _is_third_party_crate_test_impl(ctx):
     env = unittest.begin(ctx)
 
-    # A target at the root of the 3p dir is considered 3p:
-    asserts.true(env, should_encode_label_in_crate_name("third_party", "//third_party"))
+    # A target at the root of the third-party dir is considered third-party:
+    asserts.false(env, should_encode_label_in_crate_name("some_workspace", Label("//third_party:foo"), "//third_party"))
 
     # Targets in subpackages are detected properly:
-    asserts.true(env, should_encode_label_in_crate_name("third_party/serde", "//third_party"))
-    asserts.true(env, should_encode_label_in_crate_name("third_party/serde/v1", "//third_party"))
+    asserts.false(env, should_encode_label_in_crate_name("some_workspace", Label("//third_party/serde:serde"), "//third_party"))
+    asserts.false(env, should_encode_label_in_crate_name("some_workspace", Label("//third_party/serde/v1:serde"), "//third_party"))
 
     # Ensure the directory name truly matches, and doesn't just include the
-    # 3p dir as a substring (or vice versa).
-    asserts.false(env, should_encode_label_in_crate_name("third_party_decoy", "//third_party"))
-    asserts.false(env, should_encode_label_in_crate_name("decoy_third_party", "//third_party"))
-    asserts.false(env, should_encode_label_in_crate_name("third_", "//third_party"))
-    asserts.false(env, should_encode_label_in_crate_name("third_party_decoy/serde", "//third_party"))
+    # third-party dir as a substring (or vice versa).
+    asserts.true(env, should_encode_label_in_crate_name("some_workspace", Label("//third_party_decoy:thing"), "//third_party"))
+    asserts.true(env, should_encode_label_in_crate_name("some_workspace", Label("//decoy_third_party:thing"), "//third_party"))
+    asserts.true(env, should_encode_label_in_crate_name("some_workspace", Label("//third_:thing"), "//third_party"))
+    asserts.true(env, should_encode_label_in_crate_name("some_workspace", Label("//third_party_decoy/serde:serde"), "//third_party"))
+
+    # Targets in rules_rust's repo should never be renamed.
+    asserts.false(env, should_encode_label_in_crate_name("rules_rust", Label("//some_package:foo"), "//third_party"))
+
     return unittest.end(env)
 
 encode_label_as_crate_name_test = unittest.make(_encode_label_as_crate_name_test_impl)