Skip to content

feat: Improve clang hook error diagnostics#226

Merged
shenxianpeng merged 2 commits into
mainfrom
codex/improve-error-diagnostics
May 21, 2026
Merged

feat: Improve clang hook error diagnostics#226
shenxianpeng merged 2 commits into
mainfrom
codex/improve-error-diagnostics

Conversation

@shenxianpeng
Copy link
Copy Markdown
Member

@shenxianpeng shenxianpeng commented May 21, 2026

Summary

Improve first-run diagnostics for clang-format and clang-tidy hooks so adoption failures point users at concrete next steps.

Changes

  • Return a clear error for unsupported --version values and list supported wheel versions.
  • Print the resolved Python wheel version in verbose mode, including partial inputs such as --version=21.
  • Add CMake and Meson commands when compile_commands.json is missing.
  • Append Windows/MSVC clang-tidy guidance for common Visual Studio, SDK, cl.exe, and MSVC flag errors.
  • Cover the new diagnostics with focused unit tests.

Validation

  • uv run ruff check cpp_linter_hooks tests/test_util.py tests/test_clang_tidy.py tests/test_clang_format.py
  • uv run pytest tests/test_util.py tests/test_clang_tidy.py tests/test_clang_format.py -q

Summary by CodeRabbit

  • Bug Fixes

    • Improved error messages when requesting unsupported tool versions, now listing supported alternatives
    • Enhanced guidance for missing compile database issues
  • New Features

    • Added verbose diagnostics output displaying tool version resolution details

Review Change Stack

@sonarqubecloud
Copy link
Copy Markdown

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 98.59155% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.07%. Comparing base (e333f20) to head (34398c1).

Files with missing lines Patch % Lines
cpp_linter_hooks/util.py 96.77% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #226      +/-   ##
==========================================
+ Coverage   96.70%   97.07%   +0.36%     
==========================================
  Files           4        4              
  Lines         182      239      +57     
==========================================
+ Hits          176      232      +56     
- Misses          6        7       +1     

☔ 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.

@shenxianpeng shenxianpeng changed the title [codex] Improve clang hook error diagnostics feat: Improve clang hook error diagnostics May 21, 2026
@shenxianpeng shenxianpeng marked this pull request as ready for review May 21, 2026 02:58
@shenxianpeng shenxianpeng added the enhancement New feature or request label May 21, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Walkthrough

This PR refactors tool-version resolution and installation across clang-format and clang-tidy. The core change introduces resolve_install_with_diagnostics() in util.py, which validates tool versions and returns user-facing error messages for unsupported versions. Both tools now report version failures early and clang-tidy adds contextual guidance for missing compile databases and MSVC-related errors.

Changes

Version Resolution and Diagnostics Integration

Layer / File(s) Summary
Version resolution foundation
cpp_linter_hooks/util.py, tests/test_util.py
Adds resolve_tool_version() to resolve supported versions or return error messages, and resolve_install_with_diagnostics() to install and report user-facing errors with supported versions listed. Helper functions format supported-version messages and compute tool defaults. resolve_install() now delegates to the diagnostics variant.
clang-format integration
cpp_linter_hooks/clang_format.py, tests/test_clang_format.py
Switches to resolve_install_with_diagnostics for version resolution, changes --version default to None, and adds early error return when resolution fails. Tests verify unsupported version reporting and verbose mode integration.
clang-tidy version resolution
cpp_linter_hooks/clang_tidy.py
Imports resolve_install_with_diagnostics, changes --version default to None, and integrates version resolution with early error return in run_clang_tidy().
clang-tidy compile DB and error guidance
cpp_linter_hooks/clang_tidy.py, tests/test_clang_tidy.py
Adds compile-database hint messages (COMPILE_COMMANDS_HINT, MSVC_HINT) and _compile_commands_not_found_message() to generate contextual missing-DB guidance. Introduces heuristic functions (_looks_like_compile_db_error, _looks_like_msvc_error) and _append_guidance() to detect error patterns in clang-tidy output and inject relevant hints. Prints compile-DB guidance during verbose mode if -p is not present. Tests mock resolve_install_with_diagnostics, verify hint output, and cover error-pattern detection and MSVC scenarios across all clang-tidy command paths.

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

  • cpp-linter/cpp-linter-hooks#87: Both PRs modify the shared tool-version installation plumbing in cpp_linter_hooks/util.py (migration to wheel-based ensure_installed/is_installed in #87, then adding resolve_install_with_diagnostics/resolve_tool_version and switching clang_format/clang_tidy to the diagnostics-based resolver in the main PR).
  • cpp-linter/cpp-linter-hooks#201: Both PRs modify cpp_linter_hooks/clang_tidy.py around compile_commands.json handling—main PR adds diagnostics/hinting for missing or invalid compile DB cases (and verbose guidance when -p isn't present), while the retrieved PR implements the --compile-commands/auto-detection -p injection logic.
  • cpp-linter/cpp-linter-hooks#113: Both PRs refactor the clang-format/clang-tidy installation-resolution path in cpp_linter_hooks/util.py and update run_clang_format/run_clang_tidy to resolve/install the tool before invocation (main PR adds diagnostics-based resolution/early failure, retrieved PR simplifies to _resolve_install side effects).

Suggested Labels

enhancement, minor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Improve clang hook error diagnostics' accurately summarizes the main change in the PR, which introduces better error diagnostics for clang-format and clang-tidy hooks.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/improve-error-diagnostics

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: 2

🧹 Nitpick comments (1)
tests/test_util.py (1)

338-341: ⚡ Quick win

Avoid hardcoding resolved wheel patch version in test assertions.

This assertion will churn every time CLANG_TIDY_VERSIONS advances. Assert against CLANG_TIDY_VERSIONS-derived expected value instead.

🤖 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 `@tests/test_util.py` around lines 338 - 341, The test currently asserts a
hardcoded message containing the resolved wheel patch version; update the
assertion to compute the expected message from CLANG_TIDY_VERSIONS instead of
hardcoding "21.1.6". Locate the assertion in tests/test_util.py (the line
checking that the string is in capsys.readouterr().err) and build the expected
string using the CLANG_TIDY_VERSIONS mapping (or the derived value for the
selected version) so the test compares capsys.readouterr().err against a message
generated from CLANG_TIDY_VERSIONS rather than a fixed patch version.
🤖 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_hooks/util.py`:
- Around line 41-46: The _default_version_for_tool function currently returns
hardcoded constants (DEFAULT_CLANG_FORMAT_VERSION / DEFAULT_CLANG_TIDY_VERSION)
instead of the dependency-pinned defaults from pyproject.toml; change its
implementation to read the default versions from pyproject.toml (the same logic
used elsewhere for config loading) and return the clang-format or clang-tidy
value based on the tool argument, still falling back to the constants
DEFAULT_CLANG_FORMAT_VERSION and DEFAULT_CLANG_TIDY_VERSION only if the
pyproject values are missing or unparsable; update references in
_default_version_for_tool to use the pyproject-derived values so the function
satisfies the default-version contract.
- Around line 126-147: The code currently proceeds to call
_is_version_installed(tool, user_version) and _install_tool(tool, user_version)
even when resolve_tool_version returned user_version == None; add a guard after
resolve_tool_version(tool, version) to check if user_version is None and return
an explicit error (e.g., (None, "could not resolve default version for <tool>"))
instead of continuing; update the block around resolve_tool_version,
user_version, and the final return to early-return a diagnostic when
user_version is None so neither _is_version_installed nor _install_tool are
called with None.

---

Nitpick comments:
In `@tests/test_util.py`:
- Around line 338-341: The test currently asserts a hardcoded message containing
the resolved wheel patch version; update the assertion to compute the expected
message from CLANG_TIDY_VERSIONS instead of hardcoding "21.1.6". Locate the
assertion in tests/test_util.py (the line checking that the string is in
capsys.readouterr().err) and build the expected string using the
CLANG_TIDY_VERSIONS mapping (or the derived value for the selected version) so
the test compares capsys.readouterr().err against a message generated from
CLANG_TIDY_VERSIONS rather than a fixed patch version.
🪄 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: e89bb289-ab74-47d3-a8fc-623aa5ae69d2

📥 Commits

Reviewing files that changed from the base of the PR and between e333f20 and 34398c1.

📒 Files selected for processing (6)
  • cpp_linter_hooks/clang_format.py
  • cpp_linter_hooks/clang_tidy.py
  • cpp_linter_hooks/util.py
  • tests/test_clang_format.py
  • tests/test_clang_tidy.py
  • tests/test_util.py

Comment thread cpp_linter_hooks/util.py
Comment on lines +41 to +46
def _default_version_for_tool(tool: str) -> Optional[str]:
return (
DEFAULT_CLANG_FORMAT_VERSION
if tool == "clang-format"
else DEFAULT_CLANG_TIDY_VERSION
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Default tool versions are not sourced from pyproject.toml.

_default_version_for_tool() still relies on constants backed by versions.py rather than dependency-pinned defaults from pyproject.toml, which breaks the required default-version contract.

As per coding guidelines, "Store default clang-format and clang-tidy versions in DEFAULT_CLANG_FORMAT_VERSION and DEFAULT_CLANG_TIDY_VERSION constants, reading from pyproject.toml".

🤖 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_hooks/util.py` around lines 41 - 46, The _default_version_for_tool
function currently returns hardcoded constants (DEFAULT_CLANG_FORMAT_VERSION /
DEFAULT_CLANG_TIDY_VERSION) instead of the dependency-pinned defaults from
pyproject.toml; change its implementation to read the default versions from
pyproject.toml (the same logic used elsewhere for config loading) and return the
clang-format or clang-tidy value based on the tool argument, still falling back
to the constants DEFAULT_CLANG_FORMAT_VERSION and DEFAULT_CLANG_TIDY_VERSION
only if the pyproject values are missing or unparsable; update references in
_default_version_for_tool to use the pyproject-derived values so the function
satisfies the default-version contract.

Comment thread cpp_linter_hooks/util.py
Comment on lines +126 to +147
user_version, error = resolve_tool_version(tool, version)
if error is not None:
return None, error

if verbose:
if version is None:
print(
f"Using default {tool} Python wheel version {user_version}",
file=sys.stderr,
)
elif version == user_version:
print(f"Using {tool} Python wheel version {user_version}", file=sys.stderr)
else:
print(
f"Resolved {tool} --version={version} to Python wheel version "
f"{user_version}",
file=sys.stderr,
)

return (
_is_version_installed(tool, user_version) or _install_tool(tool, user_version),
None,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard against unresolved default versions before install/check.

If user_version resolves to None, this path can flow into _is_version_installed() and trigger a runtime type error (None in result.stdout) when the binary exists. Return an explicit diagnostic instead of proceeding.

Suggested fix
 def resolve_install_with_diagnostics(
     tool: str, version: Optional[str], verbose: bool = False
 ) -> Tuple[Optional[Path], Optional[str]]:
@@
     user_version, error = resolve_tool_version(tool, version)
     if error is not None:
         return None, error
+    if user_version is None:
+        return None, (
+            f"Could not determine a default {tool} wheel version from configuration."
+        )
@@
     return (
         _is_version_installed(tool, user_version) or _install_tool(tool, user_version),
         None,
     )
🤖 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_hooks/util.py` around lines 126 - 147, The code currently proceeds
to call _is_version_installed(tool, user_version) and _install_tool(tool,
user_version) even when resolve_tool_version returned user_version == None; add
a guard after resolve_tool_version(tool, version) to check if user_version is
None and return an explicit error (e.g., (None, "could not resolve default
version for <tool>")) instead of continuing; update the block around
resolve_tool_version, user_version, and the final return to early-return a
diagnostic when user_version is None so neither _is_version_installed nor
_install_tool are called with None.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 21, 2026

Merging this PR will degrade performance by 19.25%

❌ 2 regressed benchmarks
✅ 86 untouched benchmarks
🆕 2 new benchmarks
⏩ 13 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
test_run_clang_tidy_invalid[args0-1] 1.3 ms 1.5 ms -17.52%
test_clang_tidy_parallel_execution_completes 1.9 ms 2.5 ms -20.94%
🆕 test_resolve_install_with_diagnostics_invalid_version_lists_supported_versions N/A 246.1 µs N/A
🆕 test_resolve_install_with_diagnostics_verbose_prints_resolved_version N/A 980.7 µs N/A

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing codex/improve-error-diagnostics (34398c1) with main (a029d55)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 (e333f20) during the generation of this report, so a029d55 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@shenxianpeng shenxianpeng merged commit fab85d9 into main May 21, 2026
22 of 24 checks passed
@shenxianpeng shenxianpeng deleted the codex/improve-error-diagnostics branch May 21, 2026 03:11
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