================
@@ -124,133 +96,260 @@ def create_comment_text(warning: str, cpp_files: 
List[str]) -> str:
 
 <details>
 <summary>
-View the output from clang-tidy here.
+View the output from {self.name} here.
 </summary>
 
 ```
-{warning}
+{linter_output}
 ```
 
 </details>
 """
 
+    # TODO: Refactor this
+    def find_comment(self, pr: any) -> any:
+        all_linter_names = list(map(attrgetter("name"), ALL_LINTERS))
+        other_linter_names = [name for name in all_linter_names if name != 
self.name]
+
+        other_tags = [
+            self.COMMENT_TAG.format(linter=name) for name in other_linter_names
+        ]
+
+        for comment in pr.as_issue().get_comments():
+            body = comment.body
+            if self.comment_tag in body and not any(
+                other_tag in body for other_tag in other_tags
+            ):
+                return comment
+        return None
 
-def find_comment(pr: any) -> any:
-    for comment in pr.as_issue().get_comments():
-        if COMMENT_TAG in comment.body:
-            return comment
-    return None
+    def update_pr(self, comment_text: str, args: LintArgs, create_new: bool) 
-> None:
+        import github
+        from github import IssueComment, PullRequest
 
+        repo = github.Github(args.token).get_repo(args.repo)
+        pr = repo.get_issue(args.issue_number).as_pull_request()
 
-def create_comment(
-    comment_text: str, args: LintArgs, create_new: bool
-) -> Optional[dict]:
-    import github
+        comment_text = self.comment_tag + "\n\n" + comment_text
 
-    repo = github.Github(args.token).get_repo(args.repo)
-    pr = repo.get_issue(args.issue_number).as_pull_request()
+        existing_comment = self.find_comment(pr)
 
-    comment_text = COMMENT_TAG + "\n\n" + comment_text
+        if create_new or existing_comment:
+            self.comment = {"body": comment_text}
+        if existing_comment:
+            self.comment["id"] = existing_comment.id
 
-    existing_comment = find_comment(pr)
 
-    comment = None
-    if create_new or existing_comment:
-        comment = {"body": comment_text}
-    if existing_comment:
-        comment["id"] = existing_comment.id
-    return comment
+    def run(self, args: LintArgs) -> bool:
+        if args.verbose:
+            print(f"got changed files: {args.changed_files}")
 
+        files_to_lint = self.filter_changed_files(args.changed_files)
 
-def run_clang_tidy(changed_files: List[str], args: LintArgs) -> Optional[str]:
-    if not changed_files:
-        print("no c/c++ files found")
-        return None
+        if not files_to_lint and args.verbose:
+            print("no modified files found")
 
-    git_diff_cmd = [
-        "git",
-        "diff",
-        "-U0",
-        f"{args.start_rev}...{args.end_rev}",
-        "--",
-    ] + changed_files
-
-    diff_proc = subprocess.run(
-        git_diff_cmd,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
-        text=True,
-        check=False,
-    )
+        is_success = True
+        linter_output = None
 
-    if diff_proc.returncode != 0:
-        print(f"Git diff failed: {diff_proc.stderr}")
-        return None
+        if files_to_lint:
+            linter_output = self.run_linter_tool(files_to_lint, args)
+            if linter_output:
+                is_success = False
+
+        should_update_gh = args.token is not None and args.repo is not None
+
+        if is_success:
+            if should_update_gh:
+                comment_text = (
+                    ":white_check_mark: With the latest revision "
+                    f"this PR passed the {self.friendly_name}."
+                )
+                self.update_pr(comment_text, args, create_new=False)
+            return True
+        else:
+            if should_update_gh:
+                if linter_output:
+                    comment_text = self.pr_comment_text_for_diff(
+                        linter_output, files_to_lint, args
+                    )
+                    self.update_pr(comment_text, args, create_new=True)
+                else:
+                    comment_text = (
+                        f":warning: The {self.friendly_name} failed without 
printing "
+                        "an output. Check the logs for output. :warning:"
+                    )
+                    self.update_pr(comment_text, args, create_new=False)
+            else:
+                if linter_output:
+                    print(
+                        f"Warning: {self.friendly_name}, {self.name} detected "
+                        "some issues with your code..."
+                    )
+                    print(linter_output)
+                else:
+                    print(f"Warning: {self.friendly_name}, {self.name} failed 
to run.")
+            return False
+
+
+class ClangTidyLintHelper(LintHelper):
+    name = "clang-tidy"
+    friendly_name = "C/C++ code linter"
+
+    def instructions(self, cpp_files: List[str], args: LintArgs) -> str:
+        files_str = " ".join(cpp_files)
+        return f"""
+git diff -U0 origin/main...HEAD -- {files_str} |
+python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \\
+  -path {args.build_path} -p1 -quiet"""
+
+    def filter_changed_files(self, changed_files: List[str]) -> List[str]:
+        clang_tidy_changed_files = [
+            arg for arg in changed_files if "third-party" not in arg
+        ]
+
+        filtered_files = []
+        for filepath in clang_tidy_changed_files:
+            _, ext = os.path.splitext(filepath)
+            if ext not in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx"):
+                continue
+            if not self._should_lint_file(filepath):
+                continue
+            if os.path.exists(filepath):
+                filtered_files.append(filepath)
+        return filtered_files
+
+    def _should_lint_file(self, filepath: str) -> bool:
+        # TODO: Add more rules when enabling other projects to use clang-tidy 
in CI.
+        return filepath.startswith("clang-tools-extra/clang-tidy/")
+
+    def run_linter_tool(self, cpp_files: List[str], args: LintArgs) -> 
Optional[str]:
+        if not cpp_files:
+            return None
+
+        git_diff_cmd = [
+            "git",
+            "diff",
+            "-U0",
+            f"{args.start_rev}...{args.end_rev}",
+            "--",
+        ] + cpp_files
+
+        diff_proc = subprocess.run(
+            git_diff_cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+        if diff_proc.returncode != 0:
+            print(f"Git diff failed: {diff_proc.stderr}")
+            return None
+
+        diff_content = diff_proc.stdout
+        if not diff_content.strip():
+            if args.verbose:
+                print("No diff content found")
+            return None
+
+        tidy_diff_cmd = [
+            "clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py",
+            "-path",
+            args.build_path,
+            "-p1",
+            "-quiet",
+        ]
 
-    diff_content = diff_proc.stdout
-    if not diff_content.strip():
-        print("No diff content found")
+        if args.verbose:
+            print(f"Running clang-tidy-diff: {' '.join(tidy_diff_cmd)}")
+
+        proc = subprocess.run(
+            tidy_diff_cmd,
+            input=diff_content,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            check=False,
+        )
+
+        clean_output = self._clean_clang_tidy_output(proc.stdout.strip())
+        return clean_output
+
+    def _clean_clang_tidy_output(self, output: str) -> Optional[str]:
+        if not output or output == "No relevant changes found.":
+            return None
+
+        lines = output.split("\n")
+        cleaned_lines = []
+
+        for line in lines:
+            if line.startswith("Running clang-tidy in") or 
line.endswith("generated."):
+                continue
+
+            idx = line.rfind("llvm-project/")
+            if idx != -1:
+                line = line[idx + len("llvm-project/") :]
+
+            cleaned_lines.append(line)
+
+        if cleaned_lines:
+            return "\n".join(cleaned_lines)
         return None
 
-    tidy_diff_cmd = [
-        "clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py",
-        "-path",
-        args.build_path,
-        "-p1",
-        "-quiet",
-    ]
-
-    if args.verbose:
-        print(f"Running clang-tidy-diff: {' '.join(tidy_diff_cmd)}")
-
-    proc = subprocess.run(
-        tidy_diff_cmd,
-        input=diff_content,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
-        text=True,
-        check=False,
-    )
 
-    return clean_clang_tidy_output(proc.stdout.strip())
+class Doc8LintHelper(LintHelper):
+    name = "doc8"
+    friendly_name = "documentation linter"
 
+    def instructions(self, doc_files: List[str], args: LintArgs) -> str:
+        files_str = " ".join(doc_files)
+        return f"doc8 -q {files_str}"
 
-def run_linter(changed_files: List[str], args: LintArgs) -> tuple[bool, 
Optional[dict]]:
-    changed_files = [arg for arg in changed_files if "third-party" not in arg]
+    def filter_changed_files(self, changed_files: List[str]) -> List[str]:
+        filtered_files = []
+        for filepath in changed_files:
+            _, ext = os.path.splitext(filepath)
+            if ext not in (".rst"):
+                continue
+            if not 
filepath.startswith("clang-tools-extra/docs/clang-tidy/checks/"):
----------------
vbvictor wrote:

Are there any obstacles in linting all docs?
```suggestion
            if not filepath.startswith("clang-tools-extra/docs/clang-tidy/"):
```

https://github.com/llvm/llvm-project/pull/168827
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to