Skylark repositories: also record the definition of the rule

When fetching a repository fails, in particular due to an unmet dependency,
it is often also helpful to know the place where the repository rule was defined,
e.g., to find some hints on why an implicit dependency was added. So record that
information as well in the definition information shown upon failures related to
that particular repository.

Fixes #7784.

Change-Id: Ie2be1b1e48283b4b4199bfb216011d935c5250a1
PiperOrigin-RevId: 243962184
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryModule.java
index 1971f07..f5b9823 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryModule.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.analysis.skylark.SkylarkAttr.Descriptor;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
+import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.AttributeValueSource;
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.Package.NameConflictException;
@@ -89,7 +90,7 @@
     builder.setRuleDefinitionEnvironmentLabelAndHashCode(
         funcallEnv.getGlobals().getLabel(), funcallEnv.getTransitiveContentHashCode());
     builder.setWorkspaceOnly();
-    return new RepositoryRuleFunction(builder);
+    return new RepositoryRuleFunction(builder, ast.getLocation());
   }
 
   private static final class RepositoryRuleFunction extends BaseFunction
@@ -97,10 +98,12 @@
     private final RuleClass.Builder builder;
     private Label extensionLabel;
     private String exportedName;
+    private final Location ruleClassDefinitionLocation;
 
-    public RepositoryRuleFunction(RuleClass.Builder builder) {
+    public RepositoryRuleFunction(RuleClass.Builder builder, Location ruleClassDefinitionLocation) {
       super("repository_rule", FunctionSignature.KWARGS);
       this.builder = builder;
+      this.ruleClassDefinitionLocation = ruleClassDefinitionLocation;
     }
 
     @Override
@@ -157,7 +160,11 @@
         StringBuilder callStack =
             new StringBuilder("Call stack for the definition of repository '")
                 .append(externalRepoName)
-                .append("':");
+                .append("' which is a ")
+                .append(ruleClassName)
+                .append(" (rule definition at ")
+                .append(ruleClassDefinitionLocation.toString())
+                .append("):");
         for (DebugFrame frame : env.listFrames(ast.getLocation())) {
           callStack.append("\n - ").append(frame.location().toString());
         }
diff --git a/src/test/shell/bazel/external_integration_test.sh b/src/test/shell/bazel/external_integration_test.sh
index c050f7b..ca313ea 100755
--- a/src/test/shell/bazel/external_integration_test.sh
+++ b/src/test/shell/bazel/external_integration_test.sh
@@ -2188,6 +2188,10 @@
   bazel build //... > "${TEST_log}" 2>&1 && fail "expected failure" || :
 
   expect_log "add.*this_repo_is_missing.*WORKSPACE"
+  # Also verify that the repository class and its definition is reported, to
+  # help finding out where the implict dependency comes from.
+  expect_log 'data.*is.*data_repo'
+  expect_log 'data_repo.*main/withimplicit.bzl:6'
 }
 
 run_suite "external tests"