Raise errors from the status registry instead of returning codes
All checks were successful
libakerror CI Build / cmake_build (push) Successful in 2m47s
libakerror CI Build / coverage (push) Successful in 2m46s
libakerror CI Build / mutation_test (push) Successful in 14m56s

akerr_reserve_status_range() and akerr_register_status_name() returned
private int enumerations, which was the one place in the library where a
failure was not an akerr_ErrorContext *. They now return one like
everything else: NULL on success, and on refusal an error whose status is
a real code in the library's reserved band, so it can be CATCH-ed,
HANDLE-d, PASS-ed, or left to propagate into a stack trace. Both are
marked AKERR_NOIGNORE, so discarding the result warns at compile time.

AKERR_STATUS_RANGE_OK and AKERR_STATUS_NAME_OK are gone; the remaining
seven codes move into the AKERR_* offset span and get registered names.
AKERR_LAST_LIBRARY_STATUS replaces AKERR_BADEXC as the top of that span
in the reserved-band static assert and the exhaustiveness sweep.

The refusal detail that used to go straight to akerr_log_method now
travels in the error message, so a caller that handles the error decides
whether it is reported. The two-argument akerr_name_for_status() set path
is the exception: it returns a name and cannot raise, so it logs and
releases. akerr_init() likewise has no caller to raise into, so failing
to reserve its own band or name its own codes is logged and fatal --
that can only happen on a misconfigured build, and continuing would
degrade every later stack trace to "Unknown Error".

Move the 1.0.0 upgrade notice out of README.md into UPGRADING.md and
rewrite its return-code tables in terms of the statuses now raised.

Tests: ctest 29/29, coverage 97.5% line / 64.5% branch, mutation 77.5%
(was 77.6%; the new survivors are the fatal init path, which needs a
library built with an undersized name table -- TODO item 7).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-30 20:58:47 -04:00
parent ba2430bfa1
commit 64da04e83b
13 changed files with 741 additions and 380 deletions

View File

@@ -81,6 +81,47 @@ static int __attribute__((unused)) akerr_slots_in_use(void)
AKERR_CHECK((errctx)->status == (expected_status)); \
} while ( 0 )
/*
* Helpers for functions that report failure by returning akerr_ErrorContext *.
* Both release the context they consume, so a test that makes thousands of
* failing calls cannot exhaust the pool. AKERR_CHECK_RAISES keeps a copy of the
* message for AKERR_CHECK_MESSAGE_CONTAINS, since the context is gone by then.
*/
static char __attribute__((unused)) akerr_last_message[AKERR_MAX_ERROR_CONTEXT_STRING_LENGTH];
#define AKERR_CHECK_SUCCEEDS(expr) \
do { \
akerr_ErrorContext *__akerr_result = (expr); \
if ( __akerr_result != NULL ) { \
fprintf(stderr, "UNEXPECTED ERROR from %s: %d (%s): %s" \
" at %s:%d\n", #expr, __akerr_result->status, \
akerr_name_for_status(__akerr_result->status, NULL), \
__akerr_result->message, __FILE__, __LINE__); \
RELEASE_ERROR(__akerr_result); \
return 1; \
} \
} while ( 0 )
#define AKERR_CHECK_RAISES(expr, expected_status) \
do { \
akerr_ErrorContext *__akerr_result = (expr); \
AKERR_CHECK(__akerr_result != NULL); \
snprintf(akerr_last_message, sizeof(akerr_last_message), "%s", \
__akerr_result->message); \
if ( __akerr_result->status != (expected_status) ) { \
fprintf(stderr, "WRONG STATUS from %s: got %d, want %s" \
" at %s:%d\n", #expr, __akerr_result->status, \
#expected_status, __FILE__, __LINE__); \
RELEASE_ERROR(__akerr_result); \
return 1; \
} \
RELEASE_ERROR(__akerr_result); \
AKERR_CHECK(__akerr_result == NULL); \
} while ( 0 )
#define AKERR_CHECK_MESSAGE_CONTAINS(needle) \
AKERR_CHECK(strstr(akerr_last_message, (needle)) != NULL)
#define AKERR_CHECK_CONTAINS(needle) \
AKERR_CHECK(strstr(akerr_capture_buf, (needle)) != NULL)