13 ClangTidy Diff Checker 14 ====================== 16 This script reads input from a unified diff, runs clang-tidy on all changed 17 files and outputs clang-tidy warnings in changed lines only. This is useful to 18 detect clang-tidy regressions in the lines touched by a specific patch. 19 Example usage for git/svn users: 21 git diff -U0 HEAD^ | clang-tidy-diff.py -p1 22 svn diff --diff-cmd=diff -x-U0 | \ 23 clang-tidy-diff.py -fix -checks=-*,modernize-use-override 35 parser = argparse.ArgumentParser(description=
36 'Run clang-tidy against changed files, and ' 37 'output diagnostics only for modified ' 39 parser.add_argument(
'-clang-tidy-binary', metavar=
'PATH',
41 help=
'path to clang-tidy binary')
42 parser.add_argument(
'-p', metavar=
'NUM', default=0,
43 help=
'strip the smallest prefix containing P slashes')
44 parser.add_argument(
'-regex', metavar=
'PATTERN', default=
None,
45 help=
'custom pattern selecting file paths to check ' 46 '(case sensitive, overrides -iregex)')
47 parser.add_argument(
'-iregex', metavar=
'PATTERN', default=
48 r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)',
49 help=
'custom pattern selecting file paths to check ' 50 '(case insensitive, overridden by -regex)')
52 parser.add_argument(
'-fix', action=
'store_true', default=
False,
53 help=
'apply suggested fixes')
54 parser.add_argument(
'-checks',
55 help=
'checks filter, when not specified, use clang-tidy ' 58 parser.add_argument(
'-path', dest=
'build_path',
59 help=
'Path used to read a compile command database.')
60 parser.add_argument(
'-export-fixes', metavar=
'FILE', dest=
'export_fixes',
61 help=
'Create a yaml file to store suggested fixes in, ' 62 'which can be applied with clang-apply-replacements.')
63 parser.add_argument(
'-extra-arg', dest=
'extra_arg',
64 action=
'append', default=[],
65 help=
'Additional argument to append to the compiler ' 67 parser.add_argument(
'-extra-arg-before', dest=
'extra_arg_before',
68 action=
'append', default=[],
69 help=
'Additional argument to prepend to the compiler ' 71 parser.add_argument(
'-quiet', action=
'store_true', default=
False,
72 help=
'Run clang-tidy in quiet mode')
76 clang_tidy_args.extend(argv[argv.index(
'--'):])
77 argv = argv[:argv.index(
'--')]
79 args = parser.parse_args(argv)
84 for line
in sys.stdin:
85 match = re.search(
'^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % args.p, line)
87 filename = match.group(2)
91 if args.regex
is not None:
92 if not re.match(
'^%s$' % args.regex, filename):
95 if not re.match(
'^%s$' % args.iregex, filename, re.IGNORECASE):
98 match = re.search(
'^@@.*\+(\d+)(,(\d+))?', line)
100 start_line = int(match.group(1))
103 line_count = int(match.group(3))
106 end_line = start_line + line_count - 1;
107 lines_by_file.setdefault(filename, []).append([start_line, end_line])
109 if len(lines_by_file) == 0:
110 print(
"No relevant changes found.")
113 line_filter_json = json.dumps(
114 [{
"name" : name,
"lines" : lines_by_file[name]}
for name
in lines_by_file],
115 separators = (
',',
':'))
118 if sys.platform ==
'win32':
119 line_filter_json=re.sub(
r'"',
r'"""', line_filter_json)
124 command = [args.clang_tidy_binary]
125 command.append(
'-line-filter=' + quote + line_filter_json + quote)
127 command.append(
'-fix')
128 if args.export_fixes:
129 command.append(
'-export-fixes=' + args.export_fixes)
130 if args.checks !=
'':
131 command.append(
'-checks=' + quote + args.checks + quote)
133 command.append(
'-quiet')
134 if args.build_path
is not None:
135 command.append(
'-p=%s' % args.build_path)
136 command.extend(lines_by_file.keys())
137 for arg
in args.extra_arg:
138 command.append(
'-extra-arg=%s' % arg)
139 for arg
in args.extra_arg_before:
140 command.append(
'-extra-arg-before=%s' % arg)
141 command.extend(clang_tidy_args)
143 sys.exit(subprocess.call(
' '.
join(command), shell=
True))
145 if __name__ ==
'__main__':
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)