Skip to content

feat(maul): share check_nodiscard.py, which requires every internal bool marked - #124

Merged
siracozmen01 merged 2 commits into
mainfrom
feat/family-check-nodiscard
Nov 5, 2026
Merged

siracozmen01 merged 2 commits into
mainfrom
feat/family-check-nodiscard

Conversation

@siracozmen01

Copy link
Copy Markdown
Contributor

Problem

An internal function returning bool either refuses or answers, and a caller that drops it goes on past a refusal or with an out value left unset. maul-locale now marks every such function and checks that with its own tools/check_nodiscard.py (maul-locale #489). The family states no such rule and shares no check, and the shared sources under maul/renamed/src carry ten unmarked static bools that every library carrying them would fail on.

Change

  • maul/files/tools/check_nodiscard.py, lifted from maul-locale's and made family-wide:
    • the macro is any <PP>_NODISCARD a base.h under include/ (or a listed part's include/) defines;
    • it reads src/ and each tools/source-dirs.txt part's src/, subdirectories included: every bool function in a header, every static one in a source file;
    • a declaration starts at the line's first column, with only the library's macros, standard attributes ([[maybe_unused]]), static, inline and extern before bool; bool may end its line with the name first on the next, the shape clang-format writes for long declarations;
    • any other bool declaration (indented, or with something else before bool) is an error naming its place, so the check never passes what it did not read;
    • --self-test covers every shape, a part directory and a nested header.
  • maul-conventions.md states the rule and lists the check.
  • maul/renamed/src: inflate.c marks Build, Codes, Stored, Fixed, ReadLengths and Dynamic; elementary.c and elementary_float.c mark PowSpecial(F) and Atan2Special(F) and include @LIB@/base.h. Every caller already checks each one.

A library takes it by syncing, adds the check to its check.sh rules step, and marks its own bool functions in the same pull request.

Verification

  • --self-test passes.
  • Against current mains: maul-locale passes; maul-ui reports 529, maul2d 231, maul-vfx 372 and maul-tween 148 unmarked declarations, and no shape it cannot read once standard attributes and the two-line form are read (maul-ui's [[maybe_unused]] static MUI_NOINLINE bool / muiActMarked( is one).
  • In scratch trees synced from this branch: maul-ui's inflate.c and maul-tween's elementary.c and elementary_float.c pass clang-format --dry-run -Werror and build with gcc-14 and clang-20 at -std=c23 -Wall -Wextra -Werror; maul-tween's tree passes tools/check_source.py.

Closes #123

@siracozmen01 siracozmen01 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review at 2058b19: changes needed (1). Read, not run.

1. The indented two-line test fires on any indented line whose last word is bool, comments included. Problems refuses a line when re.match(r"^\s+.*\bbool\s*$", line) holds. That pattern does not require the line to look like a declaration. In a function body, a comment such as // the answer is a plain bool makes the check report "an indented bool declaration" and fail the library. So does a wrapped expression or argument list whose line happens to end with the word.

None of the five mains has such a line today (the body found no unread shapes). But a rule every library will carry should not fail on a comment. The fix is to give it the shape of the column-0 wrapped pattern: re.compile(r"^\s+" + specifiers + r"bool\s*$"), so only specifiers may come before bool. Alternatively, exclude //, /*, * and # the way unread does. A self-test case (" // returns a bool", False, 0) would pin it.

2. Column-0 non-declarations cannot reach unread. It needs a line that starts at column 0, does not start with whitespace, #, /, * or typedef, and contains bool <name>(.

  • Macro continuations: these start with whitespace, or with # for the #define line.
  • Struct initializers and file-scope variables: static bool s_x = F(1);, (bool)F( and bool (*fp)( all lack bool, whitespace, a name and then (.
  • extern "C" {: it holds no bool. Only a one-line extern "C" bool f( would be refused, and that is fail-closed on a real declaration.
  • Comments: excluded, as the self-test shows.

3. The two-line read cannot take a wrong name.

  • name_line takes the identifier immediately before ( at the first column of the very next line, which is the function's name.
  • Anything else on that line yields no match, and the ends branch then refuses the declaration: a blank line, a comment, a further macro (PP_NOINLINE Name(), a function pointer, or a variable (s_ready = false;, with no ().
  • The name line itself matches none of the patterns, so it is not counted twice.

4. The renamed edits keep every caller's check.

  • All ten functions are static, so their callers are in the same template file and identical in every library that carries it.
  • With @PP@_NODISCARD the gcc-14 and clang-20 builds at -Werror would refuse any dropped result. They pass in the synced maul-ui (inflate.c) and maul-tween (elementary.c, elementary_float.c) trees, so no caller drops one in any library.
  • The added #include "@LIB@/base.h" reaches each library's own header through its include directory.

The prefix read (^#define ([A-Z0-9]+)_NODISCARD) and the part directories are covered by the self-test's tree. The conventions line states the uniform rule and names the check. The title fits.

@siracozmen01 siracozmen01 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of the delta 2058b19 to c457f02: ready. Read, not run.

The fix is in, and only check_nodiscard.py changed. The indented test is now one pattern, ^\s+ + specifiers + bool(?:\s+(\w+)\s*\(|\s*$), so only specifiers may stand before bool, and bool must be followed by a name and ( or end the line.

What it refuses: an indented one-line declaration, and an indented static bool with the name on the next line, which the self-test keeps at 1.

What it does not touch:

  • a comment ending in "bool" (// is no specifier);
  • bool done; and bool ok = f(1);, where no ( follows the name;
  • boolValue(x), where bool is not followed by whitespace or the line's end.

The two new self-test cases pin the comment shapes. The rest is as reviewed at 2058b19.

@siracozmen01
siracozmen01 merged commit 49775fd into main Nov 5, 2026
1 check failed
siracozmen01 added a commit that referenced this pull request Nov 5, 2026
## Problem

v0.0.53's `check_nodiscard.py` reports two unmarked bool functions in the shared `maul/renamed/src/cpu.h`: `@P@IsCpuBackendSupported` and `@P@CheckCpu`. #124 marked the other renamed files but missed these, because the `@P@` placeholder hid their names from its scan. maul2d, maul3d and maul-anim carry cpu.h and cannot pass the check without editing a family file.

## Change

`cpu.h` includes `@LIB@/base.h` and marks both declarations `@PP@_NODISCARD`. A public `base.h` that also declares them (all three libraries do, unmarked) keeps its own declaration; the mark on this later one makes a dropped result warn inside the library.

## Verification

Synced from this branch into scratch trees of maul2d, maul3d and maul-anim:
- `src/cpu.h` passes `clang-format --dry-run -Werror`, and `src/cpu.c` builds with gcc-14 and clang-20 at `-std=c23 -Wall -Wextra -Wpedantic -Werror`;
- `tools/check_nodiscard.py` no longer reports cpu.h;
- a call that drops `maniCheckCpu`'s result warns under gcc-14;
- every call of the two functions in the three libraries (src, tests, bench) uses the result.

Closes #125
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The family neither states nor checks that internal bool functions are nodiscard

1 participant