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
import textwrap
20
21
22
+
21
23
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
22
24
# entry and 'False' if the entry already existed.
23
25
def adapt_cmake (module_path , check_name_camel ):
@@ -55,13 +57,28 @@ def adapt_cmake(module_path, check_name_camel):
55
57
56
58
# Adds a header for the new check.
57
59
def write_header (
58
- module_path , module , namespace , check_name , check_name_camel , description
60
+ module_path ,
61
+ module ,
62
+ namespace ,
63
+ check_name ,
64
+ check_name_camel ,
65
+ description ,
66
+ lang_restrict ,
59
67
):
60
68
wrapped_desc = "\n " .join (
61
69
textwrap .wrap (
62
70
description , width = 80 , initial_indent = "/// " , subsequent_indent = "/// "
63
71
)
64
72
)
73
+ if lang_restrict :
74
+ override_supported = """
75
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
76
+ return %s;
77
+ }""" % (
78
+ lang_restrict % {"lang" : "LangOpts" }
79
+ )
80
+ else :
81
+ override_supported = ""
65
82
filename = os .path .join (module_path , check_name_camel ) + ".h"
66
83
print ("Creating %s..." % filename )
67
84
with io .open (filename , "w" , encoding = "utf8" , newline = "\n " ) as f :
@@ -102,7 +119,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
102
119
%(check_name_camel)s(StringRef Name, ClangTidyContext *Context)
103
120
: ClangTidyCheck(Name, Context) {}
104
121
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
105
- void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
122
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;%(override_supported)s
106
123
};
107
124
108
125
} // namespace clang::tidy::%(namespace)s
@@ -116,6 +133,7 @@ class %(check_name_camel)s : public ClangTidyCheck {
116
133
"module" : module ,
117
134
"namespace" : namespace ,
118
135
"description" : wrapped_desc ,
136
+ "override_supported" : override_supported ,
119
137
}
120
138
)
121
139
@@ -306,7 +324,9 @@ def add_release_notes(module_path, module, check_name, description):
306
324
307
325
308
326
# Adds a test for the check.
309
- def write_test (module_path , module , check_name , test_extension ):
327
+ def write_test (module_path , module , check_name , test_extension , test_standard ):
328
+ if test_standard :
329
+ test_standard = f"-std={ test_standard } -or-later "
310
330
check_name_dashes = module + "-" + check_name
311
331
filename = os .path .normpath (
312
332
os .path .join (
@@ -323,7 +343,7 @@ def write_test(module_path, module, check_name, test_extension):
323
343
print ("Creating %s..." % filename )
324
344
with io .open (filename , "w" , encoding = "utf8" , newline = "\n " ) as f :
325
345
f .write (
326
- """// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
346
+ """// RUN: %%check_clang_tidy %(standard)s% %s %(check_name_dashes)s %%t
327
347
328
348
// FIXME: Add something that triggers the check here.
329
349
void f();
@@ -338,7 +358,7 @@ def write_test(module_path, module, check_name, test_extension):
338
358
// FIXME: Add something that doesn't trigger the check here.
339
359
void awesome_f2();
340
360
"""
341
- % {"check_name_dashes" : check_name_dashes }
361
+ % {"check_name_dashes" : check_name_dashes , "standard" : test_standard }
342
362
)
343
363
344
364
@@ -511,7 +531,10 @@ def format_link_alias(doc_file):
511
531
if (match or (check_name .startswith ("clang-analyzer-" ))) and check_name :
512
532
module = doc_file [0 ]
513
533
check_file = doc_file [1 ].replace (".rst" , "" )
514
- if not match or match .group (1 ) == "https://clang.llvm.org/docs/analyzer/checkers" :
534
+ if (
535
+ not match
536
+ or match .group (1 ) == "https://clang.llvm.org/docs/analyzer/checkers"
537
+ ):
515
538
title = "Clang Static Analyzer " + check_file
516
539
# Preserve the anchor in checkers.html from group 2.
517
540
target = "" if not match else match .group (1 ) + ".html" + match .group (2 )
@@ -529,29 +552,31 @@ def format_link_alias(doc_file):
529
552
if target :
530
553
# The checker is just a redirect.
531
554
return (
532
- " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n "
555
+ " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(ref_begin)s`%(title)s <%(target)s>`%(ref_end)s,%(autofix)s\n "
533
556
% {
534
557
"check_name" : check_name ,
535
558
"module" : module ,
536
559
"check_file" : check_file ,
537
560
"target" : target ,
538
561
"title" : title ,
539
562
"autofix" : autofix ,
540
- "ref_begin" : ref_begin ,
541
- "ref_end" : ref_end
542
- })
563
+ "ref_begin" : ref_begin ,
564
+ "ref_end" : ref_end ,
565
+ }
566
+ )
543
567
else :
544
568
# The checker is just a alias without redirect.
545
569
return (
546
- " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n "
570
+ " :doc:`%(check_name)s <%(module)s/%(check_file)s>`, %(title)s,%(autofix)s\n "
547
571
% {
548
572
"check_name" : check_name ,
549
573
"module" : module ,
550
574
"check_file" : check_file ,
551
575
"target" : target ,
552
576
"title" : title ,
553
577
"autofix" : autofix ,
554
- })
578
+ }
579
+ )
555
580
return ""
556
581
557
582
checks = map (format_link , doc_files )
@@ -613,6 +638,22 @@ def main():
613
638
"objc" : "m" ,
614
639
"objc++" : "mm" ,
615
640
}
641
+ cpp_language_to_requirements = {
642
+ "c++98" : "CPlusPlus" ,
643
+ "c++11" : "CPlusPlus11" ,
644
+ "c++14" : "CPlusPlus14" ,
645
+ "c++17" : "CPlusPlus17" ,
646
+ "c++20" : "CPlusPlus20" ,
647
+ "c++23" : "CPlusPlus23" ,
648
+ "c++26" : "CPlusPlus26" ,
649
+ }
650
+ c_language_to_requirements = {
651
+ "c99" : None ,
652
+ "c11" : "C11" ,
653
+ "c17" : "C17" ,
654
+ "c23" : "C23" ,
655
+ "c27" : "C2Y" ,
656
+ }
616
657
parser = argparse .ArgumentParser ()
617
658
parser .add_argument (
618
659
"--update-docs" ,
@@ -623,7 +664,7 @@ def main():
623
664
"--language" ,
624
665
help = "language to use for new check (defaults to c++)" ,
625
666
choices = language_to_extension .keys (),
626
- default = "c++" ,
667
+ default = None ,
627
668
metavar = "LANG" ,
628
669
)
629
670
parser .add_argument (
@@ -633,6 +674,16 @@ def main():
633
674
default = "FIXME: Write a short description" ,
634
675
type = str ,
635
676
)
677
+ parser .add_argument (
678
+ "--standard" ,
679
+ help = "Specify a specific version of the language" ,
680
+ choices = list (
681
+ itertools .chain (
682
+ cpp_language_to_requirements .keys (), c_language_to_requirements .keys ()
683
+ )
684
+ ),
685
+ default = None ,
686
+ )
636
687
parser .add_argument (
637
688
"module" ,
638
689
nargs = "?" ,
@@ -677,14 +728,49 @@ def main():
677
728
if not description .endswith ("." ):
678
729
description += "."
679
730
731
+ language = args .language
732
+
733
+ if args .standard :
734
+ if args .standard in cpp_language_to_requirements :
735
+ if language and language != "c++" :
736
+ raise ValueError ("C++ standard chosen when language is not C++" )
737
+ language = "c++"
738
+ elif args .standard in c_language_to_requirements :
739
+ if language and language != "c" :
740
+ raise ValueError ("C standard chosen when language is not C" )
741
+ language = "c"
742
+
743
+ if not language :
744
+ language = "c++"
745
+
746
+ language_restrict = None
747
+
748
+ if language == "c" :
749
+ language_restrict = "!%(lang)s.CPlusPlus"
750
+ extra = c_language_to_requirements .get (args .standard , None )
751
+ if extra :
752
+ language_restrict += f" && %(lang)s.{ extra } "
753
+ elif language == "c++" :
754
+ language_restrict = (
755
+ f"%(lang)s.{ cpp_language_to_requirements .get (args .standard , 'CPlusPlus' )} "
756
+ )
757
+ elif language in ["objc" , "objc++" ]:
758
+ language_restrict = "%(lang)s.ObjC"
759
+
680
760
write_header (
681
- module_path , module , namespace , check_name , check_name_camel , description
761
+ module_path ,
762
+ module ,
763
+ namespace ,
764
+ check_name ,
765
+ check_name_camel ,
766
+ description ,
767
+ language_restrict ,
682
768
)
683
769
write_implementation (module_path , module , namespace , check_name_camel )
684
770
adapt_module (module_path , module , check_name , check_name_camel )
685
771
add_release_notes (module_path , module , check_name , description )
686
- test_extension = language_to_extension .get (args . language )
687
- write_test (module_path , module , check_name , test_extension )
772
+ test_extension = language_to_extension .get (language )
773
+ write_test (module_path , module , check_name , test_extension , args . standard )
688
774
write_docs (module_path , module , check_name )
689
775
update_checks_list (clang_tidy_path )
690
776
print ("Done. Now it's your turn!" )
0 commit comments