A rust_test_suite(srcs=[]) should be empty. (#2966)

This can arise when srcs is a glob that currently matches no files.
The target may nevertheless be useful for regularity across libraries,
and to future-proof against newly-added test files being ignored.

Previously, we hit the special-case handling of `test_suite(tests=[])`:

> If the tests attribute is unspecified or empty, the rule will default
> to including all test rules in the current BUILD file that are not
> tagged as manual. These rules are still subject to tag filtering.

We can't directly turn this behavior off, so this patch uses tag
filtering to avoid including those default tests.
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 64bbd53..fc120a4 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -51,6 +51,8 @@
   - "-//test/unit/pipelined_compilation/..."
   - "-//test/unit/rustdoc/..."
   - "-//tools/runfiles/..."
+  # Runfiles used by the test only.
+  - "-//test/empty_suite:deps_test"
 crate_universe_vendor_example_targets: &crate_universe_vendor_example_targets
   - "//vendor_external:crates_vendor"
   - "//vendor_local_manifests:crates_vendor"
diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl
index 4d91707..47c0e55 100644
--- a/rust/private/rust.bzl
+++ b/rust/private/rust.bzl
@@ -1492,6 +1492,10 @@
     """
     tests = []
 
+    # If test_suite.tests is empty, Bazel will unhelpfully include all tests
+    # from the package. Require an extra tag so they are filtered out again.
+    tags = ["restrict_" + name] + kwargs.pop("tags", [])
+
     for src in srcs:
         if not src.endswith(".rs"):
             fail("srcs should have `.rs` extensions")
@@ -1506,6 +1510,7 @@
             name = test_name,
             crate_root = src,
             srcs = [src] + shared_srcs,
+            tags = tags,
             **kwargs
         )
         tests.append(test_name)
@@ -1513,7 +1518,7 @@
     native.test_suite(
         name = name,
         tests = tests,
-        tags = kwargs.get("tags", None),
+        tags = tags,
     )
 
 rust_library_group = rule(
diff --git a/test/empty_suite/BUILD.bazel b/test/empty_suite/BUILD.bazel
new file mode 100644
index 0000000..5cda18d
--- /dev/null
+++ b/test/empty_suite/BUILD.bazel
@@ -0,0 +1,47 @@
+load("//rust:defs.bzl", "rust_library", "rust_test", "rust_test_suite")
+
+# This package has a rust_test_suite containing no tests.
+# (This could happen if the srcs were seleceted via a glob).
+#
+# We test that the test suite exists and is empty, bypassing test_suite's
+# special-case behavior for empty suites.
+
+rust_library(
+    name = "code",
+    srcs = ["src/code.rs"],
+    edition = "2018",
+)
+
+rust_test(
+    name = "unrelated_unittest",
+    crate = "code",
+    edition = "2018",
+)
+
+rust_test(
+    name = "unrelated_test",
+    srcs = ["src/unrelated_test.rs"],
+    edition = "2018",
+    deps = [":code"],
+)
+
+rust_test_suite(
+    name = "suite",
+    srcs = [],
+    edition = "2018",
+)
+
+# This verifies the suite exists, and calculates its contents.
+genquery(
+    name = "deps",
+    expression = "deps(//test/empty_suite:suite, 1) - //test/empty_suite:suite",
+    scope = [":suite"],
+)
+
+# Test that the suite is empty.
+sh_test(
+    name = "deps_test",
+    srcs = ["verify_empty.sh"],
+    args = ["$(location :deps)"],
+    data = [":deps"],
+)
diff --git a/test/empty_suite/src/code.rs b/test/empty_suite/src/code.rs
new file mode 100644
index 0000000..c23780d
--- /dev/null
+++ b/test/empty_suite/src/code.rs
@@ -0,0 +1,7 @@
+pub fn hello() {}
+
+#[cfg(test)]
+#[test]
+fn test_unit() {
+    hello()
+}
diff --git a/test/empty_suite/src/unrelated_test.rs b/test/empty_suite/src/unrelated_test.rs
new file mode 100644
index 0000000..8378eb5
--- /dev/null
+++ b/test/empty_suite/src/unrelated_test.rs
@@ -0,0 +1,4 @@
+#[test]
+pub fn test_hello() {
+    code::hello()
+}
diff --git a/test/empty_suite/verify_empty.sh b/test/empty_suite/verify_empty.sh
new file mode 100755
index 0000000..493d397
--- /dev/null
+++ b/test/empty_suite/verify_empty.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Verifies that argv[1] is an empty file.
+
+set -eux
+
+test -f $1
+test ! -s $1