xerrors: first working implementation

Change-Id: Iefd80aea06b50a7cb9a171b1a864ac86659c0455
Reviewed-on: https://go-review.googlesource.com/c/158760
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
diff --git a/xerrors/adaptor.go b/xerrors/adaptor.go
index e5db137..5f0df17 100644
--- a/xerrors/adaptor.go
+++ b/xerrors/adaptor.go
@@ -7,107 +7,146 @@
 import (
 	"bytes"
 	"fmt"
+	"io"
+	"reflect"
+	"strconv"
 )
 
-func fmtError(p *pp, verb rune, err error) (handled bool) {
+// FormatError calls the FormatError method of err with a errors.Printer
+// configured according to s and verb and writes the result to s.
+func FormatError(s fmt.State, verb rune, f Formatter) {
+	// Assuming this function is only called from the Format method, and given
+	// that FormatError takes precedence over Format, it cannot be called from
+	// any package that supports errors.Formatter. It is therefore safe to
+	// disregard that State may be a specific printer implementation and use one
+	// of our choice instead.
+
+	// limitations: does not support printing error as Go struct.
+
 	var (
-		sep = " " // separator before next error
-		w   = p   // print buffer where error text is written
+		sep    = " " // separator before next error
+		p      = &state{State: s}
+		direct = true
 	)
-	switch {
+
+	var err error = f
+
+	switch verb {
 	// Note that this switch must match the preference order
 	// for ordinary string printing (%#v before %+v, and so on).
 
-	case p.fmt.sharpV:
-		if stringer, ok := p.arg.(GoStringer); ok {
-			// Print the result of GoString unadorned.
-			p.fmt.fmtS(stringer.GoString())
-			return true
+	case 'v':
+		if s.Flag('#') {
+			if stringer, ok := err.(fmt.GoStringer); ok {
+				io.WriteString(&p.buf, stringer.GoString())
+				goto exit
+			}
+			// proceed as if it were %v
+		} else if s.Flag('+') {
+			p.printDetail = true
+			sep = "\n  - "
 		}
-		return false
-
-	case p.fmt.plusV:
-		sep = "\n  - "
-		w.fmt.fmtFlags = fmtFlags{plusV: p.fmt.plusV} // only keep detail flag
-
-		// The width or precision of a detailed view could be the number of
-		// errors to print from a list.
-
-	default:
+	case 's':
+	case 'q', 'x', 'X':
 		// Use an intermediate buffer in the rare cases that precision,
 		// truncation, or one of the alternative verbs (q, x, and X) are
 		// specified.
-		switch verb {
-		case 's', 'v':
-			if (!w.fmt.widPresent || w.fmt.wid == 0) && !w.fmt.precPresent {
-				break
-			}
-			fallthrough
-		case 'q', 'x', 'X':
-			w = newPrinter()
-			defer w.free()
+		direct = false
+
+	default:
+		p.buf.WriteString("%!")
+		p.buf.WriteRune(verb)
+		p.buf.WriteByte('(')
+		switch {
+		case err != nil:
+			p.buf.WriteString(reflect.TypeOf(f).String())
 		default:
-			w.badVerb(verb)
-			return true
+			p.buf.WriteString("<nil>")
 		}
+		p.buf.WriteByte(')')
+		io.Copy(s, &p.buf)
+		return
 	}
 
 loop:
 	for {
-		w.fmt.inDetail = false
+		p.inDetail = false
+
 		switch v := err.(type) {
 		case Formatter:
-			err = v.FormatError((*errPP)(w))
+			err = v.FormatError((*printer)(p))
 		case fmt.Formatter:
-			if w.fmt.plusV {
-				v.Format((*errPPState)(w), 'v') // indent new lines
-			} else {
-				v.Format(w, 'v') // do not indent new lines
-			}
+			v.Format(p, 'v')
 			break loop
 		default:
-			w.fmtString(v.Error(), 's')
+			io.WriteString(&p.buf, v.Error())
 			break loop
 		}
 		if err == nil {
 			break
 		}
-		if !w.fmt.inDetail || !p.fmt.plusV {
-			w.buf.WriteByte(':')
+		if !p.inDetail || !p.printDetail {
+			p.buf.WriteByte(':')
 		}
 		// Strip last newline of detail.
-		if bytes.HasSuffix([]byte(w.buf), detailSep) {
-			w.buf = w.buf[:len(w.buf)-len(detailSep)]
+		if bytes.HasSuffix(p.buf.Bytes(), detailSep) {
+			p.buf.Truncate(p.buf.Len() - len(detailSep))
 		}
-		w.buf.WriteString(sep)
-		w.fmt.inDetail = false
+		p.buf.WriteString(sep)
+		p.inDetail = false
 	}
 
-	if w != p {
-		p.fmtString(string(w.buf), verb)
+exit:
+	width, okW := s.Width()
+	prec, okP := s.Precision()
+
+	if !direct || (okW && width > 0) || okP {
+		// Construct format string from State s.
+		format := []byte{'%'}
+		if s.Flag('-') {
+			format = append(format, '-')
+		}
+		if s.Flag('+') {
+			format = append(format, '+')
+		}
+		if s.Flag(' ') {
+			format = append(format, ' ')
+		}
+		if okW {
+			format = strconv.AppendInt(format, int64(width), 10)
+		}
+		if okP {
+			format = append(format, '.')
+			format = strconv.AppendInt(format, int64(prec), 10)
+		}
+		format = append(format, string(verb)...)
+		fmt.Fprintf(s, string(format), p.buf.String())
+	} else {
+		io.Copy(s, &p.buf)
 	}
-	return true
 }
 
 var detailSep = []byte("\n    ")
 
-// errPPState wraps a pp to implement State with indentation. It is used
-// for errors implementing fmt.Formatter.
-type errPPState pp
+// state tracks error printing state. It implements fmt.State.
+type state struct {
+	fmt.State
+	buf bytes.Buffer
 
-func (p *errPPState) Width() (wid int, ok bool)      { return (*pp)(p).Width() }
-func (p *errPPState) Precision() (prec int, ok bool) { return (*pp)(p).Precision() }
-func (p *errPPState) Flag(c int) bool                { return (*pp)(p).Flag(c) }
+	printDetail bool
+	inDetail    bool
+	needNewline bool
+}
 
-func (p *errPPState) Write(b []byte) (n int, err error) {
-	if !p.fmt.inDetail || p.fmt.plusV {
+func (s *state) Write(b []byte) (n int, err error) {
+	if s.printDetail {
 		if len(b) == 0 {
 			return 0, nil
 		}
-		if p.fmt.inDetail && p.fmt.needNewline {
-			p.fmt.needNewline = false
-			p.buf.WriteByte(':')
-			p.buf.Write(detailSep)
+		if s.inDetail && s.needNewline {
+			s.needNewline = false
+			s.buf.WriteByte(':')
+			s.buf.Write(detailSep)
 			if b[0] == '\n' {
 				b = b[1:]
 			}
@@ -115,41 +154,35 @@
 		k := 0
 		for i, c := range b {
 			if c == '\n' {
-				p.buf.Write(b[k:i])
-				p.buf.Write(detailSep)
+				s.buf.Write(b[k:i])
+				s.buf.Write(detailSep)
 				k = i + 1
 			}
 		}
-		p.buf.Write(b[k:])
-		p.fmt.needNewline = !p.fmt.inDetail
+		s.buf.Write(b[k:])
+		s.needNewline = !s.inDetail
+	} else if !s.inDetail {
+		s.buf.Write(b)
 	}
 	return len(b), nil
 }
 
-// errPP wraps a pp to implement a Printer.
-type errPP pp
+// printer wraps a state to implement an xerrors.Printer.
+type printer state
 
-func (p *errPP) Print(args ...interface{}) {
-	if !p.fmt.inDetail || p.fmt.plusV {
-		if p.fmt.plusV {
-			Fprint((*errPPState)(p), args...)
-		} else {
-			(*pp)(p).doPrint(args)
-		}
+func (s *printer) Print(args ...interface{}) {
+	if !s.inDetail || s.printDetail {
+		fmt.Fprint((*state)(s), args...)
 	}
 }
 
-func (p *errPP) Printf(format string, args ...interface{}) {
-	if !p.fmt.inDetail || p.fmt.plusV {
-		if p.fmt.plusV {
-			Fprintf((*errPPState)(p), format, args...)
-		} else {
-			(*pp)(p).doPrintf(format, args)
-		}
+func (s *printer) Printf(format string, args ...interface{}) {
+	if !s.inDetail || s.printDetail {
+		fmt.Fprintf((*state)(s), format, args...)
 	}
 }
 
-func (p *errPP) Detail() bool {
-	p.fmt.inDetail = true
-	return p.fmt.plusV
+func (s *printer) Detail() bool {
+	s.inDetail = true
+	return s.printDetail
 }
diff --git a/xerrors/fmt.go b/xerrors/fmt.go
index 931c04d..58dae3f 100644
--- a/xerrors/fmt.go
+++ b/xerrors/fmt.go
@@ -5,6 +5,7 @@
 package xerrors
 
 import (
+	"fmt"
 	"strings"
 
 	"golang.org/x/exp/xerrors/internal"
@@ -21,8 +22,9 @@
 // with an Unwrap method returning it.
 func Errorf(format string, a ...interface{}) error {
 	err, wrap := lastError(format, a)
+	format = formatPlusW(format)
 	if err == nil {
-		return &noWrapError{Sprintf(format, a...), nil, Caller(1)}
+		return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
 	}
 
 	// TODO: this is not entirely correct. The error value could be
@@ -30,7 +32,7 @@
 	// substitutions. With relatively small changes to doPrintf we can
 	// have it optionally ignore extra arguments and pass the argument
 	// list in its entirety.
-	msg := Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
+	msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
 	frame := Frame{}
 	if internal.EnableTrace {
 		frame = Caller(1)
@@ -41,6 +43,11 @@
 	return &noWrapError{msg, err, frame}
 }
 
+// formatPlusW is used to avoid the vet check that will barf at %w.
+func formatPlusW(s string) string {
+	return s
+}
+
 func lastError(format string, a []interface{}) (err error, wrap bool) {
 	wrap = strings.HasSuffix(format, ": %w")
 	if !wrap &&
@@ -68,9 +75,11 @@
 }
 
 func (e *noWrapError) Error() string {
-	return Sprint(e)
+	return fmt.Sprint(e)
 }
 
+func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(s, v, e) }
+
 func (e *noWrapError) FormatError(p Printer) (next error) {
 	p.Print(e.msg)
 	e.frame.Format(p)
@@ -84,9 +93,11 @@
 }
 
 func (e *wrapError) Error() string {
-	return Sprint(e)
+	return fmt.Sprint(e)
 }
 
+func (e *wrapError) Format(s fmt.State, v rune) { FormatError(s, v, e) }
+
 func (e *wrapError) FormatError(p Printer) (next error) {
 	p.Print(e.msg)
 	e.frame.Format(p)
diff --git a/xerrors/fmt_test.go b/xerrors/fmt_test.go
index f874103..228f8e0 100644
--- a/xerrors/fmt_test.go
+++ b/xerrors/fmt_test.go
@@ -132,15 +132,11 @@
 			"\n    and the 12 monkeys" +
 			"\n    are laughing",
 	}, {
-		err:  simple,
-		fmt:  "%#v",
-		want: "&xerrors_test.wrapped{msg:\"simple\", err:error(nil)}",
-	}, {
 		err: framed,
 		fmt: "%+v",
 		want: "something:" +
 			"\n    golang.org/x/exp/xerrors_test.TestErrorFormatter" +
-			"\n        .+/golang.org/x/exp/xerrors/fmt_test.go:98" +
+			"\n        .+/golang.org/x/exp/xerrors/fmt_test.go:97" +
 			"\n    something more",
 		regexp: true,
 	}, {
@@ -231,7 +227,7 @@
 			"\n    one:" +
 			"\n    somefile.go:123",
 	}, {
-		err: wrapped{"", wrapped{"inner message", nil}},
+		err: &wrapped{"", &wrapped{"inner message", nil}},
 		fmt: "%+v",
 		want: "somefile.go:123" +
 			"\n  - inner message:" +
@@ -268,17 +264,14 @@
 	}, {
 		err:  simple,
 		fmt:  "%🤪",
-		want: "%!🤪(*xerrors_test.wrapped=&{simple <nil>})",
+		want: "%!🤪(*xerrors_test.wrapped)",
+		// For 1.13:
+		//  want: "%!🤪(*xerrors_test.wrapped=&{simple <nil>})",
 	}, {
 		err:  formatError("use fmt.Formatter"),
 		fmt:  "%#v",
 		want: "use fmt.Formatter",
 	}, {
-		err: wrapped{"using xerrors.Formatter",
-			formatError("use fmt.Formatter")},
-		fmt:  "%#v",
-		want: "xerrors_test.wrapped{msg:\"using xerrors.Formatter\", err:\"use fmt.Formatter\"}",
-	}, {
 		err:  fmtTwice("%s %s", "ok", panicValue{}),
 		fmt:  "%s",
 		want: "ok %!s(PANIC=panic)/ok %!s(PANIC=panic)",
@@ -361,6 +354,10 @@
 
 func (e wrapped) Error() string { return "should call Format" }
 
+func (e wrapped) Format(s fmt.State, verb rune) {
+	xerrors.FormatError(s, verb, &e)
+}
+
 func (e wrapped) FormatError(p xerrors.Printer) (next error) {
 	p.Print(e.msg)
 	p.Detail()
@@ -388,6 +385,10 @@
 
 func (e *withFrameAndMore) Error() string { return fmt.Sprint(e) }
 
+func (e *withFrameAndMore) Format(s fmt.State, v rune) {
+	xerrors.FormatError(s, v, e)
+}
+
 func (e *withFrameAndMore) FormatError(p xerrors.Printer) (next error) {
 	p.Print("something")
 	if p.Detail() {
@@ -401,8 +402,9 @@
 
 func (e spurious) Error() string { return fmt.Sprint(e) }
 
-func (e spurious) Format(fmt.State, rune) {
-	panic("should never be called by one of the tests")
+// move to 1_12 test file
+func (e spurious) Format(s fmt.State, verb rune) {
+	xerrors.FormatError(s, verb, e)
 }
 
 func (e spurious) FormatError(p xerrors.Printer) (next error) {
@@ -472,6 +474,10 @@
 
 func (e fmtTwiceErr) Error() string { return fmt.Sprint(e) }
 
+func (e fmtTwiceErr) Format(s fmt.State, verb rune) {
+	xerrors.FormatError(s, verb, e)
+}
+
 func (e fmtTwiceErr) FormatError(p xerrors.Printer) (next error) {
 	p.Printf(e.format, e.args...)
 	p.Print("/")
diff --git a/xerrors/wrap_test.go b/xerrors/wrap_test.go
index c132727..9c25aab 100644
--- a/xerrors/wrap_test.go
+++ b/xerrors/wrap_test.go
@@ -104,7 +104,7 @@
 		&errP,
 		false,
 	}, {
-		wrapped{nil},
+		errWrap{nil},
 		&errT,
 		false,
 	}, {
@@ -151,7 +151,7 @@
 		want error
 	}{
 		{nil, nil},
-		{wrapped{nil}, nil},
+		{errWrap{nil}, nil},
 		{err1, nil},
 		{erra, err1},
 		{xerrors.Errorf("wrap 3: %w", erra), erra},
@@ -167,15 +167,15 @@
 }
 
 func TestOpaque(t *testing.T) {
-	got := xerrors.Errorf("foo: %v", xerrors.Opaque(errorT{}))
+	got := fmt.Sprintf("%v", xerrors.Errorf("foo: %v", xerrors.Opaque(errorT{})))
 	want := "foo: errorT"
-	if got.Error() != want {
+	if got != want {
 		t.Errorf("error without Format: got %v; want %v", got, want)
 	}
 
-	got = xerrors.Errorf("foo: %v", xerrors.Opaque(errorD{}))
+	got = fmt.Sprintf("%v", xerrors.Errorf("foo: %v", xerrors.Opaque(errorD{})))
 	want = "foo: errorD"
-	if got.Error() != want {
+	if got != want {
 		t.Errorf("error with Format: got %v; want %v", got, want)
 	}
 }
@@ -195,8 +195,8 @@
 	return nil
 }
 
-type wrapped struct{ error }
+type errWrap struct{ error }
 
-func (wrapped) Error() string { return "wrapped" }
+func (errWrap) Error() string { return "wrapped" }
 
-func (wrapped) Unwrap() error { return nil }
+func (errWrap) Unwrap() error { return nil }