Drop the TODO.md section numbers from 26 files
All checks were successful
libakstdlib CI Build / cmake_build (push) Successful in 5m53s
libakstdlib CI Build / sanitizers (push) Successful in 2m55s
libakstdlib CI Build / coverage (push) Successful in 2m48s
libakstdlib CI Build / mutation_test (push) Successful in 12m39s

Eighty-five comments cited section numbers -- 1.1, 2.2.6, 3.6 -- from a
numbering the file had already abandoned before the move to the tracker. They
label completed work, so the pointer was the only wrong part.

The citation is removed and the sentence kept, which is what issue #27
recommended: these are labels, not references, and a label carrying a
version-dependent pointer goes stale again at the next reorganisation. Where a
pointer earns its place it names what actually holds the content now --
UPGRADING.md for the confirmed defects, libakerror #15 for the target
namespacing, issue #7 for the mutation survivors.

README.md and akstdlib.h sent readers to TODO.md for 'what is still open';
they name the tracker.

Verified: cmake --build build && ctest --test-dir build, 19/19.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
This commit is contained in:
2026-08-02 22:01:27 -04:00
parent d6a1cd8ca8
commit 55d986c631
27 changed files with 96 additions and 94 deletions

View File

@@ -1,5 +1,5 @@
/*
* NEGATIVE COMPILE TEST -- TODO.md sections 1.3 and 2.2.5.
* NEGATIVE COMPILE TEST.
*
* This file must NOT compile. It is built by the CTest entry
* `negative_format_mismatch` with -Werror, and that test is marked WILL_FAIL.

View File

@@ -1,5 +1,5 @@
/*
* NEGATIVE COMPILE TEST -- TODO.md section 1.9.
* NEGATIVE COMPILE TEST.
*
* This file must NOT compile. It is built by the CTest entry `negative_noignore`
* with -Werror, and that test is marked WILL_FAIL, so a successful build is a

View File

@@ -1,5 +1,5 @@
/*
* List and tree additions -- src/collections.c, TODO.md section 3.6.
* List and tree additions -- src/collections.c.
*
* The bare-node list functions, the tracked aksl_List container, and the binary
* search tree. The hash map and string buffer have their own files.
@@ -407,7 +407,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *free_that_fails_once(void *ptr)
* single walk. Only the first is kept and handed back; the rest have to be
* released, or a walk over n nodes with a broken free would consume n pool slots
* and exhaust the pool -- the failure mode the whole pool-accounting section of
* TODO.md 1.9 exists to catch.
* the cross-cutting wrapper contract exists to catch.
*/
static akerr_ErrorContext AKERR_NOIGNORE *free_that_always_fails(void *ptr)
{
@@ -800,7 +800,7 @@ static int test_tree_insert_orders_the_leaves(void)
}
/*
* TODO.md 2.2.15: aksl_TreeNode.parent was declared and never touched by
* aksl_TreeNode.parent was declared and never touched by
* anything in the library. These are the functions that set it, and
* aksl_tree_remove is the one that needs it.
*/

View File

