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!
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
27 parser
= argparse
.ArgumentParser()
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",
36 help="Source file to benchmark tidy checks",
37 default
="clang/lib/Sema/Sema.cpp",
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(
51 "-P", # Omit GNU line markers
52 "-nostdinc", # Don't include stdc-predef.h
53 "-DFAST(C,T)=C", # Print fast checks only
57 for line
in text
.splitlines():
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.
68 process
= subprocess
.Popen(
71 "--check=" + args
.source
,
72 "--check-locations=0", # Skip useless slow steps.
73 "--check-tidy-time=" + args
.checks
,
75 stderr
=subprocess
.PIPE
,
78 for line
in iter(process
.stderr
.readline
, b
""):
80 print("clangd> ", line
, file=sys
.stderr
)
82 if b
"Timing AST build with individual clang-tidy checks" in line
:
85 if b
"Finished individual clang-tidy checks" in line
:
87 match
= re
.search(rb
"(\S+) = (\S+)%", line
)
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.
95 f
"""// This file is generated, do not edit it directly!
96 // Deltas are percentage regression in parsing {args.source}
98 #define FAST(CHECK, DELTA)
101 #define SLOW(CHECK, DELTA)
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
)