Rephrase InMemoryFileSystem in terms of error codes rather than exceptions, enabling better performance on statIfFound() / statNullable() calls.

RELNOTES: None
PiperOrigin-RevId: 212306308
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/BUILD b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/BUILD
index 532f27e..c4b363c 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/BUILD
@@ -16,6 +16,7 @@
         "//src/main/java/com/google/devtools/build/lib/clock",
         "//src/main/java/com/google/devtools/build/lib/concurrent",
         "//src/main/java/com/google/devtools/build/lib/vfs",
+        "//third_party:auto_value",
         "//third_party:guava",
         "//third_party:jsr305",
     ],
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
index 6be1345..006be0a 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs/InMemoryFileSystem.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.vfs.inmemoryfs;
 
+import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -20,12 +21,13 @@
 import com.google.devtools.build.lib.clock.JavaClock;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.util.OS;
+import com.google.devtools.build.lib.vfs.AbstractFileSystemWithCustomStat;
 import com.google.devtools.build.lib.vfs.DigestHashFunction;
 import com.google.devtools.build.lib.vfs.FileAccessException;
 import com.google.devtools.build.lib.vfs.FileStatus;
-import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.errorprone.annotations.CheckReturnValue;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -48,7 +50,7 @@
  * to achieve.
  */
 @ThreadSafe
