Skip to content

BUG: Give the statement macros a do/while(0) body and drop the stray semicolons - #49

Merged
hjmjohnson merged 2 commits into
InsightSoftwareConsortium:masterfrom
gdevenyi:pr/fix-extra-semi-stmt
Sep 23, 2026
Merged

hjmjohnson merged 2 commits into
InsightSoftwareConsortium:masterfrom
gdevenyi:pr/fix-extra-semi-stmt

Conversation

@gdevenyi

@gdevenyi gdevenyi commented Aug 15, 2026 •

Copy link
Copy Markdown

A macro whose body is a bare { ... } block cannot be used as a statement:

if( cond )
    FSLIOERR(...);          /* the ; ends the if, the block always runs */
else                       /* ...and this is a syntax error */

None of the current call sites is written that way, so nothing is broken
today; the next person to write one gets a silent behaviour change or a
compile error a long way from the cause. clang reports these as
-Wextra-semi-stmt, because the trailing semicolon at each call site is an
empty statement.

Wrapped: the FSLIOERR macro in fslio.c and the seven nifti__test
macros in nifti_tester001.c. The NT_FILL and NT_MAT
families in the
tool headers already have do/while bodies and need nothing.

Two genuine empty statements are removed as well: a doubled semicolon
inside unescape_string() in both io files, and a stray semicolon after
the closing brace of an if block in FslClose().

FSLIOERR is worth calling out. It expands to

{ fprintf(stderr, ...); exit(EXIT_FAILURE); }

and there are roughly fifty call sites, several of them the whole body of
an if with no braces. Those are correct today only because the macro
always exits.


Interface impact: none. On the union of all these changes, configured with USE_FSL_CODE=ON and USE_CIFTI_CODE=ON: all 448 exported symbols across libniftiio, libnifti2, libznz, libfslio, libnifticdf and libcifti are identical to master under nm -D --defined-only, and all ten installed headers are identical under gcc -E -P. Under gcc -dM -E one macro definition differs, intentionally and only in text: #61 makes FSL_RADIOLOGICAL read (-1) so it is safe inside an expression. Its value is still -1, checked by compiling against each installed fslio.h and printing it.

Verification. This branch: builds with gcc 16.1.1, ctest unchanged from master (2 of 345 fail on master itself in this environment; #31 and #29 each fix one). The union of all the PRs: 0 errors under both gcc 16.1.1 and clang 22.1.8, ctest 345/345 under each, and the whole suite under valgrind memcheck with --trace-children=yes gives 484 traced processes with no invalid access, no uninitialised value and no leak in any nifti binary.

Coordination. Every line of every branch was compared, whitespace-normalised, against the diffs of the open PRs (#11, #21, #22, #23, #24). Where one of those already changes a line, the line was left alone, and the few deliberate overlaps are named in the text above. What survives is 17 compiler warnings, all of them on those lines: 9 -Wsign-conversion (5 in fslio.c for #22, 2 in nifti2_io.c and 2 in nifti_tester001.c for #24) and 8 -Wcalloc-transposed-args in nifti_findhdrname and nifti_findimgname, which #11 rewrites. No formatting changes appear anywhere, to stay clear of #10 and #12.

One of a set of independent, single-purpose PRs. Each bases on master and can be merged on its own, in any order.

The full set of PRs (35)

The union of all of them is on the fork as all-changes, if you want to build and test the lot at once.

CI and build

Configuration and documentation

Defects

Warning and check classes

This was referenced Aug 15, 2026
@gdevenyi
gdevenyi force-pushed the pr/fix-extra-semi-stmt branch 2 times, most recently from 11aab19 to 0dcf89b Compare August 15, 2026 05:22
@gdevenyi
gdevenyi marked this pull request as ready for review August 15, 2026 05:26
@gdevenyi gdevenyi changed the title BUG: Give the statement macros a do/while(0) body BUG: Give the statement macros a do/while(0) body and drop the stray semicolons Sep 19, 2026
@hjmjohnson
hjmjohnson force-pushed the pr/fix-extra-semi-stmt branch 2 times, most recently from 8a61032 to 686dd8c Compare September 22, 2026 02:10
@hjmjohnson
hjmjohnson force-pushed the pr/fix-extra-semi-stmt branch from 686dd8c to c6e71fb Compare September 22, 2026 10:48
@hjmjohnson
hjmjohnson force-pushed the pr/fix-extra-semi-stmt branch from c6e71fb to d220497 Compare September 22, 2026 13:50
@hjmjohnson

Copy link
Copy Markdown
Member

Rebased onto master (b4876bf), cleaned the commit message, and reworked the nifti_tester001.c hunks so the diff is 2 lines per macro instead of 3-4 with realigned continuations.

I checked every call site for an observable behavior change. There is none — this is prophylactic. Evidence below.

The call-site check (the interesting part)

A { ... } macro body only misbehaves when the call site puts an else after it, or relies on the trailing ; being part of an enclosing statement. So I grepped for exactly that against master:

  • FSLIOERR — 50-odd call sites, many of the form if (fslio==NULL) FSLIOERR("..."); with no braces. Those are fine today: the {...} block becomes the if-body and the ; is a harmless null statement after it. No FSLIOERR in the tree is followed by an else.
  • The seven nifti_*_test macros in nifti_tester001.c — every invocation is a standalone statement at function scope. None appears in an if or else arm.
  • The two doubled semicolons in unescape_string() and the }; in FslClose() are null statements; removing them emits identical code.

So there is no red to prove. If one of those greps had hit, that would have been a real bug and a real test; it did not.

What is measurable is the warning count, with clang -Wextra-semi-stmt:

file master this branch
fsliolib/fslio.c 70 0
nifti2/nifti2_io.c 1 0
niftilib/nifti1_io.c 1 0
niftilib/nifti_tester001.c 99 0

Class is BUILD: 171 warnings to 0, no runtime seam.

Diff reworked to cut churn

The reviewed version inserted a separate do line above each macro's {, which forced the backslash continuation column to be realigned on several lines and produced 23 added / 18 removed lines with whitespace-only changes among them.

Each macro now changes exactly two lines — { \ becomes do { \ at the same backslash column, and } becomes } while(0). Total is 18 added / 18 removed, and git diff -w master HEAD counts the same 18 added lines as the plain diff, i.e. zero whitespace-only churn.

Message cleanup

Removed the Co-Authored-By: trailer naming an AI tool and the Claude-Session: URL; "behaviour" to "behavior". Added the grep result above to the body so the "nothing is broken today" claim is backed rather than asserted. Author and author date preserved (Gabriel A. Devenyi, 2026-08-14).

Build and test
  • Release, NIFTI_BUILD_APPLICATIONS=ON USE_NIFTI2_CODE=ON USE_CIFTI_CODE=ON USE_FSL_CODE=ON: 100% tests passed, 0 tests failed out of 362.
  • Same plus -DFSLSTYLE=ON (this PR touches fslio.c): 100% tests passed, 0 tests failed out of 364.

@hjmjohnson

Copy link
Copy Markdown
Member

Rebased onto current master. Two commits, deliberately separable: the behavior-neutral code change, and the realignment it makes sensible.

Commit Added Code Whitespace-only
BUG: Give the statement macros a do/while(0) body 18 18 0
STYLE: Align the continuations in the macros this branch rewrites 66 0 66
What the first commit changes

Three kinds of edit, all behavior-neutral:

  1. FSLIOERR gains a do { ... } while(0) body.
  2. Stray statements removed: }; becomes } in fslio.c, and { str[jj] = LF ; ; nn++ ; } loses its empty statement in both nifti1_io.c and nifti2_io.c.
  3. Seven macros in nifti_tester001.c gain a do/while(0) body.

