...

Source file src/golang.org/x/xerrors/fmt.go

Documentation: golang.org/x/xerrors

     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  

View as plain text