Skip to content

feat: use diff instead of clang-format XML output#347

Merged
2bndy5 merged 2 commits into
mainfrom
no-xml
Jun 10, 2026
Merged

feat: use diff instead of clang-format XML output#347
2bndy5 merged 2 commits into
mainfrom
no-xml

Conversation

@2bndy5

@2bndy5 2bndy5 commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

This avoids discrepancies with clang-format's XML output.; see cpp-linter/cpp-linter#168. Instead, we now save the clang-format fixed output to a project-specific cache folder. Then we take the diff between the formatted output and the original file's content. The resulting diff is how we determine the lines changed by clang-format.

This actually lays the ground work for other improvements, specifically how to assemble changes for an auto-fix commit.

Additionally, this also removes the dependency used for XML parsing.

Summary by CodeRabbit

  • New Features

    • Added local project caching with .cpp-linter-cache directory to store formatting patches.
  • Improvements

    • Refined formatting advice computation and result processing.
    • Enhanced error reporting with contextual information for cache and file operations.

@2bndy5 2bndy5 added the enhancement New feature or request label Jun 10, 2026
Comment on lines +356 to +364
let line_count = format_advice
.replacements
.iter()
.fold(0, |acc, r| acc + r.len());
let note = format!(
"- {} ({line_count} line{})\n",
file.name.to_string_lossy().replace('\\', "/"),
if line_count > 1 { "s" } else { "" }
);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yea, I forgot to mention the thread comment is augmented to display the number of lines changed by clang-format. For example:

  • src/demo/demo.cpp (4 lines)

@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.42857% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.85%. Comparing base (21b54d4) to head (646316c).

Files with missing lines Patch % Lines
cpp-linter/src/clang_tools/clang_format.rs 71.73% 13 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #347      +/-   ##
==========================================
- Coverage   92.89%   92.85%   -0.05%     
==========================================
  Files          22       22              
  Lines        3364     3316      -48     
==========================================
- Hits         3125     3079      -46     
+ Misses        239      237       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@2bndy5

2bndy5 commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator Author

The missing lines in coverage are all about error handling. These weren't covered before either. It is rather difficult to instigate errors from external binaries or the file system... and doesn't seem worth it.

@2bndy5 2bndy5 marked this pull request as ready for review June 10, 2026 09:34
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@2bndy5, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 3 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 967a2473-2ce9-4ccb-9615-b47fe95d2102

📥 Commits

Reviewing files that changed from the base of the PR and between 0e52758 and 646316c.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • cpp-linter/Cargo.toml
  • cpp-linter/src/clang_tools/clang_format.rs
  • cpp-linter/src/clang_tools/clang_tidy.rs
  • cpp-linter/src/cli/structs.rs
  • cpp-linter/src/common_fs/mod.rs
  • cpp-linter/src/error.rs
  • cpp-linter/src/rest_client/mod.rs
  • cpp-linter/src/run.rs

Walkthrough

This PR replaces XML-based clang-format output parsing with file-based diff computation and project-local caching. The FormatAdvice data model shifts from offset-keyed Replacement vectors to Range collections backed by cached patch files, eliminating the quick-xml dependency.

Changes

Clang-format diff-based output processing and caching

Layer / File(s) Summary
FormatAdvice contract and error types
cpp-linter/src/clang_tools/clang_format.rs, cpp-linter/src/error.rs
FormatAdvice struct refactored to use Vec<Range<u32>> replacements and PathBuf for cached patch files instead of Vec<Replacement> with offsets and optional in-memory bytes. Replacement struct removed. ClangCaptureError adds file I/O and cache error variants, removes XML parsing error.
Cache directory initialization in ClangParams and run_main
cpp-linter/src/cli/structs.rs, cpp-linter/src/run.rs
ClangParams gains project_cache_dir: PathBuf field initialized from repo_root/.cpp-linter-cache. run_main creates the cache directory structure with mkdir -p and writes a .gitignore file, using anyhow::Context for detailed error reporting on I/O failures.
Clang-format processing: cache file writing and diff extraction
cpp-linter/src/clang_tools/clang_format.rs, cpp-linter/Cargo.toml
run_clang_format writes clang-format stdout to a deterministic cache file under project_cache_dir/patches/<file>.format, then computes a histogram diff between original and cached patched contents. Diff hunks are filtered by requested line ranges to extract Vec<Range> replacements; patched field set to the cache file path. quick-xml dependency removed. Tests updated to remove XML parsing validation.
Consumer code updated for range-based FormatAdvice
cpp-linter/src/common_fs/mod.rs, cpp-linter/src/rest_client/mod.rs, cpp-linter/src/clang_tools/clang_tidy.rs
FileObj::make_suggestions_from_patch reads patched content from the PathBuf path via fs::read_to_string instead of decoding pre-provided Option<Vec>. RestClient annotation/comment generation iterates through replacement ranges to collect affected line indices and computes total line counts for reports. Test helpers construct FormatAdvice with range vectors and PathBuf::new().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes


