Skip to content

Commit 6b334a2

Browse files
committedNov 23, 2017
clang-tidy/rename_check.py: support for moving between modules
llvm-svn: 318905
1 parent e8bdd38 commit 6b334a2

File tree

1 file changed

+160
-17
lines changed

1 file changed

+160
-17
lines changed
 

‎clang-tools-extra/clang-tidy/rename_check.py

+160-17
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
#
1010
#===------------------------------------------------------------------------===#
1111

12-
import os
13-
import glob
1412
import argparse
13+
import glob
14+
import os
15+
import re
1516

1617

1718
def replaceInFile(fileName, sFrom, sTo):
@@ -25,7 +26,7 @@ def replaceInFile(fileName, sFrom, sTo):
2526
return
2627

2728
txt = txt.replace(sFrom, sTo)
28-
print("Replace '%s' -> '%s' in '%s'" % (sFrom, sTo, fileName))
29+
print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
2930
with open(fileName, "w") as f:
3031
f.write(txt)
3132

@@ -47,13 +48,28 @@ def generateCommentLineSource(filename):
4748

4849

4950
def fileRename(fileName, sFrom, sTo):
50-
if sFrom not in fileName:
51+
if sFrom not in fileName or sFrom == sTo:
5152
return fileName
5253
newFileName = fileName.replace(sFrom, sTo)
53-
print("Rename '%s' -> '%s'" % (fileName, newFileName))
54+
print("Renaming '%s' -> '%s'..." % (fileName, newFileName))
5455
os.rename(fileName, newFileName)
5556
return newFileName
5657

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 not_matching_lines.count == lines.count:
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
5773

5874
def getListOfFiles(clang_tidy_path):
5975
files = glob.glob(os.path.join(clang_tidy_path, '*'))
@@ -66,43 +82,170 @@ def getListOfFiles(clang_tidy_path):
6682
'clang-tidy', 'checks', '*'))
6783
return [filename for filename in files if os.path.isfile(filename)]
6884

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)
69180

70181
def main():
71182
parser = argparse.ArgumentParser(description='Rename clang-tidy check.')
72-
parser.add_argument('module', type=str,
73-
help='Module where the renamed check is defined')
74183
parser.add_argument('old_check_name', type=str,
75184
help='Old check name.')
76185
parser.add_argument('new_check_name', type=str,
77186
help='New check name.')
78187
args = parser.parse_args()
79188

80-
args.module = args.module.lower()
189+
old_module = args.old_check_name.split('-')[0]
190+
new_module = args.new_check_name.split('-')[0]
81191
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
82-
args.old_check_name.split('-'))) + 'Check'
83-
check_name_new_camel = (''.join(map(lambda elem: elem.capitalize(),
84-
args.new_check_name.split('-'))) +
192+
args.old_check_name.split('-')[1:])) + 'Check'
193+
new_check_name_camel = (''.join(map(lambda elem: elem.capitalize(),
194+
args.new_check_name.split('-')[1:])) +
85195
'Check')
86196

87197
clang_tidy_path = os.path.dirname(__file__)
88198

89-
header_guard_old = (args.module.upper() + '_' +
90-
args.old_check_name.upper().replace('-', '_'))
91-
header_guard_new = (args.module.upper() + '_' +
92-
args.new_check_name.upper().replace('-', '_'))
199+
header_guard_old = args.old_check_name.upper().replace('-', '_')
200+
header_guard_new = args.new_check_name.upper().replace('-', '_')
201+
202+
old_module_path = os.path.join(clang_tidy_path, old_module)
203+
new_module_path = os.path.join(clang_tidy_path, new_module)
204+
205+
# Remove the check from the old module.
206+
cmake_lists = os.path.join(old_module_path, 'CMakeLists.txt')
207+
check_found = deleteMatchingLines(cmake_lists, check_name_camel)
208+
if not check_found:
209+
print("Check name '%s' not found in %s. Exiting." %
210+
(check_name_camel, cmake_lists))
211+
return 1
212+
213+
modulecpp = filter(
214+
lambda p: p.lower() == old_module.lower() + 'tidymodule.cpp',
215+
os.listdir(old_module_path))[0]
216+
deleteMatchingLines(os.path.join(old_module_path, modulecpp),
217+
check_name_camel + '|' + args.old_check_name)
93218

94219
for filename in getListOfFiles(clang_tidy_path):
95220
originalName = filename
96221
filename = fileRename(filename, args.old_check_name,
97222
args.new_check_name)
98-
filename = fileRename(filename, check_name_camel, check_name_new_camel)
223+
filename = fileRename(filename, check_name_camel, new_check_name_camel)
99224
replaceInFile(filename, generateCommentLineHeader(originalName),
100225
generateCommentLineHeader(filename))
101226
replaceInFile(filename, generateCommentLineSource(originalName),
102227
generateCommentLineSource(filename))
103228
replaceInFile(filename, header_guard_old, header_guard_new)
104229
replaceInFile(filename, args.old_check_name, args.new_check_name)
105-
replaceInFile(filename, check_name_camel, check_name_new_camel)
230+
replaceInFile(filename, check_name_camel, new_check_name_camel)
231+
232+
if old_module != new_module:
233+
check_implementation_files = glob.glob(
234+
os.path.join(old_module_path, new_check_name_camel + '*'))
235+
for filename in check_implementation_files:
236+
# Move check implementation to the directory of the new module.
237+
filename = fileRename(filename, old_module_path, new_module_path)
238+
replaceInFile(filename, 'namespace ' + old_module,
239+
'namespace ' + new_module)
240+
241+
# Add check to the new module.
242+
adapt_cmake(new_module_path, new_check_name_camel)
243+
adapt_module(new_module_path, new_module, args.new_check_name,
244+
new_check_name_camel)
245+
246+
os.system(os.path.join(clang_tidy_path, 'add_new_check.py')
247+
+ ' --update-docs')
248+
add_release_notes(clang_tidy_path, args.old_check_name, args.new_check_name)
106249

107250
if __name__ == '__main__':
108251
main()

0 commit comments

Comments
 (0)
Please sign in to comment.