fmt.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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. package xerrors
  5. import (
  6. "fmt"
  7. "strings"
  8. "unicode"
  9. "unicode/utf8"
  10. "golang.org/x/xerrors/internal"
  11. )
  12. const percentBangString = "%!"
  13. // Errorf formats according to a format specifier and returns the string as a
  14. // value that satisfies error.
  15. //
  16. // The returned error includes the file and line number of the caller when
  17. // formatted with additional detail enabled. If the last argument is an error
  18. // the returned error's Format method will return it if the format string ends
  19. // with ": %s", ": %v", or ": %w". If the last argument is an error and the
  20. // format string ends with ": %w", the returned error implements an Unwrap
  21. // method returning it.
  22. //
  23. // If the format specifier includes a %w verb with an error operand in a
  24. // position other than at the end, the returned error will still implement an
  25. // Unwrap method returning the operand, but the error's Format method will not
  26. // return the wrapped error.
  27. //
  28. // It is invalid to include more than one %w verb or to supply it with an
  29. // operand that does not implement the error interface. The %w verb is otherwise
  30. // a synonym for %v.
  31. //
  32. // Note that as of Go 1.13, the fmt.Errorf function will do error formatting,
  33. // but it will not capture a stack backtrace.
  34. func Errorf(format string, a ...interface{}) error {
  35. format = formatPlusW(format)
  36. // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
  37. wrap := strings.HasSuffix(format, ": %w")
  38. idx, format2, ok := parsePercentW(format)
  39. percentWElsewhere := !wrap && idx >= 0
  40. if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) {
  41. err := errorAt(a, len(a)-1)
  42. if err == nil {
  43. return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
  44. }
  45. // TODO: this is not entirely correct. The error value could be
  46. // printed elsewhere in format if it mixes numbered with unnumbered
  47. // substitutions. With relatively small changes to doPrintf we can
  48. // have it optionally ignore extra arguments and pass the argument
  49. // list in its entirety.
  50. msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
  51. frame := Frame{}
  52. if internal.EnableTrace {
  53. frame = Caller(1)
  54. }
  55. if wrap {
  56. return &wrapError{msg, err, frame}
  57. }
  58. return &noWrapError{msg, err, frame}
  59. }
  60. // Support %w anywhere.
  61. // TODO: don't repeat the wrapped error's message when %w occurs in the middle.
  62. msg := fmt.Sprintf(format2, a...)
  63. if idx < 0 {
  64. return &noWrapError{msg, nil, Caller(1)}
  65. }
  66. err := errorAt(a, idx)
  67. if !ok || err == nil {
  68. // Too many %ws or argument of %w is not an error. Approximate the Go
  69. // 1.13 fmt.Errorf message.
  70. return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)}
  71. }
  72. frame := Frame{}
  73. if internal.EnableTrace {
  74. frame = Caller(1)
  75. }
  76. return &wrapError{msg, err, frame}
  77. }
  78. func errorAt(args []interface{}, i int) error {
  79. if i < 0 || i >= len(args) {
  80. return nil
  81. }
  82. err, ok := args[i].(error)
  83. if !ok {
  84. return nil
  85. }
  86. return err
  87. }
  88. // formatPlusW is used to avoid the vet check that will barf at %w.
  89. func formatPlusW(s string) string {
  90. return s
  91. }
  92. // Return the index of the only %w in format, or -1 if none.
  93. // Also return a rewritten format string with %w replaced by %v, and
  94. // false if there is more than one %w.
  95. // TODO: handle "%[N]w".
  96. func parsePercentW(format string) (idx int, newFormat string, ok bool) {
  97. // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
  98. idx = -1
  99. ok = true
  100. n := 0
  101. sz := 0
  102. var isW bool
  103. for i := 0; i < len(format); i += sz {
  104. if format[i] != '%' {
  105. sz = 1
  106. continue
  107. }
  108. // "%%" is not a format directive.
  109. if i+1 < len(format) && format[i+1] == '%' {
  110. sz = 2
  111. continue
  112. }
  113. sz, isW = parsePrintfVerb(format[i:])
  114. if isW {
  115. if idx >= 0 {
  116. ok = false
  117. } else {
  118. idx = n
  119. }
  120. // "Replace" the last character, the 'w', with a 'v'.
  121. p := i + sz - 1
  122. format = format[:p] + "v" + format[p+1:]
  123. }
  124. n++
  125. }
  126. return idx, format, ok
  127. }
  128. // Parse the printf verb starting with a % at s[0].
  129. // Return how many bytes it occupies and whether the verb is 'w'.
  130. func parsePrintfVerb(s string) (int, bool) {
  131. // Assume only that the directive is a sequence of non-letters followed by a single letter.
  132. sz := 0
  133. var r rune
  134. for i := 1; i < len(s); i += sz {
  135. r, sz = utf8.DecodeRuneInString(s[i:])
  136. if unicode.IsLetter(r) {
  137. return i + sz, r == 'w'
  138. }
  139. }
  140. return len(s), false
  141. }
  142. type noWrapError struct {
  143. msg string
  144. err error
  145. frame Frame
  146. }
  147. func (e *noWrapError) Error() string {
  148. return fmt.Sprint(e)
  149. }
  150. func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
  151. func (e *noWrapError) FormatError(p Printer) (next error) {
  152. p.Print(e.msg)
  153. e.frame.Format(p)
  154. return e.err
  155. }
  156. type wrapError struct {
  157. msg string
  158. err error
  159. frame Frame
  160. }
  161. func (e *wrapError) Error() string {
  162. return fmt.Sprint(e)
  163. }
  164. func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
  165. func (e *wrapError) FormatError(p Printer) (next error) {
  166. p.Print(e.msg)
  167. e.frame.Format(p)
  168. return e.err
  169. }
  170. func (e *wrapError) Unwrap() error {
  171. return e.err
  172. }