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.
|
---|
4 |
|
---|
5 | package xerrors
|
---|
6 |
|
---|
7 | import (
|
---|
8 | "fmt"
|
---|
9 | "strings"
|
---|
10 | "unicode"
|
---|
11 | "unicode/utf8"
|
---|
12 |
|
---|
13 | "golang.org/x/xerrors/internal"
|
---|
14 | )
|
---|
15 |
|
---|
16 | const percentBangString = "%!"
|
---|
17 |
|
---|
18 | // Errorf formats according to a format specifier and returns the string as a
|
---|
19 | // value that satisfies error.
|
---|
20 | //
|
---|
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.
|
---|
27 | //
|
---|
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.
|
---|
32 | //
|
---|
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
|
---|
35 | // a synonym for %v.
|
---|
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)
|
---|
44 | if err == nil {
|
---|
45 | return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
|
---|
46 | }
|
---|
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]...)
|
---|
53 | frame := Frame{}
|
---|
54 | if internal.EnableTrace {
|
---|
55 | frame = Caller(1)
|
---|
56 | }
|
---|
57 | if wrap {
|
---|
58 | return &wrapError{msg, err, frame}
|
---|
59 | }
|
---|
60 | return &noWrapError{msg, err, frame}
|
---|
61 | }
|
---|
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...)
|
---|
65 | if idx < 0 {
|
---|
66 | return &noWrapError{msg, nil, Caller(1)}
|
---|
67 | }
|
---|
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)}
|
---|
73 | }
|
---|
74 | frame := Frame{}
|
---|
75 | if internal.EnableTrace {
|
---|
76 | frame = Caller(1)
|
---|
77 | }
|
---|
78 | return &wrapError{msg, err, frame}
|
---|
79 | }
|
---|
80 |
|
---|
81 | func errorAt(args []interface{}, i int) error {
|
---|
82 | if i < 0 || i >= len(args) {
|
---|
83 | return nil
|
---|
84 | }
|
---|
85 | err, ok := args[i].(error)
|
---|
86 | if !ok {
|
---|
87 | return nil
|
---|
88 | }
|
---|
89 | return err
|
---|
90 | }
|
---|
91 |
|
---|
92 | // formatPlusW is used to avoid the vet check that will barf at %w.
|
---|
93 | func formatPlusW(s string) string {
|
---|
94 | return s
|
---|
95 | }
|
---|
96 |
|
---|
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.
|
---|
103 | idx = -1
|
---|
104 | ok = true
|
---|
105 | n := 0
|
---|
106 | sz := 0
|
---|
107 | var isW bool
|
---|
108 | for i := 0; i < len(format); i += sz {
|
---|
109 | if format[i] != '%' {
|
---|
110 | sz = 1
|
---|
111 | continue
|
---|
112 | }
|
---|
113 | // "%%" is not a format directive.
|
---|
114 | if i+1 < len(format) && format[i+1] == '%' {
|
---|
115 | sz = 2
|
---|
116 | continue
|
---|
117 | }
|
---|
118 | sz, isW = parsePrintfVerb(format[i:])
|
---|
119 | if isW {
|
---|
120 | if idx >= 0 {
|
---|
121 | ok = false
|
---|
122 | } else {
|
---|
123 | idx = n
|
---|
124 | }
|
---|
125 | // "Replace" the last character, the 'w', with a 'v'.
|
---|
126 | p := i + sz - 1
|
---|
127 | format = format[:p] + "v" + format[p+1:]
|
---|
128 | }
|
---|
129 | n++
|
---|
130 | }
|
---|
131 | return idx, format, ok
|
---|
132 | }
|
---|
133 |
|
---|
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.
|
---|
138 | sz := 0
|
---|
139 | var r rune
|
---|
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'
|
---|
144 | }
|
---|
145 | }
|
---|
146 | return len(s), false
|
---|
147 | }
|
---|
148 |
|
---|
149 | type noWrapError struct {
|
---|
150 | msg string
|
---|
151 | err error
|
---|
152 | frame Frame
|
---|
153 | }
|
---|
154 |
|
---|
155 | func (e *noWrapError) Error() string {
|
---|
156 | return fmt.Sprint(e)
|
---|
157 | }
|
---|
158 |
|
---|
159 | func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
---|
160 |
|
---|
161 | func (e *noWrapError) FormatError(p Printer) (next error) {
|
---|
162 | p.Print(e.msg)
|
---|
163 | e.frame.Format(p)
|
---|
164 | return e.err
|
---|
165 | }
|
---|
166 |
|
---|
167 | type wrapError struct {
|
---|
168 | msg string
|
---|
169 | err error
|
---|
170 | frame Frame
|
---|
171 | }
|
---|
172 |
|
---|
173 | func (e *wrapError) Error() string {
|
---|
174 | return fmt.Sprint(e)
|
---|
175 | }
|
---|
176 |
|
---|
177 | func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
|
---|
178 |
|
---|
179 | func (e *wrapError) FormatError(p Printer) (next error) {
|
---|
180 | p.Print(e.msg)
|
---|
181 | e.frame.Format(p)
|
---|
182 | return e.err
|
---|
183 | }
|
---|
184 |
|
---|
185 | func (e *wrapError) Unwrap() error {
|
---|
186 | return e.err
|
---|
187 | }
|
---|