Change Skylark's print() on a rule target to print the Skylark-exposed provider keys.

This change only affects printing a rule target directly -- it intentionally does not affect the behavior of str(target), as we want to avoid skylark code being able to parse potentially-private provider keys.

RELNOTES: In skylark, print(target) now shows the provider keys of a target, as debug information.
PiperOrigin-RevId: 186046226
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
index 9c7ec8f..89881c4 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/RuleConfiguredTarget.java
@@ -13,7 +13,9 @@
 // limitations under the License.
 package com.google.devtools.build.lib.analysis.configuredtargets;
 
+import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Interner;
@@ -171,4 +173,21 @@
   public void repr(SkylarkPrinter printer) {
     printer.append("<target " + getLabel() + ">");
   }
+
+  @Override
+  public void debugPrint(SkylarkPrinter printer) {
+    // Show the names of the provider keys that this target propagates.
+    // Provider key names might potentially be *private* information, and thus a comprehensive
+    // list of provider keys should not be exposed in any way other than for debug information.
+    printer.append("<target " + getLabel() + ", keys:[");
+    ImmutableList.Builder<String> skylarkProviderKeyStrings = ImmutableList.builder();
+    for (int providerIndex = 0; providerIndex < providers.getProviderCount(); providerIndex++) {
+      Object providerKey = providers.getProviderKeyAt(providerIndex);
+      if (providerKey instanceof Provider.Key) {
+        skylarkProviderKeyStrings.add(providerKey.toString());
+      }
+    }
+    printer.append(Joiner.on(", ").join(skylarkProviderKeyStrings.build()));
+    printer.append("]>");
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintable.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintable.java
index 39760c4..8edfbd5 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintable.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkPrintable.java
@@ -21,7 +21,7 @@
 public interface SkylarkPrintable {
 
   /**
-   * Print an official representation of object x.
+   * Prints an official representation of object x.
    *
    * <p>For regular data structures, the value should be parsable back into an equal data structure.
    *
@@ -30,7 +30,7 @@
   void repr(SkylarkPrinter printer);
 
   /**
-   * Print an informal, human-readable representation of the value.
+   * Prints an informal, human-readable representation of the value.
    *
    * <p>By default dispatches to the {@code repr} method.
    *
@@ -39,4 +39,20 @@
   default void str(SkylarkPrinter printer) {
     repr(printer);
   }
+
+  /**
+   * Prints an informal debug representation of the value.
+   *
+   * <p>This debug representation is only ever printed to the terminal or to another out-of-band
+   * channel, and is never accessible to Skylark code. Therefore, it is safe for the debug
+   * representation to reveal properties of the value that are usually hidden for the sake of
+   * performance, determinism, or forward-compatibility.
+   *
+   * <p>By default dispatches to the {@code str} method.
+   *
+   * @param printer a printer to be used for formatting nested values.
+   */
+  default void debugPrint(SkylarkPrinter printer) {
+    str(printer);
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
index f3c0d5f..b782926 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
@@ -2113,12 +2113,15 @@
     name = "print",
     returnType = Runtime.NoneType.class,
     doc =
-        "Prints <code>args</code> as output. It will be prefixed with the string <code>"
-            + "\"DEBUG\"</code> and the location (file and line number) of this call. It can be "
-            + "used for debugging."
+        "Prints <code>args</code> as debug output. It will be prefixed with the string <code>"
+            + "\"DEBUG\"</code> and the location (file and line number) of this call. The "
+            + "exact way in which the arguments are converted to strings is unspecified and may "
+            + "change at any time. In particular, it may be different from (and more detailed "
+            + "than) the formatting done by <a href='#str'><code>str()</code></a> and <a "
+            + "href='#repr'><code>repr()</code></a>."
             + "<p>Using <code>print</code> in production code is discouraged due to the spam it "
             + "creates for users. For deprecations, prefer a hard error using <a href=\"#fail\">"
-            + "fail()</a> when possible.",
+            + "<code>fail()</code></a> whenever possible.",
     parameters = {
       @Param(
         name = "sep",
@@ -2139,7 +2142,7 @@
         public Runtime.NoneType invoke(
             String sep, SkylarkList<?> starargs, Location loc, Environment env)
             throws EvalException {
-          String msg = starargs.stream().map(Printer::str).collect(joining(sep));
+          String msg = starargs.stream().map(Printer::debugPrint).collect(joining(sep));
           // As part of the integration test "skylark_flag_test.sh", if the
           // "--internal_skylark_flag_test_canary" flag is enabled, append an extra marker string to
           // the output.
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
index 69b16fe..b2248f8 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Printer.java
@@ -73,6 +73,13 @@
   // These static methods proxy to the similar methods of BasePrinter
 
   /**
+   * Format an object with Skylark's {@code debugPrint}.
+   */
+  public static String debugPrint(Object x) {
+    return getPrinter().debugPrint(x).toString();
+  }
+
+  /**
    * Format an object with Skylark's {@code str}.
    */
   public static String str(Object x) {
@@ -283,6 +290,21 @@
     }
 
     /**
+     * Print an informal debug-only representation of object x.
+     *
+     * @param o the object
+     * @return the buffer, in fluent style
+     */
+    public BasePrinter debugPrint(Object o) {
+      if (o instanceof SkylarkValue) {
+        ((SkylarkValue) o).debugPrint(this);
+        return this;
+      }
+
+      return this.str(o);
+    }
+
+    /**
      * Print an informal representation of object x. Currently only differs from repr in the
      * behavior for strings and labels at top-level, that are returned as is rather than quoted.
      *
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 6f8e384..8764ac4 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
@@ -916,6 +916,46 @@
   }
 
   @Test
+  public void testPrintProviderCollection() throws Exception {
+    scratch.file(
+        "test/skylark/rules.bzl",
+        "",
+        "FooInfo = provider()",
+        "BarInfo = provider()",
+        "",
+        "def _top_level_rule_impl(ctx):",
+        "  print('My Dep Providers:', ctx.attr.my_dep)",
+        "",
+        "def _dep_rule_impl(name):",
+        "  providers = [",
+        "      FooInfo(),",
+        "      BarInfo(),",
+        "  ]",
+        "  return providers",
+        "",
+        "top_level_rule = rule(",
+        "    implementation=_top_level_rule_impl,",
+        "    attrs={'my_dep':attr.label()}",
+        ")",
+        "",
+        "dep_rule = rule(",
+        "    implementation=_dep_rule_impl,",
+        ")");
+
+    scratch.file(
+        "test/skylark/BUILD",
+        "load('//test/skylark:rules.bzl', 'top_level_rule', 'dep_rule')",
+        "",
+        "top_level_rule(name = 'tl', my_dep=':d')",
+        "",
+        "dep_rule(name = 'd')");
+
+    getConfiguredTarget("//test/skylark:tl");
+    assertContainsEvent(
+        "My Dep Providers: <target //test/skylark:d, keys:[FooInfo, BarInfo, OutputGroupInfo]>");
+  }
+
+  @Test
   public void testRuleClassImplicitOutputFunctionPrints() throws Exception {
     scratch.file(
         "test/skylark/extension.bzl",