| // 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 fmt | |
| import ( | |
| "bytes" | |
| "strings" | |
| "golang.org/x/exp/errors" | |
| "golang.org/x/exp/errors/internal" | |
| ) | |
| // Errorf formats according to a format specifier and returns the string as a | |
| // value that satisfies error. | |
| // | |
| // The returned error includes the file and line number of the caller when | |
| // formatted with additional detail enabled. If the last argument is an error | |
| // the returned error's Format method will return it if the format string ends | |
| // with ": %s", ": %v", or ": %w". If the last argument is an error and the | |
| // format string ends with ": %w", the returned error implements errors.Wrapper | |
| // with an Unwrap method returning it. | |
| func Errorf(format string, a ...interface{}) error { | |
| err, wrap := lastError(format, a) | |
| if err == nil { | |
| return &noWrapError{Sprintf(format, a...), nil, errors.Caller(1)} | |
| } | |
| // TODO: this is not entirely correct. The error value could be | |
| // printed elsewhere in format if it mixes numbered with unnumbered | |
| // 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]...) | |
| frame := errors.Frame{} | |
| if internal.EnableTrace { | |
| frame = errors.Caller(1) | |
| } | |
| if wrap { | |
| return &wrapError{msg, err, frame} | |
| } | |
| return &noWrapError{msg, err, frame} | |
| } | |
| func lastError(format string, a []interface{}) (err error, wrap bool) { | |
| wrap = strings.HasSuffix(format, ": %w") | |
| if !wrap && | |
| !strings.HasSuffix(format, ": %s") && | |
| !strings.HasSuffix(format, ": %v") { | |
| return nil, false | |
| } | |
| if len(a) == 0 { | |
| return nil, false | |
| } | |
| err, ok := a[len(a)-1].(error) | |
| if !ok { | |
| return nil, false | |
| } | |
| return err, wrap | |
| } | |
| type noWrapError struct { | |
| msg string | |
| err error | |
| frame errors.Frame | |
| } | |
| func (e *noWrapError) Error() string { | |
| return Sprint(e) | |
| } | |
| func (e *noWrapError) FormatError(p errors.Printer) (next error) { | |
| p.Print(e.msg) | |
| e.frame.Format(p) | |
| return e.err | |
| } | |
| type wrapError struct { | |
| msg string | |
| err error | |
| frame errors.Frame | |
| } | |
| func (e *wrapError) Error() string { | |
| return Sprint(e) | |
| } | |
| func (e *wrapError) FormatError(p errors.Printer) (next error) { | |
| p.Print(e.msg) | |
| e.frame.Format(p) | |
| return e.err | |
| } | |
| func (e *wrapError) Unwrap() error { | |
| return e.err | |
| } | |
| func fmtError(p *pp, verb rune, err error) (handled bool) { | |
| var ( | |
| sep = " " // separator before next error | |
| w = p // print buffer where error text is written | |
| ) | |
| switch { | |
| // 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 | |
| } | |
| 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: | |
| // 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() | |
| default: | |
| w.badVerb(verb) | |
| return true | |
| } | |
| } | |
| loop: | |
| for { | |
| w.fmt.inDetail = false | |
| switch v := err.(type) { | |
| case errors.Formatter: | |
| err = v.FormatError((*errPP)(w)) | |
| case Formatter: | |
| if w.fmt.plusV { | |
| v.Format((*errPPState)(w), 'v') // indent new lines | |
| } else { | |
| v.Format(w, 'v') // do not indent new lines | |
| } | |
| break loop | |
| default: | |
| w.fmtString(v.Error(), 's') | |
| break loop | |
| } | |
| if err == nil { | |
| break | |
| } | |
| if !w.fmt.inDetail || !p.fmt.plusV { | |
| w.buf.WriteByte(':') | |
| } | |
| // Strip last newline of detail. | |
| if bytes.HasSuffix([]byte(w.buf), detailSep) { | |
| w.buf = w.buf[:len(w.buf)-len(detailSep)] | |
| } | |
| w.buf.WriteString(sep) | |
| w.fmt.inDetail = false | |
| } | |
| if w != p { | |
| p.fmtString(string(w.buf), verb) | |
| } | |
| 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 | |
| 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) } | |
| func (p *errPPState) Write(b []byte) (n int, err error) { | |
| if !p.fmt.inDetail || p.fmt.plusV { | |
| 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 b[0] == '\n' { | |
| b = b[1:] | |
| } | |
| } | |
| k := 0 | |
| for i, c := range b { | |
| if c == '\n' { | |
| p.buf.Write(b[k:i]) | |
| p.buf.Write(detailSep) | |
| k = i + 1 | |
| } | |
| } | |
| p.buf.Write(b[k:]) | |
| p.fmt.needNewline = !p.fmt.inDetail | |
| } | |
| return len(b), nil | |
| } | |
| // errPP wraps a pp to implement an errors.Printer. | |
| type errPP pp | |
| 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 (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 (p *errPP) Detail() bool { | |
| p.fmt.inDetail = true | |
| return p.fmt.plusV | |
| } |