Dave Cheney %!s(int64=10) %!d(string=hai) anos
pai
achega
c978824ef0
Modificáronse 2 ficheiros con 121 adicións e 13 borrados
  1. 76 4
      errors.go
  2. 45 9
      errors_test.go

+ 76 - 4
errors.go

@@ -1,16 +1,33 @@
 // Package errors implements functions to manipulate errors.
 package errors
 
-import "fmt"
+import (
+	"fmt"
+	"runtime"
+)
 
 // New returns an error that formats as the given text.
-func New(text string) error {
-	return Errorf(text)
+func New(message string) error {
+	pc, _, _, _ := runtime.Caller(1) // the caller of New
+	return struct {
+		error
+		pc uintptr
+	}{
+		fmt.Errorf(message),
+		pc,
+	}
 }
 
 // Errorf returns a formatted error.
 func Errorf(format string, args ...interface{}) error {
-	return fmt.Errorf(format, args...)
+	pc, _, _, _ := runtime.Caller(1) // the caller of Errorf
+	return struct {
+		error
+		pc uintptr
+	}{
+		fmt.Errorf(format, args...),
+		pc,
+	}
 }
 
 // Cause returns the underlying cause of the error, if possible.
@@ -35,3 +52,58 @@ func Cause(err error) error {
 	}
 	return err
 }
+
+func underlying(err error) (error, bool) {
+	if err == nil {
+		return nil, false
+	}
+	type underlying interface {
+		underlying() error
+	}
+	if err, ok := err.(underlying); ok {
+		return err.underlying(), true
+	}
+	return nil, false
+}
+
+type traced struct {
+	error // underlying error
+	pc    uintptr
+}
+
+func (t *traced) underlying() error { return t.error }
+
+// Trace adds caller information to the error.
+// If error is nil, nil will be returned.
+func Trace(err error) error {
+	if err == nil {
+		return nil
+	}
+	pc, _, _, _ := runtime.Caller(1) // the caller of Trace
+	return traced{
+		error: err,
+		pc:    pc,
+	}
+}
+
+type annotated struct {
+	error // underlying error
+	pc uintptr
+}
+
+func (a *annotated) Cause() error { return a.error }
+
+// Annotate returns a new error annotating the error provided
+// with the message, and the location of the caller of Annotate.
+// The underlying error can be recovered by calling Cause.
+// If err is nil, nil will be returned.
+func Annotate(err error, message string) error {
+	if err == nil {
+		return nil
+	}
+	pc, _, _, _ := runtime.Caller(1) // the caller of Annotate
+	return annotated{
+		error: err,
+		pc: pc,
+	}
+}

+ 45 - 9
errors_test.go

@@ -7,15 +7,6 @@ import (
 	"testing"
 )
 
-func TestNew(t *testing.T) {
-	got := New("test error")
-	want := fmt.Errorf("test error")
-
-	if !reflect.DeepEqual(got, want) {
-		t.Errorf("New: got %#v, want %#v", got, want)
-	}
-}
-
 func TestNewError(t *testing.T) {
 	tests := []struct {
 		err  string
@@ -34,6 +25,26 @@ func TestNewError(t *testing.T) {
 	}
 }
 
+func TestNewEqualNew(t *testing.T) {
+	// test that two calls to New return the same error when called from the same location
+	var errs []error
+	for i := 0; i < 2; i++ {
+		errs = append(errs, New("error"))
+	}
+	a, b := errs[0], errs[1]
+	if !reflect.DeepEqual(a, b) {
+		t.Errorf("Expected two calls to New from the same location to give the same error: %#v, %#v", a, b)
+	}
+}
+
+func TestNewNotEqualNew(t *testing.T) {
+	// test that two calls to New return different errors when called from different locations
+	a, b := New("error"), New("error")
+	if reflect.DeepEqual(a, b) {
+		t.Errorf("Expected two calls to New from the different locations give the same error: %#v, %#v", a, b)
+	}
+}
+
 type nilError struct{}
 
 func (nilError) Error() string { return "nil error" }
@@ -46,6 +57,7 @@ func (e *causeError) Error() string { return "cause error" }
 func (e *causeError) Cause() error  { return e.cause }
 
 func TestCause(t *testing.T) {
+	x := New("error")
 	tests := []struct {
 		err  error
 		want error
@@ -69,6 +81,9 @@ func TestCause(t *testing.T) {
 		// caused error returns cause
 		err:  &causeError{cause: io.EOF},
 		want: io.EOF,
+	}, {
+		err:  x, // return from errors.New
+		want: x,
 	}}
 
 	for i, tt := range tests {
@@ -78,3 +93,24 @@ func TestCause(t *testing.T) {
 		}
 	}
 }
+
+func TestTraceNotEqual(t *testing.T) {
+	// test that two calls to trace do not return identical errors
+	err := New("error")
+	a := err
+	var errs []error
+	for i := 0; i < 2; i++ {
+		err = Trace(err)
+		errs = append(errs, err)
+	}
+	b, c := errs[0], errs[1]
+	if reflect.DeepEqual(a, b) {
+		t.Errorf("a and b equal: %#v, %#v", a, b)
+	}
+	if reflect.DeepEqual(b, c) {
+		t.Errorf("b and c equal: %#v, %#v", b, c)
+	}
+	if reflect.DeepEqual(a, c) {
+		t.Errorf("a and c equal: %#v, %#v", a, c)
+	}
+}