cpre
cpre analyzes Boolean conditions in C and C++ preprocessor conditional blocks without preprocessing or parsing the surrounding source code. It identifies dead and redundant branches, simplifies Boolean conditions using ROBDD-backed reasoning, and preserves value-bearing expressions as opaque Boolean predicates.
Project status: Beta. The CLI and documented Python API are suitable for downstream integration, while broader real-world use may still uncover modeling, compatibility, or performance edge cases before a 1.0 release.
Installation
python -m pip install cpre
For development:
git clone https://github.com/sahebbiswas/cpre.git
cd cpre
python -m pip install -e ".[dev]"
Documentation
- Python API integration guide — developer-focused guidance for embedding cpre in tools, linters, and scripts.
- SARIF output — SARIF 2.1.0 format, rule mapping, fixes, and code-scanning integration.
Command-line usage
Analyze a single source file:
cpre source.c
You can also invoke the package directly:
python -m cpre source.c
On Windows, the Python launcher works as well:
py -3 -m cpre source.c
Useful options include:
--recursive recursively scan C/C++ source files under directories
--json emit the structural conditional tree as JSON
--sarif emit findings as SARIF 2.1.0
--verbose include unchanged conditional branches in text/JSON reports
--fail-on-findings exit with status 1 when dead or redundant branches are found
--json and --sarif are mutually exclusive. JSON is the structural conditional-tree representation; SARIF is the interoperable findings format for static-analysis integrations.
By default, text and JSON reports are filtered to branches that are dead, redundant, or whose condition can be simplified. --verbose restores the full conditional tree.
Examples:
cpre --recursive path/to/project
cpre --recursive --json path/to/project
cpre --recursive --sarif path/to/project > cpre.sarif
cpre --fail-on-findings source.c
See SARIF output for rule IDs, fixes, failure notifications, and GitHub code-scanning usage.
Python API
cpre exposes a stable top-level API for programmatic consumers:
import cpre
try:
result = cpre.analyze_source(source_text, filename="example.c")
except cpre.CpreError as error:
print(error.code, error.location, error.message)
else:
if not result.complete:
for diagnostic in result.incomplete:
print("analysis incomplete:", diagnostic)
else:
for finding in result.findings:
print(finding.kind, finding.location, finding.reason)
Downstream tools should check result.complete before treating an empty findings tuple as a clean analysis. Bounded ROBDD exhaustion returns a structured incomplete result and never exposes findings derived from partial proofs.
The supported API includes structured findings, exact and contextual simplifications, source edits, macro assumptions, deterministic analysis limits, and stable error codes. See the Python API integration guide for the full integration contract and examples.
Consumers should import supported symbols from cpre rather than internal modules such as cpre.robdd, cpre.parser, or compatibility-facade internals.
Configuration-aware macro assumptions
The default analysis is symbolic and configuration-independent. Callers that know part of a translation unit's preprocessor state can provide assumptions:
result = cpre.analyze_source(
source_text,
assumptions={
"FEATURE_A": True,
"FEATURE_B": False,
},
)
Use MacroAssumptions when definedness and Boolean value must be represented separately:
assumptions = cpre.MacroAssumptions(
defined={"FEATURE_A", "FEATURE_ZERO"},
undefined={"FEATURE_B"},
values={"FEATURE_A": True, "FEATURE_ZERO": False},
)
result = cpre.analyze_source(source_text, assumptions=assumptions)
Unmentioned macros remain symbolic; cpre never silently treats unknown macros as false. Each finding records whether its proof materially depends on supplied assumptions.
Simplification guarantees
The public API distinguishes two simplification guarantees:
ExactSimplificationis Boolean-equivalent to the original condition under the active assumptions.ContextualSimplificationis equivalent only within the branch's effective reachable context.
When a direct replacement is safe to represent, Finding.edit contains a SuggestedEdit with a physical source range, replacement text, and confidence. Dead/redundant branch findings deliberately do not imply branch deletion.
Structured errors and bounded analysis
Malformed source and invalid API input use the public CpreError hierarchy with stable ErrorCode values, locations, messages, and optional filenames. Consumers should use these fields rather than parsing rendered error strings.
ROBDD reasoning is deterministically bounded by AnalysisOptions (max_atoms, max_bdd_nodes, and max_work). If a limit is exceeded, AnalysisResult.complete is false and AnalysisResult.incomplete explains the resource, configured limit, observed usage, and source location when available.
See Python API integration guide for recommended handling of errors, incomplete analysis, assumptions, and edits.
Analysis behavior
Without assumptions, cpre reasons symbolically about preprocessor conditions rather than evaluating one specific build configuration. Identifiers are treated as Boolean flags, while value-bearing expressions outside the Boolean model are preserved as opaque predicates.
The analyzer can identify:
- dead or unreachable conditional branches
- redundant conditions that are always true in their effective branch context
- exact Boolean simplifications
- context-dependent simplifications derived from enclosing and preceding branch conditions
- configuration-specific findings when explicit macro assumptions are supplied
The engine uses ROBDD-backed Boolean reasoning for exact satisfiability and equivalence checks while retaining a dependency-free runtime. ROBDD nodes and implementation details are intentionally private.
Testing
Install development dependencies and run the test suite:
python -m pip install -e ".[dev]"
pytest
The GitHub Actions CI matrix runs the suite across supported Python versions on Linux, macOS, and Windows.
Extending cpre
The implementation is split into focused internal modules for the data model, expression handling, ROBDD reasoning, directive parsing, analysis, reporting, SARIF rendering, and source discovery. cpre/api.py provides the stable programmatic boundary and cpre/cli.py owns terminal behavior.
When adding analyzer behavior, preserve structured public data, deterministic ordering, complete/incomplete semantics, and compatibility of top-level public imports. Prefer extending the public result model rather than requiring consumers to parse human-readable output.
Versioning
The package version is defined in cpre/__init__.py as __version__ and is consumed by pyproject.toml during builds.
0.7.0 marks cpre's transition from Alpha to Beta. During the Beta series, the documented top-level API is intended for real downstream integrations and compatibility-sensitive changes should be deliberate and documented. The path to 1.0 will emphasize downstream integration experience and validation against larger real-world C/C++ codebases.
Public API additions use a minor version bump, while backward-compatible fixes use patch releases. Changes that affect documented public behavior should update compatibility tests alongside the implementation.
License
Licensed under the Apache License, Version 2.0. See LICENSE.
Metadata
Release files for cpre 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cpre-0.7.0.tar.gz | 50.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cpre-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 89.5 kB
Release files / cpre-0.7.0.tar.gz
| Download URL | cpre-0.7.0.tar.gz |
|---|---|
| Size | 50.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6962be0c9252da46325093979f1ee0de2a4d8ab0b5307f3c54747cf96e46d46
|
|
BLAKE2b-256 checksum How to use checksums |
21b7c83ed608898046fc21444b6c0194d8cac096c131d135ff57713b270eb3ae
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / cpre-0.7.0-py3-none-any.whl
| Download URL | cpre-0.7.0-py3-none-any.whl |
|---|---|
| Size | 39.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3238dee1112e7a6ddda454866a8aa7de43363da9849da9f902c364d576103de9
|
|
BLAKE2b-256 checksum How to use checksums |
1f23f56c33d92c7b365e7b36eb961343d96aa7d6e6acc312eb77d0b43ac19a7f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency log