12 from __future__
import print_function
22 filename = os.path.join(module_path,
'CMakeLists.txt')
23 with open(filename,
'r') as f: 26 cpp_file = check_name_camel + '.cpp' 30 if line.strip() == cpp_file:
33 print(
'Updating %s...' % filename)
34 with open(filename,
'w')
as f:
38 cpp_line = line.strip().endswith(
'.cpp')
39 if (
not file_added)
and (cpp_line
or cpp_found):
41 if (line.strip() > cpp_file)
or (
not cpp_line):
42 f.write(
' ' + cpp_file +
'\n')
50 def write_header(module_path, module, check_name, check_name_camel):
51 check_name_dashes = module +
'-' + check_name
52 filename = os.path.join(module_path, check_name_camel) +
'.h' 53 print(
'Creating %s...' % filename)
54 with open(filename,
'w')
as f:
55 header_guard = (
'LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() +
'_' 56 + check_name_camel.upper() +
'_H')
58 f.write(os.path.basename(filename))
59 f.write(
' - clang-tidy ')
60 f.write(
'-' * max(0, 42 - len(os.path.basename(filename))))
61 f.write(
'*- C++ -*-===//')
64 // The LLVM Compiler Infrastructure 66 // This file is distributed under the University of Illinois Open Source 67 // License. See LICENSE.TXT for details. 69 //===----------------------------------------------------------------------===// 71 #ifndef %(header_guard)s 72 #define %(header_guard)s 74 #include "../ClangTidy.h" 78 namespace %(module)s { 80 /// FIXME: Write a short description. 82 /// For the user-facing documentation see: 83 /// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html 84 class %(check_name)s : public ClangTidyCheck { 86 %(check_name)s(StringRef Name, ClangTidyContext *Context) 87 : ClangTidyCheck(Name, Context) {} 88 void registerMatchers(ast_matchers::MatchFinder *Finder) override; 89 void check(const ast_matchers::MatchFinder::MatchResult &Result) override; 92 } // namespace %(module)s 96 #endif // %(header_guard)s 97 """ % {
'header_guard': header_guard,
98 'check_name': check_name_camel,
99 'check_name_dashes': check_name_dashes,
105 filename = os.path.join(module_path, check_name_camel) +
'.cpp' 106 print(
'Creating %s...' % filename)
107 with open(filename,
'w')
as f:
109 f.write(os.path.basename(filename))
110 f.write(
' - clang-tidy ')
111 f.write(
'-' * max(0, 51 - len(os.path.basename(filename))))
115 // The LLVM Compiler Infrastructure 117 // This file is distributed under the University of Illinois Open Source 118 // License. See LICENSE.TXT for details. 120 //===----------------------------------------------------------------------===// 122 #include "%(check_name)s.h" 123 #include "clang/AST/ASTContext.h" 124 #include "clang/ASTMatchers/ASTMatchFinder.h" 126 using namespace clang::ast_matchers; 130 namespace %(module)s { 132 void %(check_name)s::registerMatchers(MatchFinder *Finder) { 133 // FIXME: Add matchers. 134 Finder->addMatcher(functionDecl().bind("x"), this); 137 void %(check_name)s::check(const MatchFinder::MatchResult &Result) { 138 // FIXME: Add callback implementation. 139 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x"); 140 if (MatchedDecl->getName().startswith("awesome_")) 142 diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome") 144 << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); 147 } // namespace %(module)s 150 """ % {
'check_name': check_name_camel,
156 modulecpp = list(filter(
157 lambda p: p.lower() == module.lower() +
'tidymodule.cpp',
158 os.listdir(module_path)))[0]
159 filename = os.path.join(module_path, modulecpp)
160 with open(filename,
'r') as f: 161 lines = f.readlines() 163 print('Updating %s...' % filename)
164 with open(filename,
'w')
as f:
168 check_decl = (
' CheckFactories.registerCheck<' + check_name_camel +
169 '>(\n "' + module +
'-' + check_name +
'");\n')
173 match = re.search(
'#include "(.*)"', line)
176 if match.group(1) > check_name_camel:
178 f.write(
'#include "' + check_name_camel +
'.h"\n')
181 f.write(
'#include "' + check_name_camel +
'.h"\n')
184 if line.strip() ==
'}':
188 match = re.search(
'registerCheck<(.*)>', line)
189 if match
and match.group(1) > check_name_camel:
197 check_name_dashes = module +
'-' + check_name
198 filename = os.path.normpath(os.path.join(module_path,
199 '../../docs/ReleaseNotes.rst'))
200 with open(filename,
'r') as f: 201 lines = f.readlines() 203 lineMatcher = re.compile('Improvements to clang-tidy')
204 nextSectionMatcher = re.compile(
'Improvements to include-fixer')
205 checkerMatcher = re.compile(
'- New :doc:`(.*)')
207 print(
'Updating %s...' % filename)
208 with open(filename,
'w')
as f:
211 next_header_found =
False 212 add_note_here =
False 216 match = lineMatcher.match(line)
217 match_next = nextSectionMatcher.match(line)
218 match_checker = checkerMatcher.match(line)
220 last_checker = match_checker.group(1)
221 if last_checker > check_name_dashes:
225 next_header_found =
True 233 if line.startswith(
'----'):
237 if header_found
and add_note_here:
238 if not line.startswith(
'----'):
239 f.write(
"""- New :doc:`%s 240 <clang-tidy/checks/%s>` check. 242 FIXME: add release notes. 244 """ % (check_name_dashes, check_name_dashes))
251 def write_test(module_path, module, check_name, test_extension):
252 check_name_dashes = module +
'-' + check_name
253 filename = os.path.normpath(os.path.join(module_path,
'../../test/clang-tidy',
254 check_name_dashes +
'.' + test_extension))
255 print(
'Creating %s...' % filename)
256 with open(filename,
'w')
as f:
257 f.write(
"""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t 259 // FIXME: Add something that triggers the check here. 261 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s] 263 // FIXME: Verify the applied fix. 264 // * Make the CHECK patterns specific enough and try to make verified lines 265 // unique to avoid incorrect matches. 266 // * Use {{}} for regular expressions. 267 // CHECK-FIXES: {{^}}void awesome_f();{{$}} 269 // FIXME: Add something that doesn't trigger the check here. 271 """ % {
'check_name_dashes': check_name_dashes})
276 docs_dir = os.path.join(clang_tidy_path,
'../docs/clang-tidy/checks')
277 filename = os.path.normpath(os.path.join(docs_dir,
'list.rst'))
278 with open(filename,
'r') as f: 279 lines = f.readlines() 280 doc_files = list(filter(lambda s: s.endswith(
'.rst')
and s !=
'list.rst',
281 os.listdir(docs_dir)))
284 def format_link(doc_file):
285 check_name = doc_file.replace(
'.rst',
'')
286 with open(os.path.join(docs_dir, doc_file),
'r') as doc: 288 match = re.search('.*:orphan:.*', content)
292 match = re.search(
'.*:http-equiv=refresh: \d+;URL=(.*).html.*',
295 return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
297 'target': match.group(1)
299 return ' %s\n' % check_name
301 checks = map(format_link, doc_files)
303 print(
'Updating %s...' % filename)
304 with open(filename,
'w')
as f:
307 if line.startswith(
'.. toctree::'):
314 check_name_dashes = module +
'-' + check_name
315 filename = os.path.normpath(os.path.join(
316 module_path,
'../../docs/clang-tidy/checks/', check_name_dashes +
'.rst'))
317 print(
'Creating %s...' % filename)
318 with open(filename,
'w')
as f:
319 f.write(
""".. title:: clang-tidy - %(check_name_dashes)s 321 %(check_name_dashes)s 324 FIXME: Describe what patterns does the check detect and why. Give examples. 325 """ % {
'check_name_dashes': check_name_dashes,
326 'underline':
'=' * len(check_name_dashes)})
330 language_to_extension = {
336 parser = argparse.ArgumentParser()
340 help=
'just update the list of documentation files, then exit')
343 help=
'language to use for new check (defaults to c++)',
344 choices=language_to_extension.keys(),
350 help=
'module directory under which to place the new tidy check (e.g., misc)')
354 help=
'name of new tidy check to add (e.g. foo-do-the-stuff)')
355 args = parser.parse_args()
361 if not args.module
or not args.check:
362 print(
'Module and check must be specified.')
367 check_name = args.check
369 if check_name.startswith(module):
370 print(
'Check name "%s" must not start with the module "%s". Exiting.' % (
373 check_name_camel =
''.
join(map(
lambda elem: elem.capitalize(),
374 check_name.split(
'-'))) +
'Check' 375 clang_tidy_path = os.path.dirname(sys.argv[0])
376 module_path = os.path.join(clang_tidy_path, module)
380 write_header(module_path, module, check_name, check_name_camel)
382 adapt_module(module_path, module, check_name, check_name_camel)
384 test_extension = language_to_extension.get(args.language)
385 write_test(module_path, module, check_name, test_extension)
388 print(
'Done. Now it\'s your turn!')
391 if __name__ ==
'__main__':
def write_test(module_path, module, check_name, test_extension)
def write_header(module_path, module, check_name, check_name_camel)
def write_docs(module_path, module, check_name)
def add_release_notes(module_path, module, check_name)
def write_implementation(module_path, module, check_name_camel)
def update_checks_list(clang_tidy_path)
def adapt_module(module_path, module, check_name, check_name_camel)
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
def adapt_cmake(module_path, check_name_camel)