|
| 1 | +# cpp-linter-hooks AI Coding Guide |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +Pre-commit hooks wrapper that auto-installs and runs clang-format and clang-tidy from Python wheels. Supports Python 3.9-3.14 across Windows, Linux, and macOS. |
| 5 | + |
| 6 | +## Architecture |
| 7 | + |
| 8 | +### Entry Points & Flow |
| 9 | +- **Entry scripts**: `clang-format-hook` and `clang-tidy-hook` (defined in `pyproject.toml`) |
| 10 | +- **Hook definitions**: `.pre-commit-hooks.yaml` configures both hooks with `types_or: [c++, c]` |
| 11 | +- **Execution pattern**: Parse args → resolve/install tool version → subprocess.run → return (exit_code, output) |
| 12 | + |
| 13 | +### Core Modules |
| 14 | +- **`clang_format.py`**: Wraps clang-format with `-i` (in-place), supports `--verbose` and `--dry-run` modes |
| 15 | + - Returns `-1` for dry-run to distinguish from actual failures |
| 16 | +- **`clang_tidy.py`**: Wraps clang-tidy, forces exit code 1 if "warning:" or "error:" in output |
| 17 | +- **`util.py`**: Version resolution and pip-based tool installation |
| 18 | + - `_resolve_version()`: Supports partial matches (e.g., "20" → "20.1.7") |
| 19 | + - `DEFAULT_CLANG_FORMAT_VERSION` and `DEFAULT_CLANG_TIDY_VERSION` read from `pyproject.toml` |
| 20 | +- **`versions.py`**: Auto-generated by `scripts/update_versions.py` (runs weekly via GitHub Actions) |
| 21 | + |
| 22 | +### Version Management Pattern |
| 23 | +```python |
| 24 | +# Users can specify partial versions |
| 25 | +--version=21 # Resolves to latest 21.x.x |
| 26 | +--version=21.1 # Resolves to latest 21.1.x |
| 27 | +--version=21.1.8 # Exact version |
| 28 | +``` |
| 29 | + |
| 30 | +## Development Workflows |
| 31 | + |
| 32 | +### Local Testing |
| 33 | +```bash |
| 34 | +# Test hooks locally without installing |
| 35 | +pre-commit try-repo ./.. clang-format --verbose --all-files |
| 36 | +pre-commit try-repo ./.. clang-tidy --verbose --all-files |
| 37 | + |
| 38 | +# Run test suite |
| 39 | +uv run pytest -vv # All tests |
| 40 | +uv run coverage run -m pytest # With coverage |
| 41 | +uv run pytest -m benchmark # Performance tests only |
| 42 | +``` |
| 43 | + |
| 44 | +### Adding/Modifying Features |
| 45 | +1. **Update hook logic** in `cpp_linter_hooks/{clang_format,clang_tidy}.py` |
| 46 | +2. **Add tests** in `tests/test_*.py` with `@pytest.mark.benchmark` for performance tracking |
| 47 | +3. **Test with sample files** in `testing/` directory (use `good.c` as expected output) |
| 48 | +4. **Update README.md** if user-facing behavior changes |
| 49 | + |
| 50 | +### Dependency Management |
| 51 | +- **Uses `uv`** for all dev operations (not pip directly) |
| 52 | +- **Pin versions**: Default tool versions in `pyproject.toml` dependencies section |
| 53 | +- **Update versions**: Run `python scripts/update_versions.py` (auto-runs weekly on Monday 2 AM UTC) |
| 54 | + |
| 55 | +## Project-Specific Conventions |
| 56 | + |
| 57 | +### Return Value Pattern |
| 58 | +All hook functions return `Tuple[int, str]`: |
| 59 | +- `(0, "")` → Success |
| 60 | +- `(1, output)` → Failure (print output) |
| 61 | +- `(-1, output)` → Dry-run mode (clang-format only, convert to success in main) |
| 62 | + |
| 63 | +### Testing Conventions |
| 64 | +- Use `tmp_path` fixture to avoid modifying repo files |
| 65 | +- Parametrize version tests: `@pytest.mark.parametrize` with versions 16-21 |
| 66 | +- Mark performance-sensitive tests with `@pytest.mark.benchmark` |
| 67 | +- Compare formatted output against `testing/good.c` for correctness |
| 68 | + |
| 69 | +### Argument Handling |
| 70 | +```python |
| 71 | +# Standard pattern in both hooks |
| 72 | +parser = ArgumentParser() |
| 73 | +parser.add_argument("--version", default=DEFAULT_VERSION) |
| 74 | +hook_args, other_args = parser.parse_known_args(args) |
| 75 | +# ... install tool if needed ... |
| 76 | +command = ["tool-name"] + other_args # Pass through unknown args |
| 77 | +``` |
| 78 | + |
| 79 | +## Critical Files |
| 80 | + |
| 81 | +- **`pyproject.toml`**: Defines entry points, dependencies, default versions |
| 82 | +- **`versions.py`**: Auto-updated; DO NOT edit manually (see comment) |
| 83 | +- **`.pre-commit-hooks.yaml`**: Hook metadata for pre-commit framework |
| 84 | +- **`testing/run.sh`**: Integration test script used in CI |
| 85 | + |
| 86 | +## Integration Points |
| 87 | + |
| 88 | +### PyPI Dependencies |
| 89 | +- Fetches available versions from `https://pypi.org/pypi/{package}/json` |
| 90 | +- Filters out pre-release versions using regex pattern `(alpha|beta|rc|dev|a\d+|b\d+)` |
| 91 | +- Installs via `subprocess.run([sys.executable, "-m", "pip", "install", f"{tool}=={version}"])` |
| 92 | + |
| 93 | +### Pre-commit Framework |
| 94 | +- Hooks run in parallel (`require_serial: false`) for performance |
| 95 | +- File type filtering via `types_or: [c++, c]` |
| 96 | +- Users configure via `.pre-commit-config.yaml` with `args:` list |
| 97 | + |
| 98 | +## Common Tasks |
| 99 | + |
| 100 | +**Add support for a new argument:** |
| 101 | +1. Add to ArgumentParser in hook module |
| 102 | +2. Pass to subprocess command or handle in Python |
| 103 | +3. Add test case in `tests/test_*.py` |
| 104 | + |
| 105 | +**Update default tool versions:** |
| 106 | +1. Edit `dependencies` in `pyproject.toml` |
| 107 | +2. Run tests to ensure compatibility |
| 108 | +3. Update version in README examples |
| 109 | + |
| 110 | +**Debug hook failures:** |
| 111 | +- Add `--verbose` to clang-format args for detailed output |
| 112 | +- Check `testing/run.sh` for integration test patterns |
| 113 | +- Use `pre-commit run --verbose` for detailed pre-commit logs |
0 commit comments