From ae462a1c4606253c47f166da2a72b5d38b51b843 Mon Sep 17 00:00:00 2001 From: Trevor Chaney Date: Fri, 29 Aug 2025 23:43:22 -0500 Subject: [PATCH 1/3] Add Info method to update both Err and werr --- internal/handler/handler.go | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index e80300d..63dad5a 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -74,6 +74,15 @@ func NilNoop(err error) error { return err } // func ErrorNoop(err error) {} +// updateBothErrors updates both i.Err and i.werr to keep them in sync. +func (i *Info) updateBothErrors(errPtr *error) { + if errPtr == nil { + panic("updateBothErrors: errPtr cannot be nil") + } + i.Err = errPtr + i.werr = *errPtr +} + func (i *Info) callNilHandler() { if i.CheckHandler != nil && i.safeErr() == nil { i.CheckHandler(true) @@ -86,8 +95,8 @@ func (i *Info) callNilHandler() { i.checkErrorTracer() } if i.NilFn != nil { - *i.Err = i.NilFn(i.werr) - i.werr = *i.Err // remember change both our errors! + result := i.NilFn(i.werr) + i.updateBothErrors(&result) } else { i.defaultNilHandler() } @@ -120,14 +129,16 @@ func (i *Info) callErrorHandler() { if i.ErrorFn != nil { // we want to auto-annotate error first and exec ErrorFn then i.werr = i.workError() + var result error + if i.needErrorAnnotation && i.werr != nil { i.buildFmtErr() - *i.Err = i.ErrorFn(*i.Err) + result = i.ErrorFn(*i.Err) } else { - *i.Err = i.ErrorFn(i.Any.(error)) + result = i.ErrorFn(i.Any.(error)) } - i.werr = *i.Err // remember change both our errors! + i.updateBothErrors(&result) } else { i.defaultErrorHandler() } @@ -165,8 +176,8 @@ func (i *Info) workError() (err error) { } func (i *Info) fmtErr() { - *i.Err = fmt.Errorf(i.Format+i.wrapStr(), append(i.Args, i.werr)...) - i.werr = *i.Err // remember change both our errors! + result := fmt.Errorf(i.Format+i.wrapStr(), append(i.Args, i.werr)...) + i.updateBothErrors(&result) } func (i *Info) buildFmtErr() { @@ -282,8 +293,7 @@ func PreProcess(errPtr *error, info *Info, a []any) error { // We get 3x faster defer handlers without unsing ptr to original err // named return val. Reason is unknown. err := x.Whom(errPtr != nil, *errPtr, nil) - info.Err = &err - info.werr = *info.Err // remember change both our errors! + info.updateBothErrors(&err) // We want the function who sets the handler, i.e. calls the // err2.Handle function via defer. Because call stack is in reverse @@ -315,7 +325,9 @@ func PreProcess(errPtr *error, info *Info, a []any) error { *info.Err = nil // prevent dublicate "logging" } } - return err + // Return the final processed error from info.Err, which should be the same as err + // unless handlers modified it (e.g., set it to nil as above) + return *info.Err } func buildFormatStr(info *Info, lvl int) { From 2572c0cab75564ff1dd0343689c528ba31d88901 Mon Sep 17 00:00:00 2001 From: Trevor Chaney Date: Mon, 1 Sep 2025 15:40:27 -0500 Subject: [PATCH 2/3] Update error synchronization with focused initErrors and setErrors functions --- internal/handler/handler.go | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 63dad5a..32c45ca 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -74,15 +74,27 @@ func NilNoop(err error) error { return err } // func ErrorNoop(err error) {} -// updateBothErrors updates both i.Err and i.werr to keep them in sync. -func (i *Info) updateBothErrors(errPtr *error) { +// initErrors initializes both i.Err and i.werr with a new error pointer. +// Used by PreProcess for setting up error handling with a local error variable. +func (i *Info) initErrors(errPtr *error) { if errPtr == nil { - panic("updateBothErrors: errPtr cannot be nil") + panic("initErrors: error pointer cannot be nil") } i.Err = errPtr i.werr = *errPtr } +// setErrors updates both *i.Err and i.werr to keep them in sync. +// Assumes i.Err is already initialized (non-nil pointer). +// Used by error handlers to update the error value. +func (i *Info) setErrors(err error) { + if i.Err == nil { + panic("setErrors: i.Err must be initialized before calling setErrors") + } + *i.Err = err + i.werr = err +} + func (i *Info) callNilHandler() { if i.CheckHandler != nil && i.safeErr() == nil { i.CheckHandler(true) @@ -96,7 +108,7 @@ func (i *Info) callNilHandler() { } if i.NilFn != nil { result := i.NilFn(i.werr) - i.updateBothErrors(&result) + i.setErrors(result) } else { i.defaultNilHandler() } @@ -138,7 +150,7 @@ func (i *Info) callErrorHandler() { result = i.ErrorFn(i.Any.(error)) } - i.updateBothErrors(&result) + i.setErrors(result) } else { i.defaultErrorHandler() } @@ -177,7 +189,7 @@ func (i *Info) workError() (err error) { func (i *Info) fmtErr() { result := fmt.Errorf(i.Format+i.wrapStr(), append(i.Args, i.werr)...) - i.updateBothErrors(&result) + i.setErrors(result) } func (i *Info) buildFmtErr() { @@ -290,10 +302,10 @@ func Process(info *Info) { func PreProcess(errPtr *error, info *Info, a []any) error { // Bug in Go? // start to use local error ptr only for optimization reasons. - // We get 3x faster defer handlers without unsing ptr to original err + // We get 3x faster defer handlers without using ptr to original err // named return val. Reason is unknown. err := x.Whom(errPtr != nil, *errPtr, nil) - info.updateBothErrors(&err) + info.initErrors(&err) // We want the function who sets the handler, i.e. calls the // err2.Handle function via defer. Because call stack is in reverse @@ -322,12 +334,13 @@ func PreProcess(errPtr *error, info *Info, a []any) error { const framesToSkip = 6 frame = x.Whom(ok, frame, framesToSkip) if LogOutput(frame, curErr.Error()) == nil { - *info.Err = nil // prevent dublicate "logging" + *info.Err = nil // prevent duplicate "logging" } } - // Return the final processed error from info.Err, which should be the same as err - // unless handlers modified it (e.g., set it to nil as above) - return *info.Err + + // Note: Since info.Err points to &err, any updates to *info.Err will also update err. + // Therefore, we can return the local err variable which reflects the final processed state. + return err } func buildFormatStr(info *Info, lvl int) { From 85f06111924d4b583f31ff4c57b11d62bc4e480d Mon Sep 17 00:00:00 2001 From: Trevor Chaney Date: Mon, 1 Sep 2025 16:01:22 -0500 Subject: [PATCH 3/3] Remove redundant nil checks from error synchronization functions --- internal/handler/handler.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 32c45ca..98e3831 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -77,9 +77,6 @@ func NilNoop(err error) error { return err } // initErrors initializes both i.Err and i.werr with a new error pointer. // Used by PreProcess for setting up error handling with a local error variable. func (i *Info) initErrors(errPtr *error) { - if errPtr == nil { - panic("initErrors: error pointer cannot be nil") - } i.Err = errPtr i.werr = *errPtr } @@ -88,9 +85,6 @@ func (i *Info) initErrors(errPtr *error) { // Assumes i.Err is already initialized (non-nil pointer). // Used by error handlers to update the error value. func (i *Info) setErrors(err error) { - if i.Err == nil { - panic("setErrors: i.Err must be initialized before calling setErrors") - } *i.Err = err i.werr = err }