Only apply deps limit to analysis tests using transitions

Previously, a transitive-dependency count limit was applied to all analysis_test rules for simplicity, since there was not deemed a use case for an analysis test that was "large". However, relaxing this restriction to only apply to rules using transitions facilitates a build_test-like Starlark rule which verifies an existing real target analyzes correctly without executing any actions.

Progress toward #6237

RELNOTES: None.
PiperOrigin-RevId: 235580276
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
index af2f5d2..dfba1a7 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleConfiguredTargetBuilder.java
@@ -162,6 +162,27 @@
       addProvider(new DepCountInfo(transitiveDepCount()));
     }
 
+    if (ruleContext.getRule().hasAnalysisTestTransition()) {
+      int depCount = transitiveDepCount();
+      if (depCount > ruleContext.getConfiguration().analysisTestingDepsLimit()) {
+        ruleContext.ruleError(
+            String.format(
+                "analysis test rule excedeed maximum dependency edge count. "
+                    + "Count: %s. Limit is %s. This limit is imposed on analysis test rules which "
+                    + "use analysis_test_transition attribute transitions. Exceeding this limit "
+                    + "indicates either the analysis_test has too many dependencies, or the "
+                    + "underlying toolchains may be large. Try decreasing the number of test "
+                    + "dependencies, (Analysis tests should not be very large!) or, if possible, "
+                    + "try not using configuration transitions. If underlying toolchain size is "
+                    + "to blame, it might be worth considering increasing "
+                    + "--analysis_testing_deps_limit. (Beware, however, that large values of "
+                    + "this flag can lead to no safeguards against giant "
+                    + "test suites that can lead to Out Of Memory exceptions in the build server.)",
+                depCount, ruleContext.getConfiguration().analysisTestingDepsLimit()));
+        return null;
+      }
+    }
+
     TransitiveInfoProviderMap providers = providersBuilder.build();
 
     if (ruleContext.getRule().isAnalysisTest()) {
@@ -177,22 +198,6 @@
       }
 
       AnalysisTestActionBuilder.writeAnalysisTestAction(ruleContext, testResultInfo);
-
-      int depCount = transitiveDepCount();
-      if (depCount > ruleContext.getConfiguration().analysisTestingDepsLimit()) {
-        ruleContext.ruleError(
-            String.format(
-                "analysis test rule excedeed maximum dependency edge count. "
-                    + "Count: %s. Limit is %s. This indicates either the analysis_test has too "
-                    + "many dependencies, or the underlying toolchains may be large. Try "
-                    + "decreasing the number of test dependencies. (Analysis tests should not be "
-                    + "very large!) If underlying toolchain size is to blame, it might be worth "
-                    + "considering increasing --analysis_testing_deps_limit. (Beware, however, "
-                    + "that large values of this flag can lead to no safeguards against giant "
-                    + "test suites that can lead to Out Of Memory exceptions in the build server.)",
-                depCount, ruleContext.getConfiguration().analysisTestingDepsLimit()));
-        return null;
-      }
     }
 
     AnalysisEnvironment analysisEnvironment = ruleContext.getAnalysisEnvironment();
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index 4639d41..24e1241 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -677,11 +677,14 @@
         Attribute attr = descriptor.build(name);
 
         hasStarlarkDefinedTransition |= attr.hasStarlarkDefinedTransition();
-        if (attr.hasAnalysisTestTransition() && !builder.isAnalysisTest()) {
-          throw new EvalException(
-              location,
-              "Only rule definitions with analysis_test=True may have attributes with "
-                  + "analysis_test_transition transitions");
+        if (attr.hasAnalysisTestTransition()) {
+          if (!builder.isAnalysisTest()) {
+            throw new EvalException(
+                location,
+                "Only rule definitions with analysis_test=True may have attributes with "
+                    + "analysis_test_transition transitions");
+          }
+          builder.setHasAnalysisTestTransition();
         }
         // Check for existence of the function transition whitelist attribute.
         // TODO(b/121385274): remove when we stop whitelisting starlark transitions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/Rule.java b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
index ae01bd2..c3245b4 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Rule.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Rule.java
@@ -182,6 +182,14 @@
   }
 
   /**
+   * Returns true if this rule has at least one attribute with an analysis test transition. (A
+   * starlark-defined transition using analysis_test_transition()).
+   */
+  public boolean hasAnalysisTestTransition() {
+    return ruleClass.hasAnalysisTestTransition();
+  }
+
+  /**
    * Returns true iff there were errors while constructing this rule, such as
    * attributes with missing values or values of the wrong type.
    */
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index d61cc4a..7f96fc4 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -645,6 +645,7 @@
     private boolean workspaceOnly = false;
     private boolean isExecutableSkylark = false;
     private boolean isAnalysisTest = false;
+    private boolean hasAnalysisTestTransition = false;
     private boolean isConfigMatcher = false;
     private boolean hasFunctionTransitionWhitelist = false;
     private boolean ignorePackageLicenses = false;
@@ -808,6 +809,7 @@
           workspaceOnly,
           isExecutableSkylark,
           isAnalysisTest,
