A Claude Code plugin for reviewing CPython C extensions -- finding API misuse, memory safety bugs, compatibility issues, and correctness problems specific to code that consumes the Python/C API.
Built for the specific concerns of C extension authors -- reference counting from the caller's perspective, borrowed reference lifetimes, module state management, type slot correctness, stable ABI compliance, and version compatibility -- not general-purpose C analysis.
| Concern | CPython internals (cpython-review-toolkit) | C extensions (this toolkit) |
|---|---|---|
| Perspective | Code that implements the C API | Code that calls the C API |
| Parsing | Regex (PEP 7 code is regular) | Tree-sitter (extension code varies wildly) |
| Top bug class | Refcount leaks in runtime code | Borrowed refs held across callbacks |
| Module state | N/A (CPython manages its own) | Core concern -- init style, global state |
| Type definitions | Part of the runtime | Must follow slot contracts correctly |
| ABI | Defines the ABI | Must comply with the ABI |
| Dependencies | stdlib only | tree-sitter, tree-sitter-c |
claude plugin marketplace add devdanzin/cext-review-toolkit
claude plugin install cext-review-toolkit@cext-review-toolkitclaude plugin install cext-review-toolkit --source github:devdanzin/cext-review-toolkit --path plugins/cext-review-toolkitgit clone https://github.com/devdanzin/cext-review-toolkit.git
claude --plugin-dir cext-review-toolkit/plugins/cext-review-toolkit- Claude Code installed and running
- Python 3.10+ for the analysis scripts
- tree-sitter and tree-sitter-c:
pip install tree-sitter tree-sitter-c
Navigate to a C extension project, then:
/cext-review-toolkit:health # Quick health dashboard
/cext-review-toolkit:hotspots # Refcount leaks + error bugs + complexity
/cext-review-toolkit:explore # Full exploration (all 10 agents)
/cext-review-toolkit:migrate # Modernization checklistStart with health for a quick overview, then hotspots to find the highest-impact bugs.
| Agent | What It Finds | Script |
|---|---|---|
| refcount-auditor | Leaked refs, borrowed-ref-across-callback, stolen-ref misuse, missing Py_CLEAR | scan_refcounts.py |
| error-path-analyzer | Missing NULL checks, exception clobbering, return-without-exception | scan_error_paths.py |
| null-safety-scanner | Unchecked allocations, deref-before-check | scan_null_checks.py |
| gil-discipline-checker | GIL released during Python API, blocking I/O with GIL, callback GIL issues, free-threading readiness | scan_gil_usage.py |
| Agent | What It Finds | Script |
|---|---|---|
| module-state-checker | Legacy single-phase init, global PyObject* state, missing m_traverse/m_clear, static types | scan_module_state.py |
| type-slot-checker | Missing tp_free, traverse gaps, wrong Py_NotImplemented handling, heap type issues | scan_type_slots.py |
| Agent | What It Finds |
|---|---|
| stable-abi-checker | Internal struct access, private API calls, limited API violations |
| version-compat-scanner | API calls without version guards, dead compatibility code, deprecated APIs |
| Agent | What It Finds | Script |
|---|---|---|
| c-complexity-analyzer | Functions scored by complexity, nesting, line count | measure_c_complexity.py |
| git-history-analyzer | Similar bugs elsewhere, churn-based risk prioritization | analyze_history.py |
| Command | Purpose | Agents Used |
|---|---|---|
explore |
Full analysis with selectable aspects | All (configurable) |
health |
Quick scored dashboard | All in summary mode |
hotspots |
Find worst functions to fix first | refcount + errors + complexity |
migrate |
Modernization checklist | module-state + type-slots + abi + compat |
Unlike cpython-review-toolkit (regex-based), this toolkit uses Tree-sitter for C parsing. This enables analysis that regex fundamentally cannot do:
- Borrowed reference lifetime tracking: Detect when a borrowed ref survives across a call back into Python -- the #1 extension-specific bug pattern
- Type slot cross-referencing: Connect a PyTypeObject/PyType_Spec to its struct definition and slot function implementations
- Accurate scope analysis: Distinguish file-scope static variables from local ones, track variable assignments within functions
The toolkit auto-detects C extensions in diverse project layouts:
setup.pywithext_modulespyproject.tomlwith setuptools, meson-python, or scikit-buildmeson.buildwithpy.extension_module()CMakeLists.txtwith pybind11 or Python3 library targets- Fallback: any
.cfile containing#include <Python.h>
Every finding is tagged:
| Tag | Meaning | Example |
|---|---|---|
| FIX | Bug causing crashes, leaks, or wrong behavior | Borrowed ref across callback, missing DECREF |
| CONSIDER | Likely improvement, may have migration cost | Single-phase init, missing Py_TPFLAGS_BASETYPE |
| POLICY | Design decision for the maintainer | Whether to adopt stable ABI, drop old Python versions |
| ACCEPTABLE | Noted but no action needed | Intentional global state for singleton module |
If available, the toolkit can use:
- clang-tidy with
compile_commands.jsonfor deeper data-flow analysis - cppcheck for buffer overflow and uninitialized variable detection
These are never required -- the Tree-sitter-based analysis is the baseline.
1. /cext-review-toolkit:health -> Quick overview
2. /cext-review-toolkit:hotspots -> Where are the bugs?
3. /cext-review-toolkit:explore . refcounts errors deep -> Deep dive on safety
1. /cext-review-toolkit:explore . compat abi -> What needs to change?
2. /cext-review-toolkit:migrate -> Full migration checklist
1. /cext-review-toolkit:migrate -> What to modernize
2. /cext-review-toolkit:explore . module-state type-slots deep -> Detailed guidance
1. /cext-review-toolkit:explore . all deep -> Full analysis
2. Focus on FIX findings
3. Re-run on specific files after fixes
| Phase | Agents | Purpose |
|---|---|---|
| 0 | Extension discovery | Detect layout, source files, Python targets |
| 1 | (git history noted) | Temporal context available for Phase 2F |
| 2A | refcount-auditor, error-path-analyzer | Safety-critical (highest value) |
| 2B | null-safety-scanner, gil-discipline-checker | Memory safety |
| 2C | module-state-checker, type-slot-checker | Extension correctness |
| 2D | stable-abi-checker, version-compat-scanner | Compatibility |
| 2E | c-complexity-analyzer | Code quality |
| 2F | git-history-analyzer | Similar bugs, risk prioritization |
| 3 | Synthesis | Deduplicate, resolve conflicts, produce summary |
- Tree-sitter, not a compiler: Cannot resolve macros, follow pointer aliasing, or track through complex preprocessor conditionals. Reports candidates with expected 20-40% false positive rate; agents confirm or dismiss each finding.
- Single-file scope for scripts: Scripts analyze each function independently. Cross-function reference ownership transfer is tracked only at the API boundary level.
- External tools are optional: Without
compile_commands.json, no clang-tidy or cppcheck integration. The Tree-sitter analysis is comprehensive but has inherent limits. - Struct-to-type matching is heuristic: Connecting a PyTypeObject to its backing struct uses name matching and
tp_basicsizeanalysis. Unusual naming or indirection may cause mismatches.
| Dimension | code-review-toolkit | cpython-review-toolkit | cext-review-toolkit |
|---|---|---|---|
| Language | Python | C (CPython source) | C (extensions) |
| Parsing | Python ast |
Regex | Tree-sitter |
| Target | Python projects | CPython runtime | C extensions |
| Agents | 14 | 10 | 10 |
| Scripts | 8 | 7 | 10 |
| Unique value | Test coverage, architecture | GIL, PEP 7, include graph | Module state, type slots, ABI, migrate |
Danzin
MIT -- see LICENSE for details.