aboutsummaryrefslogtreecommitdiff
path: root/tools_webrtc/clang_tidy.py
blob: b3f738eab3712f782322427e0208e62982985ba4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env vpython3

# Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
#
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file in the root of the source
# tree. An additional intellectual property rights grant can be found
# in the file PATENTS.  All contributing project authors may
# be found in the AUTHORS file in the root of the source tree.
"""Invoke clang-tidy tool.

Usage: clang_tidy.py file.cc [clang-tidy-args...]

Just a proof of concept!
We use an embedded clang-tidy whose version doesn't match clang++.
"""

import argparse
import os
import shutil
import subprocess
import sys
import tempfile
from presubmit_checks_lib.build_helpers import (GetClangTidyPath,
                                                GetCompilationCommand)

# We enable all checkers by default for investigation purpose.
# This includes clang-analyzer-* checks.
# Individual checkers can be disabled via command line options.
# TODO(bugs.webrtc.com/10258): Select checkers relevant to webrtc guidelines.
CHECKER_OPTION = '-checks=*'


def Process(filepath, args):
  # Build directory is needed to gather compilation flags.
  # Create a temporary one (instead of reusing an existing one)
  # to keep the CLI simple and unencumbered.
  out_dir = tempfile.mkdtemp('clang_tidy')

  try:
    gn_args = []  # Use default build.
    command = GetCompilationCommand(filepath, gn_args, out_dir)

    # Remove warning flags. They aren't needed and they cause trouble
    # when clang-tidy doesn't match most recent clang.
    # Same battle for -f (e.g. -fcomplete-member-pointers).
    command = [
        arg for arg in command
        if not (arg.startswith('-W') or arg.startswith('-f'))
    ]

    # Path from build dir.
    rel_path = os.path.relpath(os.path.abspath(filepath), out_dir)

    # Replace clang++ by clang-tidy
    command[0:1] = [GetClangTidyPath(), CHECKER_OPTION, rel_path
                    ] + args + ['--']  # Separator for clang flags.
    print("Running: %s" % ' '.join(command))
    # Run from build dir so that relative paths are correct.
    p = subprocess.Popen(command,
                         cwd=out_dir,
                         stdout=sys.stdout,
                         stderr=sys.stderr)
    p.communicate()
    return p.returncode
  finally:
    shutil.rmtree(out_dir, ignore_errors=True)


def ValidateCC(filepath):
  """We can only analyze .cc files. Provide explicit message about that."""
  if filepath.endswith('.cc'):
    return filepath
  msg = ('%s not supported.\n'
         'For now, we can only analyze translation units (.cc files).' %
         filepath)
  raise argparse.ArgumentTypeError(msg)


def Main():
  description = (
      "Run clang-tidy on single cc file.\n"
      "Use flags, defines and include paths as in default debug build.\n"
      "WARNING, this is a POC version with rough edges.")
  parser = argparse.ArgumentParser(description=description)
  parser.add_argument('filepath',
                      help='Specifies the path of the .cc file to analyze.',
                      type=ValidateCC)
  parser.add_argument('args',
                      nargs=argparse.REMAINDER,
                      help='Arguments passed to clang-tidy')
  parsed_args = parser.parse_args()
  return Process(parsed_args.filepath, parsed_args.args)


if __name__ == '__main__':
  sys.exit(Main())