fmt.go raw

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