Skip to content

Commit 2624c96

Browse files
committed
Extend support for specifying languages and version in add_new_check.py
- Allow specifying a language standard when adding a new check - Simplify the language standards(and or-later) handlnig in check_clang_tidy
1 parent 87f2c25 commit 2624c96

File tree

2 files changed

+129
-31
lines changed

2 files changed

+129
-31
lines changed

clang-tools-extra/clang-tidy/add_new_check.py

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
import argparse
1515
import io
16+
import itertools
1617
import os
1718
import re
1819
import sys
1920

21+
2022
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
2123
# entry and 'False' if the entry already existed.
2224
def adapt_cmake(module_path, check_name_camel):
@@ -53,7 +55,18 @@ def adapt_cmake(module_path, check_name_camel):
5355

5456

5557
# Adds a header for the new check.
56-
def write_header(module_path, module, namespace, check_name, check_name_camel):
58+
def write_header(
59+
module_path, module, namespace, check_name, check_name_camel, lang_restrict
60+
):
61+
if lang_restrict:
62+
override_supported = """
63+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
64+
return %s;
65+
}""" % (
66+
lang_restrict % {"lang": "LangOpts"}
67+
)
68+
else:
69+
override_supported = ""
5770
filename = os.path.join(module_path, check_name_camel) + ".h"
5871
print("Creating %s..." % filename)
5972
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
@@ -94,7 +107,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
94107
%(check_name_camel)s(StringRef Name, ClangTidyContext *Context)
95108
: ClangTidyCheck(Name, Context) {}
96109
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
97-
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
110+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;%(override_supported)s
98111
};
99112
100113
} // namespace clang::tidy::%(namespace)s
@@ -107,6 +120,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
107120
"check_name": check_name,
108121
"module": module,
109122
"namespace": namespace,
123+
"override_supported": override_supported,
110124
}
111125
)
112126

@@ -292,7 +306,9 @@ def add_release_notes(module_path, module, check_name):
292306

293307

