Create New C++ Lint Rule
You are creating a new C++ lint rule for the FastLED codebase. The default home for the new rule is the Rust crate ci/lint_cpp_rs/; the legacy Python tier (ci/lint_cpp/) is reserved for AST ratchets and cross-file structural checks. Follow this workflow.
Input
$ARGUMENTS
Phase 1: Design the Rule
- Parse the rule description from the input
- Research the codebase: Grep for existing patterns, violations, and edge cases
- Choose detection strategy — prefer the simplest approach that works:
- Rust regex checker (default): Use when the pattern is a keyword, token, or textual pattern that can be reliably detected with word-boundary regex / per-line state. Examples: banning a keyword, detecting
std:: namespace usage, finding #pragma directives, flagging raw new/delete. The overwhelming majority of lint rules belong here. Fast, parallel, no libclang dependency.
- Python AST ratchet (libclang): Use only when the rule requires semantic understanding that regex cannot provide — e.g., type-aware checks, matching function signatures, detecting inheritance patterns, or analyzing template instantiations. AST parsing is heavy and slow; don't use it when a Rust regex checker suffices. See
ci/tools/check_noexcept.py for the canonical pattern.
- Define scope: Which directories should be checked (src/fl/, platforms/, examples/, tests/)
- Identify exemptions: Comments, macros, templates, third_party/, platform-guarded code that should be allowed
Output:
## Rule Design
**Rule**: [one-line rule statement]
**Detection**: rust-regex / python-ast
**Scope**: [directories]
**Exemptions**: [what should NOT be flagged]
**Suppression**: [comment pattern to suppress, e.g. "// nolint" or "// ok no X"]
Phase 2: Write the Checker + Tests
Default (Rust regex) path:
- Read reference checkers: Study 1-2 similar existing checkers in
ci/lint_cpp_rs/src/checkers/ (see ci/lint_cpp_rs/src/checkers/README.md for the policy-area grouping)
- Add a checker struct to the matching file under
ci/lint_cpp_rs/src/checkers/ (e.g. basic.rs, style.rs, preprocessor.rs). Implement FileContentChecker (trait in ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs):
name() returns the class-style name (e.g. "YourRuleChecker")
should_process_file(file_path, project_root) filters by extension + scope
check_file_content(file_content) returns Vec<(usize, String)> of (line_number, message)
- Add inline Rust tests to
ci/lint_cpp_rs/src/lint_core/tests.rs with:
- Tests for violations (should flag)
- Tests for correct code (should pass)
- Tests for exemptions (comments, macros, suppression marker)
- Tests for edge cases (multi-line, templates, nested scopes)
- Pre-compile any regex in
ci/lint_cpp_rs/src/lint_core/regexes.rs — never Regex::new inside the hot loop
- Build + run the Rust tests:
uv run python ci/lint_cpp/rust_binary_cache.py (rebuilds the cached binary, which runs the inline tests during cargo build)
Python AST ratchet path (only when libclang semantics are required):
- Add a new tool under
ci/tools/check_<rule>.py following the pattern in ci/tools/check_noexcept.py (translation unit + clang-query + baseline diff)
- Wire it into
ci/lint_cpp/run_all_checkers.py next to run_noexcept_ast_check / run_array_param_ast_check
- Add a checked-in baseline so the ratchet can only ratchet down
Phase 3: Run Across Codebase (Dry Run)
- Run the Rust binary directly against the tree:
uv run python ci/lint_cpp/rust_binary_cache.py then ./ci/lint_cpp_rs/target/debug/fastled-lint --checker your_rule
(or just bash lint --cpp and grep for your checker name)
- Count violations: Report how many files/lines are affected
- Sample review: Show 5-10 representative violations to verify correctness
- Check for false positives: If any look wrong, refine
should_process_file or the detection logic and re-run
Output:
## Dry Run Results
**Violations found**: [N] across [M] files
**Sample violations**:
- file.h:42: [violation text]
- file.cpp:100: [violation text]
**False positives**: [none / list of issues found and how they were fixed]
Phase 4: Register in Lint Pipeline
Four edits, all small:
ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs:
- Add the snake_case name to
supported_checker_names()
- Add the class-style name to
supported_python_checker_names()
- Add
("your_rule", Box::new(YourRuleChecker)) to the checkers vec in create_checkers()
ci/lint_cpp/rust_bridge.py:
- Add
"YourRuleChecker" to the RUST_SUPPORTED_CHECKERS frozenset
- Verify integration: Run
bash lint --cpp — ensure it runs without breaking other checks
- If violations are expected: Add suppression comments to known exceptions, or report them
Phase 5: Apply Fixes (Selective)
Only if the rule has a clear autofix pattern:
- Create fixer script (if needed) in
ci/tools/ for batch-applying fixes
- Apply to one file first: Verify the fix is correct
- Run tests:
bash test --cpp after each batch of fixes
- Apply incrementally: Fix one directory at a time, testing after each
- Do NOT auto-fix ambiguous cases — report them for manual review
If no autofix is appropriate: Report the violation list and let the user decide.
Phase 6: Summary
## New Lint Rule Created
**Rule**: [description]
**Checker**: ci/lint_cpp_rs/src/checkers/<file>.rs::YourRuleChecker
**Tests**: ci/lint_cpp_rs/src/lint_core/tests.rs (#[test] fn your_rule_*)
**Registration**:
- ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs (3 sites)
- ci/lint_cpp/rust_bridge.py (RUST_SUPPORTED_CHECKERS)
**Detection**: [rust-regex/python-ast]
**Violations**: [N] found, [M] fixed, [K] remaining
**Files modified**: [list]
**Suppression**: Use `// [suppression comment]` to suppress individual lines
Key Rules
- Default tier is Rust — only fall back to Python for AST ratchets or cross-file structural checks
- Test FIRST — never register a checker without passing inline
#[test] functions
- Dry run FIRST — never auto-fix without reviewing the violation list
- Incremental fixes — fix one directory at a time, test after each
- Stay in project root — never
cd to subdirectories
- Use
bash test --cpp and bash lint --cpp — never bare cargo, meson, or build commands
- Return violations from
check_file_content as Vec<(usize, String)> — no shared mutable state, dispatch is rayon-parallel
- Support suppression — always allow a comment marker (e.g.
// nolint) to suppress individual lines
- Handle Windows paths — call
normalize_path() before path comparisons
1---2name: new-cpp-lint3description: Create a new C++ lint rule, test it, run it across the codebase, and selectively apply fixes. Usage - /new-cpp-lint <rule description>4---56# Create New C++ Lint Rule78You are creating a new C++ lint rule for the FastLED codebase. The default home for the new rule is the Rust crate `ci/lint_cpp_rs/`; the legacy Python tier (`ci/lint_cpp/`) is reserved for AST ratchets and cross-file structural checks. Follow this workflow.910## Input1112$ARGUMENTS1314## Phase 1: Design the Rule15161. **Parse the rule description** from the input172. **Research the codebase**: Grep for existing patterns, violations, and edge cases183. **Choose detection strategy** — **prefer the simplest approach that works**:19 - **Rust regex checker (default)**: Use when the pattern is a **keyword, token, or textual pattern** that can be reliably detected with word-boundary regex / per-line state. Examples: banning a keyword, detecting `std::` namespace usage, finding `#pragma` directives, flagging raw `new`/`delete`. The overwhelming majority of lint rules belong here. Fast, parallel, no libclang dependency.20 - **Python AST ratchet (libclang)**: Use **only** when the rule requires **semantic understanding** that regex cannot provide — e.g., type-aware checks, matching function signatures, detecting inheritance patterns, or analyzing template instantiations. AST parsing is heavy and slow; don't use it when a Rust regex checker suffices. See `ci/tools/check_noexcept.py` for the canonical pattern.214. **Define scope**: Which directories should be checked (src/fl/, platforms/, examples/, tests/)225. **Identify exemptions**: Comments, macros, templates, third_party/, platform-guarded code that should be allowed2324Output:25```26## Rule Design2728**Rule**: [one-line rule statement]29**Detection**: rust-regex / python-ast30**Scope**: [directories]31**Exemptions**: [what should NOT be flagged]32**Suppression**: [comment pattern to suppress, e.g. "// nolint" or "// ok no X"]33```3435## Phase 2: Write the Checker + Tests3637**Default (Rust regex) path:**38391. **Read reference checkers**: Study 1-2 similar existing checkers in `ci/lint_cpp_rs/src/checkers/` (see `ci/lint_cpp_rs/src/checkers/README.md` for the policy-area grouping)402. **Add a checker struct** to the matching file under `ci/lint_cpp_rs/src/checkers/` (e.g. `basic.rs`, `style.rs`, `preprocessor.rs`). Implement `FileContentChecker` (trait in `ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs`):41 - `name()` returns the class-style name (e.g. `"YourRuleChecker"`)42 - `should_process_file(file_path, project_root)` filters by extension + scope43 - `check_file_content(file_content)` returns `Vec<(usize, String)>` of `(line_number, message)`443. **Add inline Rust tests** to `ci/lint_cpp_rs/src/lint_core/tests.rs` with:45 - Tests for violations (should flag)46 - Tests for correct code (should pass)47 - Tests for exemptions (comments, macros, suppression marker)48 - Tests for edge cases (multi-line, templates, nested scopes)494. **Pre-compile any regex** in `ci/lint_cpp_rs/src/lint_core/regexes.rs` — never `Regex::new` inside the hot loop505. **Build + run the Rust tests**: `uv run python ci/lint_cpp/rust_binary_cache.py` (rebuilds the cached binary, which runs the inline tests during cargo build)5152**Python AST ratchet path** (only when libclang semantics are required):53541. Add a new tool under `ci/tools/check_<rule>.py` following the pattern in `ci/tools/check_noexcept.py` (translation unit + clang-query + baseline diff)552. Wire it into `ci/lint_cpp/run_all_checkers.py` next to `run_noexcept_ast_check` / `run_array_param_ast_check`563. Add a checked-in baseline so the ratchet can only ratchet down5758## Phase 3: Run Across Codebase (Dry Run)59601. **Run the Rust binary directly against the tree**:61 `uv run python ci/lint_cpp/rust_binary_cache.py` then `./ci/lint_cpp_rs/target/debug/fastled-lint --checker your_rule`62 (or just `bash lint --cpp` and grep for your checker name)632. **Count violations**: Report how many files/lines are affected643. **Sample review**: Show 5-10 representative violations to verify correctness654. **Check for false positives**: If any look wrong, refine `should_process_file` or the detection logic and re-run6667Output:68```69## Dry Run Results7071**Violations found**: [N] across [M] files72**Sample violations**:73- file.h:42: [violation text]74- file.cpp:100: [violation text]75**False positives**: [none / list of issues found and how they were fixed]76```7778## Phase 4: Register in Lint Pipeline7980Four edits, all small:81821. **`ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs`**:83 - Add the snake_case name to `supported_checker_names()`84 - Add the class-style name to `supported_python_checker_names()`85 - Add `("your_rule", Box::new(YourRuleChecker))` to the `checkers` vec in `create_checkers()`862. **`ci/lint_cpp/rust_bridge.py`**:87 - Add `"YourRuleChecker"` to the `RUST_SUPPORTED_CHECKERS` frozenset883. **Verify integration**: Run `bash lint --cpp` — ensure it runs without breaking other checks894. **If violations are expected**: Add suppression comments to known exceptions, or report them9091## Phase 5: Apply Fixes (Selective)9293**Only if the rule has a clear autofix pattern:**94951. **Create fixer script** (if needed) in `ci/tools/` for batch-applying fixes962. **Apply to one file first**: Verify the fix is correct973. **Run tests**: `bash test --cpp` after each batch of fixes984. **Apply incrementally**: Fix one directory at a time, testing after each995. **Do NOT auto-fix ambiguous cases** — report them for manual review100101**If no autofix is appropriate:** Report the violation list and let the user decide.102103## Phase 6: Summary104105```106## New Lint Rule Created107108**Rule**: [description]109**Checker**: ci/lint_cpp_rs/src/checkers/<file>.rs::YourRuleChecker110**Tests**: ci/lint_cpp_rs/src/lint_core/tests.rs (#[test] fn your_rule_*)111**Registration**:112 - ci/lint_cpp_rs/src/lint_core/processor_registry_cli.rs (3 sites)113 - ci/lint_cpp/rust_bridge.py (RUST_SUPPORTED_CHECKERS)114**Detection**: [rust-regex/python-ast]115116**Violations**: [N] found, [M] fixed, [K] remaining117**Files modified**: [list]118119**Suppression**: Use `// [suppression comment]` to suppress individual lines120```121122## Key Rules123124- **Default tier is Rust** — only fall back to Python for AST ratchets or cross-file structural checks125- **Test FIRST** — never register a checker without passing inline `#[test]` functions126- **Dry run FIRST** — never auto-fix without reviewing the violation list127- **Incremental fixes** — fix one directory at a time, test after each128- **Stay in project root** — never `cd` to subdirectories129- **Use `bash test --cpp`** and `bash lint --cpp` — never bare `cargo`, `meson`, or build commands130- **Return violations from `check_file_content`** as `Vec<(usize, String)>` — no shared mutable state, dispatch is `rayon`-parallel131- **Support suppression** — always allow a comment marker (e.g. `// nolint`) to suppress individual lines132- **Handle Windows paths** — call `normalize_path()` before path comparisons