Pass through information about package lookup failure into import lookup.

This fixes 'Misleading error message "file must have a corresponding package"' '#7094.

PiperOrigin-RevId: 235806611
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunction.java
index f3ca988..0cdc9e8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunction.java
@@ -14,6 +14,7 @@
 package com.google.devtools.build.lib.skyframe;
 
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.skyframe.PackageLookupValue.ErrorReason;
 import com.google.devtools.build.lib.skyframe.PackageLookupValue.IncorrectRepositoryReferencePackageLookupValue;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skyframe.SkyFunction;
@@ -49,6 +50,9 @@
       return env.getValue(ContainingPackageLookupValue.key(correctPackageIdentifier));
     }
 
+    if (ErrorReason.REPOSITORY_NOT_FOUND.equals(pkgLookupValue.getErrorReason())) {
+      return new ContainingPackageLookupValue.NoContainingPackage(pkgLookupValue.getErrorMsg());
+    }
     PathFragment parentDir = dir.getPackageFragment().getParentDirectory();
     if (parentDir == null) {
       return ContainingPackageLookupValue.NONE;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupValue.java
index 7ab319c..302d6db 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupValue.java
@@ -26,6 +26,7 @@
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
+import javax.annotation.Nonnull;
 
 /**
  * A value that represents the result of looking for the existence of a package that owns a
@@ -45,6 +46,14 @@
   /** If there is a containing package, returns its package root */
   public abstract Root getContainingPackageRoot();
 
+  /**
+   * If there is not a containing package, returns a reason why (this is usually the reason the
+   * outer-most directory isn't a package).
+   */
+  public String getReasonForNoContainingPackage() {
+    throw new IllegalStateException();
+  }
+
   public static Key key(PackageIdentifier id) {
     Preconditions.checkArgument(!id.getPackageFragment().isAbsolute(), id);
     Preconditions.checkArgument(!id.getRepository().isDefault(), id);
@@ -112,7 +121,15 @@
 
   /** Value indicating there is no containing package. */
   public static class NoContainingPackage extends ContainingPackageLookupValue {
-    private NoContainingPackage() {}
+    private final String reason;
+
+    private NoContainingPackage() {
+      this.reason = null;
+    }
+
+    NoContainingPackage(@Nonnull String reason) {
+      this.reason = reason;
+    }
 
     @Override
     public boolean hasContainingPackage() {
@@ -133,6 +150,11 @@
     public String toString() {
       return getClass().getName();
     }
+
+    @Override
+    public String getReasonForNoContainingPackage() {
+      return reason;
+    }
   }
 
   /** A successful lookup value. */
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
index aed63e4..95fe4be 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupFunction.java
@@ -321,7 +321,7 @@
     }
     if (!repositoryValue.repositoryExists()) {
       // TODO(ulfjack): Maybe propagate the error message from the repository delegator function?
-      return PackageLookupValue.NO_SUCH_REPOSITORY_VALUE;
+      return new PackageLookupValue.NoRepositoryPackageLookupValue(id.getRepository().getName());
     }
 
     // This checks for the build file names in the correct precedence order.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupValue.java
index f5c5f8c..7d24e17 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageLookupValue.java
@@ -51,10 +51,6 @@
   public static final DeletedPackageLookupValue DELETED_PACKAGE_VALUE =
       new DeletedPackageLookupValue();
 
-  @AutoCodec
-  public static final NoRepositoryPackageLookupValue NO_SUCH_REPOSITORY_VALUE =
-      new NoRepositoryPackageLookupValue();
-
   enum ErrorReason {
     /** There is no BUILD file. */
     NO_BUILD_FILE,
@@ -391,11 +387,15 @@
   }
 
   /**
-   * Marker value for repository we could not find. This can happen when looking for a label that
-   * specifies a non-existent repository.
+   * Value for repository we could not find. This can happen when looking for a label that specifies
+   * a non-existent repository.
    */
   public static class NoRepositoryPackageLookupValue extends UnsuccessfulPackageLookupValue {
-    private NoRepositoryPackageLookupValue() {}
+    private final String repositoryName;
+
+    NoRepositoryPackageLookupValue(String repositoryName) {
+      this.repositoryName = repositoryName;
+    }
 
     @Override
     ErrorReason getErrorReason() {
@@ -404,7 +404,7 @@
 
     @Override
     public String getErrorMsg() {
-      return "The repository could not be resolved";
+      return String.format("The repository '%s' could not be resolved", repositoryName);
     }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
index 0c3c081..179dc54 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
@@ -249,7 +249,8 @@
         return null;
       }
       if (!containingPackageLookupValue.hasContainingPackage()) {
-        throw SkylarkImportFailedException.noBuildFile(fileLabel.toPathFragment());
+        throw SkylarkImportFailedException.noBuildFile(
+            fileLabel, containingPackageLookupValue.getReasonForNoContainingPackage());
       }
       if (!containingPackageLookupValue.getContainingPackageName().equals(
           fileLabel.getPackageIdentifier())) {
@@ -600,7 +601,11 @@
           cause);
     }
 
-    static SkylarkImportFailedException noBuildFile(PathFragment file) {
+    static SkylarkImportFailedException noBuildFile(Label file, @Nullable String reason) {
+      if (reason != null) {
+        return new SkylarkImportFailedException(
+            String.format("Unable to find package for %s: %s.", file, reason));
+      }
       return new SkylarkImportFailedException(
           String.format("Every .bzl file must have a corresponding package, but '%s' "
               + "does not have one. Please create a BUILD file in the same or any parent directory."
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
index f857159..07bb6c4 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunctionTest.java
@@ -164,6 +164,6 @@
     assertThat(result.get(skyKey).getErrorMsg())
     .contains("Unable to load package for '@a_remote_repo//remote_pkg:BUILD'");
     assertThat(result.get(skyKey).getErrorMsg())
-        .contains("The repository could not be resolved");
+        .contains("The repository '@a_remote_repo' could not be resolved");
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
index 18bb0fb..1739a30 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ContainingPackageLookupFunctionTest.java
@@ -38,8 +38,11 @@
 import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
 import com.google.devtools.build.lib.rules.repository.RepositoryFunction;
 import com.google.devtools.build.lib.rules.repository.RepositoryLoaderFunction;
+import com.google.devtools.build.lib.skyframe.ContainingPackageLookupValue.ContainingPackage;
+import com.google.devtools.build.lib.skyframe.ContainingPackageLookupValue.NoContainingPackage;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
 import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
+import com.google.devtools.build.lib.skyframe.PackageLookupValue.ErrorReason;
 import com.google.devtools.build.lib.syntax.StarlarkSemantics;
 import com.google.devtools.build.lib.testutil.FoundationTestCase;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -185,6 +188,18 @@
         .get(key);
   }
 
+  private PackageLookupValue lookupPackage(PackageIdentifier packageIdentifier)
+      throws InterruptedException {
+    SkyKey key = PackageLookupValue.key(packageIdentifier);
+    EvaluationContext evaluationContext =
+        EvaluationContext.newBuilder()
+            .setKeepGoing(false)
+            .setNumThreads(SkyframeExecutor.DEFAULT_THREAD_COUNT)
+            .setEventHander(NullEventHandler.INSTANCE)
+            .build();
+    return driver.<PackageLookupValue>evaluate(ImmutableList.of(key), evaluationContext).get(key);
+  }
+
   @Test
   public void testNoContainingPackage() throws Exception {
     ContainingPackageLookupValue value = lookupContainingPackage("a/b");
@@ -265,4 +280,51 @@
         .addEqualityGroup(valueCOther)
         .testEquals();
   }
+
+  @Test
+  public void testNonExistentExternalRepositoryErrorReason() throws Exception {
+    PackageIdentifier identifier =
+        PackageIdentifier.create("@some_repo", PathFragment.create(":atarget"));
+    ContainingPackageLookupValue value = lookupContainingPackage(identifier);
+    assertThat(value.hasContainingPackage()).isFalse();
+    assertThat(value.getClass()).isEqualTo(NoContainingPackage.class);
+    assertThat(value.getReasonForNoContainingPackage())
+        .isEqualTo("The repository '@some_repo' could not be resolved");
+  }
+
+  @Test
+  public void testInvalidPackageLabelErrorReason() throws Exception {
+    ContainingPackageLookupValue value = lookupContainingPackage("invalidpackagename:42/BUILD");
+    assertThat(value.hasContainingPackage()).isFalse();
+    assertThat(value.getClass()).isEqualTo(NoContainingPackage.class);
+    // As for invalid package name we continue to climb up the parent packages,
+    // we will find the top-level package with the path "" - empty string.
+    assertThat(value.getReasonForNoContainingPackage()).isNull();
+  }
+
+  @Test
+  public void testDeletedPackageErrorReason() throws Exception {
+    PackageIdentifier identifier = PackageIdentifier.createInMainRepo("deletedpackage");
+    deletedPackages.set(ImmutableSet.of(identifier));
+    scratch.file("BUILD");
+
+    PackageLookupValue packageLookupValue = lookupPackage(identifier);
+    assertThat(packageLookupValue.packageExists()).isFalse();
+    assertThat(packageLookupValue.getErrorReason()).isEqualTo(ErrorReason.DELETED_PACKAGE);
+    assertThat(packageLookupValue.getErrorMsg())
+        .isEqualTo("Package is considered deleted due to --deleted_packages");
+
+    ContainingPackageLookupValue value = lookupContainingPackage(identifier);
+    assertThat(value.hasContainingPackage()).isTrue();
+    assertThat(value.getContainingPackageName().toString()).isEmpty();
+    assertThat(value.getClass()).isEqualTo(ContainingPackage.class);
+  }
+
+  @Test
+  public void testNoBuildFileErrorReason() throws Exception {
+    ContainingPackageLookupValue value = lookupContainingPackage("abc");
+    assertThat(value.hasContainingPackage()).isFalse();
+    assertThat(value.getClass()).isEqualTo(NoContainingPackage.class);
+    assertThat(value.getReasonForNoContainingPackage()).isNull();
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
index 7bfcddc..c05b615 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunctionTest.java
@@ -415,6 +415,31 @@
   }
 
   @Test
+  public void testWithNonExistentRepository_And_DisallowLoadUsingLabelThatCrossesBoundaryOfPackage()
+      throws Exception {
+    setSkylarkSemanticsOptions("--incompatible_disallow_load_labels_to_cross_package_boundaries");
+
+    scratch.file("BUILD", "load(\"@repository//dir:file.bzl\", \"foo\")");
+
+    SkyKey skylarkImportLookupKey = key("@repository//dir:file.bzl");
+    EvaluationResult<com.google.devtools.build.lib.skyframe.SkylarkImportLookupValue> result =
+        SkyframeExecutorTestUtils.evaluate(
+            getSkyframeExecutor(), skylarkImportLookupKey, /*keepGoing=*/ false, reporter);
+    assertThat(result.hasError()).isTrue();
+    assertThatEvaluationResult(result)
+        .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+        .hasExceptionThat()
+        .isInstanceOf(SkylarkImportFailedException.class);
+    assertThatEvaluationResult(result)
+        .hasErrorEntryForKeyThat(skylarkImportLookupKey)
+        .hasExceptionThat()
+        .hasMessageThat()
+        .contains(
+            "Unable to find package for @repository//dir:file.bzl: The repository '@repository' "
+                + "could not be resolved.");
+  }
+
+  @Test
   public void testLoadBzlFileFromWorkspaceWithRemapping() throws Exception {
     scratch.deleteFile(preludeLabelRelativePath);
     Path p =
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 be0ff46..4ba3f7b 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
@@ -91,7 +91,7 @@
     checkError(
         "test/skylark",
         "the_rule",
-        "no such package '@r//': The repository could not be resolved",
+        "no such package '@r//': The repository '@r' could not be resolved",
         "load('//test/skylark:extension.bzl', 'my_rule')",
         "",
         "my_rule(name='the_rule')");
diff --git a/src/test/shell/bazel/cross_repository_test.sh b/src/test/shell/bazel/cross_repository_test.sh
index 5c6a97f..abb6303 100755
--- a/src/test/shell/bazel/cross_repository_test.sh
+++ b/src/test/shell/bazel/cross_repository_test.sh
@@ -228,9 +228,9 @@
 
   # These should now fail.
   bazel query @bar//:bar >& $TEST_log && fail "build should fail"
-  expect_log "no such package '@bar//': The repository could not be resolved"
+  expect_log "no such package '@bar//': The repository '@bar' could not be resolved"
   bazel query @bar//subbar:subbar >& $TEST_log && fail "build should fail"
-  expect_log "no such package '@bar//subbar': The repository could not be resolved"
+  expect_log "no such package '@bar//subbar': The repository '@bar' could not be resolved"
 }
 
 # Test for https://github.com/bazelbuild/bazel/issues/2580