For missing implicit dependencies show doc string

If an implicit dependency is not met for a target, and the attribute
introducing that implicit dependency has a doc string, show it
to the user. Most likely, it will contain information on where to
obtain that dependency from. In this way, the rule authors, who
are the only ones having knowledge about best practices using their
rules, can provide the needed information.

Improves on #7165.

Change-Id: I2ece552080aa2bd7581a81b790891d60d943e839
PiperOrigin-RevId: 236701041
diff --git a/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java b/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java
index 1f947e6..acb0352 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/TargetUtils.java
@@ -19,6 +19,7 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
+import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import com.google.devtools.build.lib.cmdline.Label;
@@ -320,19 +321,27 @@
    * Target} target did not exist, due to {@link NoSuchThingException} e.
    */
   public static String formatMissingEdge(
-      @Nullable Target target, Label label, NoSuchThingException e) {
+      @Nullable Target target, Label label, NoSuchThingException e, @Nullable Attribute attr) {
     // instanceof returns false if target is null (which is exploited here)
     if (target instanceof Rule) {
       Rule rule = (Rule) target;
       if (isExplicitDependency(rule, label)) {
         return String.format("%s and referenced by '%s'", e.getMessage(), target.getLabel());
       } else {
+        String additionalInfo = "";
+        if (attr != null && !Strings.isNullOrEmpty(attr.getDoc())) {
+          additionalInfo =
+              String.format(
+                  "\nDocumentation for implicit attribute %s of rules of type %s:\n%s",
+                  attr.getPublicName(), rule.getRuleClass(), attr.getDoc());
+        }
         // N.B. If you see this error message in one of our integration tests during development of
         // a change that adds a new implicit dependency when running Blaze, maybe you forgot to add
         // a new mock target to the integration test's setup.
-        return String.format("every rule of type %s implicitly depends upon the target '%s', but "
-            + "this target could not be found because of: %s", rule.getRuleClass(), label,
-            e.getMessage());
+        return String.format(
+            "every rule of type %s implicitly depends upon the target '%s', but "
+                + "this target could not be found because of: %s%s",
+            rule.getRuleClass(), label, e.getMessage(), additionalInfo);
       }
     } else if (target instanceof InputFile) {
       return e.getMessage() + " (this is usually caused by a missing package group in the"
@@ -345,4 +354,9 @@
       return e.getMessage();
     }
   }
+
+  public static String formatMissingEdge(
+      @Nullable Target target, Label label, NoSuchThingException e) {
+    return formatMissingEdge(target, label, e, null);
+  }
 }