Expose structField callable methods of skylark objects to dir() and str() calls

RELNOTES: None.
PiperOrigin-RevId: 184498836
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 6baa230..c234a92 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
@@ -76,6 +76,42 @@
   };
 
   @SkylarkModule(name = "Mock", doc = "")
+  static class NativeInfoMock extends NativeInfo {
+
+    private static final NativeProvider<NativeInfoMock> CONSTRUCTOR =
+        new NativeProvider<NativeInfoMock>(NativeInfoMock.class, "native_info_mock") {};
+
+    public NativeInfoMock() {
+      super(CONSTRUCTOR);
+    }
+
+    @SkylarkCallable(name = "callable_string", doc = "", structField = false)
+    public String callableString() {
+      return "a";
+    }
+
+    @SkylarkCallable(name = "struct_field_string", doc = "", structField = true)
+    public String structFieldString() {
+      return "a";
+    }
+
+    @SkylarkCallable(name = "struct_field_callable", doc = "", structField = true)
+    public BuiltinFunction structFieldCallable() {
+      return foobar;
+    }
+
+    @SkylarkCallable(
+      name = "struct_field_none",
+      doc = "",
+      structField = true,
+      allowReturnNones = true
+    )
+    public String structFieldNone() {
+      return null;
+    }
+  }
+
+  @SkylarkModule(name = "Mock", doc = "")
   static class Mock {
     @SkylarkCallable(doc = "")
     public static Integer valueOf(String str) {
@@ -1193,7 +1229,7 @@
         "  return e",
         "e = str(func())").testLookup("e", "[3, [1, 4]]");
   }
-  
+
   @Test
   public void testDictTupleAssignmentAsLValue() throws Exception {
     new SkylarkTest().setUp("def func():",
@@ -1355,6 +1391,26 @@
   }
 
   @Test
+  public void testStrNativeInfo() throws Exception {
+    new SkylarkTest()
+        .update("mock", new NativeInfoMock())
+        .testEval(
+            "str(mock)",
+            "'struct(struct_field_callable = <built-in function foobar>, struct_field_none = None, "
+                + "struct_field_string = \"a\")'");
+  }
+
+  @Test
+  public void testDirNativeInfo() throws Exception {
+    new SkylarkTest()
+        .update("mock", new NativeInfoMock())
+        .testEval(
+            "dir(mock)",
+            "['callable_string', 'struct_field_callable', "
+                + "'struct_field_none', 'struct_field_string']");
+  }
+
+  @Test
   public void testPrint() throws Exception {
     // TODO(fwe): cannot be handled by current testing suite
     setFailFast(false);
@@ -1561,10 +1617,10 @@
     new SkylarkTest()
         .update("val", new SkylarkClassObjectWithSkylarkCallables())
         .testIfExactError(
-            // TODO(bazel-team): This should probably list callable_only_field/method as well.
+            // TODO(bazel-team): This should probably list callable_only_method as well.
             "'struct_with_skylark_callables' object has no attribute 'nonexistent_field'\n"
-                + "Available attributes: collision_field, collision_method, values_only_field, "
-                + "values_only_method",
+                + "Available attributes: callable_only_field, collision_field, collision_method, "
+                + "values_only_field, values_only_method",
             "v = val.nonexistent_field");
   }