Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12632] ファイルの概要

このコミットは、Go言語の os パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist() 関数が ERROR_PATH_NOT_FOUND エラーも適切に処理するように修正され、また os.IsExist() および os.IsNotExist() のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。

コミット

commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Mar 14 23:54:40 2012 +0800

    os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
        Also update documentation about IsExist() and IsNotExist(), they are not
        about files only.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5794073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc

元コミット内容

os: IsNotExist() はWindows上で ERROR_PATH_NOT_FOUND も考慮すべきである。 また、IsExist()IsNotExist() のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。

変更の背景

Go言語の os パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist()os.IsNotExist() が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。

しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir 試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND というエラーが返されることがありました。従来の os.IsNotExist() はこの ERROR_PATH_NOT_FOUND を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist()true を返さないという不整合が生じていました。

このコミットは、この不整合を解消し、Windows上での os.IsNotExist() の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。

前提知識の解説

  • Go言語の os パッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。
  • os.IsExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合に true を返します。
  • os.IsNotExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合に true を返します。
  • os.IsPermission(err error) bool: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合に true を返します。
  • syscall パッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例: syscall.ENOENT, syscall.EEXIST など)が定義されています。
  • Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
    • ERROR_FILE_NOT_FOUND (0x2): 指定されたファイルが見つかりません。
    • ERROR_PATH_NOT_FOUND (0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。
    • ERROR_ALREADY_EXISTS (0xB7): 指定されたファイルは既に存在します。
    • ERROR_ACCESS_DENIED (0x5): アクセスが拒否されました。
  • POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常 ENOENT (Error NO ENTry) が返されます。ファイルが既に存在する場合は EEXIST (Error EXIST) が返されます。
  • os.PathError: os パッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op (操作名)、Path (パス)、Err (元のエラー) のフィールドを持ちます。

技術的詳細

このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go ファイルにあります。

  1. isNotExist 関数の修正:

    • 変更前: return err == syscall.ENOENT || err == ErrNotExist
    • 変更後: return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist この変更により、Windows上で os.IsNotExist() が、ファイルが見つからないことを示す ERROR_FILE_NOT_FOUND に加えて、パスが見つからないことを示す ERROR_PATH_NOT_FOUND も「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへの os.Chdir() 呼び出しが ERROR_PATH_NOT_FOUND を返した場合でも、os.IsNotExist() が正しく true を返すようになります。
  2. isExist 関数の修正:

    • 変更前: return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
    • 変更後: return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist syscall.EEXIST はPOSIXシステムで使われるエラーコードであり、Windows固有の error_windows.go からは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。Windowsでは ERROR_ALREADY_EXISTSERROR_FILE_EXISTS が同等の意味を持ちます。
  3. isPermission 関数の修正:

    • 変更前: return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
    • 変更後: return err == ErrPermission 同様に、syscall.EACCES (アクセス拒否) と syscall.EPERM (操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。Windowsでは ErrPermission がより抽象化された権限エラーを示します。
  4. ドキュメントの更新 (src/pkg/os/error.go):

    • IsExistIsNotExist のコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
  5. テストケースの追加 (src/pkg/os/error_test.go):

    • TestErrIsNotExist という新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()os.Chdir() を試行して os.IsNotExist() が正しく機能するかを検証します。特に、ネストされた存在しないパス(例: /tmp/nonexistent_dir/nonexistent_file)に対してもテストが行われ、ERROR_PATH_NOT_FOUND のシナリオがカバーされていることを確認しています。

これらの変更により、Goの os パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。

コアとなるコードの変更箇所

src/pkg/os/error.go

--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ func NewSyscallError(syscall string, err error) error {
 	return &SyscallError{syscall, err}
 }
 
-// IsExist returns whether the error is known to report that a file already exists.
-// It is satisfied by ErrExist as well as some syscall errors.
+// IsExist returns whether the error is known to report that a file or directory 
+// already exists. It is satisfied by ErrExist as well as some syscall errors.
 func IsExist(err error) bool {
 	return isExist(err)
 }
 
-// IsNotExist returns whether the error is known to report that a file does not exist.
-// It is satisfied by ErrNotExist as well as some syscall errors.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
 func IsNotExist(err error) bool {
 	return isNotExist(err)
 }

src/pkg/os/error_test.go

--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
 package os_test
 
 import (
+\t"fmt"
 	"io/ioutil"
 	"os"
+\t"path/filepath"
 	"testing"
 )
 
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
 		t.Fatal("Open should have failed")
 		return
 	}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
 		return
 	}
 }
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```

### `src/pkg/os/error_windows.go`

```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
 \t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
 }
 
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
 }
 
 func isPermission(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
 }

コアとなるコードの解説

  • src/pkg/os/error.go:

    • IsExistIsNotExist 関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
  • src/pkg/os/error_test.go:

    • testErrNotExist ヘルパー関数が追加され、os.Openos.Chdir の両方で os.IsNotExist が正しく動作するかを検証します。特に os.Chdir はディレクトリ操作であり、ERROR_PATH_NOT_FOUND が発生しやすいシナリオをカバーします。
    • TestErrIsNotExist 関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対して testErrNotExist を呼び出し、os.IsNotExist の堅牢性をテストしています。
    • checkErrorPredicate ヘルパー関数は、エラー述語(os.IsExistos.IsNotExist など)が期待通りに true を返すかを汎用的にチェックするために導入されました。
  • src/pkg/os/error_windows.go:

    • isNotExist 関数内で、Windows固有のエラーコード syscall.ERROR_PATH_NOT_FOUNDos.IsNotExist の判定基準に追加されました。これにより、Windows環境でパスが存在しない場合に os.IsNotExist が正しく true を返すようになります。
    • isExist 関数から syscall.EEXIST が削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。
    • isPermission 関数から syscall.EACCESsyscall.EPERM が削除されました。これらもPOSIXシステムのエラーコードであり、Windowsでは ErrPermission がより抽象的な権限エラーを表すため、整理されました。

これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。

関連リンク

参考にした情報源リンク

このコミットは、Go言語の os パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist() 関数が ERROR_PATH_NOT_FOUND エラーも適切に処理するように修正され、また os.IsExist() および os.IsNotExist() のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。

コミット

commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Wed Mar 14 23:54:40 2012 +0800

    os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
        Also update documentation about IsExist() and IsNotExist(), they are not
        about files only.
    
    R=rsc
    CC=golang-dev
    https://golang.org/cl/5794073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc

元コミット内容

os: IsNotExist() はWindows上で ERROR_PATH_NOT_FOUND も考慮すべきである。 また、IsExist()IsNotExist() のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。

変更の背景

Go言語の os パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist()os.IsNotExist() が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。

しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir 試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND というエラーが返されることがありました。従来の os.IsNotExist() はこの ERROR_PATH_NOT_FOUND を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist()true を返さないという不整合が生じていました。

このコミットは、この不整合を解消し、Windows上での os.IsNotExist() の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。

