Code style

Format changed lines and set up your editor.

.clang-format at the repository root is authoritative for C/C++. It is a Linux-kernel/git-derived style:

Setting Value
Indentation hard tabs, width 8 (UseTab: Always)
Braces Allman (BreakBeforeBraces: Allman)
Line length 100 columns
Pointers bound to the name — char *p
Includes never reordered (SortIncludes: false)
Comments never reflowed (ReflowComments: false)
Language standard c++20, matching src/Makefile

ForEachMacros teaches clang-format about LOOP_THRU_PEOPLE, LOOP_EVENTS_CH, and LOOP_EVENTS_OBJ, so those are spaced like control statements rather than calls.

Requirement

clang-format 14 or later (18 is what this tree is validated against):

bash
sudo apt-get install clang-format    # Debian/Ubuntu; ships git-clang-format too

It is also in packaging/duris-build-deps.equivs.

Formatting your changes

bash
./scripts/format.sh                    # format the lines you changed, in place
./scripts/format.sh --check            # report only; exit 1 if anything is off
./scripts/format.sh --staged           # format staged lines directly in the index
./scripts/format.sh --rev origin/master

Without --all, the script wraps git clang-format, so it rewrites only lines your diff touches. --staged applies formatting directly to the index and mirrors it into the working tree when that can be done without conflicting with unstaged edits. --check reports without changing anything.

Pre-commit hook

bash
./scripts/install-hooks.sh              # once per clone
./scripts/install-hooks.sh --uninstall  # revert

This sets core.hooksPath to the versioned scripts/git-hooks/, whose pre-commit auto-formats staged C/C++ lines directly in the index before the commit is created. It does not stage unrelated worktree edits; when a partially staged file prevents the same formatting patch from being mirrored safely, the working tree is left alone. Hooks are per-clone git config, so each checkout runs the installer once.

  • Bypass a single commit with git commit --no-verify.
  • If clang-format is not installed, the hook warns and lets the commit through rather than blocking work.
  • core.hooksPath replaces .git/hooks wholesale; the installer warns if you already have hooks there.

The whole tree is formatted

Every tracked C/C++ file under src/, migrations/tools/, areas/src/, and tests/async/ matches .clang-format, and --all --check verifies that in about 12 seconds:

bash
./scripts/format.sh --all          # re-format everything, iterated to a fixpoint
./scripts/format.sh --all --check  # verify the tree; exit 1 and name offenders

Day to day you still want the default (changed lines only) — a full pass in a feature branch buries the real change in review. Reformat-only work belongs in its own commit, never mixed with a behavior change.

Things that fight the formatter

  • clang-format is not idempotent here. A second pass can rewrite more than the first. --all iterates until a pass changes nothing, and fails loudly if five passes are not enough.
  • One statement oscillates forever. do_vote() in src/cmd/actnew.c has literal tab characters inside an fprintf format string, which breaks clang-format's column arithmetic. It is fenced with // clang-format off.
  • Constant defines with long trailing comments. clang-format wrapped 23 of them so the value landed on a backslash continuation line (#define MAX_TRADE \ / 16 /* ... */), which is unreadable and breaks every tool that scans headers for #define NAME VALUE. Their explanations now precede the one-line defines. Move a long comment above its define; fence the region only when restructuring is not practical.

When you hit a construct the formatter mangles, fence it the same way and say why in a comment. Do not disable the option globally.

clang-tidy on changed lines

.clang-tidy at the repository root turns on the bug patterns the compilers do not warn on: clang-tidy's bugprone-*, cert-flp30-c and three performance-* checks. Each check that is off says why beside it, in the file.

bash
./scripts/tidy.sh                    # changed lines, staged and unstaged, against HEAD
./scripts/tidy.sh --staged           # staged lines (what the hook runs)
./scripts/tidy.sh --rev origin/master
./scripts/tidy.sh --all [PATH...]    # every line, with a count by check

It needs clang-tidy and clang-tidy-diff (Debian/Ubuntu: clang-tidy). There is no compile database, so the script writes bin/tidy/compile_commands.json from src/Makefile's flags: the .c files are C++20, and the MariaDB build's defines decide what is analysed, so code only under __NO_MYSQL__ is not. A finding on a changed line fails the check; the hook runs it after formatting and refuses the commit. Fix the line, or, where it is right as it is, add // NOLINT(check-name): reason. --staged checks src/ as staged, written to bin/tidy/staged/, so an unstaged edit does not change the result, and the diff it reads ignores the git config's color, prefix and diff-driver settings. A changed header's lines are checked through the nearest source that includes it, directly or through other headers, since many headers do not compile on their own.

The first full run (2026-10-10) found 11,308; most came from the checks now off. Three were bugs, each fixed in its own commit with a test: the kick messages one race short (bugprone-suspicious-missing-comma), the line editor's leaks, in code no command reaches today (bugprone-macro-repeated-side-effects) and a missing <climits> in flatfile_store.c.

Editor setup

Point your editor at the repo's .clang-format and enable format-on-save for modified lines only — whole-file format-on-save will mangle legacy files.

  • VS Code: C/C++ or clangd extension, "C_Cpp.formatting": "clangFormat", "editor.formatOnSaveMode": "modifications".
  • Vim/Neovim: clang-format.py mapped to a key, or vim-clang-format with g:clang_format#detect_style_file = 1.
  • Emacs: clang-format-region on the region you edited.

History

The config shipped with two keys no released clang-format understands, BreakAfterOpenBracketBracedList and BreakBeforeCloseBracketBracedList, which made every clang-format invocation fail with Error reading .clang-format: Invalid argument. Removing them exposed two more problems, both found by actually formatting the tree:

  • RemoveBracesLLVM: true stripped the braces from single-statement if/else. The ADD_BYTE/ADD_SHORT macros in files.h expand to a brace block that is not do { } while (0) wrapped, so if (x) ADD_BYTE(...); else ... became if (x) { ... }; else and stopped compiling.
  • AlignConsecutiveAssignments/Declarations padded columns with tabs (they only line up at tab width 8), and clang-format chose different columns for a whole-file pass than for the line ranges git clang-format formats — so the hook rejected a fully formatted tree. Both are off now, as in the kernel, LLVM, and Google styles.

The deprecated boolean spellings (AlignOperands: true, ...) were also replaced with the enum forms they map to, so the config stays valid as clang-format retires the legacy aliases.

Removing the alignment padding had one side effect worth knowing: several tests used to locate a function definition by its bare signature, and only matched the definition because the forward declaration was column-padded and therefore spelled differently. Anchor on signature\n{ instead.