-
Notifications
You must be signed in to change notification settings - Fork 9
feat(linters): Add cli tool for running clang-tidy in parallel. #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(linters): Add cli tool for running clang-tidy in parallel. #17
Conversation
WalkthroughAdds a new clang-tidy-utils Python project: gitignore entries, pyproject configuration, README, and an asyncio-based CLI that runs clang-tidy in parallel with file discovery and concurrency control. Changes
Sequence Diagram(s)sequenceDiagram
participant User as CLI user
participant Main as main()
participant Collector as _collect_target_files
participant Scheduler as _clang_tidy_parallel_execution_entry
participant Worker as _execute_clang_tidy_task
User->>Main: invoke `clang-tidy-utils [options] <paths> -- [clang-tidy args]`
Main->>Collector: collect files from paths
Main->>Scheduler: start parallel execution (num_jobs, files, args)
Scheduler->>Worker: spawn async tasks (with semaphore) per file
Worker-->>Scheduler: return ClangTidyResult (stdout/stderr/ret_code)
Scheduler-->>Main: aggregate results and determine overall status
Main->>User: print progress, failures, and exit
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested reviewers
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can discuss how to structure the cli scripts/tools. The current layout might not be the best practice.
cli/clang-tidy-utils/pyproject.toml
Outdated
@@ -0,0 +1,45 @@ | |||
[project] | |||
name = "yscope-clang-tidy-utils" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may drop yscope-
prefix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a strong preference, but if we ever do upload this to pypi it probably makes more sense to have the prefix. If we never upload it to pypi the name won't ever really matter right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer to drop the prefix for now since it's likely this will be an internal tool for the foreseeable future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
cli/clang-tidy-utils/src/yscope_clang_tidy_utils/cli.py (3)
22-30
: Consider validating file existence.While building the argument list, consider verifying that the file exists before passing it to clang-tidy, especially in large codebases where file paths may be stale. This helps provide clearer error messages if the file is missing.
48-50
: Handle unresponsive processes more aggressively.Currently, a
process.terminate()
signals the process, but clang-tidy or any subprocess may ignore SIGTERM. Consider adding a fallback mechanism (e.g., process.kill()) after a short delay to ensure the process halts cleanly.except asyncio.CancelledError: process.terminate() + try: + await asyncio.wait_for(process.wait(), timeout=2) + except asyncio.TimeoutError: + process.kill() raise
76-109
: Consider early cancellation upon the first failure.All tasks continue to run even if one file fails clang-tidy checks. If you'd like to fail early and skip checking subsequent files, you could cancel remaining tasks once an error is detected.
if 0 != result.ret_code: ret_code = 1 + # Optionally cancel remaining tasks to fail early: + for remaining_task in tasks: + if not remaining_task.done(): + remaining_task.cancel() + breakcli/clang-tidy-utils/README.md (3)
3-3
: Fix grammar in project introduction.Use "a CLI script" instead of "a CLI scripts" to maintain grammatical correctness.
-This project is a CLI scripts for running [clang-tidy][clang-tidy-home] checks. +This project is a CLI script for running [clang-tidy][clang-tidy-home] checks.🧰 Tools
🪛 LanguageTool
[grammar] ~3-~3: The plural noun “scripts” cannot be used with the article “a”. Did you mean “a CLI script”?
Context: ...scope-clang-tidy-utils This project is a CLI scripts for running [clang-tidy][clang-tidy-hom...(A_NNS)
30-30
: Remove trailing punctuation from heading.Remove the colon in "## Development:" to align with common Markdown style guidelines.
-## Development: +## Development🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
30-30: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
41-41
: Link reference [uv-venv] is unused.The link definition “[uv-venv]” is never referenced in the document. Consider removing it for clarity.
-[uv-venv]: https://docs.astral.sh/uv/pip/compatibility/#virtual-environments-by-default
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
41-41: Link and image reference definitions should be needed
Unused link or image reference definition: "uv-venv"(MD053, link-image-reference-definitions)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
cli/clang-tidy-utils/.gitignore
(1 hunks)cli/clang-tidy-utils/README.md
(1 hunks)cli/clang-tidy-utils/pyproject.toml
(1 hunks)cli/clang-tidy-utils/src/yscope_clang_tidy_utils/cli.py
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- cli/clang-tidy-utils/.gitignore
🧰 Additional context used
🪛 LanguageTool
cli/clang-tidy-utils/README.md
[grammar] ~3-~3: The plural noun “scripts” cannot be used with the article “a”. Did you mean “a CLI script”?
Context: ...scope-clang-tidy-utils This project is a CLI scripts for running [clang-tidy][clang-tidy-hom...
(A_NNS)
🪛 markdownlint-cli2 (0.17.2)
cli/clang-tidy-utils/README.md
30-30: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
41-41: Link and image reference definitions should be needed
Unused link or image reference definition: "uv-venv"
(MD053, link-image-reference-definitions)
🔇 Additional comments (5)
cli/clang-tidy-utils/src/yscope_clang_tidy_utils/cli.py (2)
61-74
: Concurrency strategy looks solid.The approach to wrap each
execute_clang_tidy_task
with a semaphore is a well-structured way to impose concurrency limits. No immediate procedural concerns here.
111-175
: Ensure clang-tidy is installed before calling.If clang-tidy is missing, the CLI may crash. Consider implementing a preliminary check for clang-tidy’s existence to offer a more helpful error message to the user.
cli/clang-tidy-utils/pyproject.toml (3)
9-10
: Verify clang-tidy version compatibility.Ensure that
clang-tidy >= 19.1.0
is fully supported in your environment. In certain older distributions, clang-tidy’s package name or version might differ.
13-18
: Development dependencies are well structured.Declaring formatting, linting, and type-checking tools fosters a consistent development workflow.
20-21
: Handy script entry point.Defining
yscope-clang-tidy-utils
in[project.scripts]
is a user-friendly approach that enhances accessibility and makes the CLI straightforward to run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a complete review but a few quick comments:
- Can we move the tool into
linters
rather thancli
? - Can we prefix internal functions with
_
? - There are some spelling/grammar mistakes in the README. Can you go over it once more?
Fixed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
linters/clang-tidy-utils/src/clang_tidy_utils/cli.py (4)
22-30
: Consider validating file existence or providing helpful error messages.
If a file path is invalid, clang-tidy will fail silently here. A quick file check or user-facing error message could enhance usability.
33-58
: Robust async approach with graceful cancellation handling.
Terminating the process upon cancellation is a good practice to avoid resource leaks. However, you may consider additional handling if clang-tidy is not installed or is unavailable in PATH.
76-109
: Optional: Provide a consolidated summary of failures.
Currently, each file's result is printed individually. For large batches, a final summary of failed files or a short report can improve usability.
111-175
: Minor docstring clarity.
In the docstring for_clang_tidy_parallel_execution_entry
, the parameterclang_tidy_args
is grouped withfiles
in the text. Splitting them out can improve clarity. Also, usingsys.exit(ret_code)
instead ofexit(ret_code)
is a common convention to maintain consistent script termination.linters/clang-tidy-utils/README.md (1)
34-34
: Remove trailing punctuation from the heading.
To adhere to markdown style guidelines (MD026), consider removing the colon in “## Development:”.-## Development: +## Development🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
34-34: Trailing punctuation in heading
Punctuation: ':'(MD026, no-trailing-punctuation)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
linters/clang-tidy-utils/.gitignore
(1 hunks)linters/clang-tidy-utils/README.md
(1 hunks)linters/clang-tidy-utils/pyproject.toml
(1 hunks)linters/clang-tidy-utils/src/clang_tidy_utils/cli.py
(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- linters/clang-tidy-utils/.gitignore
- linters/clang-tidy-utils/pyproject.toml
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
linters/clang-tidy-utils/README.md
34-34: Trailing punctuation in heading
Punctuation: ':'
(MD026, no-trailing-punctuation)
🔇 Additional comments (3)
linters/clang-tidy-utils/src/clang_tidy_utils/cli.py (3)
1-9
: Imports look good.
No issues detected; these imports are succinct and well-structured.
10-19
: Good use of a data class for encapsulating clang-tidy results.
This design cleanly stores essential details such as return code and captured outputs.
61-74
: Concurrency control is well-handled.
Using a semaphore to limit simultaneous tasks helps balance CPU usage effectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
linters/clang-tidy-utils/src/clang_tidy_utils/cli.py
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: unit-tests (ubuntu-22.04)
- GitHub Check: unit-tests (macos-15)
- GitHub Check: unit-tests (ubuntu-24.04)
def _collect_target_files(input_paths: List[str]) -> List[str]: | ||
""" | ||
Collect all the target files to lint, including all C++ source/header files under the input | ||
directory. | ||
|
||
:param input_paths: The input paths. | ||
:return: A list of target files. | ||
""" | ||
target_files = [] | ||
for path in input_paths: | ||
if os.path.isfile(path): | ||
target_files.append(os.path.abspath(path)) | ||
continue | ||
if not os.path.isdir(path): | ||
continue | ||
for root, _, files in os.walk(path): | ||
for name in files: | ||
if name.endswith('.cpp') or name.endswith('.hpp'): | ||
full_path = os.path.join(root, name) | ||
target_files.append(os.path.abspath(full_path)) | ||
return target_files |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add support for additional C/C++ file extensions
The function currently only collects .cpp
and .hpp
files, but C/C++ projects commonly use other extensions like .c
, .h
, .cc
, .cxx
, .hxx
, .C
, .H
, etc.
Apply this diff to support additional file extensions:
def _collect_target_files(input_paths: List[str]) -> List[str]:
"""
Collect all the target files to lint, including all C++ source/header files under the input
directory.
:param input_paths: The input paths.
:return: A list of target files.
"""
+ extensions = {'.c', '.cc', '.cpp', '.cxx', '.C',
+ '.h', '.hh', '.hpp', '.hxx', '.H'}
target_files = []
for path in input_paths:
if os.path.isfile(path):
target_files.append(os.path.abspath(path))
continue
if not os.path.isdir(path):
continue
for root, _, files in os.walk(path):
for name in files:
- if name.endswith('.cpp') or name.endswith('.hpp'):
+ if any(name.endswith(ext) for ext in extensions):
full_path = os.path.join(root, name)
target_files.append(os.path.abspath(full_path))
return target_files
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
def _collect_target_files(input_paths: List[str]) -> List[str]: | |
""" | |
Collect all the target files to lint, including all C++ source/header files under the input | |
directory. | |
:param input_paths: The input paths. | |
:return: A list of target files. | |
""" | |
target_files = [] | |
for path in input_paths: | |
if os.path.isfile(path): | |
target_files.append(os.path.abspath(path)) | |
continue | |
if not os.path.isdir(path): | |
continue | |
for root, _, files in os.walk(path): | |
for name in files: | |
if name.endswith('.cpp') or name.endswith('.hpp'): | |
full_path = os.path.join(root, name) | |
target_files.append(os.path.abspath(full_path)) | |
return target_files | |
def _collect_target_files(input_paths: List[str]) -> List[str]: | |
""" | |
Collect all the target files to lint, including all C++ source/header files under the input | |
directory. | |
:param input_paths: The input paths. | |
:return: A list of target files. | |
""" | |
extensions = {'.c', '.cc', '.cpp', '.cxx', '.C', | |
'.h', '.hh', '.hpp', '.hxx', '.H'} | |
target_files = [] | |
for path in input_paths: | |
if os.path.isfile(path): | |
target_files.append(os.path.abspath(path)) | |
continue | |
if not os.path.isdir(path): | |
continue | |
for root, _, files in os.walk(path): | |
for name in files: | |
if any(name.endswith(ext) for ext in extensions): | |
full_path = os.path.join(root, name) | |
target_files.append(os.path.abspath(full_path)) | |
return target_files |
async def _execute_clang_tidy_task(file: str, clang_tidy_args: List[str]) -> ClangTidyResult: | ||
""" | ||
Executes a single clang-tidy task by checking one file using a process managed by asyncio. | ||
|
||
:param file: The file to check. | ||
:param clang_tidy_args: The clang-tidy cli arguments. | ||
:return: Execution results represented by an instance of `ClangTidyResult`. | ||
""" | ||
task_args: List[str] = _create_clang_tidy_task_arg_list(file, clang_tidy_args) | ||
try: | ||
process = await asyncio.create_subprocess_exec( | ||
*task_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE | ||
) | ||
stdout, stderr = await process.communicate() | ||
except asyncio.CancelledError: | ||
process.terminate() | ||
await process.wait() | ||
raise | ||
|
||
assert process.returncode is not None | ||
return ClangTidyResult( | ||
file, | ||
process.returncode, | ||
stdout.decode("UTF-8"), | ||
stderr.decode("UTF-8"), | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Handle potential Unicode decode errors
The stdout and stderr decoding could fail if clang-tidy outputs non-UTF-8 characters. Consider using error handling for the decode operations.
Apply this diff to handle potential decode errors:
assert process.returncode is not None
return ClangTidyResult(
file,
process.returncode,
- stdout.decode("UTF-8"),
- stderr.decode("UTF-8"),
+ stdout.decode("UTF-8", errors="replace"),
+ stderr.decode("UTF-8", errors="replace"),
)
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async def _execute_clang_tidy_task(file: str, clang_tidy_args: List[str]) -> ClangTidyResult: | |
""" | |
Executes a single clang-tidy task by checking one file using a process managed by asyncio. | |
:param file: The file to check. | |
:param clang_tidy_args: The clang-tidy cli arguments. | |
:return: Execution results represented by an instance of `ClangTidyResult`. | |
""" | |
task_args: List[str] = _create_clang_tidy_task_arg_list(file, clang_tidy_args) | |
try: | |
process = await asyncio.create_subprocess_exec( | |
*task_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE | |
) | |
stdout, stderr = await process.communicate() | |
except asyncio.CancelledError: | |
process.terminate() | |
await process.wait() | |
raise | |
assert process.returncode is not None | |
return ClangTidyResult( | |
file, | |
process.returncode, | |
stdout.decode("UTF-8"), | |
stderr.decode("UTF-8"), | |
) | |
assert process.returncode is not None | |
return ClangTidyResult( | |
file, | |
process.returncode, | |
stdout.decode("UTF-8", errors="replace"), | |
stderr.decode("UTF-8", errors="replace"), | |
) |
🤖 Prompt for AI Agents
In linters/clang-tidy-utils/src/clang_tidy_utils/cli.py around lines 59-84, the
code decodes stdout and stderr with .decode("UTF-8") which can raise
UnicodeDecodeError for non-UTF-8 output; change the decoding to handle errors
(e.g., use stdout.decode("utf-8", errors="replace") and stderr.decode("utf-8",
errors="replace") or wrap the decode in a try/except UnicodeDecodeError and fall
back to decoding with errors="replace") so the function always returns safe
strings instead of raising.
async def _clang_tidy_parallel_execution_entry( | ||
num_jobs: int, | ||
files: List[str], | ||
clang_tidy_args: List[str], | ||
) -> int: | ||
""" | ||
Async entry for running clang-tidy checks in parallel. | ||
|
||
:param num_jobs: The maximum number of jobs allowed to run in parallel. | ||
:param files: The list of files to check. | ||
:param clang_tidy_args: The clang-tidy cli arguments. | ||
""" | ||
sem: asyncio.Semaphore = asyncio.Semaphore(num_jobs) | ||
tasks: List[asyncio.Task[ClangTidyResult]] = [ | ||
asyncio.create_task(_execute_clang_tidy_task_with_sem(sem, file, clang_tidy_args)) | ||
for file in files | ||
] | ||
num_total_files: int = len(files) | ||
|
||
ret_code: int = 0 | ||
try: | ||
for idx, clang_tidy_task in enumerate(asyncio.as_completed(tasks)): | ||
result: ClangTidyResult = await clang_tidy_task | ||
if 0 != result.ret_code: | ||
ret_code = 1 | ||
print(f"[{idx + 1}/{num_total_files}]: {result.file_name}") | ||
print(result.stdout) | ||
print(result.stderr) | ||
else: | ||
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]") | ||
except asyncio.CancelledError as e: | ||
print(f"\nAll tasks cancelled: {e}") | ||
for task in tasks: | ||
task.cancel() | ||
|
||
return ret_code |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Return value documentation mismatch
The docstring doesn't document the return value, but the function returns an int representing the exit code.
Apply this diff to fix the documentation:
async def _clang_tidy_parallel_execution_entry(
num_jobs: int,
files: List[str],
clang_tidy_args: List[str],
) -> int:
"""
Async entry for running clang-tidy checks in parallel.
:param num_jobs: The maximum number of jobs allowed to run in parallel.
:param files: The list of files to check.
:param clang_tidy_args: The clang-tidy cli arguments.
+ :return: 0 if all checks pass, 1 if any check fails.
"""
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async def _clang_tidy_parallel_execution_entry( | |
num_jobs: int, | |
files: List[str], | |
clang_tidy_args: List[str], | |
) -> int: | |
""" | |
Async entry for running clang-tidy checks in parallel. | |
:param num_jobs: The maximum number of jobs allowed to run in parallel. | |
:param files: The list of files to check. | |
:param clang_tidy_args: The clang-tidy cli arguments. | |
""" | |
sem: asyncio.Semaphore = asyncio.Semaphore(num_jobs) | |
tasks: List[asyncio.Task[ClangTidyResult]] = [ | |
asyncio.create_task(_execute_clang_tidy_task_with_sem(sem, file, clang_tidy_args)) | |
for file in files | |
] | |
num_total_files: int = len(files) | |
ret_code: int = 0 | |
try: | |
for idx, clang_tidy_task in enumerate(asyncio.as_completed(tasks)): | |
result: ClangTidyResult = await clang_tidy_task | |
if 0 != result.ret_code: | |
ret_code = 1 | |
print(f"[{idx + 1}/{num_total_files}]: {result.file_name}") | |
print(result.stdout) | |
print(result.stderr) | |
else: | |
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]") | |
except asyncio.CancelledError as e: | |
print(f"\nAll tasks cancelled: {e}") | |
for task in tasks: | |
task.cancel() | |
return ret_code | |
async def _clang_tidy_parallel_execution_entry( | |
num_jobs: int, | |
files: List[str], | |
clang_tidy_args: List[str], | |
) -> int: | |
""" | |
Async entry for running clang-tidy checks in parallel. | |
:param num_jobs: The maximum number of jobs allowed to run in parallel. | |
:param files: The list of files to check. | |
:param clang_tidy_args: The clang-tidy cli arguments. | |
:return: 0 if all checks pass, 1 if any check fails. | |
""" | |
sem: asyncio.Semaphore = asyncio.Semaphore(num_jobs) | |
tasks: List[asyncio.Task[ClangTidyResult]] = [ | |
asyncio.create_task(_execute_clang_tidy_task_with_sem(sem, file, clang_tidy_args)) | |
for file in files | |
] | |
num_total_files: int = len(files) | |
ret_code: int = 0 | |
try: | |
for idx, clang_tidy_task in enumerate(asyncio.as_completed(tasks)): | |
result: ClangTidyResult = await clang_tidy_task | |
if 0 != result.ret_code: | |
ret_code = 1 | |
print(f"[{idx + 1}/{num_total_files}]: {result.file_name}") | |
print(result.stdout) | |
print(result.stderr) | |
else: | |
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]") | |
except asyncio.CancelledError as e: | |
print(f"\nAll tasks cancelled: {e}") | |
for task in tasks: | |
task.cancel() | |
return ret_code |
🤖 Prompt for AI Agents
linters/clang-tidy-utils/src/clang_tidy_utils/cli.py around lines 102 to 137:
the function _clang_tidy_parallel_execution_entry returns an int exit code but
the docstring omits return documentation; update the docstring to include a
return section (e.g., ":return: Exit code integer (0 if all files passed, 1 if
any file failed)" and optionally ":rtype: int") so the return value is clearly
documented.
print(result.stdout) | ||
print(result.stderr) | ||
else: | ||
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Fix typo in success message
The success message has a grammatical error: "All check passed" should be "All checks passed".
Apply this diff to fix the typo:
- print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]")
+ print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All checks passed!]")
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All check passed!]") | |
print(f"[{idx + 1}/{num_total_files}]: {result.file_name} [All checks passed!]") |
🤖 Prompt for AI Agents
In linters/clang-tidy-utils/src/clang_tidy_utils/cli.py around line 131, the
success message string has a grammatical typo "All check passed!" — update the
printed message to "All checks passed!" so the output reads: print(f"[{idx +
1}/{num_total_files}]: {result.file_name} [All checks passed!]").
num_jobs: int | ||
if parsed_cli_args.num_jobs is not None: | ||
num_jobs = parsed_cli_args.num_jobs | ||
else: | ||
num_jobs = multiprocessing.cpu_count() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add validation for num_jobs parameter
The num_jobs
parameter should be validated to ensure it's a positive integer.
Apply this diff to add validation:
num_jobs: int
if parsed_cli_args.num_jobs is not None:
num_jobs = parsed_cli_args.num_jobs
+ if num_jobs <= 0:
+ parser.error("Number of jobs must be a positive integer")
else:
num_jobs = multiprocessing.cpu_count()
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
num_jobs: int | |
if parsed_cli_args.num_jobs is not None: | |
num_jobs = parsed_cli_args.num_jobs | |
else: | |
num_jobs = multiprocessing.cpu_count() | |
num_jobs: int | |
if parsed_cli_args.num_jobs is not None: | |
num_jobs = parsed_cli_args.num_jobs | |
if num_jobs <= 0: | |
parser.error("Number of jobs must be a positive integer") | |
else: | |
num_jobs = multiprocessing.cpu_count() |
if 0 != ret_code: | ||
# Ideally, we should return the error code directly. However, this utility is used inside | ||
# the GitHub action, and we don't want to fail the workflow. We log the error code instead | ||
# if it's not 0. | ||
print(f"\nclang-tidy-utils: Linter check failed with return code {ret_code}") | ||
|
||
exit(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
Consider making exit code behaviour configurable
The comment indicates that returning a non-zero exit code would fail GitHub Actions workflows. However, this behaviour might be unexpected for users running the tool locally or in other CI environments where they want the tool to fail on linting errors.
Consider adding a command-line flag like --ignore-errors
or --github-actions-mode
to make this behaviour configurable, allowing the tool to return proper exit codes in non-GitHub Actions environments while maintaining compatibility with your CI setup.
Description
This PR introduces a cli sub-project for running clang-tidy in parallel. For now, it is almost like a simplified script of the llvm version here.
As discussed offline, we decided to use uv to manage our Python cli scripts.
Validation performed
I have tested this cli by using it to check clp-ffi-py. Here's how it works:
Here, we parallel 10 clang-tidy tasks with extra clang-tidy arguments
-p build/compile_commands.json --line-filter="[{\"name\":\"cpp11_zone.hpp\",\"lines\":[[197,197]]}]"
to match what we specify in the linting task here. You can useecho $?
to check the return value is 0. If we remove the lint filter in the arguments above, clang-tidy will raise a warning on msgpack's source code and thus the return value ofecho $?
will be 1 in this case.Summary by CodeRabbit
Documentation
New Features
Chores