前提知識の解説

  • Go言語の os パッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。
  • os.IsExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合に true を返します。
  • os.IsNotExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合に true を返します。
  • os.IsPermission(err error) bool: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合に true を返します。
  • syscall パッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例: syscall.ENOENT, syscall.EEXIST など)が定義されています。
  • Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
    • ERROR_FILE_NOT_FOUND (0x2): 指定されたファイルが見つかりません。
    • ERROR_PATH_NOT_FOUND (0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。
    • ERROR_ALREADY_EXISTS (0xB7): 指定されたファイルは既に存在します。
    • ERROR_ACCESS_DENIED (0x5): アクセスが拒否されました。
  • POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常 ENOENT (Error NO ENTry) が返されます。ファイルが既に存在する場合は EEXIST (Error EXIST) が返されます。
  • os.PathError: os パッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op (操作名)、Path (パス)、Err (元のエラー) のフィールドを持ちます。

技術的詳細

このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go ファイルにあります。

  1. isNotExist 関数の修正:

    • 変更前: return err == syscall.ENOENT || err == ErrNotExist
    • 変更後: return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist この変更により、Windows上で os.IsNotExist() が、ファイルが見つからないことを示す ERROR_FILE_NOT_FOUND に加えて、パスが見つからないことを示す ERROR_PATH_NOT_FOUND も「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへの os.Chdir() 呼び出しが ERROR_PATH_NOT_FOUND を返した場合でも、os.IsNotExist() が正しく true を返すようになります。
  2. isExist 関数の修正:

    • 変更前: return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
    • 変更後: return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist syscall.EEXIST はPOSIXシステムで使われるエラーコードであり、Windows固有の error_windows.go からは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。Windowsでは ERROR_ALREADY_EXISTSERROR_FILE_EXISTS が同等の意味を持ちます。
  3. isPermission 関数の修正:

    • 変更前: return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
    • 変更後: return err == ErrPermission 同様に、syscall.EACCES (アクセス拒否) と syscall.EPERM (操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。Windowsでは ErrPermission がより抽象化された権限エラーを示します。
  4. ドキュメントの更新 (src/pkg/os/error.go):

    • IsExistIsNotExist のコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
  5. テストケースの追加 (src/pkg/os/error_test.go):

    • TestErrIsNotExist という新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()os.Chdir() を試行して os.IsNotExist() が正しく機能するかを検証します。特に、ネストされた存在しないパス(例: /tmp/nonexistent_dir/nonexistent_file)に対してもテストが行われ、ERROR_PATH_NOT_FOUND のシナリオがカバーされていることを確認しています。

これらの変更により、Goの os パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。

コアとなるコードの変更箇所

src/pkg/os/error.go

--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ func NewSyscallError(syscall string, err error) error {
 	return &SyscallError{syscall, err}
 }
 
-// IsExist returns whether the error is known to report that a file already exists.
-// It is satisfied by ErrExist as well as some syscall errors.
+// IsExist returns whether the error is known to report that a file or directory 
+// already exists. It is satisfied by ErrExist as well as some syscall errors.
 func IsExist(err error) bool {
 	return isExist(err)
 }
 
-// IsNotExist returns whether the error is known to report that a file does not exist.
-// It is satisfied by ErrNotExist as well as some syscall errors.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
 func IsNotExist(err error) bool {
 	return isNotExist(err)
 }

src/pkg/os/error_test.go

--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
 package os_test
 
 import (
+\t"fmt"
 	"io/ioutil"
 	"os"
+\t"path/filepath"
 	"testing"
 )
 
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
 		t.Fatal("Open should have failed")
 		return
 	}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
 		return
 	}
 }
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```

### `src/pkg/os/error_windows.go`

```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
 \t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
 }
 
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
 }
 
 func isPermission(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
 }

コアとなるコードの解説

  • src/pkg/os/error.go:

    • IsExistIsNotExist 関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
  • src/pkg/os/error_test.go:

    • testErrNotExist ヘルパー関数が追加され、os.Openos.Chdir の両方で os.IsNotExist が正しく動作するかを検証します。特に os.Chdir はディレクトリ操作であり、ERROR_PATH_NOT_FOUND が発生しやすいシナリオをカバーします。
    • TestErrIsNotExist 関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対して testErrNotExist を呼び出し、os.IsNotExist の堅牢性をテストしています。
    • checkErrorPredicate ヘルパー関数は、エラー述語(os.IsExistos.IsNotExist など)が期待通りに true を返すかを汎用的にチェックするために導入されました。
  • src/pkg/os/error_windows.go:

    • isNotExist 関数内で、Windows固有のエラーコード syscall.ERROR_PATH_NOT_FOUNDos.IsNotExist の判定基準に追加されました。これにより、Windows環境でパスが存在しない場合に os.IsNotExist が正しく true を返すようになります。
    • isExist 関数から syscall.EEXIST が削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。
    • isPermission 関数から syscall.EACCESsyscall.EPERM が削除されました。これらもPOSIXシステムのエラーコードであり、Windowsでは ErrPermission がより抽象的な権限エラーを表すため、整理されました。

これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。

関連リンク

参考にした情報源リンク