Skip to content

Conversation

LinZhihao-723
Copy link
Member

@LinZhihao-723 LinZhihao-723 commented Jan 18, 2025

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:

cd $PROJECT_DIR/clp-ffi-py
uv venv
uv pip install $PROJECT_DIR/yscope-dev-utils/cli/clang-tidy-utils
task lint:cpp-format-fix # This is to ensure clang-tidy config has been set
rm -rf build && mkdir build && cd build && cmake .. && cd ..
yscope-clang-tidy-utils -j 10 ./src/clp_ffi_py/ir/native/* -- -p build/compile_commands.json  --line-filter="[{\"name\":\"cpp11_zone.hpp\",\"lines\":[[197,197]]}]"

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 use echo $? 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 of echo $? will be 1 in this case.

Summary by CodeRabbit

  • Documentation

    • Added a README with installation, usage, and development instructions for the new clang-tidy utility.
  • New Features

    • Released a CLI tool to run clang-tidy checks in parallel with configurable jobs and argument passthrough.
    • Added project configuration defining metadata, dependencies, dev tools, and an executable entry point.
  • Chores

    • Updated .gitignore to exclude virtualenvs, caches, bytecode, and packaging artifacts.

Copy link
Contributor

coderabbitai bot commented Jan 18, 2025

Walkthrough

Adds 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

Cohort / File(s) Change Summary
Project config & packaging
linters/clang-tidy-utils/pyproject.toml
New pyproject defining project metadata, dependency on clang-tidy ~= 19.1.0, dev dependencies (black, docformatter, mypy, ruff), tool configs, and a clang-tidy-utils entry point.
Repository ignores
linters/clang-tidy-utils/.gitignore
Added ignores for virtualenvs (venv*/), __pycache__, *.egg-info, .mypy_cache, .ruff_cache.
Documentation
linters/clang-tidy-utils/README.md
New README documenting purpose, requirements, installation (venv and system), usage, options, and development/format/lint commands.
CLI implementation
linters/clang-tidy-utils/src/clang_tidy_utils/cli.py
New asyncio-based CLI: ClangTidyResult dataclass; functions for file collection, building clang-tidy args, executing clang-tidy as asyncio subprocesses with semaphore-based concurrency, task orchestration, progress reporting, cancellation handling, and main() CLI parsing.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • kirkrodrigues
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@LinZhihao-723 LinZhihao-723 changed the title feat(cli): Add cli script for running clang-tidy in parallel. feat(cli): Add a cli tool for running clang-tidy in parallel. Jan 31, 2025
@LinZhihao-723 LinZhihao-723 changed the title feat(cli): Add a cli tool for running clang-tidy in parallel. feat(cli): Add cli tool for running clang-tidy in parallel. Jan 31, 2025
@LinZhihao-723 LinZhihao-723 marked this pull request as ready for review January 31, 2025 01:21
Copy link
Member Author

@LinZhihao-723 LinZhihao-723 left a 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.

@@ -0,0 +1,45 @@
[project]
name = "yscope-clang-tidy-utils"
Copy link
Member Author

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

Copy link
Member

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?

Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed.

Copy link
Contributor

@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: 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()
+    break
cli/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

📥 Commits

Reviewing files that changed from the base of the PR and between c1593f7 and d74ac85.

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

Copy link
Member

@kirkrodrigues kirkrodrigues left a 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 than cli?
  • Can we prefix internal functions with _?
  • There are some spelling/grammar mistakes in the README. Can you go over it once more?

@LinZhihao-723
Copy link
Member Author

Not a complete review but a few quick comments:

  • Can we move the tool into linters rather than cli?
  • Can we prefix internal functions with _?
  • There are some spelling/grammar mistakes in the README. Can you go over it once more?

Fixed.

@LinZhihao-723 LinZhihao-723 changed the title feat(cli): Add cli tool for running clang-tidy in parallel. feat(linters): Add cli tool for running clang-tidy in parallel. Feb 10, 2025
Copy link
Contributor

@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: 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 parameter clang_tidy_args is grouped with files in the text. Splitting them out can improve clarity. Also, using sys.exit(ret_code) instead of exit(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

📥 Commits

Reviewing files that changed from the base of the PR and between d74ac85 and 817fdef.

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

@LinZhihao-723 LinZhihao-723 requested a review from a team as a code owner April 23, 2025 00:46
Copy link
Contributor

@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: 6

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62edc76 and 548ff02.

📒 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)

Comment on lines +36 to +56
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
Copy link
Contributor

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.

Suggested change
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

Comment on lines +59 to +84
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"),
)
Copy link
Contributor

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.

Suggested change
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.

Comment on lines +102 to +137
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
Copy link
Contributor

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.

Suggested change
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!]")
Copy link
Contributor

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.

Suggested change
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!]").

Comment on lines +184 to +189
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()

Copy link
Contributor

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.

Suggested change
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()

Comment on lines +200 to +206
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)
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants