Don't bother getting the not-real FileStateValue for a path whose parent doesn't exist.

In addition to saving a filesystem operation, this removes a source of a potential filesystem inconsistency.

--
MOS_MIGRATED_REVID=136355008
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
index 8404f53..617baad 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
+import com.google.devtools.build.lib.skyframe.FileStateValue.Type;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.util.Preconditions;
 import com.google.devtools.build.lib.vfs.Path;
@@ -80,34 +81,36 @@
       }
       realRootedPath = resolvedState.getFirst();
       realFileStateValue = resolvedState.getSecond();
+      if (realFileStateValue.getType() == Type.NONEXISTENT) {
+        return FileValue.value(
+            rootedPath,
+            FileStateValue.NONEXISTENT_FILE_STATE_NODE,
+            realRootedPath,
+            realFileStateValue);
+      }
     }
 
-    FileStateValue fileStateValue = null;
-
-    try {
-      fileStateValue =
-          (FileStateValue)
-              env.getValueOrThrow(
-                  FileStateValue.key(rootedPath), FileOutsidePackageRootsException.class);
-    } catch (FileOutsidePackageRootsException e) {
-      throw new FileFunctionException(
-          new FileOutsidePackageRootsException(rootedPath), Transience.PERSISTENT);
+    FileStateValue fileStateValue;
+    if (rootedPath.equals(realRootedPath)) {
+      fileStateValue = Preconditions.checkNotNull(realFileStateValue, rootedPath);
+    } else {
+      try {
+        fileStateValue =
+            (FileStateValue)
+                env.getValueOrThrow(
+                    FileStateValue.key(rootedPath), FileOutsidePackageRootsException.class);
+      } catch (FileOutsidePackageRootsException e) {
+        throw new FileFunctionException(
+            new FileOutsidePackageRootsException(rootedPath), Transience.PERSISTENT);
+      }
+      if (fileStateValue == null) {
+        return null;
+      }
     }
 
-    if (fileStateValue == null) {
-      return null;
-    }
     if (realFileStateValue == null) {
       realRootedPath = rootedPath;
       realFileStateValue = fileStateValue;
-    } else if (rootedPath.equals(realRootedPath) && !fileStateValue.equals(realFileStateValue)) {
-      String message = String.format(
-          "Some filesystem operations implied %s was a %s but others made us think it was a %s",
-          rootedPath.asPath().getPathString(),
-          fileStateValue.prettyPrint(),
-          realFileStateValue.prettyPrint());
-      throw new FileFunctionException(new InconsistentFilesystemException(message),
-          Transience.TRANSIENT);
     }
 
     ArrayList<RootedPath> symlinkChain = new ArrayList<>();
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
index 5257324..6308953 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FileFunctionTest.java
@@ -888,22 +888,13 @@
   }
 
   @Test
-  public void testFilesystemInconsistencies_ParentDoesntExistAndChildIsSymlink() throws Exception {
-    symlink("a/b", "doesntmatter");
-    // Our custom filesystem says "a/b" exists but "a" does not exist.
+  public void testDoesntStatChildIfParentDoesntExist() throws Exception {
+    // Our custom filesystem says "a" does not exist, so FileFunction shouldn't bother trying to
+    // think about "a/b". Test for this by having a stat of "a/b" fail with an io error, and
+    // observing that we don't encounter the error.
     fs.stubStat(path("a"), null);
-    SequentialBuildDriver driver = makeDriver();
-    SkyKey skyKey = skyKey("a/b");
-    EvaluationResult<FileValue> result =
-        driver.evaluate(
-            ImmutableList.of(skyKey), false, DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
-    assertTrue(result.hasError());
-    ErrorInfo errorInfo = result.getError(skyKey);
-    assertThat(errorInfo.getException()).isInstanceOf(InconsistentFilesystemException.class);
-    assertThat(errorInfo.getException().getMessage())
-        .contains(
-            "/root/a/b was a symlink to doesntmatter but others made us think it was a "
-                + "nonexistent path");
+    fs.stubStatError(path("a/b"), new IOException("ouch!"));
+    assertThat(valueForPath(path("a/b")).exists()).isFalse();
   }
 
   @Test
@@ -1639,8 +1630,9 @@
 
   private class CustomInMemoryFs extends InMemoryFileSystem {
 
-    private Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
-    private Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap();
+    private final Map<Path, FileStatus> stubbedStats = Maps.newHashMap();
+    private final Map<Path, IOException> stubbedStatErrors = Maps.newHashMap();
+    private final Map<Path, IOException> stubbedFastDigestErrors = Maps.newHashMap();
 
     public CustomInMemoryFs(ManualClock manualClock) {
       super(manualClock);
@@ -1667,8 +1659,15 @@
       stubbedStats.put(path, stubbedResult);
     }
 
+    public void stubStatError(Path path, IOException error) {
+      stubbedStatErrors.put(path, error);
+    }
+
     @Override
     public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+      if (stubbedStatErrors.containsKey(path)) {
+        throw stubbedStatErrors.get(path);
+      }
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
index 9175145..3c960df 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/PackageFunctionTest.java
@@ -252,7 +252,6 @@
     Path bazDir = bazFile.getParentDirectory();
     Path barDir = bazDir.getParentDirectory();
 
-    long bazFileNodeId = bazFile.stat().getNodeId();
     // Our custom filesystem says "foo/bar/baz" does not exist but it also says that "foo/bar"
     // has a child directory "baz".
     fs.stubStat(bazDir, null);
@@ -265,9 +264,7 @@
             new Dirent("baz", Dirent.Type.DIRECTORY))));
     differencer.inject(ImmutableMap.of(DirectoryListingValue.key(barDirRootedPath), barDirListing));
     SkyKey skyKey = PackageValue.key(PackageIdentifier.parse("@//foo"));
-    String expectedMessage = "Some filesystem operations implied /workspace/foo/bar/baz/baz.sh was "
-        + "a regular file with size of 0 and mtime of 0 and nodeId of " + bazFileNodeId + " and "
-        + "mtime of 0 but others made us think it was a nonexistent path";
+    String expectedMessage = "/workspace/foo/bar/baz is no longer an existing directory";
     EvaluationResult<PackageValue> result = SkyframeExecutorTestUtils.evaluate(
         getSkyframeExecutor(), skyKey, /*keepGoing=*/false, reporter);
     assertTrue(result.hasError());