294308
# Adds a test for the check.
295-
def write_test(module_path, module, check_name, test_extension):
309+
def write_test(module_path, module, check_name, test_extension, test_standard):
310+
if test_standard:
311+
test_standard = f"-std={test_standard}-or-later "
296312
check_name_dashes = module + "-" + check_name
297313
filename = os.path.normpath(
298314
os.path.join(
@@ -309,7 +325,7 @@ def write_test(module_path, module, check_name, test_extension):
309325
print("Creating %s..." % filename)
310326
with io.open(filename, "w", encoding="utf8", newline="\n") as f:
311327
f.write(
312-
"""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
328+
"""// RUN: %%check_clang_tidy %(standard)s%%s %(check_name_dashes)s %%t
313329
314330
// FIXME: Add something that triggers the check here.
315331
void f();
@@ -324,7 +340,7 @@ def write_test(module_path, module, check_name, test_extension):
324340
// FIXME: Add something that doesn't trigger the check here.
325341
void awesome_f2();
326342
"""
327-
% {"check_name_dashes": check_name_dashes}
343+
% {"check_name_dashes": check_name_dashes, "standard": test_standard}
328344
)
329345

330346

@@ -497,7 +513,10 @@ def format_link_alias(doc_file):
497513
if (match or (check_name.startswith("clang-analyzer-"))) and check_name:
498514
module = doc_file[0]
499515
check_file = doc_file[1].replace(".rst", "")
500-
if not match or match.group(1) == "https://clang.llvm.org/docs/analyzer/checkers":
516+
if (
517+
not match
518+
or match.group(1) == "https://clang.llvm.org/docs/analyzer/checkers"
519+
):
501520
title = "Clang Static Analyzer " + check_file
502521
# Preserve the anchor in checkers.html from group 2.
503522
target = "" if not match else match.group(1) + ".html" + match.group(2)
@@ -515,29 +534,31 @@ def format_link_alias(doc_file):
515534
if target:
516535
# The checker is just a redirect.
517536
return (
518-
" :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n"
537+
" :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n"
519538
% {
520539
"check_name": check_name,
521540
"module": module,
522541
"check_file": check_file,
523542
"target": target,
524543
"title": title,
525544
"autofix": autofix,
526-
"ref_begin" : ref_begin,
527-
"ref_end" : ref_end
528-
})
545+
"ref_begin": ref_begin,
546+
"ref_end": ref_end,
547+
}
548+
)
529549
else:
530550
# The checker is just a alias without redirect.
531551
return (
532-
" :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n"
552+
" :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n"
533553
% {
534554
"check_name": check_name,
535555
"module": module,
536556
"check_file": check_file,
537557
"target": target,
538558
"title": title,
539559
"autofix": autofix,
540-
})
560+
}
561+
)
541562
return ""
542563

543564
checks = map(format_link, doc_files)
@@ -599,6 +620,22 @@ def main():
599620
"objc": "m",
600621
"objc++": "mm",
601622
}
623+
cpp_language_to_requirements = {
624+
"c++98": "CPlusPlus",
625+
"c++11": "CPlusPlus11",
626+
"c++14": "CPlusPlus14",
627+
"c++17": "CPlusPlus17",
628+
"c++20": "CPlusPlus20",
629+
"c++23": "CPlusPlus23",
630+
"c++26": "CPlusPlus26",
631+
}
632+
c_language_to_requirements = {
633+
"c99": None,
634+
"c11": "C11",
635+
"c17": "C17",
636+
"c23": "C23",
637+
"c27": "C2Y",
638+
}
602639
parser = argparse.ArgumentParser()
603640
parser.add_argument(
604641
"--update-docs",
@@ -609,9 +646,19 @@ def main():
609646
"--language",
610647
help="language to use for new check (defaults to c++)",
611648
choices=language_to_extension.keys(),
612-
default="c++",
649+
default=None,
613650
metavar="LANG",
614651
)
652+
parser.add_argument(
653+
"--standard",
654+
help="Specify a specific version of the language",
655+
choices=list(
656+
itertools.chain(
657+
cpp_language_to_requirements.keys(), c_language_to_requirements.keys()
658+
)
659+
),
660+
default=None,
661+
)
615662
parser.add_argument(
616663
"module",
617664
nargs="?",
@@ -652,12 +699,43 @@ def main():
652699
else:
653700
namespace = module
654701

655-
write_header(module_path, module, namespace, check_name, check_name_camel)
702+
language = args.language
703+
704+
if args.standard:
705+
if args.standard in cpp_language_to_requirements:
706+
if language and language != "c++":
707+
raise ValueError("C++ standard chosen when language is not C++")
708+
language = "c++"
709+
elif args.standard in c_language_to_requirements:
710+
if language and language != "c":
711+
raise ValueError("C standard chosen when language is not C")
712+
language = "c"
713+
714+
if not language:
715+
language = "c++"
716+
717+
language_restrict = None
718+
719+
if language == "c":
720+
language_restrict = "!%(lang)s.CPlusPlus"
721+
extra = c_language_to_requirements.get(args.standard, None)
722+
if extra:
723+
language_restrict += f" && %(lang)s.{extra}"
724+
elif language == "c++":
725+
language_restrict = (
726+
f"%(lang)s.{cpp_language_to_requirements.get(args.standard, 'CPlusPlus')}"
727+
)
728+
elif language in ["objc", "objc++"]:
729+
language_restrict = "%(lang)s.ObjC"
730+
731+
write_header(
732+
module_path, module, namespace, check_name, check_name_camel, language_restrict
733+
)
656734
write_implementation(module_path, module, namespace, check_name_camel)
657735
adapt_module(module_path, module, check_name, check_name_camel)
658736
add_release_notes(module_path, module, check_name)
659-
test_extension = language_to_extension.get(args.language)
660-
write_test(module_path, module, check_name, test_extension)
737+
test_extension = language_to_extension.get(language)
738+
write_test(module_path, module, check_name, test_extension, args.standard)
661739
write_docs(module_path, module, check_name)
662740
update_checks_list(clang_tidy_path)
663741
print("Done. Now it's your turn!")

clang-tools-extra/test/clang-tidy/check_clang_tidy.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,11 @@ def run_clang_tidy(self):
205205
self.temp_file_name,
206206
]
207207
+ [
208-
"-fix"
209-
if self.export_fixes is None
210-
else "--export-fixes=" + self.export_fixes
208+
(
209+
"-fix"
210+
if self.export_fixes is None
211+
else "--export-fixes=" + self.export_fixes
212+
)
211213
]
212214
+ [
213215
"--checks=-*," + self.check_name,
@@ -299,19 +301,37 @@ def run(self):
299301
self.check_notes(clang_tidy_output)
300302

301303

304+
CPP_STANDARDS = [
305+
"c++98",
306+
"c++11",
307+
("c++14", "c++1y"),
308+
("c++17", "c++1z"),
309+
("c++20", "c++2a"),
310+
("c++23", "c++2b"),
311+
("c++26", "c++2c"),
312+
]
313+
C_STANDARDS = ["c99", ("c11", "c1x"), "c17", ("c23", "c2x"), "c2y"]
314+
315+
302316
def expand_std(std):
303-
if std == "c++98-or-later":
304-
return ["c++98", "c++11", "c++14", "c++17", "c++20", "c++23", "c++2c"]
305-
if std == "c++11-or-later":
306-
return ["c++11", "c++14", "c++17", "c++20", "c++23", "c++2c"]
307-
if std == "c++14-or-later":
308-
return ["c++14", "c++17", "c++20", "c++23", "c++2c"]
309-
if std == "c++17-or-later":
310-
return ["c++17", "c++20", "c++23", "c++2c"]
311-
if std == "c++20-or-later":
312-
return ["c++20", "c++23", "c++2c"]
313-
if std == "c++23-or-later":
314-
return ["c++23", "c++2c"]
317+
split_std, or_later, _ = std.partition("-or-later")
318+
319+
if not or_later:
320+
return [split_std]
321+
322+
for standard_list in (CPP_STANDARDS, C_STANDARDS):
323+
item = next(
324+
(
325+
i
326+
for i, v in enumerate(standard_list)
327+
if (split_std in v if isinstance(v, (list, tuple)) else split_std == v)
328+
),
329+
None,
330+
)
331+
if item is not None:
332+
return [split_std] + [
333+
x if isinstance(x, str) else x[0] for x in standard_list[item + 1 :]
334+
]
315335
return [std]
316336

317337

0 commit comments

Comments
 (0)