zinovy.nis updated this revision to Diff 193023.
zinovy.nis added a comment.
Herald added a subscriber: jdoerfert.

Added a test.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59449/new/

https://reviews.llvm.org/D59449

Files:
  clang-tidy/tool/run-clang-tidy.py
  test/clang-tidy/run-clang-tidy-diff.cpp

Index: test/clang-tidy/run-clang-tidy-diff.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/run-clang-tidy-diff.cpp
@@ -0,0 +1,27 @@
+// REQUIRES: shell
+// RUN: mkdir -p "%t"
+// RUN: cd       "%t"
+// RUN: sed 's/placeholder_for_f/f/' %s > diff_to.cpp
+// RUN: echo '[{"directory": "%t", "command": "clang++ -o test.o -std=c++11 diff_to.cpp", "file": "diff_to.cpp"}]' > compile_commands.json
+// RUN: clang-tidy -checks=-*,modernize-use-override diff_to.cpp -- -std=c++11 | FileCheck -check-prefix=CHECK-SANITY %s
+// RUN: not diff -U0 %s diff_to.cpp | %run_clang_tidy --diff=0 -checks=-*,modernize-use-override        2>&1 | FileCheck %s
+// RUN: not diff -U0 %s diff_to.cpp | %run_clang_tidy --diff=0 -checks=-*,modernize-use-override -quiet 2>&1 | FileCheck -check-prefix=CHECK-QUIET %s
+// RUN: not diff -U0 %s diff_to.cpp | %run_clang_tidy --diff=0 -checks=-*,modernize-use-override        2>&1 | FileCheck -check-prefix=CHECK %s
+struct A {
+  virtual void f() {}
+  virtual void g() {}
+};
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+struct B : public A {
+  void placeholder_for_f() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK: [[@LINE-2]]:8: warning: annotate this
+// CHECK-QUIET: [[@LINE-3]]:8: warning: annotate this
+  void g() {}
+// CHECK-SANITY: [[@LINE-1]]:8: warning: annotate this
+// CHECK-NOT: warning:
+// CHECK-QUIET-NOT: warning:
+};
+// CHECK-SANITY-NOT: Suppressed
+// CHECK-QUIET-NOT: Suppressed
Index: clang-tidy/tool/run-clang-tidy.py
===================================================================
--- clang-tidy/tool/run-clang-tidy.py
+++ clang-tidy/tool/run-clang-tidy.py
@@ -7,7 +7,6 @@
 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 #
 #===------------------------------------------------------------------------===#
-# FIXME: Integrate with clang-tidy-diff.py
 
 """
 Parallel clang-tidy runner
@@ -60,6 +59,7 @@
 else:
     import queue as queue
 
+
 def find_compilation_database(path):
   """Adjusts the directory until a compilation database is found."""
   result = './'
@@ -77,7 +77,7 @@
   return os.path.normpath(os.path.join(directory, f))
 
 
-def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path,
+def get_tidy_invocation(f, lines, clang_tidy_binary, checks, tmpdir, build_path,
                         header_filter, extra_arg, extra_arg_before, quiet,
                         config):
   """Gets a command line for clang-tidy."""
@@ -87,6 +87,12 @@
   else:
     # Show warnings in all in-project headers by default.
     start.append('-header-filter=^' + build_path + '/.*')
+
+  if lines is not None:
+    line_json = json.dumps([{"name": f, "lines": lines}], separators=(',', ':'))
+    # Run clang-tidy on files containing changes.
+    start.append('-line-filter=' + line_json)
+
   if checks:
     start.append('-checks=' + checks)
   if tmpdir is not None:
@@ -159,8 +165,9 @@
 def run_tidy(args, tmpdir, build_path, queue, lock, failed_files):
   """Takes filenames out of queue and runs clang-tidy on them."""
   while True:
-    name = queue.get()
-    invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks,
+    (name, lines) = queue.get()
+    invocation = get_tidy_invocation(name, lines,
+                                     args.clang_tidy_binary, args.checks,
                                      tmpdir, build_path, args.header_filter,
                                      args.extra_arg, args.extra_arg_before,
                                      args.quiet, args.config)
@@ -176,11 +183,42 @@
     queue.task_done()
 
 
+def get_changed_lines(prefix_len):
+  iregex = '^%s$' % r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc)'
+  lines_by_file = {}
+  filename = None
+  for line in sys.stdin:
+    match = re.search('^\+\+\+\ \"?(.*?/){%s}([^ \t\n\"]*)' % prefix_len, line)
+    if match:
+      filename = match.group(2)
+    if filename is None:
+      continue
+
+    if not re.match(iregex, filename, re.IGNORECASE):
+      continue
+
+    match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
+    if match:
+      start_line = int(match.group(1))
+      line_count = 1
+      if match.group(3):
+        line_count = int(match.group(3))
+      if line_count == 0:
+        continue
+      end_line = start_line + line_count - 1
+      lines_by_file.setdefault(filename, []).append([start_line, end_line])
+
+  return lines_by_file
+
+
 def main():
   parser = argparse.ArgumentParser(description='Runs clang-tidy over all files '
                                    'in a compilation database. Requires '
                                    'clang-tidy and clang-apply-replacements in '
                                    '$PATH.')
+  parser.add_argument('--diff', default=None, const=2, nargs='?', type=int,
+                      help='check only the diff read from stdin.'
+                      ' Strip the smallest prefix containing DIFF[=2] slashes')
   parser.add_argument('-clang-tidy-binary', metavar='PATH',
                       default='clang-tidy',
                       help='path to clang-tidy binary')
@@ -249,11 +287,6 @@
     print("Unable to run clang-tidy.", file=sys.stderr)
     sys.exit(1)
 
-  # Load the database and extract all files.
-  database = json.load(open(os.path.join(build_path, db_path)))
-  files = [make_absolute(entry['file'], entry['directory'])
-           for entry in database]
-
   max_task = args.j
   if max_task == 0:
     max_task = multiprocessing.cpu_count()
@@ -265,6 +298,16 @@
 
   # Build up a big regexy filter from all command line arguments.
   file_name_re = re.compile('|'.join(args.files))
+  files = None
+  changed_lines = None
+  if args.diff is not None:
+    changed_lines = get_changed_lines(args.diff)
+    files = [(k, v) for k,v in changed_lines.items() if file_name_re.search(k)]
+  else:
+    # Load the database and extract affected files.
+    database = json.load(open(os.path.join(build_path, db_path)))
+    all_files = (make_absolute(entry['file'], entry['directory']) for entry in database)
+    files = [(x, None) for x in all_files if file_name_re.search(x)]
 
   return_code = 0
   try:
@@ -281,8 +324,7 @@
 
     # Fill the queue with files.
     for name in files:
-      if file_name_re.search(name):
-        task_queue.put(name)
+      task_queue.put(name)
 
     # Wait for all threads to be done.
     task_queue.join()
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to