aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2019-08-02 11:18:56 -0700
committerDamien Neil <dneil@google.com>2019-08-02 21:09:50 +0000
commitd178c5888f06918e8dbd221f26c707e501a9fa98 (patch)
treefaca9c7b6f04f490a882f0027b3958cc83a5bc78
parent55e23cb1fe18c6784b573a44bce4c798a1983c2f (diff)
downloadgo-d178c5888f06918e8dbd221f26c707e501a9fa98.tar.gz
go-d178c5888f06918e8dbd221f26c707e501a9fa98.zip
os: don't consult Is methods on non-syscall error types
CL #163058 moves interpretation of platform-specific errors to the syscall package. Package syscall errors implement an Is method which os.IsPermission etc. consult. This results in an unintended semantic change to the os package predicate functions: The following program now prints 'true' where it used to print 'false': package main import "os" type myError struct{ error } func (e myError) Is(target error) bool { return target == os.ErrPermission } func main() { println(os.IsPermission(myError{})) } Change the os package error predicate functions to only examine syscall errors, avoiding this semantic change. This CL does retain one minor semantic change: On Plan9, os.IsPermission used to return true for any error with text containing the string "permission denied". It now only returns true for a syscall.ErrorString containing that text. Change-Id: I6b512b1de6ced46c2f1cc8d264fa2495ae7bf9f5 Reviewed-on: https://go-review.googlesource.com/c/go/+/188817 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
-rw-r--r--src/os/error.go3
-rw-r--r--src/os/error_errno.go11
-rw-r--r--src/os/error_plan9.go9
-rw-r--r--src/os/error_test.go10
4 files changed, 32 insertions, 1 deletions
diff --git a/src/os/error.go b/src/os/error.go
index 09ba158677..0e8e2d47f8 100644
--- a/src/os/error.go
+++ b/src/os/error.go
@@ -115,7 +115,8 @@ func underlyingErrorIs(err, target error) bool {
if err == target {
return true
}
- e, ok := err.(interface{ Is(error) bool })
+ // To preserve prior behavior, only examine syscall errors.
+ e, ok := err.(syscallErrorType)
return ok && e.Is(target)
}
diff --git a/src/os/error_errno.go b/src/os/error_errno.go
new file mode 100644
index 0000000000..31ae05a21e
--- /dev/null
+++ b/src/os/error_errno.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !plan9
+
+package os
+
+import "syscall"
+
+type syscallErrorType = syscall.Errno
diff --git a/src/os/error_plan9.go b/src/os/error_plan9.go
new file mode 100644
index 0000000000..af6065db56
--- /dev/null
+++ b/src/os/error_plan9.go
@@ -0,0 +1,9 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+type syscallErrorType = syscall.ErrorString
diff --git a/src/os/error_test.go b/src/os/error_test.go
index a03bd28b9a..3d921578fd 100644
--- a/src/os/error_test.go
+++ b/src/os/error_test.go
@@ -175,3 +175,13 @@ func TestPathErrorUnwrap(t *testing.T) {
t.Error("errors.Is failed, wanted success")
}
}
+
+type myErrorIs struct{ error }
+
+func (e myErrorIs) Is(target error) bool { return target == e.error }
+
+func TestErrorIsMethods(t *testing.T) {
+ if os.IsPermission(myErrorIs{os.ErrPermission}) {
+ t.Error("os.IsPermission(err) = true when err.Is(os.ErrPermission), wanted false")
+ }
+}