aboutsummaryrefslogtreecommitdiff
path: root/src/errors
diff options
context:
space:
mode:
authorMarcel van Lohuizen <mpvl@golang.org>2019-02-22 23:41:38 +0100
committerMarcel van Lohuizen <mpvl@golang.org>2019-02-27 19:07:22 +0000
commit37f84817247d3b8e687a701ccb0d6bc7ffe3cb78 (patch)
tree2d7434960e6f7b76acec199a8e7dfc032a5c2688 /src/errors
parent1d992f2e369e7e518ff57cd7508a15442d5df186 (diff)
downloadgo-37f84817247d3b8e687a701ccb0d6bc7ffe3cb78.tar.gz
go-37f84817247d3b8e687a701ccb0d6bc7ffe3cb78.zip
errors: add Frame and Formatter/Printer interfaces
errors.New now implements Formatter and includes Frame information that is reported when detail is requested. Partly implements proposal Issue #29934. Change-Id: Id76888d246d7d862595b5e92d517b9c03f23a7a6 Reviewed-on: https://go-review.googlesource.com/c/163557 Run-TryBot: Marcel van Lohuizen <mpvl@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Damien Neil <dneil@google.com>
Diffstat (limited to 'src/errors')
-rw-r--r--src/errors/errors.go14
-rw-r--r--src/errors/format.go34
-rw-r--r--src/errors/frame.go56
-rw-r--r--src/errors/frame_test.go43
4 files changed, 145 insertions, 2 deletions
diff --git a/src/errors/errors.go b/src/errors/errors.go
index b8a46921be..f23a96c43e 100644
--- a/src/errors/errors.go
+++ b/src/errors/errors.go
@@ -6,15 +6,25 @@
package errors
// New returns an error that formats as the given text.
+//
+// The returned error contains a Frame set to the caller's location and
+// implements Formatter to show this information when printed with details.
func New(text string) error {
- return &errorString{text}
+ return &errorString{text, Caller(1)}
}
// errorString is a trivial implementation of error.
type errorString struct {
- s string
+ s string
+ frame Frame
}
func (e *errorString) Error() string {
return e.s
}
+
+func (e *errorString) FormatError(p Printer) (next error) {
+ p.Print(e.s)
+ e.frame.Format(p)
+ return nil
+}
diff --git a/src/errors/format.go b/src/errors/format.go
new file mode 100644
index 0000000000..12deed3cf7
--- /dev/null
+++ b/src/errors/format.go
@@ -0,0 +1,34 @@
+// Copyright 2018 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 errors
+
+// A Formatter formats error messages.
+type Formatter interface {
+ error
+
+ // FormatError prints the receiver's first error and returns the next error in
+ // the error chain, if any.
+ FormatError(p Printer) (next error)
+}
+
+// A Printer formats error messages.
+//
+// The most common implementation of Printer is the one provided by package fmt
+// during Printf. Localization packages such as golang.org/x/text/message
+// typically provide their own implementations.
+type Printer interface {
+ // Print appends args to the message output.
+ Print(args ...interface{})
+
+ // Printf writes a formatted string.
+ Printf(format string, args ...interface{})
+
+ // Detail reports whether error detail is requested.
+ // After the first call to Detail, all text written to the Printer
+ // is formatted as additional detail, or ignored when
+ // detail has not been requested.
+ // If Detail returns false, the caller can avoid printing the detail at all.
+ Detail() bool
+}
diff --git a/src/errors/frame.go b/src/errors/frame.go
new file mode 100644
index 0000000000..a5369e5c36
--- /dev/null
+++ b/src/errors/frame.go
@@ -0,0 +1,56 @@
+// Copyright 2018 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 errors
+
+import (
+ "runtime"
+)
+
+// A Frame contains part of a call stack.
+type Frame struct {
+ // Make room for three PCs: the one we were asked for, what it called,
+ // and possibly a PC for skipPleaseUseCallersFrames. See:
+ // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
+ frames [3]uintptr
+}
+
+// Caller returns a Frame that describes a frame on the caller's stack.
+// The argument skip is the number of frames to skip over.
+// Caller(0) returns the frame for the caller of Caller.
+func Caller(skip int) Frame {
+ var s Frame
+ runtime.Callers(skip+1, s.frames[:])
+ return s
+}
+
+// location reports the file, line, and function of a frame.
+//
+// The returned function may be "" even if file and line are not.
+func (f Frame) location() (function, file string, line int) {
+ frames := runtime.CallersFrames(f.frames[:])
+ if _, ok := frames.Next(); !ok {
+ return "", "", 0
+ }
+ fr, ok := frames.Next()
+ if !ok {
+ return "", "", 0
+ }
+ return fr.Function, fr.File, fr.Line
+}
+
+// Format prints the stack as error detail.
+// It should be called from an error's Format implementation,
+// before printing any other error detail.
+func (f Frame) Format(p Printer) {
+ if p.Detail() {
+ function, file, line := f.location()
+ if function != "" {
+ p.Printf("%s\n ", function)
+ }
+ if file != "" {
+ p.Printf("%s:%d\n", file, line)
+ }
+ }
+}
diff --git a/src/errors/frame_test.go b/src/errors/frame_test.go
new file mode 100644
index 0000000000..864a6934d1
--- /dev/null
+++ b/src/errors/frame_test.go
@@ -0,0 +1,43 @@
+// Copyright 2018 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 errors_test
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "math/big"
+ "testing"
+)
+
+type myType struct{}
+
+func (myType) Format(s fmt.State, v rune) {
+ s.Write(bytes.Repeat([]byte("Hi! "), 10))
+}
+
+func BenchmarkErrorf(b *testing.B) {
+ err := errors.New("foo")
+ // pi := big.NewFloat(3.14) // Something expensive.
+ num := big.NewInt(5)
+ args := func(a ...interface{}) []interface{} { return a }
+ benchCases := []struct {
+ name string
+ format string
+ args []interface{}
+ }{
+ {"no_format", "msg: %v", args(err)},
+ {"with_format", "failed %d times: %v", args(5, err)},
+ {"method: mytype", "pi: %v", args("myfile.go", myType{}, err)},
+ {"method: number", "pi: %v", args("myfile.go", num, err)},
+ }
+ for _, bc := range benchCases {
+ b.Run(bc.name, func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ _ = fmt.Errorf(bc.format, bc.args...)
+ }
+ })
+ }
+}