+          hasAnalysisTestTransition,
           hasFunctionTransitionWhitelist,
           ignorePackageLicenses,
           implicitOutputsFunction,
@@ -1195,6 +1197,19 @@
     }
 
     /**
+     * This rule class has at least one attribute with an analysis test transition. (A
+     * starlark-defined transition using analysis_test_transition()).
+     */
+    public Builder setHasAnalysisTestTransition() {
+      this.hasAnalysisTestTransition = true;
+      return this;
+    }
+
+    public boolean hasAnalysisTestTransition() {
+      return this.hasAnalysisTestTransition;
+    }
+
+    /**
      * This rule class has the _whitelist_function_transition attribute.  Intended only for Skylark
      * rules.
      */
@@ -1390,6 +1405,7 @@
   private final boolean workspaceOnly;
   private final boolean isExecutableSkylark;
   private final boolean isAnalysisTest;
+  private final boolean hasAnalysisTestTransition;
   private final boolean isConfigMatcher;
   private final boolean hasFunctionTransitionWhitelist;
   private final boolean ignorePackageLicenses;
@@ -1519,6 +1535,7 @@
       boolean workspaceOnly,
       boolean isExecutableSkylark,
       boolean isAnalysisTest,
+      boolean hasAnalysisTestTransition,
       boolean hasFunctionTransitionWhitelist,
       boolean ignorePackageLicenses,
       ImplicitOutputsFunction implicitOutputsFunction,
@@ -1569,6 +1586,7 @@
     this.workspaceOnly = workspaceOnly;
     this.isExecutableSkylark = isExecutableSkylark;
     this.isAnalysisTest = isAnalysisTest;
+    this.hasAnalysisTestTransition = hasAnalysisTestTransition;
     this.hasFunctionTransitionWhitelist = hasFunctionTransitionWhitelist;
     this.ignorePackageLicenses = ignorePackageLicenses;
     this.configurationFragmentPolicy = configurationFragmentPolicy;
@@ -2424,6 +2442,14 @@
   }
 
   /**
+   * Returns true if this rule class has at least one attribute with an analysis test transition. (A
+   * starlark-defined transition using analysis_test_transition()).
+   */
+  public boolean hasAnalysisTestTransition() {
+    return hasAnalysisTestTransition;
+  }
+
+  /**
    * Returns true if this rule class has the _whitelist_function_transition attribute.
    */
   public boolean hasFunctionTransitionWhitelist() {
diff --git a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
index 8c70e2b..4cbb2fe 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/RuleClassTest.java
@@ -888,6 +888,7 @@
         workspaceOnly,
         outputsDefaultExecutable,
         isAnalysisTest,
+        /* hasAnalysisTestTransition=*/ false,
         /* hasFunctionTransitionWhitelist=*/ false,
         /* ignorePackageLicenses=*/ false,
         implicitOutputsFunction,
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 25ec752..be0ff46 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
@@ -2417,7 +2417,7 @@
 
   @Test
   public void testAnalysisTestOverDepsLimit() throws Exception {
-    setupAnalysisTestDepsLimitTest(10, 12);
+    setupAnalysisTestDepsLimitTest(10, 12, true);
 
     reporter.removeHandler(failFastHandler);
     getConfiguredTarget("//test:r");
@@ -2427,14 +2427,31 @@
 
   @Test
   public void testAnalysisTestUnderDepsLimit() throws Exception {
-    setupAnalysisTestDepsLimitTest(10, 8);
+    setupAnalysisTestDepsLimitTest(10, 8, true);
 
     assertThat(getConfiguredTarget("//test:r")).isNotNull();
   }
 
-  private void setupAnalysisTestDepsLimitTest(int limit, int dependencyChainSize) throws Exception {
+  @Test
+  public void testAnalysisDepsLimitOnlyForTransition() throws Exception {
+    setupAnalysisTestDepsLimitTest(3, 10, false);
+
+    assertThat(getConfiguredTarget("//test:r")).isNotNull();
+  }
+
+  private void setupAnalysisTestDepsLimitTest(
+      int limit, int dependencyChainSize, boolean useTransition) throws Exception {
     useConfiguration("--analysis_testing_deps_limit=" + limit);
 
+    String transitionDefinition;
+    if (useTransition) {
+      transitionDefinition =
+          "my_transition = analysis_test_transition("
+              + "settings = {'//command_line_option:test_arg' : ['yeehaw'] })";
+    } else {
+      transitionDefinition = "my_transition = None";
+    }
+
     scratch.file(
         "test/extension.bzl",
         "",
@@ -2443,10 +2460,8 @@
         "def dep_rule_impl(ctx):",
         "  return []",
         "",
-        "my_transition = analysis_test_transition(",
-        "    settings = {",
-        "        '//command_line_option:test_arg' : ['yeehaw'] }",
-        ")",
+        transitionDefinition,
+        "",
         "dep_rule = rule(",
         "  implementation = dep_rule_impl,",
         "  attrs = {'dep':  attr.label()}",