Index: clang-tidy/tool/run-clang-tidy.py =================================================================== --- clang-tidy/tool/run-clang-tidy.py +++ clang-tidy/tool/run-clang-tidy.py @@ -7,7 +7,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # #===------------------------------------------------------------------------===# -# FIXME: Integrate with clang-tidy-diff.py """ Parallel clang-tidy runner @@ -56,6 +55,10 @@ else: import queue as queue +quote = "" +if sys.platform != 'win32': + quote = "'" + def find_compilation_database(path): """Adjusts the directory until a compilation database is found.""" result = './' @@ -73,7 +76,7 @@ return os.path.normpath(os.path.join(directory, f)) -def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, +def get_tidy_invocation(f, lines, clang_tidy_binary, checks, tmpdir, build_path, header_filter, extra_arg, extra_arg_before, quiet, config): """Gets a command line for clang-tidy.""" @@ -83,6 +86,12 @@ else: # Show warnings in all in-project headers by default. start.append('-header-filter=^' + build_path + '/.*') + + if lines is not None: + line_json = json.dumps([{"name": f, "lines": lines}], separators=(',', ':')) + # Run clang-tidy on files containing changes. + start.append('-line-filter=' + quote + line_json + quote) + if checks: start.append('-checks=' + checks) if tmpdir is not None: @@ -155,8 +164,9 @@ def run_tidy(args, tmpdir, build_path, queue, lock, failed_files): """Takes filenames out of queue and runs clang-tidy on them.""" while True: - name = queue.get() - invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, + (name, lines) = queue.get() + invocation = get_tidy_invocation(name, lines, + args.clang_tidy_binary, args.checks, tmpdir, build_path, args.header_filter, args.extra_arg, args.extra_arg_before, args.quiet, args.config) @@ -172,11 +182,42 @@ queue.task_done() +def get_changed_lines(prefix_len): + iregex = '^%s$' % r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)' + lines_by_file = {} + filename = None + for line in sys.stdin: + match = re.search('^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % prefix_len, line) + if match: + filename = match.group(2) + if filename is None: + continue + + if not re.match(iregex, filename, re.IGNORECASE): + continue + + match = re.search('^@@.*\+(\d+)(,(\d+))?', line) + if match: + start_line = int(match.group(1)) + line_count = 1 + if match.group(3): + line_count = int(match.group(3)) + if line_count == 0: + continue + end_line = start_line + line_count - 1 + lines_by_file.setdefault(filename, []).append([start_line, end_line]) + + return lines_by_file + + def main(): parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' 'in a compilation database. Requires ' 'clang-tidy and clang-apply-replacements in ' '$PATH.') + parser.add_argument('--diff', default=None, const=2, nargs='?', type=int, + help='check only the diff read from stdin.' + ' Strip the smallest prefix containing DIFF[=2] slashes') parser.add_argument('-clang-tidy-binary', metavar='PATH', default='clang-tidy', help='path to clang-tidy binary') @@ -244,11 +285,6 @@ print("Unable to run clang-tidy.", file=sys.stderr) sys.exit(1) - # Load the database and extract all files. - database = json.load(open(os.path.join(build_path, db_path))) - files = [make_absolute(entry['file'], entry['directory']) - for entry in database] - max_task = args.j if max_task == 0: max_task = multiprocessing.cpu_count() @@ -260,6 +296,16 @@ # Build up a big regexy filter from all command line arguments. file_name_re = re.compile('|'.join(args.files)) + files = None + changed_lines = None + if args.diff is not None: + changed_lines = get_changed_lines(args.diff) + files = [(k, v) for k,v in changed_lines.items() if file_name_re.search(k)] + else: + # Load the database and extract affected files. + database = json.load(open(os.path.join(build_path, db_path))) + all_files = (make_absolute(entry['file'], entry['directory']) for entry in database) + files = [(x, None) for x in all_files if file_name_re.search(x)] return_code = 0 try: @@ -276,8 +322,7 @@ # Fill the queue with files. for name in files: - if file_name_re.search(name): - task_queue.put(name) + task_queue.put(name) # Wait for all threads to be done. task_queue.join()