1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
13 "golang.org/x/xerrors/internal"
16 const percentBangString
= "%!"
18 // Errorf formats according to a format specifier and returns the string as a
19 // value that satisfies error.
21 // The returned error includes the file and line number of the caller when
22 // formatted with additional detail enabled. If the last argument is an error
23 // the returned error's Format method will return it if the format string ends
24 // with ": %s", ": %v", or ": %w". If the last argument is an error and the
25 // format string ends with ": %w", the returned error implements an Unwrap
26 // method returning it.
28 // If the format specifier includes a %w verb with an error operand in a
29 // position other than at the end, the returned error will still implement an
30 // Unwrap method returning the operand, but the error's Format method will not
31 // return the wrapped error.
33 // It is invalid to include more than one %w verb or to supply it with an
34 // operand that does not implement the error interface. The %w verb is otherwise
36 func Errorf(format
string, a
...interface{}) error
{
37 format
= formatPlusW(format
)
38 // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
39 wrap
:= strings
.HasSuffix(format
, ": %w")
40 idx
, format2
, ok
:= parsePercentW(format
)
41 percentWElsewhere
:= !wrap
&& idx
>= 0
42 if !percentWElsewhere
&& (wrap || strings
.HasSuffix(format
, ": %s") || strings
.HasSuffix(format
, ": %v")) {
43 err
:= errorAt(a
, len(a
)-1)
45 return &noWrapError
{fmt
.Sprintf(format
, a
...), nil, Caller(1)}
47 // TODO: this is not entirely correct. The error value could be
48 // printed elsewhere in format if it mixes numbered with unnumbered
49 // substitutions. With relatively small changes to doPrintf we can
50 // have it optionally ignore extra arguments and pass the argument
51 // list in its entirety.
52 msg
:= fmt
.Sprintf(format
[:len(format
)-len(": %s")], a
[:len(a
)-1]...)
54 if internal
.EnableTrace
{
58 return &wrapError
{msg
, err
, frame
}
60 return &noWrapError
{msg
, err
, frame
}
62 // Support %w anywhere.
63 // TODO: don't repeat the wrapped error's message when %w occurs in the middle.
64 msg
:= fmt
.Sprintf(format2
, a
...)
66 return &noWrapError
{msg
, nil, Caller(1)}
68 err
:= errorAt(a
, idx
)
69 if !ok || err
== nil {
70 // Too many %ws or argument of %w is not an error. Approximate the Go
71 // 1.13 fmt.Errorf message.
72 return &noWrapError
{fmt
.Sprintf("%sw(%s)", percentBangString
, msg
), nil, Caller(1)}
75 if internal
.EnableTrace
{
78 return &wrapError
{msg
, err
, frame
}
81 func errorAt(args
[]interface{}, i
int) error
{
82 if i
< 0 || i
>= len(args
) {
85 err
, ok
:= args
[i
].(error
)
92 // formatPlusW is used to avoid the vet check that will barf at %w.
93 func formatPlusW(s
string) string {
97 // Return the index of the only %w in format, or -1 if none.
98 // Also return a rewritten format string with %w replaced by %v, and
99 // false if there is more than one %w.
100 // TODO: handle "%[N]w".
101 func parsePercentW(format
string) (idx
int, newFormat
string, ok
bool) {
102 // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
108 for i
:= 0; i
< len(format
); i
+= sz
{
109 if format
[i
] != '%' {
113 // "%%" is not a format directive.
114 if i
+1 < len(format
) && format
[i
+1] == '%' {
118 sz
, isW
= parsePrintfVerb(format
[i
:])
125 // "Replace" the last character, the 'w', with a 'v'.
127 format
= format
[:p
] + "v" + format
[p
+1:]
131 return idx
, format
, ok
134 // Parse the printf verb starting with a % at s[0].
135 // Return how many bytes it occupies and whether the verb is 'w'.
136 func parsePrintfVerb(s
string) (int, bool) {
137 // Assume only that the directive is a sequence of non-letters followed by a single letter.
140 for i
:= 1; i
< len(s
); i
+= sz
{
141 r
, sz
= utf8
.DecodeRuneInString(s
[i
:])
142 if unicode
.IsLetter(r
) {
143 return i
+ sz
, r
== 'w'
149 type noWrapError
struct {
155 func (e
*noWrapError
) Error() string {
159 func (e
*noWrapError
) Format(s fmt
.State
, v rune
) { FormatError(e
, s
, v
) }
161 func (e
*noWrapError
) FormatError(p Printer
) (next error
) {
167 type wrapError
struct {
173 func (e
*wrapError
) Error() string {
177 func (e
*wrapError
) Format(s fmt
.State
, v rune
) { FormatError(e
, s
, v
) }
179 func (e
*wrapError
) FormatError(p Printer
) (next error
) {
185 func (e
*wrapError
) Unwrap() error
{