@@ -1,7 +1,7 @@
/*
* String -> number wrappers: aksl_atoi / atol / atoll / atof.
*
* TODO.md section 1.4, complete. The cases that used to live in
* Numeric conversion, complete. The cases that used to live in
* tests/test_convert_strict.c -- registered as a known failure because the ato*
* family had no error channel at all (2.1.5) -- are folded back in here now that
* they pass: non-numeric input, empty input, trailing junk and overflow are all
@@ -95,7 +95,7 @@ static int test_trailing_junk_is_a_value_error(void)
/*
* "0x10" through the ato* forms is base 10, so parsing stops at the 'x' and the
* rest is trailing junk. TODO.md 1.4 left this open; the answer is that the
* rest is trailing junk. The wrapper plan left this open; the answer is that the
* prefix-honouring parse is aksl_strtol(nptr, NULL, 0, &dest), and tests/
* test_strto.c holds that half.
*/
@@ -149,7 +149,7 @@ static int test_atoi_narrows_to_int_range(void)
return 0;
}
/* TODO.md 1.4: the type boundaries round-trip exactly rather than nearly. */
/* The type boundaries round-trip exactly rather than nearly. */
static int test_type_boundaries_round_trip(void)
{
char buf[64];
@@ -229,7 +229,7 @@ static int test_atof_converts_and_rejects_null(void)
}
/*
* TODO.md 1.4 left "inf" and "nan" open. They are accepted, because strtod(3)
* The wrapper plan left "inf" and "nan" open. They are accepted, because strtod(3)
* accepts them and because they are exact round-trips rather than approximations
* of something else -- a caller who did not want them has a domain check to make
* that this library cannot make for it.

View File

@@ -2,7 +2,7 @@
* Formatted-output wrappers: aksl_printf / aksl_fprintf / aksl_snprintf and
* their va_list forms.
*
* TODO.md section 1.3, now complete. Each happy path asserts both halves of the
* Formatted output, complete. Each happy path asserts both halves of the
* contract -- the byte count handed back through *count and the text that
* actually landed somewhere -- and every pointer argument is checked for its
* NULL guard.
@@ -11,7 +11,7 @@
* points at the formatted-output wrapper under test and not at the stream
* wrappers, which tests/test_stream.c covers.
*
* aksl_sprintf is gone (TODO.md 2.2.4) and aksl_snprintf takes its place, so the
* aksl_sprintf is gone and aksl_snprintf takes its place, so the
* destination-overflow case that could not previously be written is here: it is
* AKERR_OUTOFBOUNDS, not the short success snprintf(3) would have reported.
*/
@@ -139,7 +139,7 @@ static int test_fprintf_writes_to_stream(void)
/*
* vfprintf on a stream opened "r" fails outright, so the wrapper reports the
* errno it saw (EBADF on glibc). *count is 0 afterwards, not vfprintf's -1:
* TODO.md 1.3 recorded the negative count as a contract gap, and this is the
* The wrapper plan recorded the negative count as a contract gap, and this is the
* assertion that closes it.
*/
static int test_fprintf_to_read_only_stream_reports_errno(void)
@@ -270,7 +270,7 @@ static int test_asprintf_allocates_to_fit(void)
}
/*
* The va_list forms are what the variadic ones are built on, and TODO.md 3.1
* The va_list forms are what the variadic ones are built on, and the wrapper contract
* wanted them exposed so consumers can write their own variadic wrappers. This
* is a consumer doing exactly that.
*/
@@ -304,7 +304,7 @@ static int test_va_list_forms_are_usable_from_outside(void)
}
/*
* Regression cover for the missing va_end (TODO.md 2.1.4). Nothing here can
* Regression cover for the missing va_end. Nothing here can
* assert on register-save state directly; the point is to run the variadic
* wrappers enough times, with enough arguments, that the sanitizer build has
* something to trip over.

View File

@@ -1,5 +1,5 @@
/*
* The fixed-capacity hash map and FNV-1a -- src/collections.c, TODO.md 3.6.
* The fixed-capacity hash map and FNV-1a -- src/collections.c.
*
* "The single most obviously-missing data structure in the library", by the
* TODO's own account: akbasic needed one three times over -- variables,

View File

@@ -1,5 +1,5 @@
/*
* Linked list -- TODO.md section 1.7, complete.
* Linked list.
*
* The two confirmed list defects are fixed, so the tests that used to live in
* tests/test_list_append_chain.c and tests/test_list_iterate_head.c are folded
@@ -59,7 +59,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *record_visit(aksl_ListNode *node, void
/* ---------------------------------------------------------------------- */
/*
* TODO.md 2.2.14: every caller used to have to remember to memset a node before
* every caller used to have to remember to memset a node before
* its first use, and a stack node that skipped it walked straight into garbage.
*/
static int test_node_init_zeroes_the_links(void)
@@ -103,7 +103,7 @@ static int test_append_single_node(void)
* The defect that made this library's list unusable: `tail` was assigned from
* Floyd's `slow` cursor *before* slow advanced, so it tracked the node behind
* the midpoint rather than the last node. Appending n1..n4 to n0 produced the
* chain "n0 -> n4" and silently dropped n1, n2 and n3. TODO.md 2.1.1.
* chain "n0 -> n4" and silently dropped n1, n2 and n3.
*/
static int test_append_builds_the_whole_chain(void)
{
@@ -218,7 +218,7 @@ static int test_append_detects_cycle_below_the_head(void)
}
/*
* TODO.md 1.7 asked for the aliasing contract to be defined. It is refusal:
* The wrapper plan asked for the aliasing contract to be defined. It is refusal:
* relinking a node that is already in the list would orphan everything between
* its old position and the tail, so the tail walk -- which happens anyway --
* doubles as the check.
@@ -281,7 +281,7 @@ static int test_iterate_single_node(void)
* Every node, exactly once, in order, starting at the head. The cycle check
* leaves Floyd's `slow` cursor at the list midpoint, and the visiting loop used
* to start from there -- so the whole first half of the list, head included, was
* never passed to the callback at all. TODO.md 2.1.2.
* never passed to the callback at all.
*/
static int test_iterate_visits_every_node_from_the_head(void)
{
@@ -470,7 +470,7 @@ static int test_pop_middle_node(void)
}
/*
* TODO.md 2.2.12: popping the head used to leave the caller's own head pointer
* popping the head used to leave the caller's own head pointer
* aimed at a node that was no longer in the list, with no way to learn the new
* one. That is what the head out-param is for, and this is the assertion.
*/
@@ -572,7 +572,7 @@ static int test_pop_then_iterate(void)
}
/*
* TODO.md 1.7's pool-accounting case. AKSL_RUN already asserts that each test
* The wrapper plan's pool-accounting case. AKSL_RUN already asserts that each test
* leaves the pool as it found it; this one drives enough failures in a row to
* exhaust the pool several times over, which is where a wrapper that raises an
* error and forgets to release it shows up as an outright exhaustion rather than

View File

@@ -1,8 +1,8 @@
/*
* Memory wrappers -- TODO.md section 1.1, now complete, plus the additions from
* Memory wrappers, complete, plus the additions from
* section 3.1.
*
* The three cases 1.1 left open are all pinned here: malloc(0) is AKERR_VALUE
* The three cases the wrapper plan left open are all pinned here: malloc(0) is AKERR_VALUE
* rather than whatever errno happened to hold when the platform's malloc(0)
* returned NULL; an allocation the system cannot satisfy reports ENOMEM and
* leaves *dst NULL rather than garbage; and overlapping memcpy is refused with
@@ -32,7 +32,7 @@ static int test_malloc_rejects_null_destination(void)
}
/*
* TODO.md 1.1 asked for this contract to be pinned down. malloc(0) is allowed to
* The wrapper plan asked for this contract to be pinned down. malloc(0) is allowed to
* return either a unique pointer or NULL, and a NULL there is not a failure and
* need not set errno -- so the old wrapper could raise an error whose status was
* 0, which every DETECT downstream reads as success while the context holds a
@@ -296,7 +296,7 @@ static int test_memcpy_zero_length_is_noop(void)
}
/*
* TODO.md 1.1's open question, decided: overlap is AKERR_VALUE. memcpy(3) calls
* The wrapper plan's open question, decided: overlap is AKERR_VALUE. memcpy(3) calls
* it undefined behaviour, which in practice means "works until the day a
* compiler version or a length changes and it does not". Callers who mean to
* overlap want aksl_memmove, and the message says so.

View File

@@ -1,12 +1,12 @@
/*
* aksl_realpath and aksl_realpath_alloc -- TODO.md section 1.5, now complete.
* aksl_realpath and aksl_realpath_alloc.
*
* The happy paths compare against realpath(3) itself rather than against a
* hard-coded string, because $TMPDIR may itself be a symlink (/tmp -> /private/tmp
* and friends) and the resolved answer is what the platform says it is.
*
* The failure cases now pass an *uninitialised* resolved_path on purpose. That
* used to be the crash case (TODO.md 2.1.6): the wrapper's own error path
* used to be the crash case: the wrapper's own error path
* formatted the buffer with %s while realpath(3) leaves its contents
* unspecified on failure, so the library read uninitialised memory while
* reporting an error. The message names only the input path now, and this test
@@ -122,7 +122,7 @@ static int test_rejects_null_arguments(void)
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_realpath(NULL, resolved, sizeof(resolved)),
AKERR_NULLPOINTER, "path=");
/*
* TODO.md 2.1.6: this used to be unchecked, and realpath(path, NULL)
* this used to be unchecked, and realpath(path, NULL)
* allocated a buffer that the wrapper then discarded and leaked.
*/
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_realpath("/tmp", NULL, PATH_MAX),

