]> Cypherpunks repositories - gostls13.git/commitdiff
os: avoid nil returns from Readdirnames, Readdir, ReadDir
authorRuss Cox <rsc@golang.org>
Wed, 4 Nov 2020 11:55:59 +0000 (06:55 -0500)
committerRuss Cox <rsc@golang.org>
Wed, 4 Nov 2020 21:17:01 +0000 (21:17 +0000)
The refactoring of this code while adding ReadDir stopped
pre-allocating a 100-entry slice for the results.
That seemed like a good idea in general, since many
directories have nowhere near 100 entries, but it had the
side effect of returning a nil slice for an empty directory.

Some “golden” tests that are too sensitive about nil vs not
inside Google broke because Readdirnames(-1) was now
returning nil instead of []string{} on an empty directory.
It seems likely there are other such tests in the wild, and
it doesn't seem worth breaking them.

This commit restores the non-nil-ness of the old result,
without restoring the excessive preallocation.

Fixes #42367.

Change-Id: I2be72030ac703346e859a97c2d4e456fadfce9b2
Reviewed-on: https://go-review.googlesource.com/c/go/+/267637
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/os/dir.go
src/os/os_test.go

index b56d9984592d455a7762198078685e5f2d92b7c1..1d90b970e7aa6b59ab2ce957184f66d5999daaf0 100644 (file)
@@ -36,6 +36,12 @@ func (f *File) Readdir(n int) ([]FileInfo, error) {
                return nil, ErrInvalid
        }
        _, _, infos, err := f.readdir(n, readdirFileInfo)
+       if infos == nil {
+               // Readdir has historically always returned a non-nil empty slice, never nil,
+               // even on error (except misuse with nil receiver above).
+               // Keep it that way to avoid breaking overly sensitive callers.
+               infos = []FileInfo{}
+       }
        return infos, err
 }
 
@@ -59,6 +65,12 @@ func (f *File) Readdirnames(n int) (names []string, err error) {
                return nil, ErrInvalid
        }
        names, _, _, err = f.readdir(n, readdirName)
+       if names == nil {
+               // Readdirnames has historically always returned a non-nil empty slice, never nil,
+               // even on error (except misuse with nil receiver above).
+               // Keep it that way to avoid breaking overly sensitive callers.
+               names = []string{}
+       }
        return names, err
 }
 
@@ -81,6 +93,10 @@ func (f *File) ReadDir(n int) ([]DirEntry, error) {
                return nil, ErrInvalid
        }
        _, dirents, _, err := f.readdir(n, readdirDirEntry)
+       if dirents == nil {
+               // Match Readdir and Readdirnames: don't return nil slices.
+               dirents = []DirEntry{}
+       }
        return dirents, err
 }
 
index 378ddf58dd4434d8efc8a8e57924e13c841be7dd..a1c05788876619bbf3c902c619d7eb97d75afff0 100644 (file)
@@ -330,6 +330,9 @@ func testReaddirnames(dir string, contents []string, t *testing.T) {
                        t.Error("could not find", m)
                }
        }
+       if s == nil {
+               t.Error("Readdirnames returned nil instead of empty slice")
+       }
 }
 
 func testReaddir(dir string, contents []string, t *testing.T) {
@@ -360,6 +363,9 @@ func testReaddir(dir string, contents []string, t *testing.T) {
                        t.Error("could not find", m)
                }
        }
+       if s == nil {
+               t.Error("Readdir returned nil instead of empty slice")
+       }
 }
 
 func testReadDir(dir string, contents []string, t *testing.T) {
@@ -408,21 +414,27 @@ func testReadDir(dir string, contents []string, t *testing.T) {
                        t.Error("could not find", m)
                }
        }
+       if s == nil {
+               t.Error("ReadDir returned nil instead of empty slice")
+       }
 }
 
 func TestReaddirnames(t *testing.T) {
        testReaddirnames(".", dot, t)
        testReaddirnames(sysdir.name, sysdir.files, t)
+       testReaddirnames(t.TempDir(), nil, t)
 }
 
 func TestReaddir(t *testing.T) {
        testReaddir(".", dot, t)
        testReaddir(sysdir.name, sysdir.files, t)
+       testReaddir(t.TempDir(), nil, t)
 }
 
 func TestReadDir(t *testing.T) {
        testReadDir(".", dot, t)
        testReadDir(sysdir.name, sysdir.files, t)
+       testReadDir(t.TempDir(), nil, t)
 }
 
 func benchmarkReaddirname(path string, b *testing.B) {