Skip to content

Commit

Permalink
[clang-tidy] Add option to export fixes to run-clang-tidy.py
Browse files Browse the repository at this point in the history
This patch adds the option to keep the list of proposed fixes even though they
should not be applied.

This allows to detect possible fixes using the parallelised run-clang-tidy.py
and apply them using clang-apply-replacements at a later time.
Essentially the patch causes the individual temporary yaml files by the parallel
clang-tidy instances to be merged into one user-defined file.

Patch by Michael F. Herbst!
Differential revision: https://reviews.llvm.org/D31326

llvm-svn: 308726
  • Loading branch information
alexfh committed Jul 21, 2017
1 parent 06451fa commit 16300e1
Showing 1 changed file with 46 additions and 8 deletions.
54 changes: 46 additions & 8 deletions clang-tools-extra/clang-tidy/tool/run-clang-tidy.py
Expand Up @@ -36,6 +36,7 @@

from __future__ import print_function
import argparse
import glob
import json
import multiprocessing
import os
Expand All @@ -47,6 +48,7 @@
import tempfile
import threading
import traceback
import yaml


def find_compilation_database(path):
Expand Down Expand Up @@ -89,6 +91,31 @@ def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
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:
Expand All @@ -101,7 +128,7 @@ def check_clang_apply_replacements_binary(args):


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')
Expand Down Expand Up @@ -143,6 +170,9 @@ def main():
'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=['.*'],
Expand Down Expand Up @@ -194,7 +224,7 @@ def main():
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()

Expand Down Expand Up @@ -222,24 +252,32 @@ def main():
# 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()

0 comments on commit 16300e1

Please sign in to comment.