diff --git a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py --- a/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py +++ b/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py @@ -35,6 +35,7 @@ import tempfile import threading import traceback +from typing import Optional try: import yaml @@ -49,7 +50,7 @@ import queue as queue -def run_tidy(task_queue, lock, timeout): +def run_tidy(task_queue, lock, timeout, result_queue: Optional[queue.Queue]): watchdog = None while True: command = task_queue.get() @@ -65,8 +66,11 @@ stdout, stderr = proc.communicate() with lock: - sys.stdout.write(stdout.decode('utf-8') + '\n') - sys.stdout.flush() + if result_queue: + parse_clang_tidy_output(result_queue, stdout.decode('utf-8')) + else: + sys.stdout.write(stdout.decode('utf-8') + '\n') + sys.stdout.flush() if stderr: sys.stderr.write(stderr.decode('utf-8') + '\n') sys.stderr.flush() @@ -83,9 +87,9 @@ task_queue.task_done() -def start_workers(max_tasks, tidy_caller, task_queue, lock, timeout): +def start_workers(max_tasks, tidy_caller, task_queue, lock, timeout, result_queue): for _ in range(max_tasks): - t = threading.Thread(target=tidy_caller, args=(task_queue, lock, timeout)) + t = threading.Thread(target=tidy_caller, args=(task_queue, lock, timeout, result_queue)) t.daemon = True t.start() @@ -115,6 +119,18 @@ open(mergefile, 'w').close() +def parse_clang_tidy_output(result_queue: queue, clang_tidy_output: str): + """Parse the clang-tidy output and add them to the result queue.""" + _regex_pattern = r"^(?P/[^:]+):\W*(?P\d+):\W*(?P\d+):\W*(?P.*)$" + for finding in re.finditer(_regex_pattern, clang_tidy_output, re.MULTILINE): + result_queue.put({ + 'path': finding['path'], + 'line': int(finding['line']), + 'character': int(finding['character']), + 'check': finding['check'], + }) + + def main(): parser = argparse.ArgumentParser(description= 'Run clang-tidy against changed files, and ' @@ -160,6 +176,8 @@ 'command line.') parser.add_argument('-quiet', action='store_true', default=False, help='Run clang-tidy in quiet mode') + parser.add_argument('-json', action='store_true', dest='json_output', + help='Output clang-tidy results in JSON format.') clang_tidy_args = [] argv = sys.argv[1:] if '--' in argv: @@ -214,8 +232,14 @@ # A lock for console output. lock = threading.Lock() + # if we want JSON output: gather results + result_queue = None + if args.json_output: + result_queue = queue.Queue() + # Run a pool of clang-tidy workers. - start_workers(max_task_count, run_tidy, task_queue, lock, args.timeout) + start_workers(max_task_count, run_tidy, task_queue, + lock, args.timeout, result_queue) # Form the common args list. common_clang_tidy_args = [] @@ -227,7 +251,7 @@ common_clang_tidy_args.append('-quiet') if args.build_path is not None: common_clang_tidy_args.append('-p=%s' % args.build_path) - if args.use_color: + if args.use_color and not args.json_output: common_clang_tidy_args.append('--use-color') for arg in args.extra_arg: common_clang_tidy_args.append('-extra-arg=%s' % arg) @@ -268,6 +292,8 @@ if tmpdir: shutil.rmtree(tmpdir) - + if args.json_output: + json_output = {'findings': list(result_queue.queue)} + json.dump(json_output, sys.stdout, indent=2) if __name__ == '__main__': main()