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