51 lines
3.0 KiB
Markdown
51 lines
3.0 KiB
Markdown
|
|
# Uncaught errors
|
||
|
|
|
||
|
|
## Misbehaving methods
|
||
|
|
|
||
|
|
Any function which returns `akerr_ErrorContext *` and completes successfully MUST call `SUCCEED_RETURN(errctx)`. Failure to do this may result in an invalid `akerr_ErrorContext *` being returned, which will cause an `AKERR_BEHAVIOR` error to be triggered from your code.
|
||
|
|
|
||
|
|
## Ensuring that all error codes are captured
|
||
|
|
|
||
|
|
Any function which returns `akerr_ErrorContext *` should also be marked with `AKERR_NOIGNORE`.
|
||
|
|
|
||
|
|
```c
|
||
|
|
akerr_ErrorContext AKERR_NOIGNORE *f(...);
|
||
|
|
```
|
||
|
|
|
||
|
|
This will cause a compile-time error if the return value of such a function is not used. "Used" here means assigned to a variable - it does not necessarily mean that the value is checked. However assuming that such functions are called inside of `ATTEMPT { ... }` blocks, it is safe to assume that such returns will be caught with `CATCH(...)`; therefore this error is a generally effective safeguard against careless coding where errors are not checked.
|
||
|
|
|
||
|
|
Beware that `AKERR_NOIGNORE` is not a failsafe - it implements the `warn_unused_result` mechanic. By design users may explicitly ignore an error code from a function marked with `warn_unused_result` by explicitly casting the return to `void`.
|
||
|
|
|
||
|
|
```c
|
||
|
|
#define AKERR_NOIGNORE __attribute__((warn_unused_result))
|
||
|
|
```
|
||
|
|
|
||
|
|
## Stack Traces
|
||
|
|
|
||
|
|
Whenever an error is captured using the `FAIL_*` or `CATCH` methods, and is unhandled such that it manages to propagate all the way to the top of the caller stack without being managed, the last `FINISH` macro to touch the error will trigger a stack trace and kill the program.
|
||
|
|
|
||
|
|
Consider the `tests/err_trace.c` program which intentionally triggers this behavior. It produces output like this:
|
||
|
|
|
||
|
|
```
|
||
|
|
tests/err_trace.c:func2:7: 1 (Null Pointer Error) : This is a failure in func2
|
||
|
|
tests/err_trace.c:func2:10
|
||
|
|
tests/err_trace.c:func1:18: Detected error 0 from array (refcount 1)
|
||
|
|
tests/err_trace.c:func1:18
|
||
|
|
tests/err_trace.c:func1:21
|
||
|
|
tests/err_trace.c:main:30: Detected error 0 from array (refcount 1)
|
||
|
|
tests/err_trace.c:main:30
|
||
|
|
tests/err_trace.c:main:33: Unhandled Error 1 (Null Pointer Error): This is a failure in func2
|
||
|
|
```
|
||
|
|
|
||
|
|
From bottom to top, we have:
|
||
|
|
|
||
|
|
* The last line printed is the `FINISH` macro call that triggered the stacktrace.
|
||
|
|
* Above that, the `CATCH()` inside of `main()` which caught the exception from `func1()` but did not handle it
|
||
|
|
* Above that, a statement that the error was detected in the `CATCH()` statement at the same line
|
||
|
|
* Above that, the `FINISH()` macro in the `func1` method which detected the presence of an unhandled error and returned it up the calling stack
|
||
|
|
* Above that, the `CATCH()` macro in the `func1` method which caught the error coming out of `func2()`
|
||
|
|
* Above that, a statement that the error was detected in the `CATCH()` statement at the same line
|
||
|
|
* Above that, the `FINISH()` macro in `func2()` which detected an unhandled error and passed it out of the function
|
||
|
|
* Above that, a reference to the line where the `FAIL()` macro set the error code and provided the message which is printed here
|
||
|
|
|