
Embedded C naming-convention linter for GitHub Actions / pre-commit hooks. Implements Barr-C:2018 and MISRA-C complementary rules across 44 rule IDs.
src/
c_name_check.py # the linter (~2 700 lines, stdlib only)
naming_convention.yaml # project-specific rule configuration
_version.py # generated by CI: git describe --tags
namecheck.options # project defaults for --options-file
project.defines # keyword/type aliases for --defines
aliases.txt # module alias map for --aliases
exclusions.yml # per-file rule suppressions
c_keywords.txt # C/C++ keyword list (--keywords-file)
c_stdlib_names.txt # C stdlib name list (--stdlib-file)
c_spell_dict.txt # spell-check dictionary (--spell-dict)
tests/
naming_convention.yaml # test-suite config (independent of src/)
c_keywords.txt # test-suite keyword list
c_stdlib_names.txt # test-suite stdlib list
c_spell_dict.txt # test-suite spell dictionary
harness.py # shared helpers: cfg_only / run / has / clean
test_barr_c.py # 42 tests: Barr-C rules
test_yoda_condition.py # 37 tests: misc.yoda_condition
test_reserved_name.py # 40 tests: reserved_name
test_dictionaries.py # 32 tests: dict file loading and CLI flags
test_misc_improvements.py # 65 tests: unsigned_suffix, loop vars, numerics
test_defines.py # 16 tests: constant.* / macro.*
test_variables.py # 32 tests: all variable.* rules
test_functions.py # 14 tests: function.*
test_typedefs.py # 8 tests: typedef.*
test_enums.py # 11 tests: enum.*
test_structs.py # 7 tests: struct.*
test_include_guards.py # 8 tests: include_guard.*
test_misc.py # 23 tests: line_length / indentation / magic / suffix
test_spell_check.py # 9 tests: spell_check
test_sign_compatibility.py # 7 tests: cross-file sign compatibility
test_cli.py # 29 tests: CLI flags end-to-end
Dockerfile/
Dockerfile # multi-platform Docker image
.dockerignore
.github/workflows/
namecheck_tests.yml # runs the test suite on every commit
naming_convention.yml # runs linter + trend page on C source commits
docker_publish.yml # builds and pushes image to GHCR and Docker Hub
requirements.txt # pip dependencies
Note:
tests/naming_convention.yamlis the configuration used by the test suite and is kept independent ofsrc/naming_convention.yamlso that the project-specific production config can be tuned without breaking tests.
pip install -r requirements.txt
python src/c_name_check.py --config src/naming_convention.yaml \
source/**/*.c source/**/*.h
python src/c_name_check.py --options-file src/namecheck.options \
source/**/*.c source/**/*.h
python src/c_name_check.py --help
python src/c_name_check.py --version
pip install -r requirements.txt
pytest tests/ -v # all tests
pytest tests/ --ignore=tests/test_cli.py -v # skip subprocess tests
pytest tests/ --cov=src --cov-report=term-missing
| Flag | Purpose |
|---|---|
--config FILE |
YAML rule config (default: naming_convention.yaml) |
--options-file FILE |
Read options from file (one per line, # = comment) |
--keywords-file FILE |
Replace built-in C keyword list (src/c_keywords.txt) |
--stdlib-file FILE |
Replace built-in C stdlib name list (src/c_stdlib_names.txt) |
--spell-dict FILE |
Replace built-in spell-check dictionary (src/c_spell_dict.txt) |
--defines FILE |
Project keyword/type aliases |
--aliases FILE |
Module alias map |
--exclusions FILE |
Per-file rule suppression YAML |
--banned-names FILE |
Extra banned identifier names |
--spell-words FILE |
Extra spell-check exempt words |
--include GLOB |
Source glob to scan (repeatable) |
--exclude GLOB |
Path/directory to exclude (repeatable) |
--github-actions |
Emit ::error/::warning GitHub Actions annotations |
--warnings-as-errors |
Promote all warnings and info to errors |
--summary |
Print violation summary table |
--log FILE |
Write output to file as well as stdout |
--verbose |
Print the directory being scanned — prevents apparent hangs on large filesets |
--version |
Print tool name and version, exit 0 |
--help / -h |
Print help, exit 0 |
Exit codes: 0 clean, 1 errors found, 2 config/invocation error.
--verbose progress outputWhen checking a large source tree the tool can appear to hang. --verbose prints
the current directory to stderr as each new directory is entered, overwriting
the same line so the terminal stays clean:
python src/c_name_check.py --verbose --include "source/**/*.c" --summary
# stderr shows: Scanning: /repo/source/drivers/uart/ (updates in place)
The structs.allowed_abbreviations list works exactly like
variables.allowed_abbreviations — uppercase tokens in the list are permitted
inside otherwise lower_snake member names:
structs:
member_case: lower_snake
allowed_abbreviations:
- FIFO
- CRC
- ADC
Members like FIFO_count, CRC_result, and ADC_raw_value then pass without
needing to be renamed to fifo_count, crc_result, etc.
tests/naming_convention.yaml is now a separate copy of the rule configuration
used exclusively by the test suite. The production config in src/ can be
customised freely without affecting any test assertions. The same applies to the
three dictionary files (c_keywords.txt, c_stdlib_names.txt, c_spell_dict.txt).
| Category | Rule IDs |
|---|---|
| Constants / macros | constant.case constant.max_length constant.prefix macro.case macro.max_length macro.prefix |
| Variables | variable.global.case variable.global.prefix variable.global.g_prefix variable.static.case variable.static.prefix variable.static.s_prefix variable.local.case variable.parameter.case variable.parameter.p_prefix variable.min_length variable.max_length variable.pointer_prefix variable.pp_prefix variable.bool_prefix variable.handle_prefix variable.no_numeric_in_name variable.prefix_order |
| Functions | function.prefix function.style function.max_length |
| Types | typedef.case typedef.suffix enum.type_case enum.type_suffix enum.member_case enum.member_prefix struct.tag_case struct.tag_suffix struct.member_case |
| Include guards | include_guard.missing include_guard.format |
| Misc | misc.line_length misc.indentation misc.magic_number misc.unsigned_suffix misc.yoda_condition misc.block_comment_spacing |
| Other | reserved_name spell_check sign_compatibility |
| File | Flag | Purpose |
|---|---|---|
src/c_keywords.txt |
--keywords-file |
C/C++ keywords for reserved_name |
src/c_stdlib_names.txt |
--stdlib-file |
C stdlib names for reserved_name |
src/c_spell_dict.txt |
--spell-dict |
Embedded-domain allowlist for spell_check |
Custom files replace the built-in lists entirely. To extend rather than replace,
concatenate: cat src/c_keywords.txt extra.txt > combined.txt
docker pull ghcr.io/<owner>/c_name_check:latest
docker run --rm \
-v "$(pwd):/repo" \
ghcr.io/<owner>/c_name_check:latest \
--verbose \
--config /app/naming_convention.yaml \
/repo/source/**/*.c /repo/source/**/*.h
Windows CMD (note forward slashes and drive letter):
docker run --rm -v "C:/MyProject:/repo" ghcr.io/<owner>/c_name_check:latest ^
--config /app/naming_convention.yaml ^
--include "/repo/source/**/*.c" --include "/repo/source/**/*.h"
Use --include instead of shell globs — globs are expanded by the Python process
inside the container, correctly handling all files regardless of host OS.
namecheck_tests.yml)Triggers on pushes touching src/ (including dictionary files), tests/, or requirements.txt.
naming_convention.yml)Triggers on pushes/PRs touching C source. Publishes a violation-trend page and badge.
docker_publish.yml)Triggers on main/master when Dockerfile/ or src/ changes, and on v*.*.* tags.
Publishes to GHCR and Docker Hub (linux/amd64, linux/arm64).
Required secrets:
| Secret | Value |
|---|---|
DOCKERHUB_USERNAME |
Your Docker Hub username |
DOCKERHUB_TOKEN |
Docker Hub access token |