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(
'-extra-arg', dest=
'extra_arg',
61 action=
'append', default=[],
62 help=
'Additional argument to append to the compiler '
64 parser.add_argument(
'-extra-arg-before', dest=
'extra_arg_before',
65 action=
'append', default=[],
66 help=
'Additional argument to prepend to the compiler '
68 parser.add_argument(
'-quiet', action=
'store_true', default=
False,
69 help=
'Run clang-tidy in quiet mode')
73 clang_tidy_args.extend(argv[argv.index(
'--'):])
74 argv = argv[:argv.index(
'--')]
76 args = parser.parse_args(argv)
81 for line
in sys.stdin:
82 match = re.search(
'^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % args.p, line)
84 filename = match.group(2)
88 if args.regex
is not None:
89 if not re.match(
'^%s$' % args.regex, filename):
92 if not re.match(
'^%s$' % args.iregex, filename, re.IGNORECASE):
95 match = re.search(
'^@@.*\+(\d+)(,(\d+))?', line)
97 start_line = int(match.group(1))
100 line_count = int(match.group(3))
103 end_line = start_line + line_count - 1;
104 lines_by_file.setdefault(filename, []).append([start_line, end_line])
106 if len(lines_by_file) == 0:
107 print(
"No relevant changes found.")
110 line_filter_json = json.dumps(
111 [{
"name" : name,
"lines" : lines_by_file[name]}
for name
in lines_by_file],
112 separators = (
',',
':'))
115 if sys.platform ==
'win32':
116 line_filter_json=re.sub(
r'"',
r'"""', line_filter_json)
121 command = [args.clang_tidy_binary]
122 command.append(
'-line-filter=' + quote + line_filter_json + quote)
124 command.append(
'-fix')
125 if args.checks !=
'':
126 command.append(
'-checks=' + quote + args.checks + quote)
128 command.append(
'-quiet')
129 if args.build_path
is not None:
130 command.append(
'-p=%s' % args.build_path)
131 command.extend(lines_by_file.keys())
132 for arg
in args.extra_arg:
133 command.append(
'-extra-arg=%s' % arg)
134 for arg
in args.extra_arg_before:
135 command.append(
'-extra-arg-before=%s' % arg)
136 command.extend(clang_tidy_args)
138 sys.exit(subprocess.call(
' '.
join(command), shell=
True))
140 if __name__ ==
'__main__':
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)