Replace slashes with underscores in default crate names. (#1336)

This is another attempt at #1334, which sought to make "/" characters legal in Rust target names.

This PR differs from #1334 in how it handles build artifacts that are based on `crate_info.output`. There are a few places that currently assume that calling `ctx.actions.declare_file(...)` will always declare a file in the same directory as `crate_info.output`, i.e. as `ctx.actions.declare_file(ctx.label.name + ...)`. In a world where label names may include slashes (which is the case today!), this is not a safe assumption, since a slash in the target name causes bazel to create a subdirectory for the output.

Specifically, this PR fixes incorrect locations for the following items (when the target name includes a slash):
* PDB files
* dSYM files
* .lib files

This leaves a few things that look like they make the same assumption, but I'm not sure how they're used or if they should be updated:
* `"_ambiguous_libs/" + crate_info.output.basename + "/" + symlink_name` for ambiguous libraries
* The linkstamp output path (`"_objs/" + crate_info.output.basename + "/" + ...`)
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 0c70aa9..c34e250 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -923,7 +923,7 @@
     if toolchain.os == "windows" and crate_info.type == "cdylib":
         # Rustc generates the import library with a `.dll.lib` extension rather than the usual `.lib` one that msvc
         # expects (see https://github.com/rust-lang/rust/pull/29520 for more context).
-        interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib")
+        interface_library = ctx.actions.declare_file(crate_info.output.basename + ".lib", sibling = crate_info.output)
         outputs.append(interface_library)
 
     # The action might generate extra output that we don't want to include in the `DefaultInfo` files.
@@ -935,10 +935,10 @@
     dsym_folder = None
     if crate_info.type in ("cdylib", "bin") and not crate_info.is_test:
         if toolchain.os == "windows":
-            pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb")
+            pdb_file = ctx.actions.declare_file(crate_info.output.basename[:-len(crate_info.output.extension)] + "pdb", sibling = crate_info.output)
             action_outputs.append(pdb_file)
         elif toolchain.os == "darwin":
-            dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM")
+            dsym_folder = ctx.actions.declare_directory(crate_info.output.basename + ".dSYM", sibling = crate_info.output)
             action_outputs.append(dsym_folder)
 
     if ctx.executable._process_wrapper:
diff --git a/rust/private/utils.bzl b/rust/private/utils.bzl
index 1e61993..83dbb1e 100644
--- a/rust/private/utils.bzl
+++ b/rust/private/utils.bzl
@@ -272,7 +272,9 @@
     Returns:
         str: The name of the crate for this target.
     """
-    return name.replace("-", "_")
+    for illegal in ("-", "/"):
+        name = name.replace(illegal, "_")
+    return name
 
 def _invalid_chars_in_crate_name(name):
     """Returns any invalid chars in the given crate name.
diff --git a/test/unit/crate_name/crate_name_test.bzl b/test/unit/crate_name/crate_name_test.bzl
index 968f2b5..8671951 100644
--- a/test/unit/crate_name/crate_name_test.bzl
+++ b/test/unit/crate_name/crate_name_test.bzl
@@ -48,7 +48,7 @@
 
 def _invalid_default_crate_name_test_impl(ctx):
     env = analysistest.begin(ctx)
-    asserts.expect_failure(env, "contains invalid character(s): /")
+    asserts.expect_failure(env, "contains invalid character(s): @")
     return analysistest.end(env)
 
 def _invalid_custom_crate_name_test_impl(ctx):
@@ -119,7 +119,7 @@
 
 def _crate_name_test():
     rust_library(
-        name = "default-crate-name-library",
+        name = "default/crate-name-library",
         srcs = ["lib.rs"],
         edition = "2018",
     )
@@ -132,7 +132,7 @@
     )
 
     rust_binary(
-        name = "default-crate-name-binary",
+        name = "default/crate-name-binary",
         srcs = ["main.rs"],
         edition = "2018",
     )
@@ -145,7 +145,7 @@
     )
 
     rust_test(
-        name = "default-crate-name-test",
+        name = "default/crate-name-test",
         srcs = ["main.rs"],
         edition = "2018",
     )
@@ -158,7 +158,7 @@
     )
 
     rust_library(
-        name = "invalid/default-crate-name",
+        name = "invalid@default-crate-name",
         srcs = ["lib.rs"],
         edition = "2018",
         tags = ["manual", "norustfmt"],
@@ -197,7 +197,7 @@
 
     default_crate_name_library_test(
         name = "default_crate_name_library_test",
-        target_under_test = ":default-crate-name-library",
+        target_under_test = ":default/crate-name-library",
     )
 
     custom_crate_name_library_test(
@@ -207,7 +207,7 @@
 
     default_crate_name_binary_test(
         name = "default_crate_name_binary_test",
-        target_under_test = ":default-crate-name-binary",
+        target_under_test = ":default/crate-name-binary",
     )
 
     custom_crate_name_binary_test(
@@ -217,7 +217,7 @@
 
     default_crate_name_test_test(
         name = "default_crate_name_test_test",
-        target_under_test = ":default-crate-name-test",
+        target_under_test = ":default/crate-name-test",
     )
 
     custom_crate_name_test_test(
@@ -227,7 +227,7 @@
 
     invalid_default_crate_name_test(
         name = "invalid_default_crate_name_test",
-        target_under_test = ":invalid/default-crate-name",
+        target_under_test = ":invalid@default-crate-name",
     )
 
     invalid_custom_crate_name_test(