[RISCV] Add LUI/AUIPC+ADDI fusions to sifive-p450. (#78501)
[llvm-project.git] / clang-tools-extra / clang-tidy / rename_check.py
blobbf9c886699cb2543065098e4cd893325d6b25f35
1 #!/usr/bin/env python3
3 # ===- rename_check.py - clang-tidy check renamer ------------*- python -*--===#
5 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6 # See https://llvm.org/LICENSE.txt for license information.
7 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9 # ===-----------------------------------------------------------------------===#
11 from __future__ import unicode_literals
13 import argparse
14 import glob
15 import io
16 import os
17 import re
20 def replaceInFileRegex(fileName, sFrom, sTo):
21 if sFrom == sTo:
22 return
24 # The documentation files are encoded using UTF-8, however on Windows the
25 # default encoding might be different (e.g. CP-1252). To make sure UTF-8 is
26 # always used, use `io.open(filename, mode, encoding='utf8')` for reading and
27 # writing files here and elsewhere.
28 txt = None
29 with io.open(fileName, "r", encoding="utf8") as f:
30 txt = f.read()
32 txt = re.sub(sFrom, sTo, txt)
33 print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
34 with io.open(fileName, "w", encoding="utf8") as f:
35 f.write(txt)
38 def replaceInFile(fileName, sFrom, sTo):
39 if sFrom == sTo:
40 return
41 txt = None
42 with io.open(fileName, "r", encoding="utf8") as f:
43 txt = f.read()
45 if sFrom not in txt:
46 return
48 txt = txt.replace(sFrom, sTo)
49 print("Replacing '%s' -> '%s' in '%s'..." % (sFrom, sTo, fileName))
50 with io.open(fileName, "w", encoding="utf8") as f:
51 f.write(txt)
54 def generateCommentLineHeader(filename):
55 return "".join(
57 "//===--- ",
58 os.path.basename(filename),
59 " - clang-tidy ",
60 "-" * max(0, 42 - len(os.path.basename(filename))),
61 "*- C++ -*-===//",
66 def generateCommentLineSource(filename):
67 return "".join(
69 "//===--- ",
70 os.path.basename(filename),
71 " - clang-tidy",
72 "-" * max(0, 52 - len(os.path.basename(filename))),
73 "-===//",
78 def fileRename(fileName, sFrom, sTo):
79 if sFrom not in fileName or sFrom == sTo:
80 return fileName
81 newFileName = fileName.replace(sFrom, sTo)
82 print("Renaming '%s' -> '%s'..." % (fileName, newFileName))
83 os.rename(fileName, newFileName)
84 return newFileName
87 def deleteMatchingLines(fileName, pattern):
88 lines = None
89 with io.open(fileName, "r", encoding="utf8") as f:
90 lines = f.readlines()
92 not_matching_lines = [l for l in lines if not re.search(pattern, l)]
93 if len(not_matching_lines) == len(lines):
94 return False
96 print("Removing lines matching '%s' in '%s'..." % (pattern, fileName))
97 print(" " + " ".join([l for l in lines if re.search(pattern, l)]))
98 with io.open(fileName, "w", encoding="utf8") as f:
99 f.writelines(not_matching_lines)
101 return True
104 def getListOfFiles(clang_tidy_path):
105 files = glob.glob(os.path.join(clang_tidy_path, "**"), recursive=True)
106 files += [
107 os.path.normpath(os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst"))
109 files += glob.glob(
110 os.path.join(clang_tidy_path, "..", "test", "clang-tidy", "checkers", "**"),
111 recursive=True,
113 files += glob.glob(
114 os.path.join(clang_tidy_path, "..", "docs", "clang-tidy", "checks", "*.rst")
116 files += glob.glob(
117 os.path.join(
118 clang_tidy_path, "..", "docs", "clang-tidy", "checks", "*", "*.rst"
120 recursive=True,
122 return [filename for filename in files if os.path.isfile(filename)]
125 # Adapts the module's CMakelist file. Returns 'True' if it could add a new
126 # entry and 'False' if the entry already existed.
127 def adapt_cmake(module_path, check_name_camel):
128 filename = os.path.join(module_path, "CMakeLists.txt")
129 with io.open(filename, "r", encoding="utf8") as f:
130 lines = f.readlines()
132 cpp_file = check_name_camel + ".cpp"
134 # Figure out whether this check already exists.
135 for line in lines:
136 if line.strip() == cpp_file:
137 return False
139 print("Updating %s..." % filename)
140 with io.open(filename, "w", encoding="utf8") as f:
141 cpp_found = False
142 file_added = False
143 for line in lines:
144 cpp_line = line.strip().endswith(".cpp")
145 if (not file_added) and (cpp_line or cpp_found):
146 cpp_found = True
147 if (line.strip() > cpp_file) or (not cpp_line):
148 f.write(" " + cpp_file + "\n")
149 file_added = True
150 f.write(line)
152 return True
155 # Modifies the module to include the new check.
156 def adapt_module(module_path, module, check_name, check_name_camel):
157 modulecpp = next(
158 iter(
159 filter(
160 lambda p: p.lower() == module.lower() + "tidymodule.cpp",
161 os.listdir(module_path),
165 filename = os.path.join(module_path, modulecpp)
166 with io.open(filename, "r", encoding="utf8") as f:
167 lines = f.readlines()
169 print("Updating %s..." % filename)
170 with io.open(filename, "w", encoding="utf8") as f:
171 header_added = False
172 header_found = False
173 check_added = False
174 check_decl = (
175 " CheckFactories.registerCheck<"
176 + check_name_camel
177 + '>(\n "'
178 + check_name
179 + '");\n'
182 for line in lines:
183 if not header_added:
184 match = re.search('#include "(.*)"', line)
185 if match:
186 header_found = True
187 if match.group(1) > check_name_camel:
188 header_added = True
189 f.write('#include "' + check_name_camel + '.h"\n')
190 elif header_found:
191 header_added = True
192 f.write('#include "' + check_name_camel + '.h"\n')
194 if not check_added:
195 if line.strip() == "}":
196 check_added = True
197 f.write(check_decl)
198 else:
199 match = re.search("registerCheck<(.*)>", line)
200 if match and match.group(1) > check_name_camel:
201 check_added = True
202 f.write(check_decl)
203 f.write(line)
206 # Adds a release notes entry.
207 def add_release_notes(clang_tidy_path, old_check_name, new_check_name):
208 filename = os.path.normpath(
209 os.path.join(clang_tidy_path, "../docs/ReleaseNotes.rst")
211 with io.open(filename, "r", encoding="utf8") as f:
212 lines = f.readlines()
214 lineMatcher = re.compile("Renamed checks")
215 nextSectionMatcher = re.compile("Improvements to include-fixer")
216 checkMatcher = re.compile("- The '(.*)")
218 print("Updating %s..." % filename)
219 with io.open(filename, "w", encoding="utf8") as f:
220 note_added = False
221 header_found = False
222 add_note_here = False
224 for line in lines:
225 if not note_added:
226 match = lineMatcher.match(line)
227 match_next = nextSectionMatcher.match(line)
228 match_check = checkMatcher.match(line)
229 if match_check:
230 last_check = match_check.group(1)
231 if last_check > old_check_name:
232 add_note_here = True
234 if match_next:
235 add_note_here = True
237 if match:
238 header_found = True
239 f.write(line)
240 continue
242 if line.startswith("^^^^"):
243 f.write(line)
244 continue
246 if header_found and add_note_here:
247 if not line.startswith("^^^^"):
248 f.write(
249 """- The '%s' check was renamed to :doc:`%s
250 <clang-tidy/checks/%s/%s>`
254 old_check_name,
255 new_check_name,
256 new_check_name.split("-", 1)[0],
257 "-".join(new_check_name.split("-")[1:]),
260 note_added = True
262 f.write(line)
265 def main():
266 parser = argparse.ArgumentParser(description="Rename clang-tidy check.")
267 parser.add_argument("old_check_name", type=str, help="Old check name.")
268 parser.add_argument("new_check_name", type=str, help="New check name.")
269 parser.add_argument(
270 "--check_class_name",
271 type=str,
272 help="Old name of the class implementing the check.",
274 args = parser.parse_args()
276 old_module = args.old_check_name.split("-")[0]
277 new_module = args.new_check_name.split("-")[0]
278 old_name = "-".join(args.old_check_name.split("-")[1:])
279 new_name = "-".join(args.new_check_name.split("-")[1:])
281 if args.check_class_name:
282 check_name_camel = args.check_class_name
283 else:
284 check_name_camel = (
285 "".join(map(lambda elem: elem.capitalize(), old_name.split("-"))) + "Check"
288 new_check_name_camel = (
289 "".join(map(lambda elem: elem.capitalize(), new_name.split("-"))) + "Check"
292 clang_tidy_path = os.path.dirname(__file__)
294 header_guard_variants = [
295 (args.old_check_name.replace("-", "_")).upper() + "_CHECK",
296 (old_module + "_" + check_name_camel).upper(),
297 (old_module + "_" + new_check_name_camel).upper(),
298 args.old_check_name.replace("-", "_").upper(),
300 header_guard_new = (new_module + "_" + new_check_name_camel).upper()
302 old_module_path = os.path.join(clang_tidy_path, old_module)
303 new_module_path = os.path.join(clang_tidy_path, new_module)
305 if old_module != new_module:
306 # Remove the check from the old module.
307 cmake_lists = os.path.join(old_module_path, "CMakeLists.txt")
308 check_found = deleteMatchingLines(cmake_lists, "\\b" + check_name_camel)
309 if not check_found:
310 print(
311 "Check name '%s' not found in %s. Exiting."
312 % (check_name_camel, cmake_lists)
314 return 1
316 modulecpp = next(
317 iter(
318 filter(
319 lambda p: p.lower() == old_module.lower() + "tidymodule.cpp",
320 os.listdir(old_module_path),
324 deleteMatchingLines(
325 os.path.join(old_module_path, modulecpp),
326 "\\b" + check_name_camel + "|\\b" + args.old_check_name,
329 for filename in getListOfFiles(clang_tidy_path):
330 originalName = filename
331 filename = fileRename(
332 filename, old_module + "/" + old_name, new_module + "/" + new_name
334 filename = fileRename(filename, args.old_check_name, args.new_check_name)
335 filename = fileRename(filename, check_name_camel, new_check_name_camel)
336 replaceInFile(
337 filename,
338 generateCommentLineHeader(originalName),
339 generateCommentLineHeader(filename),
341 replaceInFile(
342 filename,
343 generateCommentLineSource(originalName),
344 generateCommentLineSource(filename),
346 for header_guard in header_guard_variants:
347 replaceInFile(filename, header_guard, header_guard_new)
349 if new_module + "/" + new_name + ".rst" in filename:
350 replaceInFile(
351 filename,
352 args.old_check_name + "\n" + "=" * len(args.old_check_name) + "\n",
353 args.new_check_name + "\n" + "=" * len(args.new_check_name) + "\n",
356 replaceInFile(filename, args.old_check_name, args.new_check_name)
357 replaceInFile(
358 filename,
359 old_module + "::" + check_name_camel,
360 new_module + "::" + new_check_name_camel,
362 replaceInFile(
363 filename,
364 old_module + "/" + check_name_camel,
365 new_module + "/" + new_check_name_camel,
367 replaceInFile(
368 filename, old_module + "/" + old_name, new_module + "/" + new_name
370 replaceInFile(filename, check_name_camel, new_check_name_camel)
372 if old_module != new_module or new_module == "llvm":
373 if new_module == "llvm":
374 new_namespace = new_module + "_check"
375 else:
376 new_namespace = new_module
377 check_implementation_files = glob.glob(
378 os.path.join(old_module_path, new_check_name_camel + "*")
380 for filename in check_implementation_files:
381 # Move check implementation to the directory of the new module.
382 filename = fileRename(filename, old_module_path, new_module_path)
383 replaceInFileRegex(
384 filename,
385 "namespace clang::tidy::" + old_module + "[^ \n]*",
386 "namespace clang::tidy::" + new_namespace,
389 if old_module != new_module:
391 # Add check to the new module.
392 adapt_cmake(new_module_path, new_check_name_camel)
393 adapt_module(
394 new_module_path, new_module, args.new_check_name, new_check_name_camel
397 os.system(os.path.join(clang_tidy_path, "add_new_check.py") + " --update-docs")
398 add_release_notes(clang_tidy_path, args.old_check_name, args.new_check_name)
401 if __name__ == "__main__":
402 main()