njames93 created this revision.
njames93 added reviewers: aaron.ballman, Eugene.Zelenko, gribozavr2, alexfh,
hokein.
Herald added subscribers: cfe-commits, xazax.hun.
Herald added a project: clang.
Factored out some similar code in add_new_check.py and rename_check.py.
Made rename_check.py less error prone by targetting string replacements
Haven't linted the code as that is a can of worms given the 100+ different
style options currently used in the python files
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D75134
Files:
clang-tools-extra/clang-tidy/add_new_check.py
clang-tools-extra/clang-tidy/rename_check.py
clang-tools-extra/clang-tidy/utils/__init__.py
clang-tools-extra/clang-tidy/utils/modify_check.py
Index: clang-tools-extra/clang-tidy/utils/modify_check.py
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/utils/modify_check.py
@@ -0,0 +1,122 @@
+import os
+import re
+
+def get_camel_name(check_name):
+ return ''.join(map(lambda elem: elem.capitalize(),
+ check_name.split('-'))) + 'Check'
+
+# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
+# and 'False' if the entry already existed.
+def adapt_cmake(module_path, check_name_camel):
+ filename = os.path.join(module_path, 'CMakeLists.txt')
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+
+ cpp_file = check_name_camel + '.cpp'
+
+ # Figure out whether this check already exists.
+ for line in lines:
+ if line.strip() == cpp_file:
+ return False
+
+ print('Updating %s...' % filename)
+ with open(filename, 'w') as f:
+ cpp_found = False
+ file_added = False
+ for line in lines:
+ cpp_line = line.strip().endswith('.cpp')
+ if (not file_added) and (cpp_line or cpp_found):
+ cpp_found = True
+ if (line.strip() > cpp_file) or (not cpp_line):
+ f.write(' ' + cpp_file + '\n')
+ file_added = True
+ f.write(line)
+
+ return True
+
+# Recreates the list of checks in the docs/clang-tidy/checks directory.
+def update_checks_list(clang_tidy_path):
+ docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
+ filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
+ # Read the content of the current list.rst file
+ with open(filename, 'r') as f:
+ lines = f.readlines()
+ # Get all existing docs
+ doc_files = list(filter(lambda s: s.endswith('.rst') and s != 'list.rst',
+ os.listdir(docs_dir)))
+ doc_files.sort()
+
+ def has_auto_fix(check_name):
+ dirname, _, check_name = check_name.partition("-")
+
+ checkerCode = os.path.join(dirname, get_camel_name(check_name)) + ".cpp"
+
+ if not os.path.isfile(checkerCode):
+ return ""
+
+ with open(checkerCode) as f:
+ code = f.read()
+ if 'FixItHint' in code or "ReplacementText" in code or "fixit" in code:
+ # Some simple heuristics to figure out if a checker has an autofix or not.
+ return ' "Yes"'
+ return ""
+
+ def process_doc(doc_file):
+ check_name = doc_file.replace('.rst', '')
+
+ with open(os.path.join(docs_dir, doc_file), 'r') as doc:
+ content = doc.read()
+ match = re.search('.*:orphan:.*', content)
+
+ if match:
+ # Orphan page, don't list it.
+ return '', ''
+
+ match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
+ content)
+ # Is it a redirect?
+ return check_name, match
+
+ def format_link(doc_file):
+ check_name, match = process_doc(doc_file)
+ if not match and check_name:
+ return ' `%(check)s <%(check)s.html>`_,%(autofix)s\n' % {
+ 'check': check_name,
+ 'autofix': has_auto_fix(check_name)
+ }
+ else:
+ return ''
+
+ def format_link_alias(doc_file):
+ check_name, match = process_doc(doc_file)
+ if match and check_name:
+ if match.group(1) == 'https://clang.llvm.org/docs/analyzer/checkers':
+ title_redirect = 'Clang Static Analyzer'
+ else:
+ title_redirect = match.group(1)
+ # The checker is just a redirect.
+ return ' `%(check)s <%(check)s.html>`_, `%(title)s <%(target)s.html>`_,%(autofix)s\n' % {
+ 'check': check_name,
+ 'target': match.group(1),
+ 'title': title_redirect,
+ 'autofix': has_auto_fix(match.group(1))
+ }
+ return ''
+
+ checks = map(format_link, doc_files)
+ checks_alias = map(format_link_alias, doc_files)
+
+ print('Updating %s...' % filename)
+ with open(filename, 'w') as f:
+ for line in lines:
+ f.write(line)
+ if line.strip() == ".. csv-table::":
+ # We dump the checkers
+ f.write(' :header: "Name", "Offers fixes"\n\n')
+ f.writelines(checks)
+ # and the aliases
+ f.write('\n\n')
+ f.write('.. csv-table:: Aliases..\n')
+ f.write(' :header: "Name", "Redirect", "Offers fixes"\n\n')
+ f.writelines(checks_alias)
+ break
Index: clang-tools-extra/clang-tidy/rename_check.py
===================================================================
--- clang-tools-extra/clang-tidy/rename_check.py
+++ clang-tools-extra/clang-tidy/rename_check.py
@@ -12,6 +12,7 @@
import glob
import os
import re
+from utils.modify_check import *
def replaceInFileRegex(fileName, sFrom, sTo):
@@ -20,13 +21,46 @@
txt = None
with open(fileName, "r") as f:
txt = f.read()
+
+ # hack that prevents using nonlocal to get the return from the callback
+ Replacements = {'Any' : False }
- txt = re.sub(sFrom, sTo, txt)
- print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
- with open(fileName, "w") as f:
- f.write(txt)
+ def callback(match):
+ Replacements['Any'] = True
+ actFrom = match.group()
+ actTo = match.expand(sTo)
+ print("Replacing '%s' -> '%s' in '%s'..." % (actFrom, actTo, fileName))
+ return actTo
+
+ txt = re.sub(sFrom, callback, txt)
+
+
+ if Replacements['Any']:
+ with open(fileName, "w") as f:
+ f.write(txt)
+
+def replaceWordInFile(fileName, sFrom, sTo):
+ if sFrom == sTo:
+ return
+ txt = None
+ with open(fileName, "r") as f:
+ txt = f.read()
+
+ Replacements = {'Any' : False }
+
+ def callback(match):
+ Replacements['Any'] = True
+ return match.expand(r'\1%s\2' % sTo)
+
+ txt = re.sub(r'(^|\s|{0}){1}($|\s|{0})'.format(r'[":;\(\)<>=\.,/\\`\`\[\]]', re.escape(sFrom)), callback, txt)
+
+ if Replacements['Any']:
+ print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
+ with open(fileName, "w") as f:
+ f.write(txt)
def replaceInFile(fileName, sFrom, sTo):
+ print ("From:{}\nTo:{}".format(sFrom, sTo))
if sFrom == sTo:
return
txt = None
@@ -49,17 +83,38 @@
'-' * max(0, 42 - len(os.path.basename(filename))),
'*- C++ -*-===//'])
+def replaceHeaderComment(newHeader):
+ txt = open(newHeader, "r").read()
+ txt = re.sub(r'//===---[ -][^-.].+',
+ generateCommentLineHeader(newHeader), txt)
+ open(newHeader, "w").write(txt)
def generateCommentLineSource(filename):
return ''.join(['//===--- ',
os.path.basename(filename),
- ' - clang-tidy',
- '-' * max(0, 52 - len(os.path.basename(filename))),
+ ' - clang-tidy ',
+ '-' * max(0, 51 - len(os.path.basename(filename))),
'-===//'])
+def replaceSourceComment(newSource):
+ txt = open(newSource, "r").read()
+ txt = re.sub(r'//===---[ -][^-.].+',
+ generateCommentLineSource(newSource), txt)
+ open(newSource, "w").write(txt)
+
+def replaceHeaderGuard(newHeader, new_module, new_check_name_camel):
+ txt = open(newHeader, "r").read()
+ newGuard = '_'.join(['LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY',new_module.upper(), new_check_name_camel.upper(), 'H'])
+
+ txt = re.sub(r'#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY.+', r'#ifndef %s' % newGuard, txt)
+ txt = re.sub(r'#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY.+', r'#define %s' % newGuard, txt)
+ txt = re.sub(r'#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY.+', r'#endif // %s' % newGuard, txt)
+
+ open(newHeader, "w").write(txt)
def fileRename(fileName, sFrom, sTo):
- if sFrom not in fileName or sFrom == sTo:
+ namePart = os.path.split(fileName)[1]
+ if not namePart.startswith(sFrom) or sFrom == sTo:
return fileName
newFileName = fileName.replace(sFrom, sTo)
print("Renaming '%s' -> '%s'..." % (fileName, newFileName))
@@ -93,34 +148,30 @@
'clang-tidy', 'checks', '*'))
return [filename for filename in files if os.path.isfile(filename)]
-# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
-# and 'False' if the entry already existed.
-def adapt_cmake(module_path, check_name_camel):
- filename = os.path.join(module_path, 'CMakeLists.txt')
- with open(filename, 'r') as f:
- lines = f.readlines()
+def getHeaderFile(clang_tidy_path, module_name, check_name):
+ return os.path.join
- cpp_file = check_name_camel + '.cpp'
+def getClangTidyFiles(clang_tidy_path):
+ files = glob.glob(os.path.join(clang_tidy_path, '*'))
+ for dirname in files:
+ if os.path.isdir(dirname):
+ files += glob.glob(os.path.join(dirname, '*'))
+ return [filename for filename in files if os.path.isfile(filename)]
- # Figure out whether this check already exists.
- for line in lines:
- if line.strip() == cpp_file:
- return False
+def getDocFiles(clang_tidy_path):
+ files = glob.glob(os.path.join(clang_tidy_path, '..', 'docs',
+ 'clang-tidy', 'checks', '*'))
+ return [filename for filename in files if os.path.isfile(filename)]
- print('Updating %s...' % filename)
- with open(filename, 'wb') as f:
- cpp_found = False
- file_added = False
- for line in lines:
- cpp_line = line.strip().endswith('.cpp')
- if (not file_added) and (cpp_line or cpp_found):
- cpp_found = True
- if (line.strip() > cpp_file) or (not cpp_line):
- f.write(' ' + cpp_file + '\n')
- file_added = True
- f.write(line)
+def getTestFiles(clang_tidy_path):
+ files = glob.glob(os.path.join(clang_tidy_path, '..', 'test',
+ 'clang-tidy', 'checkers', '*'))
+ return [filename for filename in files if os.path.isfile(filename)]
- return True
+def getUnitTestFiles(clang_tidy_path):
+ files = glob.glob(os.path.join(clang_tidy_path, '..', 'unittests',
+ 'clang-tidy', '*'))
+ return [filename for filename in files if os.path.isfile(filename)]
# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
@@ -238,13 +289,6 @@
clang_tidy_path = os.path.dirname(__file__)
- header_guard_variants = [
- (args.old_check_name.replace('-', '_')).upper() + '_CHECK',
- (old_module + '_' + check_name_camel).upper(),
- (old_module + '_' + new_check_name_camel).upper(),
- args.old_check_name.replace('-', '_').upper()]
- header_guard_new = (new_module + '_' + new_check_name_camel).upper()
-
old_module_path = os.path.join(clang_tidy_path, old_module)
new_module_path = os.path.join(clang_tidy_path, new_module)
@@ -263,30 +307,51 @@
deleteMatchingLines(os.path.join(old_module_path, modulecpp),
'\\b' + check_name_camel + '|\\b' + args.old_check_name)
- for filename in getListOfFiles(clang_tidy_path):
- originalName = filename
- filename = fileRename(filename, args.old_check_name,
- args.new_check_name)
- filename = fileRename(filename, check_name_camel, new_check_name_camel)
- replaceInFile(filename, generateCommentLineHeader(originalName),
- generateCommentLineHeader(filename))
- replaceInFile(filename, generateCommentLineSource(originalName),
- generateCommentLineSource(filename))
- for header_guard in header_guard_variants:
- replaceInFile(filename, header_guard, header_guard_new)
-
- if args.new_check_name + '.rst' in filename:
- replaceInFile(
- filename,
+ ImplHeader = os.path.join(old_module_path, check_name_camel + ".h")
+ ImplSource = os.path.join(old_module_path, check_name_camel + ".cpp")
+ if os.path.isfile(ImplHeader) and os.path.isfile(ImplSource):
+ NewHeader = os.path.join(new_module_path, new_check_name_camel + ".h")
+ NewSource = os.path.join(new_module_path, new_check_name_camel + ".cpp")
+ os.rename(ImplHeader, NewHeader)
+ os.rename(ImplSource, NewSource)
+ replaceHeaderComment(NewHeader)
+ replaceSourceComment(NewSource)
+ replaceHeaderGuard(NewHeader, new_module, new_check_name_camel)
+ else:
+ print("Check implementation files not found:\nHeader: {}\nSource: {}".format(ImplHeader, ImplSource))
+
+ DocFolder = os.path.join(clang_tidy_path, '..', 'docs',
+ 'clang-tidy', 'checks');
+
+ DocFile = os.path.join(DocFolder, args.old_check_name + ".rst")
+ NewDocFile = os.path.join(DocFolder, args.new_check_name + ".rst")
+ os.rename(DocFile, NewDocFile)
+ replaceInFile(
+ NewDocFile,
args.old_check_name + '\n' + '=' * len(args.old_check_name) + '\n',
args.new_check_name + '\n' + '=' * len(args.new_check_name) + '\n')
- replaceInFile(filename, args.old_check_name, args.new_check_name)
- replaceInFile(filename, old_module + '::' + check_name_camel,
+ for filename in getDocFiles(clang_tidy_path):
+ replaceWordInFile(filename, args.old_check_name, args.new_check_name)
+
+ for filename in getClangTidyFiles(clang_tidy_path):
+ replaceWordInFile(filename, args.old_check_name, args.new_check_name)
+ replaceWordInFile(filename, old_module + '::' + check_name_camel,
new_module + '::' + new_check_name_camel)
- replaceInFile(filename, old_module + '/' + check_name_camel,
+ replaceWordInFile(filename, old_module + '/' + check_name_camel,
new_module + '/' + new_check_name_camel)
- replaceInFile(filename, check_name_camel, new_check_name_camel)
+ replaceWordInFile(filename, check_name_camel, new_check_name_camel)
+
+ for filename in getTestFiles(clang_tidy_path):
+ replaceWordInFile(filename, args.old_check_name, args.new_check_name)
+
+ for filename in getUnitTestFiles(clang_tidy_path):
+ replaceWordInFile(filename, args.old_check_name, args.new_check_name)
+ replaceWordInFile(filename, old_module + '::' + check_name_camel,
+ new_module + '::' + new_check_name_camel)
+ replaceWordInFile(filename, old_module + '/' + check_name_camel,
+ new_module + '/' + new_check_name_camel)
+ replaceWordInFile(filename, check_name_camel, new_check_name_camel)
if old_module != new_module or new_module == 'llvm':
if new_module == 'llvm':
@@ -308,9 +373,7 @@
adapt_cmake(new_module_path, new_check_name_camel)
adapt_module(new_module_path, new_module, args.new_check_name,
new_check_name_camel)
-
- os.system(os.path.join(clang_tidy_path, 'add_new_check.py')
- + ' --update-docs')
+ update_checks_list(clang_tidy_path)
add_release_notes(clang_tidy_path, args.old_check_name, args.new_check_name)
if __name__ == '__main__':
Index: clang-tools-extra/clang-tidy/add_new_check.py
===================================================================
--- clang-tools-extra/clang-tidy/add_new_check.py
+++ clang-tools-extra/clang-tidy/add_new_check.py
@@ -14,36 +14,7 @@
import os
import re
import sys
-
-# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
-# and 'False' if the entry already existed.
-def adapt_cmake(module_path, check_name_camel):
- filename = os.path.join(module_path, 'CMakeLists.txt')
- with open(filename, 'r') as f:
- lines = f.readlines()
-
- cpp_file = check_name_camel + '.cpp'
-
- # Figure out whether this check already exists.
- for line in lines:
- if line.strip() == cpp_file:
- return False
-
- print('Updating %s...' % filename)
- with open(filename, 'w') as f:
- cpp_found = False
- file_added = False
- for line in lines:
- cpp_line = line.strip().endswith('.cpp')
- if (not file_added) and (cpp_line or cpp_found):
- cpp_found = True
- if (line.strip() > cpp_file) or (not cpp_line):
- f.write(' ' + cpp_file + '\n')
- file_added = True
- f.write(line)
-
- return True
-
+from utils.modify_check import *
# Adds a header for the new check.
def write_header(module_path, module, namespace, check_name, check_name_camel):
@@ -150,7 +121,6 @@
'module': module,
'namespace': namespace})
-
# Modifies the module to include the new check.
def adapt_module(module_path, module, check_name, check_name_camel):
modulecpp = list(filter(
@@ -290,94 +260,6 @@
""" % {'check_name_dashes': check_name_dashes})
-# Recreates the list of checks in the docs/clang-tidy/checks directory.
-def update_checks_list(clang_tidy_path):
- docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
- filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
- # Read the content of the current list.rst file
- with open(filename, 'r') as f:
- lines = f.readlines()
- # Get all existing docs
- doc_files = list(filter(lambda s: s.endswith('.rst') and s != 'list.rst',
- os.listdir(docs_dir)))
- doc_files.sort()
-
- def has_auto_fix(check_name):
- dirname, _, check_name = check_name.partition("-")
-
- checkerCode = os.path.join(dirname, get_camel_name(check_name)) + ".cpp"
-
- if not os.path.isfile(checkerCode):
- return ""
-
- with open(checkerCode) as f:
- code = f.read()
- if 'FixItHint' in code or "ReplacementText" in code or "fixit" in code:
- # Some simple heuristics to figure out if a checker has an autofix or not.
- return ' "Yes"'
- return ""
-
- def process_doc(doc_file):
- check_name = doc_file.replace('.rst', '')
-
- with open(os.path.join(docs_dir, doc_file), 'r') as doc:
- content = doc.read()
- match = re.search('.*:orphan:.*', content)
-
- if match:
- # Orphan page, don't list it.
- return '', ''
-
- match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
- content)
- # Is it a redirect?
- return check_name, match
-
- def format_link(doc_file):
- check_name, match = process_doc(doc_file)
- if not match and check_name:
- return ' `%(check)s <%(check)s.html>`_,%(autofix)s\n' % {
- 'check': check_name,
- 'autofix': has_auto_fix(check_name)
- }
- else:
- return ''
-
- def format_link_alias(doc_file):
- check_name, match = process_doc(doc_file)
- if match and check_name:
- if match.group(1) == 'https://clang.llvm.org/docs/analyzer/checkers':
- title_redirect = 'Clang Static Analyzer'
- else:
- title_redirect = match.group(1)
- # The checker is just a redirect.
- return ' `%(check)s <%(check)s.html>`_, `%(title)s <%(target)s.html>`_,%(autofix)s\n' % {
- 'check': check_name,
- 'target': match.group(1),
- 'title': title_redirect,
- 'autofix': has_auto_fix(match.group(1))
- }
- return ''
-
- checks = map(format_link, doc_files)
- checks_alias = map(format_link_alias, doc_files)
-
- print('Updating %s...' % filename)
- with open(filename, 'w') as f:
- for line in lines:
- f.write(line)
- if line.strip() == ".. csv-table::":
- # We dump the checkers
- f.write(' :header: "Name", "Offers fixes"\n\n')
- f.writelines(checks)
- # and the aliases
- f.write('\n\n')
- f.write('.. csv-table:: Aliases..\n')
- f.write(' :header: "Name", "Redirect", "Offers fixes"\n\n')
- f.writelines(checks_alias)
- break
-
-
# Adds a documentation for the check.
def write_docs(module_path, module, check_name):
check_name_dashes = module + '-' + check_name
@@ -395,11 +277,6 @@
'underline': '=' * len(check_name_dashes)})
-def get_camel_name(check_name):
- return ''.join(map(lambda elem: elem.capitalize(),
- check_name.split('-'))) + 'Check'
-
-
def main():
language_to_extension = {
'c': 'c',
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits