8
8
#
9
9
# ===-----------------------------------------------------------------------===#
10
10
11
- from __future__ import print_function
12
- from __future__ import unicode_literals
13
-
14
11
import argparse
15
12
import io
16
13
import itertools
19
16
import sys
20
17
import textwrap
21
18
19
+ # FIXME Python 3.9: Replace typing.Tuple with builtins.tuple.
20
+ from typing import Optional , Tuple
21
+
22
22
23
23
# Adapts the module's CMakelist file. Returns 'True' if it could add a new
24
24
# entry and 'False' if the entry already existed.
25
- def adapt_cmake (module_path , check_name_camel ) :
25
+ def adapt_cmake (module_path : str , check_name_camel : str ) -> bool :
26
26
filename = os .path .join (module_path , "CMakeLists.txt" )
27
27
28
28
# The documentation files are encoded using UTF-8, however on Windows the
@@ -57,14 +57,14 @@ def adapt_cmake(module_path, check_name_camel):
57
57
58
58
# Adds a header for the new check.
59
59
def write_header (
60
- module_path ,
61
- module ,
62
- namespace ,
63
- check_name ,
64
- check_name_camel ,
65
- description ,
66
- lang_restrict ,
67
- ):
60
+ module_path : str ,
61
+ module : str ,
62
+ namespace : str ,
63
+ check_name : str ,
64
+ check_name_camel : str ,
65
+ description : str ,
66
+ lang_restrict : str ,
67
+ ) -> None :
68
68
wrapped_desc = "\n " .join (
69
69
textwrap .wrap (
70
70
description , width = 80 , initial_indent = "/// " , subsequent_indent = "/// "
@@ -139,7 +139,9 @@ class %(check_name_camel)s : public ClangTidyCheck {
139
139
140
140
141
141
# Adds the implementation of the new check.
142
- def write_implementation (module_path , module , namespace , check_name_camel ):
142
+ def write_implementation (
143
+ module_path : str , module : str , namespace : str , check_name_camel : str
144
+ ) -> None :
143
145
filename = os .path .join (module_path , check_name_camel ) + ".cpp"
144
146
print ("Creating %s..." % filename )
145
147
with io .open (filename , "w" , encoding = "utf8" , newline = "\n " ) as f :
@@ -187,7 +189,7 @@ def write_implementation(module_path, module, namespace, check_name_camel):
187
189
188
190
189
191
# Returns the source filename that implements the module.
190
- def get_module_filename (module_path , module ) :
192
+ def get_module_filename (module_path : str , module : str ) -> str :
191
193
modulecpp = list (
192
194
filter (
193
195
lambda p : p .lower () == module .lower () + "tidymodule.cpp" ,
@@ -198,7 +200,9 @@ def get_module_filename(module_path, module):
198
200
199
201
200
202
# Modifies the module to include the new check.
201
- def adapt_module (module_path , module , check_name , check_name_camel ):
203
+ def adapt_module (
204
+ module_path : str , module : str , check_name : str , check_name_camel : str
205
+ ) -> None :
202
206
filename = get_module_filename (module_path , module )
203
207
with io .open (filename , "r" , encoding = "utf8" ) as f :
204
208
lines = f .readlines ()
@@ -217,10 +221,10 @@ def adapt_module(module_path, module, check_name, check_name_camel):
217
221
+ '");\n '
218
222
)
219
223
220
- lines = iter (lines )
224
+ lines_iter = iter (lines )
221
225
try :
222
226
while True :
223
- line = next (lines )
227
+ line = next (lines_iter )
224
228
if not header_added :
225
229
match = re .search ('#include "(.*)"' , line )
226
230
if match :
@@ -247,10 +251,11 @@ def adapt_module(module_path, module, check_name, check_name_camel):
247
251
# If we didn't find the check name on this line, look on the
248
252
# next one.
249
253
prev_line = line
250
- line = next (lines )
254
+ line = next (lines_iter )
251
255
match = re .search (' *"([^"]*)"' , line )
252
256
if match :
253
257
current_check_name = match .group (1 )
258
+ assert current_check_name
254
259
if current_check_name > check_fq_name :
255
260
check_added = True
256
261
f .write (check_decl )
@@ -262,7 +267,9 @@ def adapt_module(module_path, module, check_name, check_name_camel):
262
267
263
268
264
269
# Adds a release notes entry.
265
- def add_release_notes (module_path , module , check_name , description ):
270
+ def add_release_notes (
271
+ module_path : str , module : str , check_name : str , description : str
272
+ ) -> None :
266
273
wrapped_desc = "\n " .join (
267
274
textwrap .wrap (
268
275
description , width = 80 , initial_indent = " " , subsequent_indent = " "
@@ -324,9 +331,14 @@ def add_release_notes(module_path, module, check_name, description):
324
331
325
332
326
333
# Adds a test for the check.
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 "
334
+ def write_test (
335
+ module_path : str ,
336
+ module : str ,
337
+ check_name : str ,
338
+ test_extension : str ,
339
+ test_standard : Optional [str ],
340
+ ) -> None :
341
+ test_standard = f"-std={ test_standard } -or-later " if test_standard else ""
330
342
check_name_dashes = module + "-" + check_name
331
343
filename = os .path .normpath (
332
344
os .path .join (
@@ -362,7 +374,7 @@ def write_test(module_path, module, check_name, test_extension, test_standard):
362
374
)
363
375
364
376
365
- def get_actual_filename (dirname , filename ) :
377
+ def get_actual_filename (dirname : str , filename : str ) -> str :
366
378
if not os .path .isdir (dirname ):
367
379
return ""
368
380
name = os .path .join (dirname , filename )
@@ -376,7 +388,7 @@ def get_actual_filename(dirname, filename):
376
388
377
389
378
390
# Recreates the list of checks in the docs/clang-tidy/checks directory.
379
- def update_checks_list (clang_tidy_path ) :
391
+ def update_checks_list (clang_tidy_path : str ) -> None :
380
392
docs_dir = os .path .join (clang_tidy_path , "../docs/clang-tidy/checks" )
381
393
filename = os .path .normpath (os .path .join (docs_dir , "list.rst" ))
382
394
# Read the content of the current list.rst file
@@ -390,12 +402,12 @@ def update_checks_list(clang_tidy_path):
390
402
for file in filter (
391
403
lambda s : s .endswith (".rst" ), os .listdir (os .path .join (docs_dir , subdir ))
392
404
):
393
- doc_files .append ([ subdir , file ] )
405
+ doc_files .append (( subdir , file ) )
394
406
doc_files .sort ()
395
407
396
408
# We couldn't find the source file from the check name, so try to find the
397
409
# class name that corresponds to the check in the module file.
398
- def filename_from_module (module_name , check_name ) :
410
+ def filename_from_module (module_name : str , check_name : str ) -> str :
399
411
module_path = os .path .join (clang_tidy_path , module_name )
400
412
if not os .path .isdir (module_path ):
401
413
return ""
@@ -433,7 +445,7 @@ def filename_from_module(module_name, check_name):
433
445
return ""
434
446
435
447
# Examine code looking for a c'tor definition to get the base class name.
436
- def get_base_class (code , check_file ) :
448
+ def get_base_class (code : str , check_file : str ) -> str :
437
449
check_class_name = os .path .splitext (os .path .basename (check_file ))[0 ]
438
450
ctor_pattern = check_class_name + r"\([^:]*\)\s*:\s*([A-Z][A-Za-z0-9]*Check)\("
439
451
matches = re .search (r"\s+" + check_class_name + "::" + ctor_pattern , code )
@@ -452,7 +464,7 @@ def get_base_class(code, check_file):
452
464
return ""
453
465
454
466
# Some simple heuristics to figure out if a check has an autofix or not.
455
- def has_fixits (code ) :
467
+ def has_fixits (code : str ) -> bool :
456
468
for needle in [
457
469
"FixItHint" ,
458
470
"ReplacementText" ,
@@ -464,7 +476,7 @@ def has_fixits(code):
464
476
return False
465
477
466
478
# Try to figure out of the check supports fixits.
467
- def has_auto_fix (check_name ) :
479
+ def has_auto_fix (check_name : str ) -> str :
468
480
dirname , _ , check_name = check_name .partition ("-" )
469
481
470
482
check_file = get_actual_filename (
@@ -499,7 +511,7 @@ def has_auto_fix(check_name):
499
511
500
512
return ""
501
513
502
- def process_doc (doc_file ) :
514
+ def process_doc (doc_file : Tuple [ str , str ]) -> Tuple [ str , Optional [ re . Match [ str ]]] :
503
515
check_name = doc_file [0 ] + "-" + doc_file [1 ].replace (".rst" , "" )
504
516
505
517
with io .open (os .path .join (docs_dir , * doc_file ), "r" , encoding = "utf8" ) as doc :
@@ -508,13 +520,13 @@ def process_doc(doc_file):
508
520
509
521
if match :
510
522
# Orphan page, don't list it.
511
- return "" , ""
523
+ return "" , None
512
524
513
525
match = re .search (r".*:http-equiv=refresh: \d+;URL=(.*).html(.*)" , content )
514
526
# Is it a redirect?
515
527
return check_name , match
516
528
517
- def format_link (doc_file ) :
529
+ def format_link (doc_file : Tuple [ str , str ]) -> str :
518
530
check_name , match = process_doc (doc_file )
519
531
if not match and check_name and not check_name .startswith ("clang-analyzer-" ):
520
532
return " :doc:`%(check_name)s <%(module)s/%(check)s>`,%(autofix)s\n " % {
@@ -526,7 +538,7 @@ def format_link(doc_file):
526
538
else :
527
539
return ""
528
540
529
- def format_link_alias (doc_file ) :
541
+ def format_link_alias (doc_file : Tuple [ str , str ]) -> str :
530
542
check_name , match = process_doc (doc_file )
531
543
if (match or (check_name .startswith ("clang-analyzer-" ))) and check_name :
532
544
module = doc_file [0 ]
@@ -543,6 +555,7 @@ def format_link_alias(doc_file):
543
555
ref_end = "_"
544
556
else :
545
557
redirect_parts = re .search (r"^\.\./([^/]*)/([^/]*)$" , match .group (1 ))
558
+ assert redirect_parts
546
559
title = redirect_parts [1 ] + "-" + redirect_parts [2 ]
547
560
target = redirect_parts [1 ] + "/" + redirect_parts [2 ]
548
561
autofix = has_auto_fix (title )
@@ -599,7 +612,7 @@ def format_link_alias(doc_file):
599
612
600
613
601
614
# Adds a documentation for the check.
602
- def write_docs (module_path , module , check_name ) :
615
+ def write_docs (module_path : str , module : str , check_name : str ) -> None :
603
616
check_name_dashes = module + "-" + check_name
604
617
filename = os .path .normpath (
605
618
os .path .join (
@@ -623,15 +636,15 @@ def write_docs(module_path, module, check_name):
623
636
)
624
637
625
638
626
- def get_camel_name (check_name ) :
639
+ def get_camel_name (check_name : str ) -> str :
627
640
return "" .join (map (lambda elem : elem .capitalize (), check_name .split ("-" )))
628
641
629
642
630
- def get_camel_check_name (check_name ) :
643
+ def get_camel_check_name (check_name : str ) -> str :
631
644
return get_camel_name (check_name ) + "Check"
632
645
633
646
634
- def main ():
647
+ def main () -> None :
635
648
language_to_extension = {
636
649
"c" : "c" ,
637
650
"c++" : "cpp" ,
@@ -756,6 +769,8 @@ def main():
756
769
)
757
770
elif language in ["objc" , "objc++" ]:
758
771
language_restrict = "%(lang)s.ObjC"
772
+ else :
773
+ raise ValueError (f"Unsupported language '{ language } ' was specified" )
759
774
760
775
write_header (
761
776
module_path ,
@@ -769,7 +784,7 @@ def main():
769
784
write_implementation (module_path , module , namespace , check_name_camel )
770
785
adapt_module (module_path , module , check_name , check_name_camel )
771
786
add_release_notes (module_path , module , check_name , description )
772
- test_extension = language_to_extension . get ( language )
787
+ test_extension = language_to_extension [ language ]
773
788
write_test (module_path , module , check_name , test_extension , args .standard )
774
789
write_docs (module_path , module , check_name )
775
790
update_checks_list (clang_tidy_path )
0 commit comments