Index: clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py =================================================================== --- clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py +++ clang-tools-extra/trunk/clang-tidy/tool/run-clang-tidy.py @@ -36,6 +36,7 @@ from __future__ import print_function import argparse +import glob import json import multiprocessing import os @@ -47,6 +48,7 @@ import tempfile import threading import traceback +import yaml def find_compilation_database(path): @@ -89,6 +91,31 @@ return start +def merge_replacement_files(tmpdir, mergefile): + """Merge all replacement files in a directory into a single file""" + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey="Diagnostics" + merged=[] + for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')): + content = yaml.safe_load(open(replacefile, 'r')) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = { 'MainSourceFile': '', mergekey: merged } + with open(mergefile, 'w') as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, 'w').close() + + def check_clang_apply_replacements_binary(args): """Checks if invoking supplied clang-apply-replacements binary works.""" try: @@ -101,7 +128,7 @@ def apply_fixes(args, tmpdir): - """Calls clang-apply-fixes on a given directory. Deletes the dir when done.""" + """Calls clang-apply-fixes on a given directory.""" invocation = [args.clang_apply_replacements_binary] if args.format: invocation.append('-format') @@ -143,6 +170,9 @@ 'headers to output diagnostics from. Diagnostics from ' 'the main file of each translation unit are always ' 'displayed.') + parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes', + help='Create a yaml file to store suggested fixes in, ' + 'which can be applied with clang-apply-replacements.') parser.add_argument('-j', type=int, default=0, help='number of tidy instances to be run in parallel.') parser.add_argument('files', nargs='*', default=['.*'], @@ -194,7 +224,7 @@ max_task = multiprocessing.cpu_count() tmpdir = None - if args.fix: + if args.fix or args.export_fixes: check_clang_apply_replacements_binary(args) tmpdir = tempfile.mkdtemp() @@ -222,24 +252,32 @@ # This is a sad hack. Unfortunately subprocess goes # bonkers with ctrl-c and we start forking merrily. print('\nCtrl-C detected, goodbye.') - if args.fix: + if tmpdir: shutil.rmtree(tmpdir) os.kill(0, 9) + return_code = 0 + if args.export_fixes: + print('Writing fixes to ' + args.export_fixes + ' ...') + try: + merge_replacement_files(tmpdir, args.export_fixes) + except: + print('Error exporting fixes.\n', file=sys.stderr) + traceback.print_exc() + return_code=1 + if args.fix: print('Applying fixes ...') - successfully_applied = False - try: apply_fixes(args, tmpdir) - successfully_applied = True except: print('Error applying fixes.\n', file=sys.stderr) traceback.print_exc() + return_code=1 + if tmpdir: shutil.rmtree(tmpdir) - if not successfully_applied: - sys.exit(1) + sys.exit(return_code) if __name__ == '__main__': main()