summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Goldsborough <peter@goldsborough.me>2018-05-09 10:08:53 -0700
committerSoumith Chintala <soumith@gmail.com>2018-05-09 13:08:53 -0400
commit23be4ac3a230349b273f473dc70bda96c4130731 (patch)
tree70a09ce061c0c8bb15a7915e7d4995097b54ff75
parent769397eb7714c9da1ff3a472f67f063e2da7c483 (diff)
downloadpytorch-23be4ac3a230349b273f473dc70bda96c4130731.tar.gz
pytorch-23be4ac3a230349b273f473dc70bda96c4130731.tar.bz2
pytorch-23be4ac3a230349b273f473dc70bda96c4130731.zip
Add clang tidy tooling (#7412)
-rw-r--r--.clang-tidy40
-rw-r--r--tools/clang_tidy.py196
-rw-r--r--tools/cpp_build/libtorch/CMakeLists.txt1
3 files changed, 237 insertions, 0 deletions
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000000..19dad14435
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,40 @@
+---
+# NOTE: there must be no spaces before the '-', so put the comma first.
+Checks: '
+ *
+ ,modernize-*
+ ,clang-analyzer-*
+ ,-clang-diagnostic-*
+ ,-hicpp-no-array-decay
+ ,-fuchsia-*
+ ,-google-readability-namespace-comments
+ ,-llvm-namespace-comment
+ ,-google-readability-todo
+ ,-cppcoreguidelines-pro-bounds-array-to-pointer-decay
+ ,-cert-err60-cpp
+ ,-llvm-header-guard
+ ,-cppcoreguidelines-special-member-functions
+ ,-misc-unused-parameters
+ ,-hicpp-braces-around-statements
+ ,-hicpp-special-member-functions
+ ,-readability-braces-around-statements
+ ,-modernize-use-default-member-init
+ ,-google-runtime-references
+ ,-cppcoreguidelines-pro-type-vararg
+ ,-google-readability-braces-around-statements
+ ,-google-build-using-namespace
+ ,-hicpp-vararg
+ ,-hicpp-explicit-conversions
+ ,-performance-unnecessary-value-param
+ ,-google-runtime-references
+ ,-cppcoreguidelines-pro-type-static-cast-downcast
+ ,-cppcoreguidelines-pro-bounds-constant-array-index
+ ,-cert-err58-cpp
+ ,-modernize-make-unique
+ ,-cppcoreguidelines-owning-memory
+ '
+WarningsAsErrors: ''
+HeaderFilterRegex: 'torch/csrc/'
+AnalyzeTemporaryDtors: false
+CheckOptions:
+...
diff --git a/tools/clang_tidy.py b/tools/clang_tidy.py
new file mode 100644
index 0000000000..f8c0563633
--- /dev/null
+++ b/tools/clang_tidy.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+
+import argparse
+import json
+import os.path
+import re
+import subprocess
+import sys
+
+DEFAULT_FILE_PATTERN = r".*\.[ch](pp)?"
+
+# @@ -start,count +start,count @@
+CHUNK_PATTERN = r"^@@\s+-\d+,\d+\s+\+(\d+)(?:,(\d+))?\s+@@"
+
+
+def run_shell_command(arguments, process_name=None):
+ """Executes a shell command."""
+ assert len(arguments) > 0
+ try:
+ output = subprocess.check_output(arguments, stderr=subprocess.STDOUT)
+ except OSError:
+ _, e, _ = sys.exc_info()
+ process_name = process_name or arguments[0]
+ raise RuntimeError("Error executing {}: {}".format(process_name, e))
+ else:
+ return output.decode()
+
+
+def transform_globs_into_regexes(globs):
+ """Turns glob patterns into regular expressions."""
+ return [glob.replace("*", ".*").replace("?", ".") for glob in globs]
+
+
+def get_file_patterns(globs, regexes):
+ """Returns a list of compiled regex objects from globs and regex pattern strings."""
+ regexes += transform_globs_into_regexes(globs)
+ if not regexes:
+ regexes = [DEFAULT_FILE_PATTERN]
+ return [re.compile(regex + "$") for regex in regexes]
+
+
+def git_diff(args, verbose):
+ """Executes a git diff command in the shell and returns its output."""
+ # --no-pager gets us the plain output, without pagination.
+ # --no-color removes color codes.
+ command = ["git", "--no-pager", "diff", "--no-color"] + args
+ if verbose:
+ print(" ".join(command))
+ return run_shell_command(command, process_name="git diff")
+
+
+def filter_files(files, file_patterns):
+ """Returns all files that match any of the patterns."""
+ filtered = []
+ for file in files:
+ for pattern in file_patterns:
+ if pattern.match(file):
+ filtered.append(file)
+ return filtered
+
+
+def get_changed_files(revision, paths, verbose):
+ """Runs git diff to get the paths of all changed files."""
+ # --diff-filter AMU gets us files that are (A)dded, (M)odified or (U)nmerged (in the working copy).
+ # --name-only makes git diff return only the file paths, without any of the source changes.
+ args = ["--diff-filter", "AMU", "--ignore-all-space", "--name-only", revision]
+ output = git_diff(args + paths, verbose)
+ return output.split("\n")
+
+
+def get_all_files(paths):
+ """Yields all files in any of the given paths"""
+ for path in paths:
+ for root, _, files in os.walk(path):
+ for file in files:
+ yield os.path.join(root, file)
+
+
+def get_changed_lines(revision, filename, verbose):
+ """Runs git diff to get the line ranges of all file changes."""
+ output = git_diff(["--unified=0", revision, filename], verbose)
+ changed_lines = []
+ for chunk in re.finditer(CHUNK_PATTERN, output, re.MULTILINE):
+ start = int(chunk.group(1))
+ count = int(chunk.group(2) or 1)
+ changed_lines.append([start, start + count])
+
+ return {"name": filename, "lines": changed_lines}
+
+
+def run_clang_tidy(options, line_filters, files):
+ """Executes the actual clang-tidy command in the shell."""
+ command = [options.clang_tidy_exe, "-p", options.compile_commands_dir]
+ if not options.config_file and os.path.exists(".clang-tidy"):
+ options.config_file = ".clang-tidy"
+ if options.config_file:
+ import yaml
+
+ with open(options.config_file) as config:
+ # Here we convert the YAML config file to a JSON blob.
+ command += ["-config", json.dumps(yaml.load(config))]
+ if options.checks:
+ command += ["-checks", options.checks]
+ if line_filters:
+ command += ["-line-filter", json.dumps(line_filters)]
+ command += ["-{}".format(arg) for arg in options.extra_args]
+ command += files
+
+ if options.verbose:
+ print(" ".join(command))
+ if options.show_command_only:
+ command = [re.sub(r"^([{[].*[]}])$", r"'\1'", arg) for arg in command]
+ return " ".join(command)
+
+ return run_shell_command(command)
+
+
+def parse_options():
+ parser = argparse.ArgumentParser(description="Run Clang-Tidy (on your Git changes)")
+ parser.add_argument(
+ "-c",
+ "--clang-tidy-exe",
+ default="clang-tidy",
+ help="{ath to clang-tidy executable",
+ )
+ parser.add_argument(
+ "-e",
+ "--extra-args",
+ nargs="+",
+ default=[],
+ help="Extra arguments to forward to clang-tidy",
+ )
+ parser.add_argument(
+ "-g",
+ "--glob",
+ nargs="+",
+ default=[],
+ help="File patterns as UNIX globs (no **)",
+ )
+ parser.add_argument(
+ "-x", "--regex", nargs="+", default=[], help="File patterns as regexes"
+ )
+ parser.add_argument(
+ "-d",
+ "--compile-commands-dir",
+ default=".",
+ help="Path to the folder containing compile_commands.json",
+ )
+ parser.add_argument("-r", "--revision", help="Git revision to get changes from")
+ parser.add_argument(
+ "-p", "--paths", nargs="+", default=["."], help="Lint only the given paths"
+ )
+ parser.add_argument(
+ "-s",
+ "--show-command-only",
+ action="store_true",
+ help="Only show the command to be executed, without running it",
+ )
+ parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
+ parser.add_argument(
+ "--config-file",
+ help="Path to a clang-tidy config file. Defaults to '.clang-tidy'.",
+ )
+ parser.add_argument(
+ "--checks", help="Appends checks to those from the config file (if any)"
+ )
+ return parser.parse_args()
+
+
+def main():
+ options = parse_options()
+ if options.revision:
+ files = get_changed_files(options.revision, options.paths, options.verbose)
+ else:
+ files = get_all_files(options.paths)
+ file_patterns = get_file_patterns(options.glob, options.regex)
+ files = filter_files(files, file_patterns)
+
+ # clang-tidy error's when it does not get input files.
+ if not files:
+ print("No files detected.")
+ sys.exit()
+
+ line_filters = []
+ if options.revision:
+ for filename in files:
+ changed_lines = get_changed_lines(
+ options.revision, filename, options.verbose
+ )
+ line_filters.append(changed_lines)
+
+ print(run_clang_tidy(options, line_filters, files))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/cpp_build/libtorch/CMakeLists.txt b/tools/cpp_build/libtorch/CMakeLists.txt
index 6f30a879ce..e546529aab 100644
--- a/tools/cpp_build/libtorch/CMakeLists.txt
+++ b/tools/cpp_build/libtorch/CMakeLists.txt
@@ -4,6 +4,7 @@ cmake_policy(VERSION 3.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (VERBOSE)
message(STATUS "ATEN_PATH is ${ATEN_PATH}")