Create infrastructure to restrict top-level Starlark objects by flag

As proof of concept, this restricts AnalysisFailureInfo and AnalysisTestResultInfo to be unavailable as top-level symbols without --experimental_analysis_testing_improvements . This is technically a breaking change, but these symbols were unusable before this change, documented as being experimental, and are not included in any binary release of Bazel.

RELNOTES: None.
PiperOrigin-RevId: 217593936
diff --git a/src/test/java/com/google/devtools/build/docgen/SkylarkDocumentationTest.java b/src/test/java/com/google/devtools/build/docgen/SkylarkDocumentationTest.java
index 412a74e..16c0aa8 100644
--- a/src/test/java/com/google/devtools/build/docgen/SkylarkDocumentationTest.java
+++ b/src/test/java/com/google/devtools/build/docgen/SkylarkDocumentationTest.java
@@ -18,7 +18,6 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.docgen.skylark.SkylarkBuiltinMethodDoc;
 import com.google.devtools.build.docgen.skylark.SkylarkJavaMethodDoc;
 import com.google.devtools.build.docgen.skylark.SkylarkMethodDoc;
 import com.google.devtools.build.docgen.skylark.SkylarkModuleDoc;
@@ -87,9 +86,8 @@
     Map<String, SkylarkModuleDoc> modules = SkylarkDocumentationCollector.collectModules();
     SkylarkModuleDoc topLevel =
         modules.remove(SkylarkDocumentationCollector.getTopLevelModule().name());
