13
13
14
14
import argparse
15
15
import io
16
+ import itertools
16
17
import os
17
18
import re
18
19
import sys
19
20
21
+
20
22
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
21
23
# entry and 'False' if the entry already existed.
22
24
def adapt_cmake (module_path , check_name_camel ):
@@ -53,7 +55,18 @@ def adapt_cmake(module_path, check_name_camel):
53
55
54
56
55
57
# 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 = ""
57
70
filename = os .path .join (module_path , check_name_camel ) + ".h"
58
71
print ("Creating %s..." % filename )
59
72
with io .open (filename , "w" , encoding = "utf8" , newline = "\n " ) as f :
@@ -94,7 +107,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
94
107
%(check_name_camel)s(StringRef Name, ClangTidyContext *Context)
95
108
: ClangTidyCheck(Name, Context) {}
96
109
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
98
111
};
99
112
100
113
} // namespace clang::tidy::%(namespace)s
@@ -107,6 +120,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
107
120
"check_name" : check_name ,
108
121
"module" : module ,
109
122
"namespace" : namespace ,
123
+ "override_supported" : override_supported ,
110
124
}
111
125
)
112
126
@@ -292,7 +306,9 @@ def add_release_notes(module_path, module, check_name):
292
306
293
307
294
308
# 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 "
296
312
check_name_dashes = module + "-" + check_name
297
313
filename = os .path .normpath (
298
314
os .path .join (
@@ -309,7 +325,7 @@ def write_test(module_path, module, check_name, test_extension):
309
325
print ("Creating %s..." % filename )
310
326
with io .open (filename , "w" , encoding = "utf8" , newline = "\n " ) as f :
311
327
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
313
329
314
330
// FIXME: Add something that triggers the check here.
315
331
void f();
@@ -324,7 +340,7 @@ def write_test(module_path, module, check_name, test_extension):
324
340
// FIXME: Add something that doesn't trigger the check here.
325
341
void awesome_f2();
326
342
"""
327
- % {"check_name_dashes" : check_name_dashes }
343
+ % {"check_name_dashes" : check_name_dashes , "standard" : test_standard }
328
344
)
329
345
330
346
@@ -497,7 +513,10 @@ def format_link_alias(doc_file):
497
513
if (match or (check_name .startswith ("clang-analyzer-" ))) and check_name :
498
514
module = doc_file [0 ]
499
515
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
+ ):
501
520
title = "Clang Static Analyzer " + check_file
502
521
# Preserve the anchor in checkers.html from group 2.
503
522
target = "" if not match else match .group (1 ) + ".html" + match .group (2 )
@@ -515,29 +534,31 @@ def format_link_alias(doc_file):
515
534
if target :
516
535
# The checker is just a redirect.
517
536
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 "
519
538
% {
520
539
"check_name" : check_name ,
521
540
"module" : module ,
522
541
"check_file" : check_file ,
523
542
"target" : target ,
524
543
"title" : title ,
525
544
"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
+ )
529
549
else :
530
550
# The checker is just a alias without redirect.
531
551
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 "
533
553
% {
534
554
"check_name" : check_name ,
535
555
"module" : module ,
536
556
"check_file" : check_file ,
537
557
"target" : target ,
538
558
"title" : title ,
539
559
"autofix" : autofix ,
540
- })
560
+ }
561
+ )
541
562
return ""
542
563
543
564
checks = map (format_link , doc_files )
@@ -599,6 +620,22 @@ def main():
599
620
"objc" : "m" ,
600
621
"objc++" : "mm" ,
601
622
}
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
+ }
602
639
parser = argparse .ArgumentParser ()
603
640
parser .add_argument (
604
641
"--update-docs" ,
@@ -609,9 +646,19 @@ def main():
609
646
"--language" ,
610
647
help = "language to use for new check (defaults to c++)" ,
611
648
choices = language_to_extension .keys (),
612
- default = "c++" ,
649
+ default = None ,
613
650
metavar = "LANG" ,
614
651
)
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
+ )
615
662
parser .add_argument (
616
663
"module" ,
617
664
nargs = "?" ,
@@ -652,12 +699,43 @@ def main():
652
699
else :
653
700
namespace = module
654
701
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
+ )
656
734
write_implementation (module_path , module , namespace , check_name_camel )
657
735
adapt_module (module_path , module , check_name , check_name_camel )
658
736
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 )
661
739
write_docs (module_path , module , check_name )
662
740
update_checks_list (clang_tidy_path )
663
741
print ("Done. Now it's your turn!" )
0 commit comments