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 5package xerrors 6 7import ( 8 "fmt" 9 "strings" 10 "unicode" 11 "unicode/utf8" 12 13 "golang.org/x/xerrors/internal" 14) 15 16const 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. 36func 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 81func 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. 93func 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". 101func 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'. 136func 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 149type noWrapError struct { 150 msg string 151 err error 152 frame Frame 153} 154 155func (e *noWrapError) Error() string { 156 return fmt.Sprint(e) 157} 158 159func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } 160 161func (e *noWrapError) FormatError(p Printer) (next error) { 162 p.Print(e.msg) 163 e.frame.Format(p) 164 return e.err 165} 166 167type wrapError struct { 168 msg string 169 err error 170 frame Frame 171} 172 173func (e *wrapError) Error() string { 174 return fmt.Sprint(e) 175} 176 177func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } 178 179func (e *wrapError) FormatError(p Printer) (next error) { 180 p.Print(e.msg) 181 e.frame.Format(p) 182 return e.err 183} 184 185func (e *wrapError) Unwrap() error { 186 return e.err 187} 188