This is prophylactic, not a repair. I checked before claiming it: no FSLIOERR call site is followed by an else, and no test-macro invocation is. Today if (x) FSLIOERR("..."); expands to if (x) { ... };, and the trailing empty statement is harmless precisely because nothing follows it. The moment anyone writes an else after one of the 63 braceless sites, it stops compiling. The change removes that trap rather than fixing a present defect.

FSLIOERR keeps calling exit(); that is settled and untouched here. This is the macro's shape, not its behavior.

What the second commit changes, and why it is separate

Within a single macro the backslashes sat at four different columns — 62, 64, 73 and 86 — with one past 80. Each macro is now aligned to its own width, one space past its longest line.

Aligning per block rather than to a global column matters: the 86-column snprintf line was forcing its whole macro wide, and the block now sits at 66.

Only the seven macros the first commit already rewrites are touched. PrintTest, the eighth macro in the file, is left alone because this branch does not otherwise modify it.

It is a separate commit because it is 66 lines of pure whitespace. Keeping it apart leaves the code change readable at 18 lines with zero style noise, and lets either commit be taken without the other.

Verification
Configuration Result
Release, cifti + FSL + FSLSTYLE=ON 367/367, 0 warnings
Release, cifti only (FSL off, the default) 365/365

FSLSTYLE=ON is the configuration that compiles fsliolib at all, so it is the one the FSLIOERR change needed. The realignment also cleared the last compiler warning in that build.

No test is added and none applies. The change is behavior-neutral by construction: do { X } while(0) and { X } execute identically wherever both parse, and every current call site is one where both parse. A test would pass identically with and without the change.

gdevenyi and others added 2 commits September 22, 2026 19:32
A macro whose body is a bare { ... } block cannot be used as a
statement:

    if( cond )
        FSLIOERR(...);         /* the ; ends the if, the block runs */
    else                       /* ...and this is a syntax error */

No current call site is written that way -- FSLIOERR has no `else`
after it anywhere, and none of the nifti_*_test macros appears in an
if or else arm -- so the change is prophylactic and nothing observable
moves.  The next person to write one, though, gets a syntax error a
long way from the cause.  clang reports the call sites as
-Wextra-semi-stmt, because the trailing semicolon at each is an empty
statement.

Wrapped: FSLIOERR in fslio.c and the seven nifti_*_test macros in
nifti_tester001.c.  The NT_FILL and NT_MAT* families in the tool
headers already have do/while bodies.

Two genuine empty statements go too: a doubled semicolon inside
unescape_string() in both io files, and a stray semicolon after the
closing brace of an if block in FslClose().

FSLIOERR is worth calling out: it expands to fprintf plus
exit(EXIT_FAILURE), and of its roughly fifty call sites several are
the whole body of a braceless if.  Those are correct today only
because the macro always exits.
The backslashes sat at four different columns within a single macro, one
of them past 80. Each macro is now aligned to its own width, one space
past its longest line, so a block stays as narrow as its content allows
rather than being widened by one long line.

Only the seven macros the preceding commit gives a do/while(0) body are
touched.
@hjmjohnson
hjmjohnson force-pushed the pr/fix-extra-semi-stmt branch from 6f451c0 to a91aa16 Compare September 23, 2026 00:41
@hjmjohnson
hjmjohnson merged commit 3f14c7e into InsightSoftwareConsortium:master Sep 23, 2026
26 checks passed
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.

3 participants