Skip to main content

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"

impact:
  requirements:
    file: docs/requirements.csv
    id_column: "Req ID"
    name_column: "Name"
    format: csv

3. Add doxygen to your functions

/** @module 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), @version must be updated
  • Tag syntax — tag values validated against configured patterns
  • Requirement coverage — functions must have @req or an exemption tag

Sequence Diagrams (auto-generated)

Diagrams are generated from the AST — most behavioral tags are inferred, not manually written:

Tag Purpose Manual?
@req Requirement mapping Yes — per function
@module Participant identity Yes — once per file
@triggers State annotations Yes — per function
@return Return value documentation Yes — per function
@emit_source Infrastructure root (e.g., on Event_post) Yes — once
@handle_source Registration root (e.g., on Event_register) Yes — once
@emits Events emitted Inferred from AST
@handles Events handled Inferred from AST
@ext Cross-module 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.

Project-defined function calls are also shown in diagrams — any standalone call to a function defined in your scanned source files produces an edge, even without trace tags. Method calls (.get(), .strip()) are excluded via AST node type, not heuristics.

Diagram Features

  • Return types on arrows (derived from AST or @return tag override)
  • Control flow blocks (if/else, loops, try/catch, switch)
  • Error recovery notes in catch blocks (callee extraction)
  • Activation bars with auto-close at diagram end
  • Incremental generation — SHA-256 manifest skips unchanged diagrams (493x speedup)
  • Cross-REQ handler chain following with configurable depth

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
external list [] External participants with receives_prefix
external_fallback string External Default name for unresolved external sources

trace.options

Key Type Default Description
autonumber bool true Number sequence arrows
box_label string System Internal participant box label
min_edges int 1 Minimum behavioral edges to generate a diagram
show_returns bool true Show return arrows on ext calls
show_return_values bool true Label return arrows with type info
show_project_calls bool true Show calls to project-defined functions
show_recovery_notes bool true Show callee names in empty catch blocks
cross_req_depth int 1 Handler chain hops across REQ boundaries (-1=unlimited)
max_condition_length int 80 Truncation limit for alt/loop conditions
infer_emits bool true Infer @emits from emit function calls
infer_ext bool true Infer @ext from cross-module calls
event_emit_functions list [] Emit function names for generated code (escape hatch)
event_register_functions list [] Registration function names for generated code (escape hatch)
legend bool false Render arrow style legend
label_mode string full Label style: full, brief, label-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

External participants

Route unhandled events to named external actors by event prefix. Entries can be a name with config, or a plain string:

trace:
  external:
    - Cloud:
        receives_prefix: ["EVENT_CLOUD_"]
    - Hardware:
        receives_prefix: ["EVENT_HW_"]
    - "User Action"             # plain string, no prefix matching
  external_fallback: "External" # default name for unresolved sources

Events matching receives_prefix are routed to that participant. Events not matching any prefix use external_fallback.

File-Level Doxygen

Each source file should have a file-level doxygen block. Use @module to set the participant name for all functions in the file:

/**
 * @file
 * @brief Sensor hardware abstraction layer.
 * @version 1.0
 * @module Sensor Driver
 */

The @module tag overrides the requirements-file participant_field for participant resolution. Enable validate.presence.require_file_doxygen: true to enforce file-level blocks.

For Python:

## @file
## @brief Configuration loading and validation.
## @version 1.0
## @module 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
 * @emit_source
 */
void Event_post(uint64_t event, void *data) { ... }

/**
 * @brief Register a handler for events matching a bitmask.
 * @version 1.0
 * @req REQ-0050
 * @handle_source
 */
void Event_register(uint64_t mask, event_handler_fn handler) { ... }

With these in place, @emits and @handles 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_emit_functions and event_register_functions as config-level overrides:

trace:
  options:
    event_emit_functions: ["dm_event_callback"]
    event_register_functions: ["register_fsm_handler"]

These tell the tool to treat calls to these functions as emit/registration points, equivalent to @emit_source / @handle_source but without requiring tags on the generated source. Use this only for generated code you cannot annotate. For code you own, use @emit_source / @handle_source tags instead.

Both default to empty lists — @emit_source / @handle_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:

  1. Start with validation only — add @brief and @version to functions as you touch them. Use version_gate to only enforce @req on functions added after a specific version:
validate:
  version_gate:
    current_version: "auto:git"
    version_field: "Min Version"
  1. Add @module to each file — one line per file sets the participant name for diagrams.

  2. Add @return to non-void functions — required by default. Disable with presence.require_return: false during migration.

  3. Mark infrastructure roots — if you have an event bus or message dispatcher, add @emit_source/@handle_source once to enable automatic inference.

  4. Exclude paths you're not ready to cover:

validate:
  exclude:
    - "^vendor/"
    - "^legacy/"

The tool generates useful diagrams from day one — show_project_calls shows all project function calls even before any trace tags are added.

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

doxygen_guard-1.1.10.tar.gz (924.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

doxygen_guard-1.1.10-py3-none-any.whl (63.4 kB view details)

Uploaded Python 3

File details

Details for the file doxygen_guard-1.1.10.tar.gz.

File metadata

  • Download URL: doxygen_guard-1.1.10.tar.gz
  • Upload date:
  • Size: 924.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for doxygen_guard-1.1.10.tar.gz
Algorithm Hash digest
SHA256 271eec81f45c1706571665645f6e543240230783d0b032f639ebf6025d93f98f
MD5 6858d300f52ed02c7a5463786ffc4b55
BLAKE2b-256 29148c3fc037576ad3c4067d799479992fa0399b56baeaabbc75816de89a8585

See more details on using hashes here.

Provenance

The following attestation bundles were made for doxygen_guard-1.1.10.tar.gz:

Publisher: release.yml on tvanfossen/doxygen-guard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file doxygen_guard-1.1.10-py3-none-any.whl.

File metadata

  • Download URL: doxygen_guard-1.1.10-py3-none-any.whl
  • Upload date:
  • Size: 63.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for doxygen_guard-1.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 04c0c5320ffc8bca52ce000e2950cc41a7563c2e3fd4cde6b558a5522c3df428
MD5 95a2d934b383008f32c41f90987a5de7
BLAKE2b-256 baa3cf8d7c8988281aaeeb53fb2fc53c17b6441085319ec3f2b3feb0797988e5

See more details on using hashes here.

Provenance

The following attestation bundles were made for doxygen_guard-1.1.10-py3-none-any.whl:

Publisher: release.yml on tvanfossen/doxygen-guard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page