Skip to content

Commit f6eac19

Browse files
committed
Split clang-tidy refactoring
1 parent 29ebebe commit f6eac19

File tree

1 file changed

+46
-104
lines changed

1 file changed

+46
-104
lines changed

llvm/utils/git/code-lint-helper.py

100755100644
Lines changed: 46 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -18,68 +18,60 @@
1818
"""
1919

2020
import argparse
21+
import github
22+
import json
2123
from operator import attrgetter
2224
import os
2325
import subprocess
2426
import sys
25-
from typing import List, Optional
27+
from typing import Any, Dict, Final, List, Optional
2628

2729

2830
class LintArgs:
29-
start_rev: str = None
30-
end_rev: str = None
31-
repo: str = None
32-
changed_files: List[str] = []
33-
token: str = None
31+
start_rev: Optional[str] = None
32+
end_rev: Optional[str] = None
33+
repo: Optional[str] = None
34+
changed_files: Optional[str] = None
35+
token: Optional[str] = None
3436
verbose: bool = True
3537
issue_number: int = 0
3638
build_path: str = "build"
3739
clang_tidy_binary: str = "clang-tidy"
38-
doc8_binary: str = "doc8"
3940

4041
def __init__(self, args: argparse.Namespace) -> None:
4142
if not args is None:
4243
self.start_rev = args.start_rev
4344
self.end_rev = args.end_rev
4445
self.repo = args.repo
4546
self.token = args.token
46-
if args.changed_files:
47-
self.changed_files = args.changed_files.split(",")
48-
else:
49-
self.changed_files = []
47+
self.changed_files = args.changed_files
5048
self.issue_number = args.issue_number
5149
self.verbose = args.verbose
5250
self.build_path = args.build_path
5351
self.clang_tidy_binary = args.clang_tidy_binary
54-
self.doc8_binary = args.doc8_binary
5552

5653

5754
class LintHelper:
5855
COMMENT_TAG = "<!--LLVM CODE LINT COMMENT: {linter}-->"
5956
name: str
6057
friendly_name: str
61-
comment: dict = None
58+
comment: Optional[Dict[str, Any]] = None
6259

6360
@property
6461
def comment_tag(self) -> str:
6562
return self.COMMENT_TAG.format(linter=self.name)
6663

67-
@property
6864
def instructions(self) -> str:
6965
raise NotImplementedError()
7066

71-
def filter_changed_files(self, changed_files: List[str]) -> List[str]:
67+
def filter_changed_files(self) -> List[str]:
7268
raise NotImplementedError()
7369

74-
def run_linter_tool(
75-
self, files_to_lint: List[str], args: LintArgs
76-
) -> Optional[str]:
70+
def run_linter_tool(self) -> Optional[str]:
7771
raise NotImplementedError()
7872

79-
def pr_comment_text_for_diff(
80-
self, linter_output: str, files_to_lint: List[str], args: LintArgs
81-
) -> str:
82-
instructions = self.instructions(files_to_lint, args)
73+
def create_comment_text(self, linter_output: str) -> str:
74+
instructions = self.instructions()
8375
return f"""
8476
:warning: {self.friendly_name}, {self.name} found issues in your code. :warning:
8577
@@ -107,7 +99,7 @@ def pr_comment_text_for_diff(
10799
"""
108100

109101
# TODO: Refactor this
110-
def find_comment(self, pr: any) -> any:
102+
def find_comment(self, pr: Any) -> Any:
111103
all_linter_names = list(map(attrgetter("name"), ALL_LINTERS))
112104
other_linter_names = [name for name in all_linter_names if name != self.name]
113105

@@ -124,27 +116,25 @@ def find_comment(self, pr: any) -> any:
124116
return None
125117

126118
def update_pr(self, comment_text: str, args: LintArgs, create_new: bool) -> None:
127-
import github
128-
from github import IssueComment, PullRequest
129-
119+
assert args.repo is not None
130120
repo = github.Github(args.token).get_repo(args.repo)
131121
pr = repo.get_issue(args.issue_number).as_pull_request()
132122

133-
comment_text = self.comment_tag + "\n\n" + comment_text
123+
comment_text = f"{self.comment_tag}\n\n{comment_text}"
134124

135125
existing_comment = self.find_comment(pr)
136126

137-
if create_new or existing_comment:
138-
self.comment = {"body": comment_text}
139127
if existing_comment:
140-
self.comment["id"] = existing_comment.id
128+
self.comment = {"body": comment_text, "id": existing_comment.id}
129+
elif create_new:
130+
self.comment = {"body": comment_text}
141131

142132

143-
def run(self, args: LintArgs) -> bool:
133+
def run(self, changed_files: List[str], args: LintArgs) -> bool:
144134
if args.verbose:
145-
print(f"got changed files: {args.changed_files}")
135+
print(f"got changed files: {changed_files}")
146136

147-
files_to_lint = self.filter_changed_files(args.changed_files)
137+
files_to_lint = self.filter_changed_files()
148138

149139
if not files_to_lint and args.verbose:
150140
print("no modified files found")
@@ -153,7 +143,7 @@ def run(self, args: LintArgs) -> bool:
153143
linter_output = None
154144

155145
if files_to_lint:
156-
linter_output = self.run_linter_tool(files_to_lint, args)
146+
linter_output = self.run_linter_tool()
157147
if linter_output:
158148
is_success = False
159149

@@ -170,11 +160,11 @@ def run(self, args: LintArgs) -> bool:
170160
else:
171161
if should_update_gh:
172162
if linter_output:
173-
comment_text = self.pr_comment_text_for_diff(
174-
linter_output, files_to_lint, args
175-
)
163+
comment_text = self.create_comment_text(linter_output)
176164
self.update_pr(comment_text, args, create_new=True)
177165
else:
166+
# The linter failed but didn't output a result (e.g. some sort of
167+
# infrastructure failure).
178168
comment_text = (
179169
f":warning: The {self.friendly_name} failed without printing "
180170
"an output. Check the logs for output. :warning:"
@@ -193,14 +183,14 @@ def run(self, args: LintArgs) -> bool:
193183

194184

195185
class ClangTidyLintHelper(LintHelper):
196-
name = "clang-tidy"
197-
friendly_name = "C/C++ code linter"
186+
name: Final[str] = "clang-tidy"
187+
friendly_name: Final[str] = "C/C++ code linter"
198188

199189
def instructions(self, cpp_files: List[str], args: LintArgs) -> str:
200190
files_str = " ".join(cpp_files)
201191
return f"""
202192
git diff -U0 origin/main...HEAD -- {files_str} |
203-
python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \\
193+
python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \
204194
-path {args.build_path} -p1 -quiet"""
205195

206196
def filter_changed_files(self, changed_files: List[str]) -> List[str]:
@@ -211,7 +201,7 @@ def filter_changed_files(self, changed_files: List[str]) -> List[str]:
211201
filtered_files = []
212202
for filepath in clang_tidy_changed_files:
213203
_, ext = os.path.splitext(filepath)
214-
if ext not in (".cpp", ".c", ".h", ".hpp", ".hxx", ".cxx"):
204+
if ext not in (".c", ".cpp", ".cxx", ".h", ".hpp", ".hxx"):
215205
continue
216206
if not self._should_lint_file(filepath):
217207
continue
@@ -277,6 +267,11 @@ def run_linter_tool(self, cpp_files: List[str], args: LintArgs) -> Optional[str]
277267
return clean_output
278268

279269
def _clean_clang_tidy_output(self, output: str) -> Optional[str]:
270+
"""
271+
- Remove 'Running clang-tidy in X threads...' line
272+
- Remove 'N warnings generated.' line
273+
- Strip leading workspace path from file paths
274+
"""
280275
if not output or output == "No relevant changes found.":
281276
return None
282277

@@ -287,6 +282,7 @@ def _clean_clang_tidy_output(self, output: str) -> Optional[str]:
287282
if line.startswith("Running clang-tidy in") or line.endswith("generated."):
288283
continue
289284

285+
# Remove everything up to rightmost "llvm-project/" for correct files names
290286
idx = line.rfind("llvm-project/")
291287
if idx != -1:
292288
line = line[idx + len("llvm-project/") :]
@@ -298,58 +294,7 @@ def _clean_clang_tidy_output(self, output: str) -> Optional[str]:
298294
return None
299295

300296

301-
class Doc8LintHelper(LintHelper):
302-
name = "doc8"
303-
friendly_name = "documentation linter"
304-
305-
def instructions(self, doc_files: List[str], args: LintArgs) -> str:
306-
files_str = " ".join(doc_files)
307-
return f"doc8 -q {files_str}"
308-
309-
def filter_changed_files(self, changed_files: List[str]) -> List[str]:
310-
filtered_files = []
311-
for filepath in changed_files:
312-
_, ext = os.path.splitext(filepath)
313-
if ext not in (".rst"):
314-
continue
315-
if not filepath.startswith("clang-tools-extra/docs/clang-tidy/checks/"):
316-
continue
317-
if os.path.exists(filepath):
318-
filtered_files.append(filepath)
319-
return filtered_files
320-
321-
def run_linter_tool(self, doc_files: List[str], args: LintArgs) -> Optional[str]:
322-
if not doc_files:
323-
return None
324-
325-
doc8_cmd = [args.doc8_binary, "-q"] + doc_files
326-
327-
if args.verbose:
328-
print(f"Running doc8: {' '.join(doc8_cmd)}")
329-
330-
proc = subprocess.run(
331-
doc8_cmd,
332-
stdout=subprocess.PIPE,
333-
stderr=subprocess.PIPE,
334-
text=True,
335-
check=False,
336-
)
337-
338-
if proc.returncode == 0:
339-
return None
340-
341-
output = proc.stdout.strip()
342-
if output:
343-
return output
344-
345-
error_output = proc.stderr.strip()
346-
if error_output:
347-
return error_output
348-
349-
return f"doc8 exited with return code {proc.returncode} but no output."
350-
351-
352-
ALL_LINTERS = (ClangTidyLintHelper(), Doc8LintHelper())
297+
ALL_LINTERS = (ClangTidyLintHelper(),)
353298

354299

355300
if __name__ == "__main__":
@@ -390,42 +335,39 @@ def run_linter_tool(self, doc_files: List[str], args: LintArgs) -> Optional[str]
390335
default="clang-tidy",
391336
help="Path to clang-tidy binary",
392337
)
393-
parser.add_argument(
394-
"--doc8-binary",
395-
type=str,
396-
default="doc8",
397-
help="Path to doc8 binary",
398-
)
399338
parser.add_argument(
400339
"--verbose", action="store_true", default=True, help="Verbose output"
401340
)
402341

403342
parsed_args = parser.parse_args()
404343
args = LintArgs(parsed_args)
405344

345+
changed_files = []
346+
if args.changed_files:
347+
changed_files = args.changed_files.split(",")
348+
406349
overall_success = True
407-
failed_linters = []
408-
all_comments = []
350+
failed_linters: List[str] = []
351+
all_comments: List[Dict[str, Any]] = []
409352

410353
for linter in ALL_LINTERS:
411354
if args.verbose:
412355
print(f"running linter {linter.name}")
413356

414-
linter_passed = linter.run(args)
357+
linter_passed = linter.run(changed_files, args)
415358
if not linter_passed:
416359
overall_success = False
417360
failed_linters.append(linter.name)
418361
if args.verbose:
419362
print(f"linter {linter.name} failed")
420363

364+
# Write comments file if we have a comment
421365
if linter.comment:
422366
if args.verbose:
423367
print(f"linter {linter.name} has comment: {linter.comment}")
424368
all_comments.append(linter.comment)
425369

426370
if len(all_comments):
427-
import json
428-
429371
existing_comments = []
430372
if os.path.exists("comments"):
431373
try:

0 commit comments

Comments
 (0)