[clang-tools-extra] 89e663c - [clang-tidy] Improve add_new_check.py to recognize more checks

via cfe-commits cfe-commits at lists.llvm.org
Mon May 23 08:48:42 PDT 2022


Author: Richard
Date: 2022-05-23T09:47:54-06:00
New Revision: 89e663c4f83a6736fc74a01ec48cb4f01210f86f

URL: https://github.com/llvm/llvm-project/commit/89e663c4f83a6736fc74a01ec48cb4f01210f86f
DIFF: https://github.com/llvm/llvm-project/commit/89e663c4f83a6736fc74a01ec48cb4f01210f86f.diff

LOG: [clang-tidy] Improve add_new_check.py to recognize more checks

When looking for whether or not a check provides fixits, the script
examines the implementation of the check.  Some checks are not
implemented in source files that correspond one-to-one with the check
name, e.g. cert-dcl21-cpp.  So if we can't find the check implementation
directly from the check name, open up the corresponding module file and
look for the class name that is registered with the check.  Then consult
the file corresponding to the class name.

Some checks are derived from a base class that implements fixits.  So if
we can't find fixits in the implementation file for a check, scrape out
the name of it's base class.  If it's not ClangTidyCheck, then consult
the base class implementation to look for fixit support.

Differential Revision: https://reviews.llvm.org/D126134

Fixes #55630

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/add_new_check.py
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/add_new_check.py b/clang-tools-extra/clang-tidy/add_new_check.py
index fc83974486314..2e8684ca756dc 100644
--- a/clang-tools-extra/clang-tidy/add_new_check.py
+++ b/clang-tools-extra/clang-tidy/add_new_check.py
@@ -158,12 +158,17 @@ def write_implementation(module_path, module, namespace, check_name_camel):
        'namespace': namespace})
 
 
-# Modifies the module to include the new check.
-def adapt_module(module_path, module, check_name, check_name_camel):
+# Returns the source filename that implements the module.
+def get_module_filename(module_path, module):
   modulecpp = list(filter(
       lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
       os.listdir(module_path)))[0]
-  filename = os.path.join(module_path, modulecpp)
+  return os.path.join(module_path, modulecpp)
+
+
+# Modifies the module to include the new check.
+def adapt_module(module_path, module, check_name, check_name_camel):
+  filename = get_module_filename(module_path, module)
   with io.open(filename, 'r', encoding='utf8') as f:
     lines = f.readlines()
 
@@ -320,24 +325,100 @@ def update_checks_list(clang_tidy_path):
                      os.listdir(docs_dir)))
   doc_files.sort()
 
+  # We couldn't find the source file from the check name, so try to find the
+  # class name that corresponds to the check in the module file.
+  def filename_from_module(module_name, check_name):
+    module_path = os.path.join(clang_tidy_path, module_name)
+    if not os.path.isdir(module_path):
+      return ''
+    module_file = get_module_filename(module_path, module_name)
+    if not os.path.isfile(module_file):
+      return ''
+    with io.open(module_file, 'r') as f:
+      code = f.read()
+      full_check_name = module_name + '-' + check_name
+      name_pos = code.find('"' + full_check_name + '"')
+      if name_pos == -1:
+        return ''
+      stmt_end_pos = code.find(';', name_pos)
+      if stmt_end_pos == -1:
+        return ''
+      stmt_start_pos = code.rfind(';', 0, name_pos)
+      if stmt_start_pos == -1:
+        stmt_start_pos = code.rfind('{', 0, name_pos)
+      if stmt_start_pos == -1:
+        return ''
+      stmt = code[stmt_start_pos+1:stmt_end_pos]
+      matches = re.search('registerCheck<([^>:]*)>\(\s*"([^"]*)"\s*\)', stmt)
+      if matches and matches[2] == full_check_name:
+        class_name = matches[1]
+        if '::' in class_name:
+          parts = class_name.split('::')
+          class_name = parts[-1]
+          class_path = os.path.join(clang_tidy_path, module_name, '..', *parts[0:-1])
+        else:
+          class_path = os.path.join(clang_tidy_path, module_name)
+        return get_actual_filename(class_path, class_name + '.cpp')
+
+    return ''
+
+  # Examine code looking for a c'tor definition to get the base class name.
+  def get_base_class(code, check_file):
+    check_class_name = os.path.splitext(os.path.basename(check_file))[0]
+    ctor_pattern = check_class_name + '\([^:]*\)\s*:\s*([A-Z][A-Za-z0-9]*Check)\('
+    matches = re.search('\s+' + check_class_name + '::' + ctor_pattern, code)
+
+    # The constructor might be inline in the header.
+    if not matches:
+      header_file = os.path.splitext(check_file)[0] + '.h'
+      if not os.path.isfile(header_file):
+        return ''
+      with io.open(header_file, encoding='utf8') as f:
+        code = f.read()
+      matches = re.search(' ' + ctor_pattern, code)
+
+    if matches and matches[1] != 'ClangTidyCheck':
+      return matches[1]
+    return ''
+
+  # Some simple heuristics to figure out if a check has an autofix or not.
+  def has_fixits(code):
+    for needle in ['FixItHint', 'ReplacementText', 'fixit',
+                   'TransformerClangTidyCheck']:
+      if needle in code:
+        return True
+    return False
+
+  # Try to figure out of the check supports fixits.
   def has_auto_fix(check_name):
     dirname, _, check_name = check_name.partition('-')
 
-    checker_code = get_actual_filename(os.path.join(clang_tidy_path, dirname),
+    check_file = get_actual_filename(os.path.join(clang_tidy_path, dirname),
                                        get_camel_check_name(check_name) + '.cpp')
-    if not os.path.isfile(checker_code):
+    if not os.path.isfile(check_file):
       # Some older checks don't end with 'Check.cpp'
-      checker_code = get_actual_filename(os.path.join(clang_tidy_path, dirname),
+      check_file = get_actual_filename(os.path.join(clang_tidy_path, dirname),
                                          get_camel_name(check_name) + '.cpp')
-      if not os.path.isfile(checker_code):
-        return ''
+      if not os.path.isfile(check_file):
+        # Some checks aren't in a file based on the check name.
+        check_file = filename_from_module(dirname, check_name)
+        if not check_file or not os.path.isfile(check_file):
+          return ''
 
-    with io.open(checker_code, encoding='utf8') as f:
+    with io.open(check_file, encoding='utf8') as f:
       code = f.read()
-      for needle in ['FixItHint', 'ReplacementText', 'fixit', 'TransformerClangTidyCheck']:
-        if needle in code:
-          # Some simple heuristics to figure out if a checker has an autofix or not.
-          return ' "Yes"'
+      if has_fixits(code):
+        return ' "Yes"'
+
+    base_class = get_base_class(code, check_file)
+    if base_class:
+      base_file = os.path.join(clang_tidy_path, dirname, base_class + '.cpp')
+      if os.path.isfile(base_file):
+        with io.open(base_file, encoding='utf8') as f:
+          code = f.read()
+          if has_fixits(code):
+            return ' "Yes"'
+
     return ''
 
   def process_doc(doc_file):

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index cfab8a01e42d3..a099e4bdfbc8a 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -37,19 +37,19 @@ Clang-Tidy Checks
    `altera-struct-pack-align <altera-struct-pack-align.html>`_, "Yes"
    `altera-unroll-loops <altera-unroll-loops.html>`_,
    `android-cloexec-accept <android-cloexec-accept.html>`_, "Yes"
-   `android-cloexec-accept4 <android-cloexec-accept4.html>`_,
+   `android-cloexec-accept4 <android-cloexec-accept4.html>`_, "Yes"
    `android-cloexec-creat <android-cloexec-creat.html>`_, "Yes"
    `android-cloexec-dup <android-cloexec-dup.html>`_, "Yes"
-   `android-cloexec-epoll-create <android-cloexec-epoll-create.html>`_,
-   `android-cloexec-epoll-create1 <android-cloexec-epoll-create1.html>`_,
-   `android-cloexec-fopen <android-cloexec-fopen.html>`_,
-   `android-cloexec-inotify-init <android-cloexec-inotify-init.html>`_,
-   `android-cloexec-inotify-init1 <android-cloexec-inotify-init1.html>`_,
-   `android-cloexec-memfd-create <android-cloexec-memfd-create.html>`_,
-   `android-cloexec-open <android-cloexec-open.html>`_,
+   `android-cloexec-epoll-create <android-cloexec-epoll-create.html>`_, "Yes"
+   `android-cloexec-epoll-create1 <android-cloexec-epoll-create1.html>`_, "Yes"
+   `android-cloexec-fopen <android-cloexec-fopen.html>`_, "Yes"
+   `android-cloexec-inotify-init <android-cloexec-inotify-init.html>`_, "Yes"
+   `android-cloexec-inotify-init1 <android-cloexec-inotify-init1.html>`_, "Yes"
+   `android-cloexec-memfd-create <android-cloexec-memfd-create.html>`_, "Yes"
+   `android-cloexec-open <android-cloexec-open.html>`_, "Yes"
    `android-cloexec-pipe <android-cloexec-pipe.html>`_, "Yes"
-   `android-cloexec-pipe2 <android-cloexec-pipe2.html>`_,
-   `android-cloexec-socket <android-cloexec-socket.html>`_,
+   `android-cloexec-pipe2 <android-cloexec-pipe2.html>`_, "Yes"
+   `android-cloexec-socket <android-cloexec-socket.html>`_, "Yes"
    `android-comparison-in-temp-failure-retry <android-comparison-in-temp-failure-retry.html>`_,
    `boost-use-to-string <boost-use-to-string.html>`_, "Yes"
    `bugprone-argument-comment <bugprone-argument-comment.html>`_, "Yes"
@@ -105,7 +105,7 @@ Clang-Tidy Checks
    `bugprone-terminating-continue <bugprone-terminating-continue.html>`_, "Yes"
    `bugprone-throw-keyword-missing <bugprone-throw-keyword-missing.html>`_,
    `bugprone-too-small-loop-variable <bugprone-too-small-loop-variable.html>`_,
-   `bugprone-unchecked-optional-access <bugprone-unchecked-optional-access.html>`_, "Yes"
+   `bugprone-unchecked-optional-access <bugprone-unchecked-optional-access.html>`_,
    `bugprone-undefined-memory-manipulation <bugprone-undefined-memory-manipulation.html>`_,
    `bugprone-undelegated-constructor <bugprone-undelegated-constructor.html>`_,
    `bugprone-unhandled-exception-at-new <bugprone-unhandled-exception-at-new.html>`_,


        


More information about the cfe-commits mailing list