-public class InMemoryFileSystem extends FileSystem {
+public class InMemoryFileSystem extends AbstractFileSystemWithCustomStat {
 
   protected final Clock clock;
 
@@ -140,6 +142,10 @@
       return message;
     }
 
+    public InodeOrErrno asInodeOrErrno() {
+      return InodeOrErrno.createError(this);
+    }
+
     /** Implemented by exceptions that contain the extra info of which Error caused them. */
     private interface WithError {
       Error getError();
@@ -267,11 +273,22 @@
    * Inserts inode 'childInode' into the existing directory 'dir' under the
    * specified 'name'.  Dual to unlink.  Fails if the directory was read-only.
    */
-  private void insert(InMemoryDirectoryInfo dir, String child,
-                      InMemoryContentInfo childInode, Path errorPath)
-      throws IOException {
-    if (!dir.isWritable()) { throw Error.EACCES.exception(errorPath); }
+  @CheckReturnValue
+  private Error insert(InMemoryDirectoryInfo dir, String child,
+                       InMemoryContentInfo childInode) {
+    if (!dir.isWritable()) {
+      return Error.EACCES;
+    }
     dir.addChild(child, childInode);
+    return null;
+  }
+
+  private void insert(InMemoryDirectoryInfo dir, String child,
+      InMemoryContentInfo childInode, Path errorPath) throws IOException {
+    Error error = insert(dir, child, childInode);
+    if (error != null) {
+      throw error.exception(errorPath);
+    }
   }
 
   /**
@@ -280,23 +297,30 @@
    * try to create it. May fail with ENOTDIR, EACCES, ENOENT. Error messages
    * will be reported against file 'path'.
    */
-  private InMemoryContentInfo directoryLookup(InMemoryContentInfo dir,
-                                              String name,
-                                              boolean create,
-                                              Path path) throws IOException {
-    if (!dir.isDirectory()) { throw Error.ENOTDIR.exception(path); }
+  private InodeOrErrno directoryLookupErrno(InMemoryContentInfo dir,
+                                            String name,
+                                            boolean create,
+                                            Path path) {
+    if (!dir.isDirectory()) {
+      return Error.ENOTDIR.asInodeOrErrno();
+    }
     InMemoryDirectoryInfo imdi = (InMemoryDirectoryInfo) dir;
-    if (!imdi.isExecutable()) { throw Error.EACCES.exception(path); }
+    if (!imdi.isExecutable()) {
+      return Error.EACCES.asInodeOrErrno();
+    }
     InMemoryContentInfo child = imdi.getChild(name);
     if (child == null) {
       if (!create)  {
-        throw Error.ENOENT.exception(path);
+        return Error.ENOENT.asInodeOrErrno();
       } else {
         child = newFile(clock, path);
-        insert(imdi, name, child, path);
+        Error error = insert(imdi, name, child);
+        if (error != null) {
+          return error.asInodeOrErrno();
+        }
       }
     }
-    return child;
+    return InodeOrErrno.createInode(child);
   }
 
   protected FileInfo newFile(Clock clock, Path path) {
@@ -314,7 +338,7 @@
    *
    * <p>May fail with ENOTDIR, ENOENT, EACCES, ELOOP.
    */
-  private synchronized InMemoryContentInfo pathWalk(Path path, boolean create) throws IOException {
+  private synchronized InodeOrErrno pathWalkErrno(Path path, boolean create) {
     Stack<String> stack = new Stack<>();
     for (Path p = path; !isRootDirectory(p); p = p.getParentDirectory()) {
       String name = baseNameOrWindowsDrive(p);
@@ -330,14 +354,20 @@
       String name = stack.pop();
 
       // ENOENT on last segment with 'create' => create a new file.
-      InMemoryContentInfo child = directoryLookup(inode, name, create && stack.isEmpty(), path);
+      InodeOrErrno childOrError =
+          directoryLookupErrno(inode, name, create && stack.isEmpty(), path);
+      if (childOrError.hasError()) {
+        return childOrError;
+      }
+
+      InMemoryContentInfo child = childOrError.inode();
       if (child.isSymbolicLink()) {
         PathFragment linkTarget = ((InMemoryLinkInfo) child).getNormalizedLinkContent();
         if (linkTarget.isAbsolute()) {
           inode = rootInode;
         }
         if (traversals > MAX_TRAVERSALS) {
-          throw Error.ELOOP.exception(path);
+          return Error.ELOOP.asInodeOrErrno();
         }
         List<String> segments = linkTarget.getSegments();
         for (int ii = segments.size() - 1; ii >= 0; --ii) {
@@ -354,7 +384,26 @@
         inode = child;
       }
     }
-    return inode;
+    return InodeOrErrno.createInode(inode);
+  }
+
+  /**
+   * Given 'path', returns the existing directory inode it designates,
+   * following symbolic links.
+   *
+   * <p>May fail with ENOTDIR, or any exception from pathWalk.
+   */
+  private InodeOrErrno getDirectoryErrno(Path path) {
+    InodeOrErrno dirInfoOrError = pathWalkErrno(path, false);
+    if (dirInfoOrError.hasError()) {
+      return dirInfoOrError;
+    }
+    InMemoryContentInfo dirInfo = dirInfoOrError.inode();
+    if (!dirInfo.isDirectory()) {
+      return Error.ENOTDIR.asInodeOrErrno();
+    } else {
+      return dirInfoOrError;
+    }
   }
 
   /**
@@ -364,21 +413,19 @@
    * <p>May fail with ENOTDIR, or any exception from pathWalk.
    */
   private InMemoryDirectoryInfo getDirectory(Path path) throws IOException {
-    InMemoryContentInfo dirInfo = pathWalk(path, false);
-    if (!dirInfo.isDirectory()) {
-      throw Error.ENOTDIR.exception(path);
-    } else {
-      return (InMemoryDirectoryInfo) dirInfo;
-    }
+    return (InMemoryDirectoryInfo) getDirectoryErrno(path).valueOrThrow(path);
   }
 
   /**
    * Helper method for stat and inodeStat: return the path's (no symlink-followed) stat.
    */
-  private synchronized InMemoryContentInfo noFollowStat(Path path)
-      throws IOException  {
-    InMemoryDirectoryInfo dirInfo = getDirectory(path.getParentDirectory());
-    return directoryLookup(dirInfo, baseNameOrWindowsDrive(path), /*create=*/ false, path);
+  private synchronized InodeOrErrno noFollowStatErrno(Path path) {
+    InodeOrErrno dirInfoOrError = getDirectoryErrno(path.getParentDirectory());
+    if (dirInfoOrError.hasError()) {
+      return dirInfoOrError;
+    }
+    return directoryLookupErrno(dirInfoOrError.inode(), baseNameOrWindowsDrive(path),
+        /*create=*/ false, path);
   }
 
   /**
@@ -388,37 +435,48 @@
    */
   @Override
   public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
-    return inodeStat(path, followSymlinks);
+    return inodeStatErrno(path, followSymlinks).valueOrThrow(path);
   }
 
   @Override
   @Nullable
   public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
-    try {
-      return stat(path, followSymlinks);
-    } catch (IOException e) {
-      if (e instanceof Error.WithError) {
-        Error errorCode = ((Error.WithError) e).getError();
-        if  (errorCode == Error.ENOENT || errorCode == Error.ENOTDIR) {
-          return null;
-        }
+      InodeOrErrno inodeOrErrno = inodeStatErrno(path, followSymlinks);
+    if (inodeOrErrno.hasError()) {
+      Error errorCode = inodeOrErrno.error();
+      if (errorCode == Error.ENOENT || errorCode == Error.ENOTDIR) {
+        return null;
       }
-      throw e;
+      throw errorCode.exception(path);
+    } else {
+      return inodeOrErrno.inode();
     }
   }
 
+  @Override
+  protected FileStatus statNullable(Path path, boolean followSymlinks) {
+    InodeOrErrno inodeOrErrno = inodeStatErrno(path, followSymlinks);
+    return inodeOrErrno.hasError() ? null : inodeOrErrno.inode();
+  }
+
   /**
-   * Version of stat that returns an inode of the input path.
+   * Version of stat that returns an InodeOrErrno of the input path.
    */
-  protected InMemoryContentInfo inodeStat(Path path, boolean followSymlinks)
-      throws IOException {
+  @CheckReturnValue
+  protected InodeOrErrno inodeStatErrno(Path path, boolean followSymlinks) {
     if (followSymlinks) {
-      return pathWalk(path, false);
+      return pathWalkErrno(path, false);
     } else {
-      return isRootDirectory(path) ? rootInode : noFollowStat(path);
+      return isRootDirectory(path)
+          ? InodeOrErrno.createInode(rootInode)
+          : noFollowStatErrno(path);
     }
   }
 
+  private InMemoryContentInfo inodeStat(Path path, boolean followSymlinks) throws IOException {
+    return inodeStatErrno(path, followSymlinks).valueOrThrow(path);
+  }
+
   /****************************************************************************
    *  FileSystem methods
    */
@@ -441,49 +499,8 @@
   }
 
   @Override
-  protected boolean isDirectory(Path path, boolean followSymlinks) {
-    try {
-      return stat(path, followSymlinks).isDirectory();
-    } catch (IOException e) {
-      return false;
-    }
-  }
-
-  @Override
-  protected boolean isFile(Path path, boolean followSymlinks) {
-    try {
-      return stat(path, followSymlinks).isFile();
-    } catch (IOException e) {
-      return false;
-    }
-  }
-
-  @Override
-  protected boolean isSpecialFile(Path path, boolean followSymlinks) {
-    try {
-      return stat(path, followSymlinks).isSpecialFile();
-    } catch (IOException e) {
-      return false;
-    }
-  }
-
-  @Override
-  protected boolean isSymbolicLink(Path path) {
-    try {
-      return stat(path, false).isSymbolicLink();
-    } catch (IOException e) {
-      return false;
-    }
-  }
-
-  @Override
   protected boolean exists(Path path, boolean followSymlinks) {
-    try {
-      stat(path, followSymlinks);
-      return true;
-    } catch (IOException e) {
-      return false;
-    }
+    return statNullable(path, followSymlinks) != null;
   }
 
   @Override
@@ -732,7 +749,7 @@
     // resolving a path with the intent to create it: instead of failing with
     // ENOENT they have to return an open file.  This is exactly how UNIX
     // kernels do it, which is what we're trying to emulate.
-    InMemoryContentInfo child = pathWalk(path, /*create=*/true);
+    InMemoryContentInfo child = pathWalkErrno(path, /*create=*/true).valueOrThrow(path);
     Preconditions.checkNotNull(child);
     if (child.isDirectory()) {
       throw Error.EISDIR.exception(path);
@@ -845,4 +862,35 @@
     String name = path.getBaseName();
     return !name.isEmpty() ? name : path.getDriveStr();
   }
+
+  /**
+   * A class representing either an {@link Error} or an {@link InMemoryContentInfo}.
+   */
+  @AutoValue
+  protected abstract static class InodeOrErrno {
+    static InodeOrErrno createInode(InMemoryContentInfo info) {
+      return new AutoValue_InMemoryFileSystem_InodeOrErrno(Preconditions.checkNotNull(info), null);
+    }
+
+    static InodeOrErrno createError(Error error) {
+      return new AutoValue_InMemoryFileSystem_InodeOrErrno(null, Preconditions.checkNotNull(error));
+    }
+
+    @Nullable
+    public abstract InMemoryContentInfo inode();
+
+    @Nullable
+    public abstract Error error();
+
+    public boolean hasError() {
+      return error() != null;
+    }
+
+    public InMemoryContentInfo valueOrThrow(Path path) throws IOException {
+      if (hasError()) {
+        throw error().exception(path);
+      }
+      return inode();
+    }
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
index 67d9b62..dfb8401 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/AnalysisWithIOExceptionsTest.java
@@ -38,12 +38,12 @@
   protected FileSystem createFileSystem() {
     return new InMemoryFileSystem(BlazeClock.instance()) {
       @Override
-      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+      public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
         String crash = crashMessage.apply(path);
         if (crash != null) {
           throw new IOException(crash);
         }
-        return super.stat(path, followSymlinks);
+        return super.statIfFound(path, followSymlinks);
       }
     };
   }
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
index 7273270..ef6cda8 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/IOExceptionsTest.java
@@ -80,13 +80,14 @@
   @Override
   protected FileSystem createFileSystem() {
     return new InMemoryFileSystem(BlazeClock.instance()) {
+      @Nullable
       @Override
-      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+      public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
         String crash = crashMessage.apply(path);
         if (crash != null) {
           throw new IOException(crash);
         }
-        return super.stat(path, followSymlinks);
+        return super.statIfFound(path, followSymlinks);
       }
     };
   }
diff --git a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
index 2ab8977..540760d 100644
--- a/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
+++ b/src/test/java/com/google/devtools/build/lib/pkgcache/TargetPatternEvaluatorIOTest.java
@@ -63,6 +63,23 @@
         return transformer.stat(defaultResult, path, followSymlinks);
       }
 
+      @Nullable
+      @Override
+      public FileStatus statIfFound(Path path, boolean followSymlinks) {
+        return statNullable(path, followSymlinks);
+      }
+
+      @Nullable
+      @Override
+      public FileStatus statNullable(Path path, boolean followSymlinks) {
+        FileStatus defaultResult = super.statNullable(path, followSymlinks);
+        try {
+          return transformer.stat(defaultResult, path, followSymlinks);
+        } catch (IOException e) {
+          return null;
+        }
+      }
+
       @Override
       protected Collection<Dirent> readdir(Path path, boolean followSymlinks) throws IOException {
         Collection<Dirent> defaultResult = super.readdir(path, followSymlinks);
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 5395b90..f857159 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
@@ -45,12 +45,12 @@
     boolean statThrowsIoException;
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (statThrowsIoException
           && path.asFragment().getPathString().equals("/workspace/" + preludeLabelRelativePath)) {
         throw new IOException("bork");
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
index c23b003..6f8a519 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
@@ -175,11 +175,11 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+          public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
             if (path.getBaseName().equals("bad")) {
               throw exception;
             }
-            return super.stat(path, followSymlinks);
+            return super.statIfFound(path, followSymlinks);
           }
         });
     try {
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 b76bbcd..e2b32f5 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
@@ -1636,14 +1636,14 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStatErrors.containsKey(path)) {
         throw stubbedStatErrors.get(path);
       }
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index 8ae2219..4e47c00 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -843,11 +843,11 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (statThrowsRuntimeException) {
         throw new RuntimeException("bork");
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
 
     @Override
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
index 7d118c6..fb76a9f 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/GlobFunctionTest.java
@@ -789,11 +789,11 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
   }
 }
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 1a3dcdd..48dc8b4 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
@@ -772,11 +772,11 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path).get();
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
 
     public void scheduleMakeUnreadableAfterReaddir(Path path) {
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
index b03a0e9..ba66167 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ParallelBuilderTest.java
@@ -240,8 +240,8 @@
   public void testUpdateCacheError() throws Exception {
     FileSystem fs = new InMemoryFileSystem() {
       @Override
-      public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
-        final FileStatus stat = super.stat(path, followSymlinks);
+      public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
+        final FileStatus stat = super.statIfFound(path, followSymlinks);
         if (path.toString().endsWith("/out/foo")) {
           return new FileStatus() {
             private final FileStatus original = stat;
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
index fae9d22..0afd49c 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SkyframeLabelVisitorTestCase.java
@@ -254,11 +254,11 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStats.containsKey(path)) {
         return stubbedStats.get(path);
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
index 6778dc8..9d53a09 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TargetMarkerFunctionTest.java
@@ -153,11 +153,11 @@
     }
 
     @Override
-    public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+    public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
       if (stubbedStatExceptions.containsKey(path)) {
         throw stubbedStatExceptions.get(path);
       }
-      return super.stat(path, followSymlinks);
+      return super.statIfFound(path, followSymlinks);
     }
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
index 0f5d21b..3bb8353 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
@@ -181,11 +181,11 @@
     setupRoot(
         new CustomInMemoryFs() {
           @Override
-          public FileStatus stat(Path path, boolean followSymlinks) throws IOException {
+          public FileStatus statIfFound(Path path, boolean followSymlinks) throws IOException {
             if (path.getBaseName().equals("one")) {
               throw exception;
             }
-            return super.stat(path, followSymlinks);
+            return super.statIfFound(path, followSymlinks);
           }
         });
     try {