-    for (Map.Entry<String, SkylarkBuiltinMethodDoc> entry :
-        topLevel.getBuiltinMethods().entrySet()) {
-      docMap.put(entry.getKey(), entry.getValue().getDocumentation());
+    for (SkylarkMethodDoc method : topLevel.getMethods()) {
+      docMap.put(method.getName(), method.getDocumentation());
     }
     for (Map.Entry<String, SkylarkModuleDoc> entry : modules.entrySet()) {
       docMap.put(entry.getKey(), entry.getValue().getDocumentation());
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 92b16ec..d89f9fb 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
@@ -2119,7 +2119,9 @@
 
     reporter.removeHandler(failFastHandler);
     getConfiguredTarget("//test:r");
-    assertContainsEvent("'Provider' object is not callable");
+    assertContainsEvent(
+        "AnalysisTestResultInfo is experimental and thus unavailable with the current flags. "
+            + "It may be enabled by setting --experimental_analysis_testing_improvements");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index bec987f..071019e 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -36,6 +36,7 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
 import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
+import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier;
 import com.google.devtools.build.lib.testutil.TestMode;
 import java.util.List;
 import java.util.Map;
@@ -2296,4 +2297,80 @@
         .testIfErrorContains("'AnalysisFailure' has no field 'message'", "val.message")
         .testIfErrorContains("'AnalysisFailure' has no field 'label'", "val.label");
   }
+
+  @Test
+  public void testExperimentalFlagGuardedValue() throws Exception {
+    // This test uses an arbitrary experimental flag to verify this functionality. If this
+    // experimental flag were to go away, this test may be updated to use any experimental flag.
+    // The flag itself is unimportant to the test.
+    FlagGuardedValue val = FlagGuardedValue.onlyWhenExperimentalFlagIsTrue(
+        FlagIdentifier.EXPERIMENTAL_ANALYSIS_TESTING_IMPROVEMENTS,
+        "foo");
+    String errorMessage = "GlobalSymbol is experimental and thus unavailable with the current "
+        + "flags. It may be enabled by setting --experimental_analysis_testing_improvements";
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--experimental_analysis_testing_improvements=true")
+        .setUp("var = GlobalSymbol")
+        .testLookup("var", "foo");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--experimental_analysis_testing_improvements=false")
+        .testIfErrorContains(errorMessage,
+            "var = GlobalSymbol");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--experimental_analysis_testing_improvements=false")
+        .testIfErrorContains(errorMessage,
+            "def my_function():",
+            "  var = GlobalSymbol");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--experimental_analysis_testing_improvements=false")
+        .setUp("GlobalSymbol = 'other'",
+            "var = GlobalSymbol")
+        .testLookup("var", "other");
+  }
+
+  @Test
+  public void testIncompatibleFlagGuardedValue() throws Exception {
+    // This test uses an arbitrary incompatible flag to verify this functionality. If this
+    // incompatible flag were to go away, this test may be updated to use any incompatible flag.
+    // The flag itself is unimportant to the test.
+    FlagGuardedValue val = FlagGuardedValue.onlyWhenIncompatibleFlagIsFalse(
+        FlagIdentifier.INCOMPATIBLE_NO_TARGET_OUTPUT_GROUP,
+        "foo");
+    String errorMessage = "GlobalSymbol is deprecated and will be removed soon. It may be "
+        + "temporarily re-enabled by setting --incompatible_no_target_output_group=false";
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--incompatible_no_target_output_group=false")
+        .setUp("var = GlobalSymbol")
+        .testLookup("var", "foo");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--incompatible_no_target_output_group=true")
+        .testIfErrorContains(errorMessage,
+            "var = GlobalSymbol");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--incompatible_no_target_output_group=true")
+        .testIfErrorContains(errorMessage,
+            "def my_function():",
+            "  var = GlobalSymbol");
+
+    new SkylarkTest(
+            ImmutableMap.of("GlobalSymbol", val),
+            "--incompatible_no_target_output_group=true")
+        .setUp("GlobalSymbol = 'other'",
+            "var = GlobalSymbol")
+        .testLookup("var", "other");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
index 4f6c725..ac07655 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
@@ -17,6 +17,7 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.truth.Ordered;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventCollector;
@@ -39,6 +40,7 @@
 import com.google.devtools.build.lib.testutil.TestMode;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import org.junit.Before;
 
 /**
@@ -98,12 +100,17 @@
 
   protected Environment newEnvironmentWithSkylarkOptions(String... skylarkOptions)
       throws Exception {
+    return newEnvironmentWithBuiltinsAndSkylarkOptions(ImmutableMap.of(), skylarkOptions);
+  }
+
+  protected Environment newEnvironmentWithBuiltinsAndSkylarkOptions(Map<String, Object> builtins,
+      String... skylarkOptions) throws Exception {
     if (testMode == null) {
       throw new IllegalArgumentException(
           "TestMode is null. Please set a Testmode via setMode() or set the "
               + "Environment manually by overriding newEnvironment()");
     }
-    return testMode.createEnvironment(getEventHandler(), skylarkOptions);
+    return testMode.createEnvironment(getEventHandler(), builtins, skylarkOptions);
   }
 
   /**
@@ -117,6 +124,17 @@
     env = newEnvironmentWithSkylarkOptions(skylarkOptions);
   }
 
+  protected void setMode(TestMode testMode, Map<String, Object> builtins,
+      String... skylarkOptions) throws Exception {
+    this.testMode = testMode;
+    env = newEnvironmentWithBuiltinsAndSkylarkOptions(builtins, skylarkOptions);
+  }
+
+  protected void enableSkylarkMode(Map<String, Object> builtins,
+      String... skylarkOptions) throws Exception {
+    setMode(TestMode.SKYLARK, builtins, skylarkOptions);
+  }
+
   protected void enableSkylarkMode(String... skylarkOptions) throws Exception {
     setMode(TestMode.SKYLARK, skylarkOptions);
   }
@@ -589,14 +607,20 @@
    */
   protected class SkylarkTest extends ModalTestCase {
     private final String[] skylarkOptions;
+    private final Map<String, Object> builtins;
 
     public SkylarkTest(String... skylarkOptions) {
+      this(ImmutableMap.of(), skylarkOptions);
+    }
+
+    public SkylarkTest(Map<String, Object> builtins, String... skylarkOptions) {
+      this.builtins = builtins;
       this.skylarkOptions = skylarkOptions;
     }
 
     @Override
     protected void run(Testable testable) throws Exception {
-      enableSkylarkMode(skylarkOptions);
+      enableSkylarkMode(builtins, skylarkOptions);
       testable.run();
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
index 9b848bd..e850ac6 100644
--- a/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
@@ -13,13 +13,16 @@
 // limitations under the License.
 package com.google.devtools.build.lib.testutil;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
 import com.google.devtools.build.lib.events.EventHandler;
-import com.google.devtools.build.lib.packages.BazelLibrary;
 import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions;
 import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.Environment.GlobalFrame;
 import com.google.devtools.build.lib.syntax.Mutability;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
 import com.google.devtools.common.options.OptionsParser;
+import java.util.Map;
 
 /**
  * Describes a particular testing mode by determining how the
@@ -36,10 +39,12 @@
   public static final TestMode BUILD =
       new TestMode() {
         @Override
-        public Environment createEnvironment(EventHandler eventHandler, String... skylarkOptions)
+        public Environment createEnvironment(EventHandler eventHandler,
+            Map<String, Object> builtins,
+            String... skylarkOptions)
             throws Exception {
           return Environment.builder(Mutability.create("build test"))
-              .setGlobals(BazelLibrary.GLOBALS)
+              .setGlobals(createGlobalFrame(builtins))
               .setEventHandler(eventHandler)
               .setSemantics(TestMode.parseSkylarkSemantics(skylarkOptions))
               .build();
@@ -49,16 +54,26 @@
   public static final TestMode SKYLARK =
       new TestMode() {
         @Override
-        public Environment createEnvironment(EventHandler eventHandler, String... skylarkOptions)
+        public Environment createEnvironment(EventHandler eventHandler,
+            Map<String, Object> builtins, String... skylarkOptions)
             throws Exception {
           return Environment.builder(Mutability.create("skylark test"))
-              .setGlobals(BazelLibrary.GLOBALS)
+              .setGlobals(createGlobalFrame(builtins))
               .setEventHandler(eventHandler)
               .setSemantics(TestMode.parseSkylarkSemantics(skylarkOptions))
               .build();
         }
       };
 
-  public abstract Environment createEnvironment(EventHandler eventHandler, String... skylarkOptions)
-      throws Exception;
+  private static GlobalFrame createGlobalFrame(Map<String, Object> builtins) {
+    ImmutableMap.Builder<String, Object> envBuilder = ImmutableMap.builder();
+
+    SkylarkModules.addSkylarkGlobalsToBuilder(envBuilder);
+    envBuilder.putAll(builtins);
+    return GlobalFrame.createForBuiltins(envBuilder.build());
+  }
+
+  public abstract Environment createEnvironment(EventHandler eventHandler,
+      Map<String, Object> builtins,
+      String... skylarkOptions) throws Exception;
 }