Possibly related PRs

  • cpp-linter/cpp-linter-rs#101: Clang-format refactoring overlaps with main PR's replacement of XML deserialization and quick-xml removal in clang_format.rs.
  • cpp-linter/cpp-linter-rs#230: Both PRs modify FormatAdvice and how replacements are parsed/constructed from clang-format output.
  • cpp-linter/cpp-linter-rs#343: Main PR's error type changes are connected to this PR's switch to concrete ClangCaptureError-based error handling in clang tool execution.

Suggested labels

dependencies

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main architectural change across multiple files: replacing XML-based parsing of clang-format output with a diff-based approach.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch no-xml

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cpp-linter/src/clang_tools/clang_format.rs (1)

109-137: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't derive FormatAdvice from a failed clang-format invocation.

This path caches output.stdout and builds a diff even when output.status.success() is false. On a non-zero exit with empty or partial stdout, that turns a tool failure into bogus formatting advice instead of surfacing the failure cleanly. Bail out before the cache write/diff unless the subprocess exited successfully.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp-linter/src/clang_tools/clang_format.rs` around lines 109 - 137, Check
output.status.success() immediately after running clang-format and bail out on
non-zero exit before writing cache_format_fixes or computing diffs/FormatAdvice;
specifically, if !output.status.success() return an Err variant of
ClangCaptureError that surfaces the tool failure (include stderr and exit code)
so you do not proceed to fs::write(&cache_format_fixes, &output.stdout) or build
patched_contents/original_contents and derive FormatAdvice from potentially
empty/partial stdout.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp-linter/src/clang_tools/clang_format.rs`:
- Around line 141-159: The replacements vector currently stores hunk.before
verbatim, but pure-insertion hunks have an empty hunk.before which causes
downstream code (RestClient::make_annotations() and make_format_comment()) to
count zero affected lines; detect when hunk.before.is_empty() inside the
diff.hunks() filter_map and replace it with a one-line anchored range
immediately before the insertion point (e.g., let anchor_start =
hunk.before.start.saturating_sub(1); use a Range from anchor_start to
anchor_start + 1) before pushing into FormatAdvice.replacements so
insertion-only fixes report as affecting one line.

In `@cpp-linter/src/run.rs`:
- Around line 163-172: ClangParams::from(&cli) currently computes
project_cache_dir relative to the original repo_root before the code calls
set_current_dir(), causing double-prefix paths; fix by ensuring
project_cache_dir is resolved to an absolute path before changing directories or
by rebuilding/normalizing it after set_current_dir(): either call
std::env::set_current_dir(...) first and then recreate clang_params with
ClangParams::from(&cli), or canonicalize the existing
clang_params.project_cache_dir (e.g., std::fs::canonicalize or join with
std::env::current_dir()) so project_cache_dir is absolute before using it in
create_dir_all/write for project_cache_dir and .gitignore; update uses of
ClangParams::project_cache_dir accordingly.

---

Outside diff comments:
In `@cpp-linter/src/clang_tools/clang_format.rs`:
- Around line 109-137: Check output.status.success() immediately after running
clang-format and bail out on non-zero exit before writing cache_format_fixes or
computing diffs/FormatAdvice; specifically, if !output.status.success() return
an Err variant of ClangCaptureError that surfaces the tool failure (include
stderr and exit code) so you do not proceed to fs::write(&cache_format_fixes,
&output.stdout) or build patched_contents/original_contents and derive
FormatAdvice from potentially empty/partial stdout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01bf8d26-b860-4155-af83-0e0ab4078338

📥 Commits

Reviewing files that changed from the base of the PR and between 21b54d4 and 0e52758.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • cpp-linter/Cargo.toml
  • cpp-linter/src/clang_tools/clang_format.rs
  • cpp-linter/src/clang_tools/clang_tidy.rs
  • cpp-linter/src/cli/structs.rs
  • cpp-linter/src/common_fs/mod.rs
  • cpp-linter/src/error.rs
  • cpp-linter/src/rest_client/mod.rs
  • cpp-linter/src/run.rs
💤 Files with no reviewable changes (1)
  • cpp-linter/Cargo.toml

Comment thread cpp-linter/src/clang_tools/clang_format.rs
Comment thread cpp-linter/src/run.rs
2bndy5 added 2 commits June 10, 2026 03:18
This avoids discrepancies with clang-format's XML output.; see cpp-linter/cpp-linter#168. Instead, we now save the clang-format fixed output to a project-specific cache folder. Then we take the diff between the formatted output and the original file's content. The resulting diff is how we determine the lines changed by clang-format.

This actually lays the ground work for other improvements, specifically how to assemble changes for an auto-fix commit.

Additionally, this also removes the dependency used for XML parsing .
@2bndy5 2bndy5 merged commit 1089800 into main Jun 10, 2026
73 checks passed
@2bndy5 2bndy5 deleted the no-xml branch June 10, 2026 11:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant