Bump version to 19.1.0git
[llvm-project.git] / clang-tools-extra / clangd / TidyFastChecks.py
blobb544551e6e041f68511e957f9a795ac5e3be7be8
1 #!/usr/bin/env python3
3 # Determines which clang-tidy checks are "fast enough" to run in clangd.
4 # This runs clangd --check --check-tidy-time and parses the output.
5 # This program outputs a header fragment specifying which checks are fast:
6 # FAST(bugprone-argument-comment, 5)
7 # SLOW(misc-const-correctness, 200)
8 # If given the old header fragment as input, we lean to preserve its choices.
10 # This is not deterministic or hermetic, but should be run occasionally to
11 # update the list of allowed checks. From llvm-project:
12 # clang-tools-extra/clangd/TidyFastChecks.py --clangd=build-opt/bin/clangd
13 # Be sure to use an optimized, no-asserts, tidy-enabled build of clangd!
15 import argparse
16 import os
17 import re
18 import subprocess
19 import sys
21 # Checks faster than FAST_THRESHOLD are fast, slower than SLOW_THRESHOLD slow.
22 # If a check is in between, we stick with our previous decision. This avoids
23 # enabling/disabling checks between releases due to random measurement jitter.
24 FAST_THRESHOLD = 8 # percent
25 SLOW_THRESHOLD = 15
27 parser = argparse.ArgumentParser()
28 parser.add_argument(
29 "--target",
30 help="X-macro output file. "
31 "If it exists, existing contents will be used for hysteresis",
32 default="clang-tools-extra/clangd/TidyFastChecks.inc",
34 parser.add_argument(
35 "--source",
36 help="Source file to benchmark tidy checks",
37 default="clang/lib/Sema/Sema.cpp",
39 parser.add_argument(
40 "--clangd", help="clangd binary to invoke", default="build/bin/clangd"
42 parser.add_argument("--checks", help="check glob to run", default="*")
43 parser.add_argument("--verbose", help="log clangd output", action="store_true")
44 args = parser.parse_args()
46 # Use the preprocessor to extract the list of previously-fast checks.
47 def read_old_fast(path):
48 text = subprocess.check_output(
50 "cpp",
51 "-P", # Omit GNU line markers
52 "-nostdinc", # Don't include stdc-predef.h
53 "-DFAST(C,T)=C", # Print fast checks only
54 path,
57 for line in text.splitlines():
58 if line.strip():
59 yield line.strip().decode("utf-8")
62 old_fast = list(read_old_fast(args.target)) if os.path.exists(args.target) else []
63 print(f"Old fast checks: {old_fast}", file=sys.stderr)
65 # Runs clangd --check --check-tidy-time.
66 # Yields (check, percent-overhead) pairs.
67 def measure():
68 process = subprocess.Popen(
70 args.clangd,
71 "--check=" + args.source,
72 "--check-locations=0", # Skip useless slow steps.
73 "--check-tidy-time=" + args.checks,
75 stderr=subprocess.PIPE,
77 recording = False
78 for line in iter(process.stderr.readline, b""):
79 if args.verbose:
80 print("clangd> ", line, file=sys.stderr)
81 if not recording:
82 if b"Timing AST build with individual clang-tidy checks" in line:
83 recording = True
84 continue
85 if b"Finished individual clang-tidy checks" in line:
86 return
87 match = re.search(rb"(\S+) = (\S+)%", line)
88 if match:
89 yield (match.group(1).decode("utf-8"), float(match.group(2)))
92 with open(args.target, "w", buffering=1) as target:
93 # Produce an includable X-macros fragment with our decisions.
94 print(
95 f"""// This file is generated, do not edit it directly!
96 // Deltas are percentage regression in parsing {args.source}
97 #ifndef FAST
98 #define FAST(CHECK, DELTA)
99 #endif
100 #ifndef SLOW
101 #define SLOW(CHECK, DELTA)
102 #endif
103 """,
104 file=target,
107 for check, time in measure():
108 threshold = SLOW_THRESHOLD if check in old_fast else FAST_THRESHOLD
109 decision = "FAST" if time <= threshold else "SLOW"
110 print(f"{decision} {check} {time}% <= {threshold}%", file=sys.stderr)
111 print(f"{decision}({check}, {time})", file=target)
113 print(
115 #undef FAST
116 #undef SLOW
117 """,
118 file=target,