11 from __future__
import print_function
21 filename = os.path.join(module_path,
'CMakeLists.txt')
22 with open(filename,
'r') as f: 25 cpp_file = check_name_camel + '.cpp' 29 if line.strip() == cpp_file:
32 print(
'Updating %s...' % filename)
33 with open(filename,
'w')
as f:
37 cpp_line = line.strip().endswith(
'.cpp')
38 if (
not file_added)
and (cpp_line
or cpp_found):
40 if (line.strip() > cpp_file)
or (
not cpp_line):
41 f.write(
' ' + cpp_file +
'\n')
49 def write_header(module_path, module, namespace, check_name, check_name_camel):
50 check_name_dashes = module +
'-' + check_name
51 filename = os.path.join(module_path, check_name_camel) +
'.h' 52 print(
'Creating %s...' % filename)
53 with open(filename,
'w')
as f:
54 header_guard = (
'LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() +
'_' 55 + check_name_camel.upper() +
'_H')
57 f.write(os.path.basename(filename))
58 f.write(
' - clang-tidy ')
59 f.write(
'-' * max(0, 42 - len(os.path.basename(filename))))
60 f.write(
'*- C++ -*-===//')
63 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 64 // See https://llvm.org/LICENSE.txt for license information. 65 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67 //===----------------------------------------------------------------------===// 69 #ifndef %(header_guard)s 70 #define %(header_guard)s 72 #include "../ClangTidyCheck.h" 76 namespace %(namespace)s { 78 /// FIXME: Write a short description. 80 /// For the user-facing documentation see: 81 /// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html 82 class %(check_name)s : public ClangTidyCheck { 84 %(check_name)s(StringRef Name, ClangTidyContext *Context) 85 : ClangTidyCheck(Name, Context) {} 86 void registerMatchers(ast_matchers::MatchFinder *Finder) override; 87 void check(const ast_matchers::MatchFinder::MatchResult &Result) override; 90 } // namespace %(namespace)s 94 #endif // %(header_guard)s 95 """ % {
'header_guard': header_guard,
96 'check_name': check_name_camel,
97 'check_name_dashes': check_name_dashes,
99 'namespace': namespace})
104 filename = os.path.join(module_path, check_name_camel) +
'.cpp' 105 print(
'Creating %s...' % filename)
106 with open(filename,
'w')
as f:
108 f.write(os.path.basename(filename))
109 f.write(
' - clang-tidy ')
110 f.write(
'-' * max(0, 51 - len(os.path.basename(filename))))
114 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 115 // See https://llvm.org/LICENSE.txt for license information. 116 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 118 //===----------------------------------------------------------------------===// 120 #include "%(check_name)s.h" 121 #include "clang/AST/ASTContext.h" 122 #include "clang/ASTMatchers/ASTMatchFinder.h" 124 using namespace clang::ast_matchers; 128 namespace %(namespace)s { 130 void %(check_name)s::registerMatchers(MatchFinder *Finder) { 131 // FIXME: Add matchers. 132 Finder->addMatcher(functionDecl().bind("x"), this); 135 void %(check_name)s::check(const MatchFinder::MatchResult &Result) { 136 // FIXME: Add callback implementation. 137 const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x"); 138 if (MatchedDecl->getName().startswith("awesome_")) 140 diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome") 142 diag(MatchedDecl->getLocation(), "insert 'awesome'", DiagnosticIDs::Note) 143 << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_"); 146 } // namespace %(namespace)s 149 """ % {
'check_name': check_name_camel,
151 'namespace': namespace})
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_fq_name = module +
'-' + check_name
169 check_decl = (
' CheckFactories.registerCheck<' + check_name_camel +
170 '>(\n "' + check_fq_name +
'");\n')
177 match = re.search(
'#include "(.*)"', line)
180 if match.group(1) > check_name_camel:
182 f.write(
'#include "' + check_name_camel +
'.h"\n')
185 f.write(
'#include "' + check_name_camel +
'.h"\n')
188 if line.strip() ==
'}':
192 match = re.search(
'registerCheck<(.*)> *\( *(?:"([^"]*)")?', line)
195 current_check_name = match.group(2)
196 if current_check_name
is None:
201 match = re.search(
' *"([^"]*)"', line)
203 current_check_name = match.group(1)
204 if current_check_name > check_fq_name:
210 except StopIteration:
216 check_name_dashes = module +
'-' + check_name
217 filename = os.path.normpath(os.path.join(module_path,
218 '../../docs/ReleaseNotes.rst'))
219 with open(filename,
'r') as f: 220 lines = f.readlines() 222 lineMatcher = re.compile('Improvements to clang-tidy')
223 nextSectionMatcher = re.compile(
'Improvements to clang-include-fixer')
224 checkerMatcher = re.compile(
'- New :doc:`(.*)')
226 print(
'Updating %s...' % filename)
227 with open(filename,
'w')
as f:
230 next_header_found =
False 231 add_note_here =
False 235 match = lineMatcher.match(line)
236 match_next = nextSectionMatcher.match(line)
237 match_checker = checkerMatcher.match(line)
239 last_checker = match_checker.group(1)
240 if last_checker > check_name_dashes:
244 next_header_found =
True 252 if line.startswith(
'----'):
256 if header_found
and add_note_here:
257 if not line.startswith(
'----'):
258 f.write(
"""- New :doc:`%s 259 <clang-tidy/checks/%s>` check. 261 FIXME: add release notes. 263 """ % (check_name_dashes, check_name_dashes))
270 def write_test(module_path, module, check_name, test_extension):
271 check_name_dashes = module +
'-' + check_name
272 filename = os.path.normpath(os.path.join(module_path,
'../../test/clang-tidy/checkers',
273 check_name_dashes +
'.' + test_extension))
274 print(
'Creating %s...' % filename)
275 with open(filename,
'w')
as f:
276 f.write(
"""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t 278 // FIXME: Add something that triggers the check here. 280 // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s] 282 // FIXME: Verify the applied fix. 283 // * Make the CHECK patterns specific enough and try to make verified lines 284 // unique to avoid incorrect matches. 285 // * Use {{}} for regular expressions. 286 // CHECK-FIXES: {{^}}void awesome_f();{{$}} 288 // FIXME: Add something that doesn't trigger the check here. 290 """ % {
'check_name_dashes': check_name_dashes})
295 docs_dir = os.path.join(clang_tidy_path,
'../docs/clang-tidy/checks')
296 filename = os.path.normpath(os.path.join(docs_dir,
'list.rst'))
298 with open(filename,
'r') as f: 299 lines = f.readlines() 301 doc_files = list(filter(
lambda s: s.endswith(
'.rst')
and s !=
'list.rst',
302 os.listdir(docs_dir)))
305 def has_auto_fix(check_name):
306 dirname, _, check_name = check_name.partition(
"-")
308 checkerCode = os.path.join(dirname,
get_camel_name(check_name)) +
".cpp" 310 if not os.path.isfile(checkerCode):
313 with open(checkerCode)
as f:
315 if 'FixItHint' in code
or "ReplacementText" in code
or "fixit" in code:
320 def process_doc(doc_file):
321 check_name = doc_file.replace(
'.rst',
'')
323 with open(os.path.join(docs_dir, doc_file),
'r') as doc: 325 match = re.search('.*:orphan:.*', content)
331 match = re.search(
'.*:http-equiv=refresh: \d+;URL=(.*).html.*',
334 return check_name, match
336 def format_link(doc_file):
337 check_name, match = process_doc(doc_file)
338 if not match
and check_name:
339 return ' `%(check)s <%(check)s.html>`_,%(autofix)s\n' % {
341 'autofix': has_auto_fix(check_name)
346 def format_link_alias(doc_file):
347 check_name, match = process_doc(doc_file)
348 if match
and check_name:
349 if match.group(1) ==
'https://clang.llvm.org/docs/analyzer/checkers':
350 title_redirect =
'Clang Static Analyzer' 352 title_redirect = match.group(1)
354 return ' `%(check)s <%(check)s.html>`_, `%(title)s <%(target)s.html>`_,%(autofix)s\n' % {
356 'target': match.group(1),
357 'title': title_redirect,
358 'autofix': has_auto_fix(match.group(1))
362 checks = map(format_link, doc_files)
363 checks_alias = map(format_link_alias, doc_files)
365 print(
'Updating %s...' % filename)
366 with open(filename,
'w')
as f:
369 if line.strip() ==
".. csv-table::":
371 f.write(
' :header: "Name", "Offers fixes"\n\n')
375 f.write(
'.. csv-table:: Aliases..\n')
376 f.write(
' :header: "Name", "Redirect", "Offers fixes"\n\n')
377 f.writelines(checks_alias)
383 check_name_dashes = module +
'-' + check_name
384 filename = os.path.normpath(os.path.join(
385 module_path,
'../../docs/clang-tidy/checks/', check_name_dashes +
'.rst'))
386 print(
'Creating %s...' % filename)
387 with open(filename,
'w')
as f:
388 f.write(
""".. title:: clang-tidy - %(check_name_dashes)s 390 %(check_name_dashes)s 393 FIXME: Describe what patterns does the check detect and why. Give examples. 394 """ % {
'check_name_dashes': check_name_dashes,
395 'underline':
'=' * len(check_name_dashes)})
399 return ''.
join(map(
lambda elem: elem.capitalize(),
400 check_name.split(
'-'))) +
'Check' 404 language_to_extension = {
410 parser = argparse.ArgumentParser()
414 help=
'just update the list of documentation files, then exit')
417 help=
'language to use for new check (defaults to c++)',
418 choices=language_to_extension.keys(),
424 help=
'module directory under which to place the new tidy check (e.g., misc)')
428 help=
'name of new tidy check to add (e.g. foo-do-the-stuff)')
429 args = parser.parse_args()
435 if not args.module
or not args.check:
436 print(
'Module and check must be specified.')
441 check_name = args.check
443 if check_name.startswith(module):
444 print(
'Check name "%s" must not start with the module "%s". Exiting.' % (
447 clang_tidy_path = os.path.dirname(sys.argv[0])
448 module_path = os.path.join(clang_tidy_path, module)
455 namespace = module +
'_check' 459 write_header(module_path, module, namespace, check_name, check_name_camel)
461 adapt_module(module_path, module, check_name, check_name_camel)
463 test_extension = language_to_extension.get(args.language)
464 write_test(module_path, module, check_name, test_extension)
467 print(
'Done. Now it\'s your turn!')
470 if __name__ ==
'__main__':
def write_header(module_path, module, namespace, check_name, check_name_camel)
def write_test(module_path, module, check_name, test_extension)
def write_implementation(module_path, module, namespace, check_name_camel)
def write_docs(module_path, module, check_name)
def add_release_notes(module_path, module, check_name)
def get_camel_name(check_name)
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)