Skip to content

feat: add support for compilation databases in clang-tidy#201

Merged
shenxianpeng merged 5 commits intomainfrom
fix-197
Mar 31, 2026
Merged

feat: add support for compilation databases in clang-tidy#201
shenxianpeng merged 5 commits intomainfrom
fix-197

Conversation

@shenxianpeng
Copy link
Copy Markdown
Member

@shenxianpeng shenxianpeng commented Mar 31, 2026

closes #197

Summary by CodeRabbit

Release Notes

  • New Features

    • Added compilation database support for clang-tidy with automatic detection of compile_commands.json in common build directories
    • New --compile-commands CLI option to explicitly specify the database directory
    • New --no-compile-commands flag to disable automatic detection
    • Added -v/--verbose option for detailed logging output
  • Documentation

    • Updated README with compilation database configuration guide, usage examples, and FAQ comparison table

@github-actions github-actions bot added the documentation Improvements or additions to documentation label Mar 31, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.59%. Comparing base (b29757a) to head (8a0b41a).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #201      +/-   ##
==========================================
+ Coverage   95.72%   96.59%   +0.87%     
==========================================
  Files           4        4              
  Lines         117      147      +30     
==========================================
+ Hits          112      142      +30     
  Misses          5        5              

☔ View full report in Codecov by Sentry.
📢 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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Warning

Rate limit exceeded

@shenxianpeng has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 31 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 18 minutes and 31 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f26032bf-14cf-43d7-a5e4-74395615ee6d

📥 Commits

Reviewing files that changed from the base of the PR and between f018506 and 8a0b41a.

📒 Files selected for processing (3)
  • .pre-commit-config.yaml
  • cpp_linter_hooks/clang_tidy.py
  • tests/test_clang_tidy.py

Walkthrough

Added clang-tidy compilation database support with auto-detection of compile_commands.json in common build directories, new CLI flags (--compile-commands, --no-compile-commands), logic to inject -p <path> into clang-tidy invocation with proper precedence handling, comprehensive unit tests, and updated documentation with configuration examples.

Changes

Cohort / File(s) Summary
Documentation
README.md
Added section describing compilation database auto-detection and configuration, updated "Verbose Output" guidance, extended FAQ comparison table, and updated table of contents.
Implementation
cpp_linter_hooks/clang_tidy.py
Added _find_compile_commands() function for auto-detection in common build directories, introduced --compile-commands and --no-compile-commands CLI flags with path validation, implemented argument precedence logic (user -p overrides --compile-commands with warning), and integrated -p injection into clang-tidy invocation.
Tests
tests/test_clang_tidy.py
Added comprehensive unit tests with mocked subprocess and dependency resolution, covering explicit configuration, auto-detection with fallbacks, precedence handling, invalid paths, and warning scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 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 change: adding support for compilation databases in clang-tidy, which is the primary focus across all modified files (README documentation, clang_tidy.py implementation, and comprehensive tests).

✏️ 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 fix-197

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
tests/test_clang_tidy.py (1)

29-35: ⚠️ Potential issue | 🟡 Minor

Test does not use tmp_path fixture correctly.

The test writes to testing/main.c instead of copying the file to tmp_path. This violates the coding guideline and modifies repository files during tests.

🛠️ Proposed fix
-def test_run_clang_tidy_valid(args, expected_retval):
-    # copy test file to tmp_path to prevent modifying repo data
-    test_file = Path("testing/main.c")
-    test_file.write_bytes(Path("testing/main.c").read_bytes())
+def test_run_clang_tidy_valid(args, expected_retval, tmp_path):
+    # copy test file to tmp_path to prevent modifying repo data
+    test_file = tmp_path / "main.c"
+    test_file.write_bytes(Path("testing/main.c").read_bytes())
     ret, output = run_clang_tidy(args + [str(test_file)])

As per coding guidelines: "Use tmp_path fixture in tests to avoid modifying repository files".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_clang_tidy.py` around lines 29 - 35, The test
test_run_clang_tidy_valid modifies the repo by writing to testing/main.c instead
of using the tmp_path fixture; fix it by copying the source file into tmp_path
and pointing test_file at that copy before calling run_clang_tidy (use tmp_path
/ "main.c" and copy bytes from Path("testing/main.c") into it), then call ret,
output = run_clang_tidy(args + [str(test_file)]) and assert as before; update
any variable names (test_file) accordingly so the test no longer touches
repository files.
🧹 Nitpick comments (4)
tests/test_clang_tidy.py (2)

64-68: Remove unused _patch() helper function.

This helper is defined but never called—tests inline the patching directly. Remove to avoid dead code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_clang_tidy.py` around lines 64 - 68, Remove the unused helper
function _patch(): delete the entire def _patch() block (including its return
tuple of patch(...) calls) from tests/test_clang_tidy.py since it's never
invoked; ensure no remaining references to _patch() exist and run tests to
confirm nothing else relies on it.

61-61: Shared mutable MagicMock may cause test interference.

_MOCK_RUN is a module-level mutable object. If any test mutates attributes on it, subsequent tests could be affected. Consider creating a fresh mock per test or using spec to prevent attribute mutation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_clang_tidy.py` at line 61, _MOCK_RUN is defined as a shared
module-level MagicMock which can be mutated by tests and cause cross-test
interference; replace it with a factory or fixture that returns a fresh
MagicMock(returncode=0, stdout="", stderr="") for each test (or create the mock
with spec/autospec if you need to prevent setting arbitrary attributes). Update
tests that currently reference _MOCK_RUN to call the factory/fixture (or to use
the per-test mock) and ensure any patching uses the new per-test instance so no
state is shared across tests.
cpp_linter_hooks/clang_tidy.py (2)

55-55: Use list unpacking syntax.

Per Ruff RUF005, prefer unpacking over concatenation for readability.

-        other_args = ["-p", compile_db_path] + other_args
+        other_args = ["-p", compile_db_path, *other_args]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp_linter_hooks/clang_tidy.py` at line 55, Replace the list concatenation
that prepends the compile DB args to other_args by constructing a new list that
starts with the "-p" marker and compile_db_path and then unpacks the existing
other_args into it (i.e., use Python list unpacking with * to include the
current other_args). Update the assignment to other_args so it uses this
unpacking approach instead of using the + operator; refer to the other_args
variable and compile_db_path in clang_tidy.py when making the change.

25-56: Refactor to reduce cognitive complexity.

SonarCloud flags this function at 23 complexity vs. 15 allowed. Consider extracting the compile-commands resolution logic into a helper function.

♻️ Suggested refactor
+def _resolve_compile_db(hook_args, other_args) -> Tuple[Optional[str], Optional[Tuple[int, str]]]:
+    """Resolve compile database path. Returns (path, error) where error is a return tuple if invalid."""
+    has_p = any(a == "-p" or a.startswith("-p=") for a in other_args)
+    
+    if hook_args.no_compile_commands:
+        return None, None
+    
+    if hook_args.compile_commands:
+        if has_p:
+            print("Warning: --compile-commands ignored; -p already in args", file=sys.stderr)
+            return None, None
+        p = Path(hook_args.compile_commands)
+        if not p.is_dir() or not (p / "compile_commands.json").exists():
+            return None, (1, f"--compile-commands: no compile_commands.json in '{hook_args.compile_commands}'")
+        return hook_args.compile_commands, None
+    
+    if not has_p:
+        return _find_compile_commands(), None
+    return None, None
+
+
 def run_clang_tidy(args=None) -> Tuple[int, str]:
     hook_args, other_args = parser.parse_known_args(args)
     if hook_args.version:
         resolve_install("clang-tidy", hook_args.version)
 
-    # Covers both "-p ./build" (two tokens) and "-p=./build" (one token)
-    has_p = any(a == "-p" or a.startswith("-p=") for a in other_args)
-
-    compile_db_path = None
-    if not hook_args.no_compile_commands:
-        if hook_args.compile_commands:
-            if has_p:
-                print(
-                    "Warning: --compile-commands ignored; -p already in args",
-                    file=sys.stderr,
-                )
-            else:
-                p = Path(hook_args.compile_commands)
-                if not p.is_dir() or not (p / "compile_commands.json").exists():
-                    return 1, (
-                        f"--compile-commands: no compile_commands.json"
-                        f" in '{hook_args.compile_commands}'"
-                    )
-                compile_db_path = hook_args.compile_commands
-        elif not has_p:
-            compile_db_path = _find_compile_commands()
+    compile_db_path, error = _resolve_compile_db(hook_args, other_args)
+    if error:
+        return error
 
     if compile_db_path:
         if hook_args.verbose:
             print(f"Using compile_commands.json from: {compile_db_path}", file=sys.stderr)
-        other_args = ["-p", compile_db_path] + other_args
+        other_args = ["-p", compile_db_path, *other_args]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp_linter_hooks/clang_tidy.py` around lines 25 - 56, The run_clang_tidy
function has high cognitive complexity due to inline compile-commands
resolution; extract that logic into a new helper function (e.g.,
resolve_compile_db_path(hook_args, other_args)) that returns (compile_db_path,
warning_message_or_none, error_tuple_or_none). Move all checks around
hook_args.no_compile_commands, hook_args.compile_commands, has_p detection,
validation of compile_commands dir and _find_compile_commands call into that
helper, and have run_clang_tidy call it to receive compile_db_path and handle
printing warnings/errors and prepending ["-p", compile_db_path] to other_args;
keep references to parser, hook_args, other_args, _find_compile_commands and
preserve current stderr prints and return behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cpp_linter_hooks/clang_tidy.py`:
- Around line 1-5: CI failed because files (including
cpp_linter_hooks/clang_tidy.py) were reformatted by ruff-format; run the ruff
formatter locally across the repo (e.g., format the changed files or entire
project), stage the resulting changes, and commit them so the formatted version
of clang_tidy.py and any other modified files are included in the PR.

---

Outside diff comments:
In `@tests/test_clang_tidy.py`:
- Around line 29-35: The test test_run_clang_tidy_valid modifies the repo by
writing to testing/main.c instead of using the tmp_path fixture; fix it by
copying the source file into tmp_path and pointing test_file at that copy before
calling run_clang_tidy (use tmp_path / "main.c" and copy bytes from
Path("testing/main.c") into it), then call ret, output = run_clang_tidy(args +
[str(test_file)]) and assert as before; update any variable names (test_file)
accordingly so the test no longer touches repository files.

---

Nitpick comments:
In `@cpp_linter_hooks/clang_tidy.py`:
- Line 55: Replace the list concatenation that prepends the compile DB args to
other_args by constructing a new list that starts with the "-p" marker and
compile_db_path and then unpacks the existing other_args into it (i.e., use
Python list unpacking with * to include the current other_args). Update the
assignment to other_args so it uses this unpacking approach instead of using the
+ operator; refer to the other_args variable and compile_db_path in
clang_tidy.py when making the change.
- Around line 25-56: The run_clang_tidy function has high cognitive complexity
due to inline compile-commands resolution; extract that logic into a new helper
function (e.g., resolve_compile_db_path(hook_args, other_args)) that returns
(compile_db_path, warning_message_or_none, error_tuple_or_none). Move all checks
around hook_args.no_compile_commands, hook_args.compile_commands, has_p
detection, validation of compile_commands dir and _find_compile_commands call
into that helper, and have run_clang_tidy call it to receive compile_db_path and
handle printing warnings/errors and prepending ["-p", compile_db_path] to
other_args; keep references to parser, hook_args, other_args,
_find_compile_commands and preserve current stderr prints and return behavior.

In `@tests/test_clang_tidy.py`:
- Around line 64-68: Remove the unused helper function _patch(): delete the
entire def _patch() block (including its return tuple of patch(...) calls) from
tests/test_clang_tidy.py since it's never invoked; ensure no remaining
references to _patch() exist and run tests to confirm nothing else relies on it.
- Line 61: _MOCK_RUN is defined as a shared module-level MagicMock which can be
mutated by tests and cause cross-test interference; replace it with a factory or
fixture that returns a fresh MagicMock(returncode=0, stdout="", stderr="") for
each test (or create the mock with spec/autospec if you need to prevent setting
arbitrary attributes). Update tests that currently reference _MOCK_RUN to call
the factory/fixture (or to use the per-test mock) and ensure any patching uses
the new per-test instance so no state is shared across tests.
🪄 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: bd04d263-46bc-4e95-ab09-17843b968303

📥 Commits

Reviewing files that changed from the base of the PR and between b29757a and f018506.

📒 Files selected for processing (3)
  • README.md
  • cpp_linter_hooks/clang_tidy.py
  • tests/test_clang_tidy.py

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Mar 31, 2026

Merging this PR will degrade performance by 16.55%

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

❌ 14 regressed benchmarks
✅ 49 untouched benchmarks
⏩ 13 skipped benchmarks1

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_run_clang_tidy_valid[args2-1] 1.1 ms 1.2 ms -13.48%
test_run_clang_tidy_valid[args5-1] 890.7 µs 1,052.1 µs -15.34%
test_run_clang_tidy_valid[args6-1] 892.6 µs 1,060.5 µs -15.84%
test_run_clang_tidy_valid[args4-1] 1.1 ms 1.2 ms -13.29%
test_run_clang_tidy_invalid[args5-1] 837.8 µs 1,002.9 µs -16.47%
test_run_clang_tidy_invalid[args0-1] 876.7 µs 1,031.1 µs -14.98%
test_run_clang_tidy_invalid[args6-1] 847.9 µs 1,012.4 µs -16.24%
test_run_clang_tidy_valid[args0-1] 872.7 µs 1,042.6 µs -16.3%
test_run_clang_tidy_invalid[args1-1] 998.6 µs 1,167.2 µs -14.44%
test_run_clang_tidy_invalid[args4-1] 1 ms 1.2 ms -14.18%
test_run_clang_tidy_invalid[args2-1] 995.6 µs 1,163.6 µs -14.44%
test_run_clang_tidy_valid[args1-1] 1.1 ms 1.2 ms -13.74%
test_run_clang_tidy_valid[args3-1] 885.1 µs 1,053.1 µs -15.95%
test_run_clang_tidy_invalid[args3-1] 842.7 µs 1,009.8 µs -16.55%

Comparing fix-197 (8a0b41a) with main (451e2e0)2

Open in CodSpeed

Footnotes

  1. 13 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (b29757a) during the generation of this report, so 451e2e0 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@sonarqubecloud
Copy link
Copy Markdown

@shenxianpeng shenxianpeng merged commit edc4ae4 into main Mar 31, 2026
18 of 19 checks passed
@shenxianpeng shenxianpeng deleted the fix-197 branch March 31, 2026 01:05
@shenxianpeng shenxianpeng added minor A minor version bump enhancement New feature or request and removed documentation Improvements or additions to documentation labels Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request minor A minor version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Compilation Database Support

1 participant