View File

@@ -1,5 +1,5 @@
/*
* Cross-cutting properties every wrapper has to have -- TODO.md section 1.9.
* Cross-cutting properties every wrapper has to have.
*
* Two of them, and neither is about what any individual function computes:
*

View File

@@ -1,5 +1,5 @@
/*
* The growable string buffer -- src/collections.c, TODO.md 3.6.
* The growable string buffer -- src/collections.c.
*
* The bounded formatting wrappers are the right answer when the destination is
* a fixed buffer and no answer at all when the length is not known in advance.

View File

@@ -1,7 +1,7 @@
/*
* Stream wrappers: aksl_fopen / aksl_fread / aksl_fwrite / aksl_fclose.
*
* TODO.md section 1.2, now complete. The happy paths, the round trip, every
* Stream wrappers, complete. The happy paths, the round trip, every
* NULL guard, both stream-error statuses, the transferred-member count that
* aksl_fread and aksl_fwrite report through nmemb_out, and the three cases that
* needed a hostile file to produce: a mode-denied path (EACCES), a full device
@@ -81,7 +81,7 @@ static int test_fopen_rejects_null_arguments(void)
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fopen(path, "r", NULL),
AKERR_NULLPOINTER, "fp=");
/* TODO.md 2.2.2: both of these used to go straight through to fopen(3). */
/* both of these used to go straight through to fopen(3). */
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fopen(NULL, "r", &fp),
AKERR_NULLPOINTER, "pathname=");
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fopen(path, NULL, &fp),
@@ -144,7 +144,7 @@ static int test_fread_short_read_is_eof_and_reports_the_count(void)
AKSL_CHECK_OK(aksl_fopen(path, "r", &fp));
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fread(buf, 1, sizeof(buf), fp, &moved),
AKERR_EOF, "EOF");
/* TODO.md 2.2.3: this is what the caller could not previously find out. */
/* this is what the caller could not previously find out. */
AKSL_CHECK(moved == 4);
AKSL_CHECK_OK(aksl_fclose(fp));
@@ -160,7 +160,7 @@ static int test_fread_short_read_is_eof_and_reports_the_count(void)
*
* That is a change from the old wrapper, which read ferror() and reported
* AKERR_IO without ever consulting errno. Routing it through AKSL_ERRNO_OR
* (TODO.md 2.2.1) keeps AKERR_IO as the fallback for the case where the stream
* keeps AKERR_IO as the fallback for the case where the stream
* is in error and errno says nothing, and hands back the real reason otherwise.
*/
static int test_fread_from_write_only_stream_reports_errno(void)
@@ -194,7 +194,7 @@ static int test_fread_rejects_null_arguments(void)
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fread(buf, 1, sizeof(buf), NULL, &moved),
AKERR_NULLPOINTER, "fp=");
/* TODO.md 2.2.3: ptr was never checked in either direction. */
/* ptr was never checked in either direction. */
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fread(NULL, 1, sizeof(buf), fp, &moved),
AKERR_NULLPOINTER, "ptr=");
AKSL_CHECK_STATUS_MSG_CONTAINS(aksl_fread(buf, 1, sizeof(buf), fp, NULL),

View File

@@ -1,5 +1,5 @@
/*
* Stream wrappers beyond open/read/write/close -- src/stream.c, TODO.md 3.1.
* Stream wrappers beyond open/read/write/close -- src/stream.c.
*
* tests/test_stream.c covers fopen/fread/fwrite/fclose. This one covers
* positioning, flushing, character and line I/O, stream state, and formatted

View File

@@ -1,5 +1,5 @@
/*
* aksl_strhash_djb2 -- TODO.md section 1.6.
* aksl_strhash_djb2.
*
* The expected values are the canonical djb2 ones: h = 5381, then
* h = h * 33 + byte for each of len bytes, truncated to 32 bits. They were
@@ -7,7 +7,7 @@
*
* Most vectors here are 7-bit ASCII, where signed and unsigned char agree and
* the test therefore says nothing either way about byte signedness. The high-bit
* vector is the one that pins TODO.md 2.2.6 down.
* vector is the one that pins the sign-extension defect down.
*/
#include "aksl_capture.h"
@@ -47,7 +47,7 @@ static int test_known_answer_vectors(void)
}
/*
* TODO.md 2.2.6, pinned. The cursor is an unsigned char * now, so bytes at or
* Pinned. The cursor is an unsigned char * now, so bytes at or
* above 0x80 contribute their unsigned value. Iterating a plain char * on x86 or
* ARM Linux made them negative, giving 5859874 here instead of 5868578 -- a hash
* that disagreed with canonical djb2 and, worse, disagreed with itself across
@@ -68,7 +68,7 @@ static int test_high_bit_bytes_are_unsigned(void)
return 0;
}
/* The NUL-terminated convenience form (TODO.md 3.6) agrees with the length one. */
/* The NUL-terminated convenience form agrees with the length one. */
static int test_str_form_matches_the_length_form(void)
{
const char *s = "libakstdlib";

View File

@@ -1,5 +1,5 @@
/*
* String wrappers -- src/string.c, TODO.md section 3.1.
* String wrappers -- src/string.c.
*
* The two contracts worth testing hardest are the ones that differ from libc:
* every copying function takes the destination size and treats truncation as an

View File

@@ -1,5 +1,5 @@
/*
* The strto* family -- TODO.md section 3.1.
* The strto* family.
*
* These are the real implementation behind the ato* wrappers and the thing
* akbasic had to hand-write for itself (its src/convert.c, ~60 lines) because

View File

@@ -1,5 +1,5 @@
/*
* Tree traversal -- TODO.md section 1.8, complete.
* Tree traversal.
*
* The old version of this file counted steps, which cannot tell the three
* depth-first orders apart because all three visit all seven nodes -- and could
@@ -183,7 +183,7 @@ static int test_dfs_is_an_alias_for_preorder(void)
/*
* BFS was AKERR_NOT_IMPLEMENTED, and the lalloc/lfree parameters that existed to
* serve it were defaulted and then never called -- TODO.md 2.2.8 and 2.2.10.
* serve it were defaulted and then never called. See UPGRADING.md.
* Both modes work now, and the allocator test below proves the queue is real.
*/
static int test_bfs_visits_level_by_level(void)
@@ -249,7 +249,7 @@ static akerr_ErrorContext AKERR_NOIGNORE *counting_free(void *ptr)
}
/*
* TODO.md 1.8: "Custom lalloc/lfree are actually invoked -- currently they are
* The wrapper plan asked that "custom lalloc/lfree are actually invoked -- currently they are
* stored and never called". They are called now, once per node enqueued, and
* every allocation is released. The depth-first modes allocate nothing at all,
* which is the other half of the contract.
@@ -382,7 +382,7 @@ static int test_degenerate_chains(void)
}
/*
* TODO.md 1.8 / 2.2.7: a chain deeper than the recursion can take. It used to
* A chain deeper than the recursion can take. It used to
* overflow the stack; it is AKERR_OUTOFBOUNDS now, and the message names the
* documented limit. Built one node past the cap so the failure is the cap itself
* and not some incidental shortfall. `static` because AKSL_TREE_MAX_DEPTH nodes
@@ -488,7 +488,7 @@ static int test_cyclic_tree_is_caught(void)
* that raised the break handled it in its own PROCESS/HANDLE block and returned
* success, so the parent frame's PASS saw nothing wrong and carried straight on
* into the sibling subtree. All seven nodes were visited no matter where the
* break was raised. TODO.md 2.1.3.
* break was raised.
*
* One case per order, each breaking on a node that is *not* last in that order --
* which is precisely what the old test could not do, because it hid its target
@@ -579,7 +579,7 @@ static int test_null_arguments(void)
}
/*
* TODO.md 2.2.9: the switch had no default, so an unrecognised mode -- and
* the switch had no default, so an unrecognised mode -- and
* AKSL_TREE_SEARCH_VISIT, which the header documented but nothing implemented --
* fell straight through to SUCCEED_RETURN having visited nothing at all. A
* traversal that silently did not happen, reported as success.

View File

@@ -1,5 +1,5 @@
/*
* Version reporting -- TODO.md section 2.2.16.
* Version reporting.
*
* There are two versions in play and the whole point of this API is that they
* are allowed to differ: