native.existing_rule() should not see finalizer-instantiated targets

Working towards https://github.com/bazelbuild/bazel/issues/23160

PiperOrigin-RevId: 678773063
Change-Id: Iea8f319e8c6dae9a735420707f0d0a799df6fabd
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index b8d7e8f..1b8efc1 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -1728,6 +1728,24 @@
     }
 
     /**
+     * Returns a non-finalizer-instantiated rule target with the provided name belonging to this
+     * package at the time of this call. If such a rule target cannot be returned, returns null.
+     */
+    // TODO(https://github.com/bazelbuild/bazel/issues/23765): when we restrict
+    // native.existing_rule() to be usable only in finalizer context, we can replace this method
+    // with {@code getRulesSnapshotView().get(name)}; we don't do so at present because we do not
+    // want to make unnecessary snapshots.
+    @Nullable
+    Rule getNonFinalizerInstantiatedRule(String name) {
+      if (rulesSnapshotViewForFinalizers != null) {
+        return rulesSnapshotViewForFinalizers.get(name);
+      } else {
+        Target target = regEnv.getTargetMap().get(name);
+        return target instanceof Rule ? (Rule) target : null;
+      }
+    }
+
+    /**
      * Creates an input file target in this package with the specified name, if it does not yet
      * exist.
      *
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
index d0f253e..4cc70c6 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkNativeModule.java
@@ -434,8 +434,8 @@
     }
     Package.Builder pkgBuilder =
         Package.Builder.fromOrFailDisallowNonFinalizerMacros(thread, "existing_rule()");
-    Target target = pkgBuilder.getTarget(name);
-    if (target instanceof Rule rule) {
+    @Nullable Rule rule = pkgBuilder.getNonFinalizerInstantiatedRule(name);
+    if (rule != null) {
       return new ExistingRuleView(rule);
     } else {
       return Starlark.NONE;
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleFinalizerTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleFinalizerTest.java
index 6fa16d3..855771f 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleFinalizerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleFinalizerTest.java
@@ -200,11 +200,38 @@
   }
 
   @Test
-  public void finalizer_nativeExistingRules_seesOnlyNonFinalizerTargets_inAllLexicalPositions()
+  public void finalizer_nativeExistingRule_seesOnlyNonFinalizerTargets_inAllLexicalPositions()
       throws Exception {
     scratch.file(
         "pkg/foo.bzl",
         """
+        EXPECTED = [
+            "top_level_lexically_before_finalizer",
+            "macro_lexically_before_finalizer_inner_lib",
+            "top_level_lexically_after_finalizer",
+            "macro_lexically_after_finalizer_inner_lib",
+        ]
+
+        UNEXPECTED = [
+            "finalizer_inner_lib",
+            "finalizer_inner_macro_inner_lib",
+            "finalizer_inner_finalizer_inner_lib",
+            "other_finalizer_inner_lib",
+            "other_finalizer_inner_macro_inner_lib",
+            "other_finalizer_inner_finalizer_inner_lib",
+        ]
+
+        def check_existing_rules():
+            if (sorted(native.existing_rules().keys()) != sorted(EXPECTED)):
+                fail("native.existing_rules().keys(): " + native.existing_rules().keys())
+            for t in EXPECTED:
+                if native.existing_rule(t) == None:
+                    fail("native.existing_rule(" + t + ") == None")
+            for t in UNEXPECTED:
+                if native.existing_rule(t) != None:
+                    fail("native.existing_rule(" + t + ") != None")
+            print("native.existing_rules and native.existing_rule are as expected")
+
         def _impl_macro(name):
             native.cc_library(name = name + "_inner_lib")
 
@@ -212,8 +239,7 @@
 
         def _impl_inner_finalizer(name):
             native.cc_library(name = name + "_inner_lib")
-            for r in native.existing_rules().values():
-                print("saw " + r["name"])
+            check_existing_rules()
 
         inner_finalizer = macro(implementation = _impl_inner_finalizer, finalizer = True)
 
@@ -221,8 +247,7 @@
             native.cc_library(name = name + "_inner_lib")
             my_macro(name = name + "_inner_macro")
             inner_finalizer(name = name + "_inner_finalizer")
-            for r in native.existing_rules().values():
-                print("saw " + r["name"])
+            check_existing_rules()
 
         my_finalizer = macro(implementation = _impl_finalizer, finalizer = True)
         """);
@@ -240,15 +265,7 @@
 
     Package pkg = getPackage("pkg");
     assertPackageNotInError(pkg);
-    assertContainsEventWithFrequency("saw top_level_lexically_before_finalizer", 4);
-    assertContainsEventWithFrequency("saw macro_lexically_before_finalizer_inner_lib", 4);
-    assertContainsEventWithFrequency("saw top_level_lexically_after_finalizer", 4);
-    assertContainsEventWithFrequency("saw macro_lexically_after_finalizer_inner_lib", 4);
-    assertDoesNotContainEvent("saw finalizer_inner_lib");
-    assertDoesNotContainEvent("saw finalizer_inner_macro_inner_lib");
-    assertDoesNotContainEvent("saw finalizer_inner_finalizer_inner_lib");
-    assertDoesNotContainEvent("saw other_finalizer_inner_lib");
-    assertDoesNotContainEvent("saw other_finalizer_inner_macro_inner_lib");
-    assertDoesNotContainEvent("saw other_finalizer_inner_finalizer_inner_lib");
+    assertContainsEventWithFrequency(
+        "native.existing_rules and native.existing_rule are as expected", 4);
   }
 }