Pre-commit hook that validates doxygen comments for presence, version staleness, and custom tag syntax
Project description
doxygen-guard
Pre-commit hook that validates doxygen comments, generates PlantUML sequence diagrams, and produces change-impact reports. Architecture-agnostic — works with event-driven, sequential, async IPC, and any other pattern.
Quick Start
1. Add the hook to .pre-commit-config.yaml
repos:
- repo: https://github.com/tvanfossen/doxygen-guard
rev: main
hooks:
- id: doxygen-guard
types_or: [c, c++, java, python]
2. Create .doxygen-guard.yaml in your repo root
output_dir: docs/generated/
validate:
exclude:
- "^tests/"
- "^\\.venv/"
trace:
format: plantuml
participant_field: "Subsystem"
static_participants:
- name: User
type: actor
external:
- Cloud:
receives_prefix: ["MQTT:"]
boundary_functions: ["CloudMgr_Publish"]
label_template: "MQTT: {arg0}"
entry_chain:
- { from: User, to: Mobile, label: "{req_name}" }
- { from: Mobile, to: Cloud, label: "{req_name}" }
- MCU:
receives_prefix: ["DURABLE:"]
boundary_functions: ["DurableEventCb"]
options:
show_returns: false
label_mode: brief
impact:
requirements:
file: docs/requirements.csv
id_column: "Req ID"
name_column: "Name"
format: csv
3. Add doxygen to your functions
/** @participant Sensor Driver */
/**
* @brief Read temperature from sensor hardware.
* @version 1.0
* @req REQ-0010
* @return Raw ADC value
*/
int Sensor_ReadTemperature(void) {
int raw = hw_read_adc(TEMP_CHANNEL);
event_post(EVENT_SENSOR_DATA_READY, raw);
return raw;
}
Run pre-commit run --all-files — diagrams appear in docs/generated/sequences/.
What It Does
Validation (pre-commit gate)
Every function in staged files is checked for:
- Presence — must have
@brief,@version, and@return(non-void functions) - Version staleness — if function body changed (git diff),
@versionmust be updated - Tag syntax — tag values validated against configured patterns
- Requirement coverage — functions must have
@reqor an exemption tag
Sequence Diagrams (auto-generated)
Behavioral sequence diagrams are generated from annotations. Most tags are inferred from AST:
| Tag | Purpose | Manual? |
|---|---|---|
@req |
Requirement mapping | Yes — per function |
@participant |
Participant identity | Yes — once per file |
@note |
Diagram notes (doxygen built-in) | Yes — per function |
@after |
Precondition cross-reference | Yes — per function |
@loop |
Wrap handler in loop block | Yes — per function |
@group |
Wrap handler in group block | Yes — per function |
@return |
Return value documentation | Yes — per function |
@send_source |
Infrastructure root (e.g., on Event_post) |
Yes — once |
@receive_source |
Registration root (e.g., on Event_register) |
Yes — once |
@sends |
Events emitted | Inferred from AST |
@receives |
Events handled | Inferred from AST |
@calls |
Cross-module boundary calls | Inferred from AST |
The tool scans function bodies via tree-sitter to detect event_post() calls, Event_register() patterns, and cross-participant function calls automatically.
Diagram Features
- Entry chains — upstream participant arrows (User→Mobile→Cloud) prepended from config
- Label templates — protocol labels from boundary function args (
MQTT: {arg0}) - Hub backtracking — REQs routed through dispatch hubs get entry chains automatically
- Control flow blocks (if/else, loops) from AST
- Loop/group wrappers —
@loopand@grouptags for repeated/grouped handlers - Boundary argument extraction — function args rendered on arrows via tree-sitter
- Incremental generation — SHA-256 manifest skips unchanged diagrams
Exemption Tags
| Tag | Validation | Trace |
|---|---|---|
@internal |
Exempt from @req |
Invisible — never in any diagram |
@utility |
Exempt from @req |
Visible — appears as call target |
@callback |
Exempt from @req |
Normal visibility |
Change-Impact Reports
Cross-references git diff with parsed functions to show which requirements are affected by staged changes. Reports in markdown and JSON at <output_dir>/impact/.
Configuration Reference
validate section
| Key | Type | Default | Description |
|---|---|---|---|
languages |
dict | C, C++, Java, Python | Per-language function patterns and comment styles |
presence.require_doxygen |
bool | true |
Require doxygen on every function |
presence.require_return |
bool | true |
Require @return on non-void functions |
presence.skip_forward_declarations |
bool | true |
Skip C/C++ forward declarations |
version.require_present |
bool | true |
Require @version tag |
version.require_increment_on_change |
bool | true |
Require version bump when body changes |
exclude |
list | [] |
Regex patterns for files to skip (applies to both validation and trace) |
tags.req.cross_reference |
bool | true |
Validate @req IDs exist in requirements file |
version_gate.current_version |
string | — | auto:git, auto:cmake, or explicit version |
version_gate.version_field |
string | — | Column in requirements file for version gating |
trace section
| Key | Type | Default | Description |
|---|---|---|---|
format |
string | plantuml |
Diagram output format |
participant_field |
string | — | Requirements file column for participant resolution |
static_participants |
list | [] |
Actors/entities always shown (e.g., {name: User, type: actor}) |
external |
list | [] |
External participants (see below) |
external_fallback |
string | External |
Fallback name for unresolved sources (warns and omits in behavioral mode) |
External participants
Route events to named external actors by prefix. Boundary functions produce protocol-labeled arrows via label_template. Entry chains prepend upstream participant arrows.
trace:
external:
- Cloud:
receives_prefix: ["MQTT:"]
boundary_functions: ["CloudMgr_Publish"]
label_template: "MQTT: {arg0}"
entry_chain:
- { from: User, to: Mobile, label: "{req_name}" }
- { from: Mobile, to: Cloud, label: "{req_name}" }
- MCU:
receives_prefix: ["DURABLE:"]
boundary_functions: ["DurableEventCb"]
- Hardware:
receives_prefix: ["EVENT_HW_"]
| Sub-key | Purpose |
|---|---|
receives_prefix |
Route @receives events matching these prefixes to this participant |
boundary_functions |
Function names that cross this system boundary (for @calls edges) |
label_template |
Format boundary call labels — {arg0}, {arg1} substituted from call args |
entry_chain |
Upstream participant arrows prepended before entry edge |
trace.options
| Key | Type | Default | Description |
|---|---|---|---|
autonumber |
bool | true |
Number sequence arrows |
box_label |
string | System |
Internal participant box label |
box_color |
string | #LightBlue |
Internal participant box color |
min_edges |
int | 1 |
Minimum behavioral edges to generate a diagram |
show_returns |
bool | false |
Show return arrows and activation bars on @calls edges |
show_recovery_notes |
bool | true |
Show callee names in empty catch blocks |
max_condition_length |
int | 80 |
Truncation limit for alt/loop conditions |
infer_sends |
bool | true |
Infer @sends from event post function calls |
infer_calls |
bool | true |
Infer @calls from cross-module calls |
event_send_functions |
list | [] |
Event post function names for generated code (escape hatch) |
event_receive_functions |
list | [] |
Registration function names for generated code (escape hatch) |
legend |
bool | false |
Render arrow style legend |
label_mode |
string | brief |
Label style: full (event+function), brief (event name only) |
impact section
| Key | Type | Default | Description |
|---|---|---|---|
requirements.file |
string | — | Path to requirements file |
requirements.format |
string | csv |
Format: csv, json, or yaml |
requirements.id_column |
string | Req ID |
Requirement ID column/field |
requirements.name_column |
string | Requirement Name |
Requirement name column/field |
File-Level Doxygen
Each source file should have a file-level doxygen block. Use @participant to set the participant name for all functions in the file:
/**
* @file
* @brief Sensor hardware abstraction layer.
* @version 1.0
* @participant Sensor Driver
*/
The requirements-file participant_field takes precedence over @participant for participant resolution. @participant is the fallback when no requirements mapping exists. Enable validate.presence.require_file_doxygen: true to enforce file-level blocks.
For Python:
## @file
## @brief Configuration loading and validation.
## @version 1.0
## @participant Config
Infrastructure Roots
Mark event bus functions once to enable automatic inference:
/**
* @brief Post an event to all registered handlers.
* @version 1.0
* @req REQ-0050
* @send_source
*/
void Event_post(uint64_t event, void *data) { ... }
/**
* @brief Register a handler for events matching a bitmask.
* @version 1.0
* @req REQ-0050
* @receive_source
*/
void Event_register(uint64_t mask, event_handler_fn handler) { ... }
With these in place, @sends and @receives are derived from the AST for all other functions — zero manual behavioral tags needed.
Generated Code
For code generators that rebuild files on every build (UDM, protobuf, HAL generators), annotations on generated files are destroyed. Use event_send_functions and event_receive_functions as config-level overrides:
trace:
options:
event_send_functions: ["dm_event_callback"]
event_receive_functions: ["register_fsm_handler"]
These tell the tool to treat calls to these functions as send/registration points, equivalent to @send_source / @receive_source but without requiring tags on the generated source. Use this only for generated code you cannot annotate. For code you own, use @send_source / @receive_source tags instead.
Both default to empty lists — @send_source / @receive_source tags are the primary mechanism.
Config Validation
The config file is validated at load time against a built-in schema. Unknown keys are rejected with an error message:
doxygen-guard config error: Unknown key 'trace.optoins' — did you mean 'trace.options'?
trace.options values are type-checked after merge with defaults. Invalid types produce warnings:
WARNING: trace.options.min_edges must be int >= 0, got 'banana'
Adopting on an Existing Codebase
For repos with existing code that has no doxygen, adopt incrementally:
- Start with validation only — add
@briefand@versionto functions as you touch them. Useversion_gateto only enforce@reqon functions added after a specific version:
validate:
version_gate:
current_version: "auto:git"
version_field: "Min Version"
-
Add
@participantto each file — one line per file sets the participant name for diagrams. -
Add
@returnto non-void functions — required by default. Disable withpresence.require_return: falseduring migration. -
Mark infrastructure roots — if you have an event bus or message dispatcher, add
@send_source/@receive_sourceonce to enable automatic inference. -
Exclude paths you're not ready to cover:
validate:
exclude:
- "^vendor/"
- "^legacy/"
Supported Languages
| Language | Extensions | Comment Style | Body Detection |
|---|---|---|---|
| C | .c, .h |
/** ... */ |
Brace matching |
| C++ | .cpp, .hpp, .cc, .cxx |
/** ... */ |
Brace matching |
| Java | .java |
/** ... */ |
Brace matching |
| Python | .py |
## ... |
Indentation |
C++ template functions (template<typename T> void func(...)) are fully supported — doxygen comments are associated via tree-sitter AST sibling detection, handling template_declaration wrappers correctly.
CLI Usage
# Pre-commit mode (default — called by pre-commit)
doxygen-guard [--config path] [files...]
# Explicit subcommands
doxygen-guard validate --no-git src/*.c
doxygen-guard trace --all src/
doxygen-guard trace --req REQ-001 src/
doxygen-guard impact --staged src/*.c
doxygen-guard coverage src/
# Verbose mode
doxygen-guard -v trace --all src/
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file doxygen_guard-1.2.5.tar.gz.
File metadata
- Download URL: doxygen_guard-1.2.5.tar.gz
- Upload date:
- Size: 915.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d56ebdc04a6f637bbca9a3194a89851fce23d627c78393e4e9d21312c2ee3dee
|
|
| MD5 |
02ef2bbd961ab229e8dfb10a7dd5cc8f
|
|
| BLAKE2b-256 |
c134243b1a7d25aa086fe54536bff1c9ba86b8e98a956bde3ded3c12ec89d71f
|
Provenance
The following attestation bundles were made for doxygen_guard-1.2.5.tar.gz:
Publisher:
release.yml on tvanfossen/doxygen-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
doxygen_guard-1.2.5.tar.gz -
Subject digest:
d56ebdc04a6f637bbca9a3194a89851fce23d627c78393e4e9d21312c2ee3dee - Sigstore transparency entry: 1285735302
- Sigstore integration time:
-
Permalink:
tvanfossen/doxygen-guard@57935262a7ffacc4d7ac7abea631bc9bb2fad7cc -
Branch / Tag:
refs/tags/v1.2.5 - Owner: https://github.com/tvanfossen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57935262a7ffacc4d7ac7abea631bc9bb2fad7cc -
Trigger Event:
push
-
Statement type:
File details
Details for the file doxygen_guard-1.2.5-py3-none-any.whl.
File metadata
- Download URL: doxygen_guard-1.2.5-py3-none-any.whl
- Upload date:
- Size: 59.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb874bc73e85c397c1e205a175f492506d6e667ffc179b70331af3cb62e8da77
|
|
| MD5 |
ce9f5617039c018c60c7fb4014f0ab66
|
|
| BLAKE2b-256 |
7599ab0d016c92520635fed6fbd0a8b7f109e7b27ce5788926afad004badcc8e
|
Provenance
The following attestation bundles were made for doxygen_guard-1.2.5-py3-none-any.whl:
Publisher:
release.yml on tvanfossen/doxygen-guard
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
doxygen_guard-1.2.5-py3-none-any.whl -
Subject digest:
eb874bc73e85c397c1e205a175f492506d6e667ffc179b70331af3cb62e8da77 - Sigstore transparency entry: 1285735361
- Sigstore integration time:
-
Permalink:
tvanfossen/doxygen-guard@57935262a7ffacc4d7ac7abea631bc9bb2fad7cc -
Branch / Tag:
refs/tags/v1.2.5 - Owner: https://github.com/tvanfossen
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@57935262a7ffacc4d7ac7abea631bc9bb2fad7cc -
Trigger Event:
push
-
Statement type: