// Copyright 2018 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package xerrorsimport ()const percentBangString = "%!"// Errorf formats according to a format specifier and returns the string as a// value that satisfies error.//// The returned error includes the file and line number of the caller when// formatted with additional detail enabled. If the last argument is an error// the returned error's Format method will return it if the format string ends// with ": %s", ": %v", or ": %w". If the last argument is an error and the// format string ends with ": %w", the returned error implements an Unwrap// method returning it.//// If the format specifier includes a %w verb with an error operand in a// position other than at the end, the returned error will still implement an// Unwrap method returning the operand, but the error's Format method will not// return the wrapped error.//// It is invalid to include more than one %w verb or to supply it with an// operand that does not implement the error interface. The %w verb is otherwise// a synonym for %v.//// Note that as of Go 1.13, the fmt.Errorf function will do error formatting,// but it will not capture a stack backtrace.func ( string, ...any) error { = formatPlusW()// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. := strings.HasSuffix(, ": %w") , , := parsePercentW() := ! && >= 0if ! && ( || strings.HasSuffix(, ": %s") || strings.HasSuffix(, ": %v")) { := errorAt(, len()-1)if == nil {return &noWrapError{fmt.Sprintf(, ...), nil, Caller(1)} }// TODO: this is not entirely correct. The error value could be // printed elsewhere in format if it mixes numbered with unnumbered // substitutions. With relatively small changes to doPrintf we can // have it optionally ignore extra arguments and pass the argument // list in its entirety. := fmt.Sprintf([:len()-len(": %s")], [:len()-1]...) := Frame{}ifinternal.EnableTrace { = Caller(1) }if {return &wrapError{, , } }return &noWrapError{, , } }// Support %w anywhere. // TODO: don't repeat the wrapped error's message when %w occurs in the middle. := fmt.Sprintf(, ...)if < 0 {return &noWrapError{, nil, Caller(1)} } := errorAt(, )if ! || == nil {// Too many %ws or argument of %w is not an error. Approximate the Go // 1.13 fmt.Errorf message.return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, ), nil, Caller(1)} } := Frame{}ifinternal.EnableTrace { = Caller(1) }return &wrapError{, , }}func errorAt( []any, int) error {if < 0 || >= len() {returnnil } , := [].(error)if ! {returnnil }return}// formatPlusW is used to avoid the vet check that will barf at %w.func formatPlusW( string) string {return}// Return the index of the only %w in format, or -1 if none.// Also return a rewritten format string with %w replaced by %v, and// false if there is more than one %w.// TODO: handle "%[N]w".func parsePercentW( string) ( int, string, bool) {// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. = -1 = true := 0 := 0varboolfor := 0; < len(); += {if [] != '%' { = 1continue }// "%%" is not a format directive.if +1 < len() && [+1] == '%' { = 2continue } , = parsePrintfVerb([:])if {if >= 0 { = false } else { = }// "Replace" the last character, the 'w', with a 'v'. := + - 1 = [:] + "v" + [+1:] } ++ }return , , }// Parse the printf verb starting with a % at s[0].// Return how many bytes it occupies and whether the verb is 'w'.func parsePrintfVerb( string) (int, bool) {// Assume only that the directive is a sequence of non-letters followed by a single letter. := 0varrunefor := 1; < len(); += { , = utf8.DecodeRuneInString([:])ifunicode.IsLetter() {return + , == 'w' } }returnlen(), false}type noWrapError struct { msg string err error frame Frame}func ( *noWrapError) () string {returnfmt.Sprint()}func ( *noWrapError) ( fmt.State, rune) { FormatError(, , ) }func ( *noWrapError) ( Printer) ( error) { .Print(.msg) .frame.Format()return .err}type wrapError struct { msg string err error frame Frame}func ( *wrapError) () string {returnfmt.Sprint()}func ( *wrapError) ( fmt.State, rune) { FormatError(, , ) }func ( *wrapError) ( Printer) ( error) { .Print(.msg) .frame.Format()return .err}func ( *wrapError) () error {return .err}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.