// 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 xerrors import ( "fmt" "strings" "golang.org/x/xerrors/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 Wrapper // 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{fmt.Sprintf(format, a...), nil, 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 := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) frame := Frame{} if internal.EnableTrace { frame = Caller(1) } if wrap { return &wrapError{msg, err, frame} } 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 && !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 Frame } func (e *noWrapError) Error() string { return fmt.Sprint(e) } func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } func (e *noWrapError) FormatError(p Printer) (next error) { p.Print(e.msg) e.frame.Format(p) return e.err } type wrapError struct { msg string err error frame Frame } func (e *wrapError) Error() string { return fmt.Sprint(e) } func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } func (e *wrapError) FormatError(p Printer) (next error) { p.Print(e.msg) e.frame.Format(p) return e.err } func (e *wrapError) Unwrap() error { return e.err }