clang-tools 6.0.0

rename_check.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #
3 #===- rename_check.py - clang-tidy check renamer -------------*- python -*--===#
4 #
5 # The LLVM Compiler Infrastructure
6 #
7 # This file is distributed under the University of Illinois Open Source
8 # License. See LICENSE.TXT for details.
9 #
10 #===------------------------------------------------------------------------===#
11
12 import argparse
13 import glob
14 import os
15 import re
16
17
18 def replaceInFile(fileName, sFrom, sTo):
19 if sFrom == sTo:
20 return
21 txt = None
22 with open(fileName, "r") as f:
23 txt = f.read()
24
25 if sFrom not in txt:
26 return
27
28 txt = txt.replace(sFrom, sTo)
29 print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
30 with open(fileName, "w") as f:
31 f.write(txt)
32
33
34 def generateCommentLineHeader(filename):
35 return ''.join(['//===--- ',
36 os.path.basename(filename),
37 ' - clang-tidy ',
38 '-' * max(0, 42 - len(os.path.basename(filename))),
39 '*- C++ -*-===//'])
40
41
42 def generateCommentLineSource(filename):
43 return ''.join(['//===--- ',
44 os.path.basename(filename),
45 ' - clang-tidy',
46 '-' * max(0, 52 - len(os.path.basename(filename))),
47 '-===//'])
48
49
50 def fileRename(fileName, sFrom, sTo):
51 if sFrom not in fileName or sFrom == sTo:
52 return fileName
53 newFileName = fileName.replace(sFrom, sTo)
54 print("Renaming '%s' -> '%s'..." % (fileName, newFileName))
55 os.rename(fileName, newFileName)
56 return newFileName
57
58 def deleteMatchingLines(fileName, pattern):
59 lines = None
60 with open(fileName, "r") as f:
61 lines = f.readlines()
62
63 not_matching_lines = [l for l in lines if not re.search(pattern, l)]
64 if len(not_matching_lines) == len(lines):
65 return False
66
67 print("Removing lines matching '%s' in '%s'..." % (pattern, fileName))
68 print(' ' + ' '.join([l for l in lines if re.search(pattern, l)]))
69 with open(fileName, "w") as f:
70 f.writelines(not_matching_lines)
71
72 return True
73
74 def getListOfFiles(clang_tidy_path):
75 files = glob.glob(os.path.join(clang_tidy_path, '*'))
76 for dirname in files:
77 if os.path.isdir(dirname):
78 files += glob.glob(os.path.join(dirname, '*'))
79 files += glob.glob(os.path.join(clang_tidy_path, '..', 'test',
80 'clang-tidy', '*'))
81 files += glob.glob(os.path.join(clang_tidy_path, '..', 'docs',
82 'clang-tidy', 'checks', '*'))
83 return [filename for filename in files if os.path.isfile(filename)]
84
85 # Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
86 # and 'False' if the entry already existed.
87 def adapt_cmake(module_path, check_name_camel):
88 filename = os.path.join(module_path, 'CMakeLists.txt')
89 with open(filename, 'r') as f:
90 lines = f.readlines()
91
92 cpp_file = check_name_camel + '.cpp'
93
94 # Figure out whether this check already exists.
95 for line in lines:
96 if line.strip() == cpp_file:
97 return False
98
99 print('Updating %s...' % filename)
100 with open(filename, 'wb') as f:
101 cpp_found = False
102 file_added = False
103 for line in lines:
104 cpp_line = line.strip().endswith('.cpp')
105 if (not file_added) and (cpp_line or cpp_found):
106 cpp_found = True
107 if (line.strip() > cpp_file) or (not cpp_line):
108 f.write(' ' + cpp_file + '\n')
109 file_added = True
110 f.write(line)
111
112 return True
113
114 # Modifies the module to include the new check.
115 def adapt_module(module_path, module, check_name, check_name_camel):
116 modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
117 os.listdir(module_path))[0]
118 filename = os.path.join(module_path, modulecpp)
119 with open(filename, 'r') as f:
120 lines = f.readlines()
121
122 print('Updating %s...' % filename)
123 with open(filename, 'wb') as f:
124 header_added = False
125 header_found = False
126 check_added = False
127 check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
128 '>(\n "' + check_name + '");\n')
129
130 for line in lines:
131 if not header_added:
132 match = re.search('#include "(.*)"', line)
133 if match:
134 header_found = True
135 if match.group(1) > check_name_camel:
136 header_added = True
137 f.write('#include "' + check_name_camel + '.h"\n')
138 elif header_found:
139 header_added = True
140 f.write('#include "' + check_name_camel + '.h"\n')
141
142 if not check_added:
143 if line.strip() == '}':
144 check_added = True
145 f.write(check_decl)
146 else:
147 match = re.search('registerCheck<(.*)>', line)
148 if match and match.group(1) > check_name_camel:
149 check_added = True
150 f.write(check_decl)
151 f.write(line)
152
153
154 # Adds a release notes entry.
155 def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
156 filename = os.path.normpath(os.path.join(clang_tidy_path,
157 '../docs/ReleaseNotes.rst'))
158 with open(filename, 'r') as f:
159 lines = f.readlines()
160
161 print('Updating %s...' % filename)
162 with open(filename, 'wb') as f:
163 note_added = False
164 header_found = False
165
166 for line in lines:
167 if not note_added:
168 match = re.search('Improvements to clang-tidy', line)
169 if match:
170 header_found = True
171 elif header_found:
172 if not line.startswith('----'):
173 f.write("""
174 - The '%s' check was renamed to `%s
175 <http://clang.llvm.org/extra/clang-tidy/checks/%s.html>`_
176 """ % (old_check_name, new_check_name, new_check_name))
177 note_added = True
178
179 f.write(line)
180
181 def main():
182 parser = argparse.ArgumentParser(description='Rename clang-tidy check.')
183 parser.add_argument('old_check_name', type=str,
184 help='Old check name.')
185 parser.add_argument('new_check_name', type=str,
186 help='New check name.')
187 parser.add_argument('--check_class_name', type=str,
188 help='Old name of the class implementing the check.')
189 args = parser.parse_args()
190
191 old_module = args.old_check_name.split('-')[0]
192 new_module = args.new_check_name.split('-')[0]
193 if args.check_class_name:
194 check_name_camel = args.check_class_name
195 else:
196 check_name_camel = (''.join(map(lambda elem: elem.capitalize(),
197 args.old_check_name.split('-')[1:])) +
198 'Check')
199
200 new_check_name_camel = (''.join(map(lambda elem: elem.capitalize(),
201 args.new_check_name.split('-')[1:])) +
202 'Check')
203
204 clang_tidy_path = os.path.dirname(__file__)
205
206 header_guard_variants = [
207 (old_module + '_' + new_check_name_camel).upper(),
208 args.old_check_name.replace('-', '_').upper()]
209 header_guard_new = (new_module + '_' + new_check_name_camel).upper()
210
211 old_module_path = os.path.join(clang_tidy_path, old_module)
212 new_module_path = os.path.join(clang_tidy_path, new_module)
213
214 # Remove the check from the old module.
215 cmake_lists = os.path.join(old_module_path, 'CMakeLists.txt')
216 check_found = deleteMatchingLines(cmake_lists, check_name_camel)
217 if not check_found:
218 print("Check name '%s' not found in %s. Exiting." %
219 (check_name_camel, cmake_lists))
220 return 1
221
222 modulecpp = filter(
223 lambda p: p.lower() == old_module.lower() + 'tidymodule.cpp',
224 os.listdir(old_module_path))[0]
225 deleteMatchingLines(os.path.join(old_module_path, modulecpp),
226 check_name_camel + '|' + args.old_check_name)
227
228 for filename in getListOfFiles(clang_tidy_path):
229 originalName = filename
230 filename = fileRename(filename, args.old_check_name,
231 args.new_check_name)
232 filename = fileRename(filename, check_name_camel, new_check_name_camel)
233 replaceInFile(filename, generateCommentLineHeader(originalName),
234 generateCommentLineHeader(filename))
235 replaceInFile(filename, generateCommentLineSource(originalName),
236 generateCommentLineSource(filename))
237 for header_guard in header_guard_variants:
238 replaceInFile(filename, header_guard, header_guard_new)
239
240 if args.new_check_name + '.rst' in filename:
241 replaceInFile(
242 filename,
243 args.old_check_name + '\n' + '=' * len(args.old_check_name) + '\n',
244 args.new_check_name + '\n' + '=' * len(args.new_check_name) + '\n')
245
246 replaceInFile(filename, args.old_check_name, args.new_check_name)
247 replaceInFile(filename, old_module + '::' + check_name_camel,
248 new_module + '::' + new_check_name_camel)
249 replaceInFile(filename, old_module + '/' + check_name_camel,
250 new_module + '/' + new_check_name_camel)
251 replaceInFile(filename, check_name_camel, new_check_name_camel)
252
253 if old_module != new_module:
254 check_implementation_files = glob.glob(
255 os.path.join(old_module_path, new_check_name_camel + '*'))
256 for filename in check_implementation_files:
257 # Move check implementation to the directory of the new module.
258 filename = fileRename(filename, old_module_path, new_module_path)
259 replaceInFile(filename, 'namespace ' + old_module,
260 'namespace ' + new_module)
261
262 # Add check to the new module.
263 adapt_cmake(new_module_path, new_check_name_camel)
264 adapt_module(new_module_path, new_module, args.new_check_name,
265 new_check_name_camel)
266
267 os.system(os.path.join(clang_tidy_path, 'add_new_check.py')
268 + ' --update-docs')
269 add_release_notes(clang_tidy_path, args.old_check_name, args.new_check_name)
270
271 if __name__ == '__main__':
272 main()
rename_check.main
def main()
Definition: rename_check.py:181
rename_check.generateCommentLineHeader
def generateCommentLineHeader(filename)
Definition: rename_check.py:34
rename_check.replaceInFile
def replaceInFile(fileName, sFrom, sTo)
Definition: rename_check.py:18
rename_check.fileRename
def fileRename(fileName, sFrom, sTo)
Definition: rename_check.py:50
rename_check.add_release_notes
def add_release_notes(clang_tidy_path, old_check_name, new_check_name)
Definition: rename_check.py:155
rename_check.generateCommentLineSource
def generateCommentLineSource(filename)
Definition: rename_check.py:42
rename_check.adapt_cmake
def adapt_cmake(module_path, check_name_camel)
Definition: rename_check.py:87
rename_check.adapt_module
def adapt_module(module_path, module, check_name, check_name_camel)
Definition: rename_check.py:115
rename_check.getListOfFiles
def getListOfFiles(clang_tidy_path)
Definition: rename_check.py:74
rename_check.deleteMatchingLines
def deleteMatchingLines(fileName, pattern)
Definition: rename_check.py:58
clang::tidy::cppcoreguidelines::join
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
Definition: SpecialMemberFunctionsCheck.cpp:82