lizard 1.17.30__py2.py3-none-any.whl → 1.18.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/METADATA +6 -1
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/RECORD +25 -24
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/entry_points.txt +1 -0
- lizard.py +43 -22
- lizard_ext/__init__.py +14 -0
- lizard_ext/checkstyleoutput.py +33 -0
- lizard_ext/version.py +1 -1
- lizard_languages/__init__.py +4 -2
- lizard_languages/clike.py +60 -1
- lizard_languages/code_reader.py +19 -0
- lizard_languages/go.py +1 -1
- lizard_languages/java.py +6 -3
- lizard_languages/javascript.py +1 -1
- lizard_languages/perl.py +20 -21
- lizard_languages/php.py +14 -14
- lizard_languages/python.py +37 -1
- lizard_languages/r.py +290 -0
- lizard_languages/rust.py +7 -2
- lizard_languages/script_language.py +2 -2
- lizard_languages/st.py +139 -0
- lizard_languages/tsx.py +445 -11
- lizard_languages/typescript.py +215 -15
- lizard_languages/js_style_language_states.py +0 -184
- lizard_languages/jsx.py +0 -337
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/LICENSE.txt +0 -0
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/WHEEL +0 -0
- {lizard-1.17.30.dist-info → lizard-1.18.0.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lizard
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.18.0
|
|
4
4
|
Summary: A code analyzer without caring the C/C++ header files. It works with Java, C/C++, JavaScript, Python, Ruby, Swift, Objective C. Metrics includes cyclomatic complexity number etc.
|
|
5
5
|
Home-page: http://www.lizard.ws
|
|
6
6
|
Download-URL: https://pypi.python.org/lizard/
|
|
@@ -73,6 +73,8 @@ A list of supported languages:
|
|
|
73
73
|
- Erlang
|
|
74
74
|
- Zig
|
|
75
75
|
- Perl
|
|
76
|
+
- Structured Text (St)
|
|
77
|
+
- R
|
|
76
78
|
|
|
77
79
|
By default lizard will search for any source code that it knows and mix
|
|
78
80
|
all the results together. This might not be what you want. You can use
|
|
@@ -196,6 +198,7 @@ Options
|
|
|
196
198
|
generate report in Jenkins server
|
|
197
199
|
--csv Generate CSV output as a transform of the default output
|
|
198
200
|
-H, --html Output HTML report
|
|
201
|
+
--checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
|
|
199
202
|
-m, --modified Calculate modified cyclomatic complexity number , which count a
|
|
200
203
|
switch/case with multiple cases as one CCN.
|
|
201
204
|
-E EXTENSIONS, --extension EXTENSIONS
|
|
@@ -412,3 +415,5 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
|
|
|
412
415
|
- `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
|
|
413
416
|
- `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
|
|
414
417
|
|
|
418
|
+
|
|
419
|
+
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
lizard.py,sha256=
|
|
2
|
-
lizard_ext/__init__.py,sha256=
|
|
1
|
+
lizard.py,sha256=B0g5lEp5me31bQpjfMKVayF0iUfH0PalVecOKeUX3-A,41365
|
|
2
|
+
lizard_ext/__init__.py,sha256=AkZYVqCgd7XX1hMwLMyh_ABm9geIHmobEch-thYXZz8,932
|
|
3
3
|
lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
|
|
4
|
+
lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
|
|
4
5
|
lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
|
|
5
6
|
lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
|
|
6
7
|
lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
|
|
@@ -25,44 +26,44 @@ lizard_ext/lizardns.py,sha256=8pztUoRS_UWN24MawwxeHEJgYh49id5PWODUBb6O72U,4184
|
|
|
25
26
|
lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
|
|
26
27
|
lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
|
|
27
28
|
lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
|
|
28
|
-
lizard_ext/version.py,sha256=
|
|
29
|
+
lizard_ext/version.py,sha256=WiI5ub_2twuTf6tscOzwP6MNdZV6kq-ElL2-2HqdUXc,181
|
|
29
30
|
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
30
|
-
lizard_languages/__init__.py,sha256=
|
|
31
|
-
lizard_languages/clike.py,sha256=
|
|
32
|
-
lizard_languages/code_reader.py,sha256=
|
|
31
|
+
lizard_languages/__init__.py,sha256=NsYspQ6h--XHMJrIcmOxlZRF4f_l5M7JprenLBz2oIE,1559
|
|
32
|
+
lizard_languages/clike.py,sha256=RICnhzBzLbMwpceo3X-z7_-hcTZmGAZKGqpKgJf0G0o,13598
|
|
33
|
+
lizard_languages/code_reader.py,sha256=IfEHg9lzKnyCipX9xscgyGEOovll5qr9dCe5cSX2sJM,6852
|
|
33
34
|
lizard_languages/csharp.py,sha256=EfFAIOIcJXUUhXTlZApXGSlzG34NZvHM9OSe6m7hpv0,2141
|
|
34
35
|
lizard_languages/erlang.py,sha256=7YJS2cMyXDKEV_kpH8DzBARxFCFcjKuTOPSQ3K52auU,3860
|
|
35
36
|
lizard_languages/fortran.py,sha256=KATDsnfjob5W3579A_VxbwrbTkK7Rx3p0eXdBgjx25I,8973
|
|
36
37
|
lizard_languages/gdscript.py,sha256=KwlGoODilQnFgXvODpq_XlA6fV3hGbN9fd7bsiEUn78,637
|
|
37
|
-
lizard_languages/go.py,sha256=
|
|
38
|
+
lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
|
|
38
39
|
lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
|
|
39
|
-
lizard_languages/java.py,sha256=
|
|
40
|
-
lizard_languages/javascript.py,sha256=
|
|
41
|
-
lizard_languages/js_style_language_states.py,sha256=wpkpDI7f0-ZYpCzYN8w1aReAkSGQfV9KIvy4G7bOYFc,6395
|
|
40
|
+
lizard_languages/java.py,sha256=HQBTZjUKbUJwgmtLYIzJrWtPpFP3ZdBP_NJK7YOXZC0,6424
|
|
41
|
+
lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
|
|
42
42
|
lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
|
|
43
|
-
lizard_languages/jsx.py,sha256=G89LX4lwnN1vYsXyllJbVIFPiazxa1f5iPWGdqcrqKs,11936
|
|
44
43
|
lizard_languages/kotlin.py,sha256=v_o2orEzA5gB9vM_0h-E4QXjrc5Yum-0K6W6_laOThc,2844
|
|
45
44
|
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
46
45
|
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
47
|
-
lizard_languages/perl.py,sha256=
|
|
48
|
-
lizard_languages/php.py,sha256=
|
|
49
|
-
lizard_languages/python.py,sha256=
|
|
46
|
+
lizard_languages/perl.py,sha256=136w620eECe_t-kmlRUGrsZSxQNo2JQ_PZTSQfCSmHY,11987
|
|
47
|
+
lizard_languages/php.py,sha256=UV40p8WzNC64NQ5qElPKzcFTjVt5kenLMz-eKYlcnMY,9940
|
|
48
|
+
lizard_languages/python.py,sha256=AsL0SmQ73zhNS1iGi4Z8VtuUE0VjqBzo9W8W0mjqL0E,5790
|
|
49
|
+
lizard_languages/r.py,sha256=IoyMhmFtUmTji6rm6-fqss_j_kvIHu3JjABRh6RNys0,12583
|
|
50
50
|
lizard_languages/ruby.py,sha256=HL1ZckeuUUJU3QSVAOPsG_Zsl0C6X2PX5_VaWqclzkM,2277
|
|
51
51
|
lizard_languages/rubylike.py,sha256=dAGZ2wqW8nqaESMU8HkeR9gwQ-q9fmZqE6AANvVZD1Q,3426
|
|
52
|
-
lizard_languages/rust.py,sha256=
|
|
52
|
+
lizard_languages/rust.py,sha256=WarDHnFZv99Yu3_C5DpZfLS8dVWz6AcOzo2dzLW94rA,817
|
|
53
53
|
lizard_languages/scala.py,sha256=6Jr_TG945VYqB3o5weD7jN7S4beHt4aVj3r-fmKeMAM,1316
|
|
54
|
-
lizard_languages/script_language.py,sha256=
|
|
54
|
+
lizard_languages/script_language.py,sha256=SKe45AbO6Z-axbN8KW_g7jf9g7YTXZ6dWzJj4ubDsM8,1172
|
|
55
55
|
lizard_languages/solidity.py,sha256=Z0GD7U5bI5eUikdy7m_iKWeFD5yXRYq4r3zycscOhJQ,553
|
|
56
|
+
lizard_languages/st.py,sha256=7fpOfNAoUjNY8RCHSYLufnOzZTUkKwjVvcyRyM1xP2Y,4160
|
|
56
57
|
lizard_languages/swift.py,sha256=p8S2OAkQOx9YQ02yhoVXFkr7pMqUH1Nb3RVXPHRU_9M,2450
|
|
57
58
|
lizard_languages/tnsdl.py,sha256=pGcalA_lHY362v2wwPS86seYBOOBBjvmU6vd4Yy3A9g,2803
|
|
58
|
-
lizard_languages/tsx.py,sha256=
|
|
59
|
+
lizard_languages/tsx.py,sha256=1oOVCcz5yHkmYLYGhSarCMSXfGVasweklAqqapkuNR4,17160
|
|
59
60
|
lizard_languages/ttcn.py,sha256=ygjw_raBmPF-4mgoM8m6CAdyEMpTI-n1kZJK1RL4Vxo,2131
|
|
60
|
-
lizard_languages/typescript.py,sha256=
|
|
61
|
+
lizard_languages/typescript.py,sha256=C8VWmHLGGYwCrP6hJ9HmJ-PETrKux4npjOzDN6AkTGU,12347
|
|
61
62
|
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
62
63
|
lizard_languages/zig.py,sha256=NX1iyBstBuJFeAGBOAIaRfrmeBREne2HX6Pt4fXZZTQ,586
|
|
63
|
-
lizard-1.
|
|
64
|
-
lizard-1.
|
|
65
|
-
lizard-1.
|
|
66
|
-
lizard-1.
|
|
67
|
-
lizard-1.
|
|
68
|
-
lizard-1.
|
|
64
|
+
lizard-1.18.0.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
|
|
65
|
+
lizard-1.18.0.dist-info/METADATA,sha256=BX2GeLwA0iQpWmDi0wYg4xXCTISh6Un8alaLcJhuRxc,16260
|
|
66
|
+
lizard-1.18.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
|
67
|
+
lizard-1.18.0.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
|
|
68
|
+
lizard-1.18.0.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
|
|
69
|
+
lizard-1.18.0.dist-info/RECORD,,
|
lizard.py
CHANGED
|
@@ -43,6 +43,7 @@ try:
|
|
|
43
43
|
from lizard_ext import print_csv
|
|
44
44
|
from lizard_ext import html_output
|
|
45
45
|
from lizard_ext import auto_open, auto_read
|
|
46
|
+
from lizard_ext import print_checkstyle
|
|
46
47
|
except ImportError:
|
|
47
48
|
sys.stderr.write("Cannot find the lizard_ext modules.")
|
|
48
49
|
|
|
@@ -217,6 +218,11 @@ def arg_parser(prog=None):
|
|
|
217
218
|
const="modified",
|
|
218
219
|
dest="extensions",
|
|
219
220
|
default=[])
|
|
221
|
+
parser.add_argument("--checkstyle",
|
|
222
|
+
help='''Generate Checkstyle XML output for integration with Jenkins and other tools.''',
|
|
223
|
+
action="store_const",
|
|
224
|
+
const=print_checkstyle,
|
|
225
|
+
dest="printer")
|
|
220
226
|
_extension_arg(parser)
|
|
221
227
|
parser.add_argument("-s", "--sort",
|
|
222
228
|
help='''Sort the warning with field. The field can be
|
|
@@ -617,7 +623,7 @@ def whitelist_filter(warnings, script=None, whitelist=None):
|
|
|
617
623
|
|
|
618
624
|
def get_whitelist(whitelist):
|
|
619
625
|
if os.path.isfile(whitelist):
|
|
620
|
-
return
|
|
626
|
+
return auto_read(whitelist)
|
|
621
627
|
if whitelist != DEFAULT_WHITELIST:
|
|
622
628
|
print("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
|
623
629
|
print("WARNING: the whitelist \""+whitelist+"\" doesn't exist.")
|
|
@@ -902,7 +908,7 @@ def get_all_source_files(paths, exclude_patterns, lans):
|
|
|
902
908
|
'''
|
|
903
909
|
Function counts md5 hash for the given file and checks if it isn't a
|
|
904
910
|
duplicate using set of hashes for previous files.
|
|
905
|
-
|
|
911
|
+
|
|
906
912
|
If a .gitignore file is found in any of the given paths, it will be used
|
|
907
913
|
to filter out files that match the gitignore patterns.
|
|
908
914
|
'''
|
|
@@ -917,12 +923,12 @@ def get_all_source_files(paths, exclude_patterns, lans):
|
|
|
917
923
|
for path in paths:
|
|
918
924
|
gitignore_path = os.path.join(path, '.gitignore')
|
|
919
925
|
if os.path.exists(gitignore_path):
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
+
gitignore_file = auto_read(gitignore_path)
|
|
927
|
+
# Read lines and strip whitespace and empty lines
|
|
928
|
+
patterns = [line.strip() for line in gitignore_file.splitlines()]
|
|
929
|
+
patterns = [p for p in patterns if p and not p.startswith('#')]
|
|
930
|
+
gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
|
|
931
|
+
base_path = path
|
|
926
932
|
break
|
|
927
933
|
except ImportError:
|
|
928
934
|
pass
|
|
@@ -1000,7 +1006,10 @@ def parse_args(argv):
|
|
|
1000
1006
|
if opt.output_file:
|
|
1001
1007
|
inferred_printer = infer_printer_from_file_ext(opt.output_file)
|
|
1002
1008
|
if inferred_printer:
|
|
1003
|
-
|
|
1009
|
+
# Always use print_checkstyle for .checkstyle.xml
|
|
1010
|
+
if opt.output_file.lower().endswith('.checkstyle.xml'):
|
|
1011
|
+
opt.printer = inferred_printer
|
|
1012
|
+
elif not opt.printer:
|
|
1004
1013
|
opt.printer = inferred_printer
|
|
1005
1014
|
elif opt.printer != inferred_printer:
|
|
1006
1015
|
msg = "Warning: overriding output file extension.\n"
|
|
@@ -1009,15 +1018,16 @@ def parse_args(argv):
|
|
|
1009
1018
|
|
|
1010
1019
|
|
|
1011
1020
|
def infer_printer_from_file_ext(path):
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
+
lower_path = path.lower()
|
|
1022
|
+
if lower_path.endswith(".checkstyle.xml"):
|
|
1023
|
+
return print_checkstyle
|
|
1024
|
+
if lower_path.endswith(".html"):
|
|
1025
|
+
return html_output
|
|
1026
|
+
if lower_path.endswith(".xml"):
|
|
1027
|
+
return print_xml
|
|
1028
|
+
if lower_path.endswith(".csv"):
|
|
1029
|
+
return print_csv
|
|
1030
|
+
return None
|
|
1021
1031
|
|
|
1022
1032
|
|
|
1023
1033
|
def open_output_file(path):
|
|
@@ -1072,16 +1082,27 @@ def main(argv=None):
|
|
|
1072
1082
|
options.paths = auto_read(options.input_file).splitlines()
|
|
1073
1083
|
original_stdout = sys.stdout
|
|
1074
1084
|
output_file = None
|
|
1075
|
-
if options.output_file:
|
|
1076
|
-
output_file = open_output_file(options.output_file)
|
|
1077
|
-
sys.stdout = output_file
|
|
1078
1085
|
result = analyze(
|
|
1079
1086
|
options.paths,
|
|
1080
1087
|
options.exclude,
|
|
1081
1088
|
options.working_threads,
|
|
1082
1089
|
options.extensions,
|
|
1083
1090
|
options.languages)
|
|
1084
|
-
warning_count =
|
|
1091
|
+
warning_count = None
|
|
1092
|
+
if options.output_file:
|
|
1093
|
+
output_file = open_output_file(options.output_file)
|
|
1094
|
+
sys.stdout = output_file
|
|
1095
|
+
# Special handling for checkstyle output
|
|
1096
|
+
if getattr(printer, "__name__", "") == "print_checkstyle":
|
|
1097
|
+
warning_count = printer(result, options, schema, AllResult, file=output_file)
|
|
1098
|
+
else:
|
|
1099
|
+
warning_count = printer(result, options, schema, AllResult)
|
|
1100
|
+
else:
|
|
1101
|
+
# Special handling for checkstyle output
|
|
1102
|
+
if getattr(printer, "__name__", "") == "print_checkstyle":
|
|
1103
|
+
warning_count = printer(result, options, schema, AllResult, file=original_stdout)
|
|
1104
|
+
else:
|
|
1105
|
+
warning_count = printer(result, options, schema, AllResult)
|
|
1085
1106
|
print_extension_results(options.extensions)
|
|
1086
1107
|
list(result)
|
|
1087
1108
|
if output_file:
|
lizard_ext/__init__.py
CHANGED
|
@@ -6,6 +6,7 @@ from .htmloutput import html_output
|
|
|
6
6
|
from .csvoutput import csv_output
|
|
7
7
|
from .xmloutput import xml_output
|
|
8
8
|
from .auto_open import auto_open, auto_read
|
|
9
|
+
from .checkstyleoutput import checkstyle_output
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def print_xml(results, options, _, total_factory):
|
|
@@ -16,3 +17,16 @@ def print_xml(results, options, _, total_factory):
|
|
|
16
17
|
def print_csv(results, options, _, total_factory):
|
|
17
18
|
csv_output(total_factory(list(results)), options)
|
|
18
19
|
return 0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def print_checkstyle(results, options, _, total_factory, file=None):
|
|
23
|
+
import sys
|
|
24
|
+
print("DEBUG: print_checkstyle called", file=sys.stderr)
|
|
25
|
+
output = checkstyle_output(total_factory(list(results)), options.verbose)
|
|
26
|
+
if file is None:
|
|
27
|
+
file = sys.stdout
|
|
28
|
+
file.write(output)
|
|
29
|
+
if not output.endswith("\n"):
|
|
30
|
+
file.write("\n")
|
|
31
|
+
file.flush()
|
|
32
|
+
return 0
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'''
|
|
2
|
+
Checkstyle XML output for Lizard
|
|
3
|
+
'''
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def checkstyle_output(all_result, verbose):
|
|
7
|
+
result = all_result.result
|
|
8
|
+
import xml.etree.ElementTree as ET
|
|
9
|
+
|
|
10
|
+
checkstyle = ET.Element('checkstyle', version="4.3")
|
|
11
|
+
|
|
12
|
+
for source_file in result:
|
|
13
|
+
if source_file:
|
|
14
|
+
file_elem = ET.SubElement(checkstyle, 'file', name=source_file.filename)
|
|
15
|
+
for func in source_file.function_list:
|
|
16
|
+
# Each function with a warning (e.g., CCN > threshold) could be an <error>
|
|
17
|
+
# For now, output all functions as <error> for demonstration
|
|
18
|
+
ET.SubElement(
|
|
19
|
+
file_elem,
|
|
20
|
+
'error',
|
|
21
|
+
line=str(func.start_line),
|
|
22
|
+
column="0",
|
|
23
|
+
severity="info",
|
|
24
|
+
message=f"{func.name} has {func.nloc} NLOC, {func.cyclomatic_complexity} CCN, "
|
|
25
|
+
f"{func.token_count} token, {len(func.parameters)} PARAM, {func.length} length",
|
|
26
|
+
source="lizard"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Pretty print
|
|
30
|
+
import xml.dom.minidom
|
|
31
|
+
rough_string = ET.tostring(checkstyle, 'utf-8')
|
|
32
|
+
reparsed = xml.dom.minidom.parseString(rough_string)
|
|
33
|
+
return reparsed.toprettyxml(indent=" ")
|
lizard_ext/version.py
CHANGED
lizard_languages/__init__.py
CHANGED
|
@@ -21,10 +21,11 @@ from .rust import RustReader
|
|
|
21
21
|
from .typescript import TypeScriptReader
|
|
22
22
|
from .fortran import FortranReader
|
|
23
23
|
from .solidity import SolidityReader
|
|
24
|
-
from .jsx import JSXReader
|
|
25
24
|
from .tsx import TSXReader
|
|
26
25
|
from .vue import VueReader
|
|
27
26
|
from .perl import PerlReader
|
|
27
|
+
from .st import StReader
|
|
28
|
+
from .r import RReader
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
def languages():
|
|
@@ -50,10 +51,11 @@ def languages():
|
|
|
50
51
|
SolidityReader,
|
|
51
52
|
ErlangReader,
|
|
52
53
|
ZigReader,
|
|
53
|
-
JSXReader,
|
|
54
54
|
TSXReader,
|
|
55
55
|
VueReader,
|
|
56
56
|
PerlReader,
|
|
57
|
+
StReader,
|
|
58
|
+
RReader,
|
|
57
59
|
]
|
|
58
60
|
|
|
59
61
|
|
lizard_languages/clike.py
CHANGED
|
@@ -19,7 +19,7 @@ class CCppCommentsMixin(object): # pylint: disable=R0903
|
|
|
19
19
|
class CLikeReader(CodeReader, CCppCommentsMixin):
|
|
20
20
|
''' This is the reader for C, C++ and Java. '''
|
|
21
21
|
|
|
22
|
-
ext = ["c", "cpp", "cc", "
|
|
22
|
+
ext = ["c", "cpp", "cc", "cxx", "h", "hpp"]
|
|
23
23
|
language_names = ['cpp', 'c']
|
|
24
24
|
macro_pattern = re.compile(r"#\s*(\w+)\s*(.*)", re.M | re.S)
|
|
25
25
|
|
|
@@ -162,6 +162,11 @@ class CLikeStates(CodeStateMachine):
|
|
|
162
162
|
def _state_global(self, token):
|
|
163
163
|
if token[0].isalpha() or token[0] in '_~':
|
|
164
164
|
self.try_new_function(token)
|
|
165
|
+
elif token == '[':
|
|
166
|
+
# Check if this might be a lambda expression (C++ only)
|
|
167
|
+
# Java doesn't have lambda expressions, so skip lambda detection for Java
|
|
168
|
+
if not hasattr(self, 'class_name'): # JavaStates has class_name attribute
|
|
169
|
+
self._state = self._state_lambda_check
|
|
165
170
|
|
|
166
171
|
def _state_function(self, token):
|
|
167
172
|
if token == '(':
|
|
@@ -310,3 +315,57 @@ class CLikeStates(CodeStateMachine):
|
|
|
310
315
|
def _state_attribute(self, _):
|
|
311
316
|
"Ignores function attributes with C++11 syntax, i.e., [[ attribute ]]."
|
|
312
317
|
pass
|
|
318
|
+
|
|
319
|
+
def _state_lambda_check(self, token):
|
|
320
|
+
"""Check if this is a lambda expression or a function attribute."""
|
|
321
|
+
if token == ']':
|
|
322
|
+
# This is a lambda expression [](params) or [capture](params)
|
|
323
|
+
# Skip the lambda and continue parsing normally
|
|
324
|
+
self._state = self._state_lambda_params
|
|
325
|
+
elif token == '[':
|
|
326
|
+
# This is a function attribute [[attribute]]
|
|
327
|
+
self._state = self._state_attribute
|
|
328
|
+
else:
|
|
329
|
+
# This is a lambda with capture list [capture](params)
|
|
330
|
+
# Skip until we find the closing bracket
|
|
331
|
+
self._state = self._state_lambda_capture
|
|
332
|
+
|
|
333
|
+
def _state_lambda_params(self, token):
|
|
334
|
+
"""Handle lambda parameters and body."""
|
|
335
|
+
if token == '(':
|
|
336
|
+
# Start of parameter list, skip until closing parenthesis
|
|
337
|
+
self._state = self._state_lambda_param_list
|
|
338
|
+
else:
|
|
339
|
+
# No parameters, check for body
|
|
340
|
+
self._state = self._state_lambda_body
|
|
341
|
+
|
|
342
|
+
def _state_lambda_param_list(self, token):
|
|
343
|
+
"""Handle lambda parameter list."""
|
|
344
|
+
if token == ')':
|
|
345
|
+
# End of parameter list, check for body
|
|
346
|
+
self._state = self._state_lambda_body
|
|
347
|
+
# Otherwise, continue in parameter list
|
|
348
|
+
|
|
349
|
+
def _state_lambda_body(self, token):
|
|
350
|
+
"""Handle lambda body."""
|
|
351
|
+
if token == '{':
|
|
352
|
+
# Start of lambda body, skip until closing brace
|
|
353
|
+
self._state = self._state_lambda_body_skip
|
|
354
|
+
elif token == ';':
|
|
355
|
+
# Lambda without body, just a semicolon
|
|
356
|
+
self._state = self._state_global
|
|
357
|
+
# Otherwise, continue
|
|
358
|
+
|
|
359
|
+
def _state_lambda_body_skip(self, token):
|
|
360
|
+
"""Skip lambda body until closing brace."""
|
|
361
|
+
if token == '}':
|
|
362
|
+
# End of lambda body, continue parsing normally
|
|
363
|
+
self._state = self._state_global
|
|
364
|
+
# Otherwise, continue skipping
|
|
365
|
+
|
|
366
|
+
def _state_lambda_capture(self, token):
|
|
367
|
+
"""Handle lambda capture list."""
|
|
368
|
+
if token == ']':
|
|
369
|
+
# End of capture list, continue parsing normally
|
|
370
|
+
self._state = self._state_global
|
|
371
|
+
# Otherwise, continue in capture list
|
lizard_languages/code_reader.py
CHANGED
|
@@ -184,6 +184,13 @@ class CodeReader:
|
|
|
184
184
|
def __call__(self, tokens, reader):
|
|
185
185
|
self.context = reader.context
|
|
186
186
|
for token in tokens:
|
|
187
|
+
# Allow language-specific token processing
|
|
188
|
+
if self.process_token(token):
|
|
189
|
+
for state in self.parallel_states:
|
|
190
|
+
state(token)
|
|
191
|
+
yield token
|
|
192
|
+
continue
|
|
193
|
+
|
|
187
194
|
for state in self.parallel_states:
|
|
188
195
|
state(token)
|
|
189
196
|
yield token
|
|
@@ -193,3 +200,15 @@ class CodeReader:
|
|
|
193
200
|
|
|
194
201
|
def eof(self):
|
|
195
202
|
pass
|
|
203
|
+
|
|
204
|
+
def process_token(self, token):
|
|
205
|
+
"""Process a token before normal handling.
|
|
206
|
+
Return True if the token has been handled specially and should skip normal processing.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
token: The token being processed
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
bool: True if the token should skip normal processing, False otherwise
|
|
213
|
+
"""
|
|
214
|
+
return False
|
lizard_languages/go.py
CHANGED
lizard_languages/java.py
CHANGED
|
@@ -34,6 +34,7 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
34
34
|
def _state_imp(self, token):
|
|
35
35
|
# When entering a function implementation, set the flag
|
|
36
36
|
self.in_method_body = True
|
|
37
|
+
|
|
37
38
|
def callback():
|
|
38
39
|
# When exiting the function implementation, clear the flag
|
|
39
40
|
self.in_method_body = False
|
|
@@ -105,6 +106,7 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
105
106
|
self.in_record_constructor = False
|
|
106
107
|
self._state = self._state_global
|
|
107
108
|
|
|
109
|
+
|
|
108
110
|
class JavaFunctionBodyStates(JavaStates):
|
|
109
111
|
def __init__(self, context):
|
|
110
112
|
super(JavaFunctionBodyStates, self).__init__(context)
|
|
@@ -132,12 +134,12 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
132
134
|
self.handling_dot_class = False
|
|
133
135
|
if token == "class":
|
|
134
136
|
return # Skip the 'class' token after a dot
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
# Special handling for tokens that could confuse the parser
|
|
137
139
|
if self.ignore_tokens:
|
|
138
140
|
self.ignore_tokens = False
|
|
139
141
|
return
|
|
140
|
-
|
|
142
|
+
|
|
141
143
|
if token == "new":
|
|
142
144
|
self.next(self._state_new)
|
|
143
145
|
else:
|
|
@@ -145,7 +147,7 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
145
147
|
# This ensures that local classes are properly detected
|
|
146
148
|
if self._try_start_a_class(token):
|
|
147
149
|
return
|
|
148
|
-
|
|
150
|
+
|
|
149
151
|
if self.br_count == 0:
|
|
150
152
|
self.statemachine_return()
|
|
151
153
|
|
|
@@ -166,6 +168,7 @@ class JavaFunctionBodyStates(JavaStates):
|
|
|
166
168
|
return
|
|
167
169
|
self.next(self._state_global, token)
|
|
168
170
|
|
|
171
|
+
|
|
169
172
|
class JavaClassBodyStates(JavaStates):
|
|
170
173
|
def __init__(self, class_name, is_record, context):
|
|
171
174
|
super(JavaClassBodyStates, self).__init__(context)
|
lizard_languages/javascript.py
CHANGED
lizard_languages/perl.py
CHANGED
|
@@ -19,7 +19,8 @@ class PerlReader(CodeReader, ScriptLanguageMixIn):
|
|
|
19
19
|
|
|
20
20
|
ext = ['pl', 'pm']
|
|
21
21
|
language_names = ['perl']
|
|
22
|
-
_conditions = set(['if', 'elsif', 'unless', 'while', 'until', 'for', 'foreach',
|
|
22
|
+
_conditions = set(['if', 'elsif', 'unless', 'while', 'until', 'for', 'foreach',
|
|
23
|
+
'&&', '||', '?', ':', 'when', 'given', 'default', 'do'])
|
|
23
24
|
|
|
24
25
|
def __init__(self, context):
|
|
25
26
|
super(PerlReader, self).__init__(context)
|
|
@@ -55,7 +56,7 @@ class PerlReader(CodeReader, ScriptLanguageMixIn):
|
|
|
55
56
|
stripped = token.lstrip('#').strip()
|
|
56
57
|
if stripped.startswith('lizard forgives') or stripped.startswith('#lizard forgives'):
|
|
57
58
|
return '#lizard forgives' # Return standardized forgiveness comment
|
|
58
|
-
return stripped # Return the stripped comment for other
|
|
59
|
+
return stripped # Return the stripped comment for other case
|
|
59
60
|
return None
|
|
60
61
|
|
|
61
62
|
@staticmethod
|
|
@@ -64,7 +65,8 @@ class PerlReader(CodeReader, ScriptLanguageMixIn):
|
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
class PerlStates(CodeStateMachine):
|
|
67
|
-
_conditions = set(['if', 'elsif', 'unless', 'while', 'until', 'for', 'foreach',
|
|
68
|
+
_conditions = set(['if', 'elsif', 'unless', 'while', 'until', 'for', 'foreach',
|
|
69
|
+
'&&', '||', '?', ':', 'when', 'given', 'default', 'do'])
|
|
68
70
|
|
|
69
71
|
def __init__(self, context):
|
|
70
72
|
super(PerlStates, self).__init__(context)
|
|
@@ -90,7 +92,7 @@ class PerlStates(CodeStateMachine):
|
|
|
90
92
|
elif token == '(':
|
|
91
93
|
self.paren_count += 1
|
|
92
94
|
self.next(self._state_function_call)
|
|
93
|
-
elif token
|
|
95
|
+
elif token in ('$', 'my', 'our', 'local'):
|
|
94
96
|
self.variable_name = ''
|
|
95
97
|
self.next(self._state_variable)
|
|
96
98
|
|
|
@@ -101,7 +103,7 @@ class PerlStates(CodeStateMachine):
|
|
|
101
103
|
|
|
102
104
|
def _state_variable(self, token):
|
|
103
105
|
if token == '$':
|
|
104
|
-
# Skip the $ in variable
|
|
106
|
+
# Skip the $ in variable name
|
|
105
107
|
pass
|
|
106
108
|
elif token == '=':
|
|
107
109
|
self.next(self._state_assignment)
|
|
@@ -122,10 +124,10 @@ class PerlStates(CodeStateMachine):
|
|
|
122
124
|
if token == 'sub':
|
|
123
125
|
# Inline anonymous subroutine as argument
|
|
124
126
|
self.anonymous_count += 1
|
|
125
|
-
full_name =
|
|
127
|
+
full_name = "<anonymous>"
|
|
126
128
|
if self.package_name:
|
|
127
129
|
full_name = f"{self.package_name}::{full_name}"
|
|
128
|
-
|
|
130
|
+
|
|
129
131
|
self.context.try_new_function(full_name)
|
|
130
132
|
self.context.confirm_new_function()
|
|
131
133
|
self.next(self._state_anon_brace_search)
|
|
@@ -140,13 +142,13 @@ class PerlStates(CodeStateMachine):
|
|
|
140
142
|
if token == '{':
|
|
141
143
|
self.brace_count = 1
|
|
142
144
|
full_name = '<anonymous>'
|
|
143
|
-
# Use variable name if available for more readable function
|
|
145
|
+
# Use variable name if available for more readable function name
|
|
144
146
|
if self.variable_name:
|
|
145
147
|
full_name = '$' + self.variable_name
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
if self.package_name:
|
|
148
150
|
full_name = f"{self.package_name}::{full_name}"
|
|
149
|
-
|
|
151
|
+
|
|
150
152
|
self.context.try_new_function(full_name)
|
|
151
153
|
self.context.confirm_new_function()
|
|
152
154
|
self.next(self._state_function_body)
|
|
@@ -182,7 +184,7 @@ class PerlStates(CodeStateMachine):
|
|
|
182
184
|
elif token == 'sub':
|
|
183
185
|
# Handle anonymous subroutine like 'callback(sub { ... })'
|
|
184
186
|
self.anonymous_count += 1
|
|
185
|
-
full_name =
|
|
187
|
+
full_name = "<anonymous>"
|
|
186
188
|
if self.package_name:
|
|
187
189
|
full_name = f"{self.package_name}::{full_name}"
|
|
188
190
|
self.context.try_new_function(full_name)
|
|
@@ -231,7 +233,7 @@ class PerlStates(CodeStateMachine):
|
|
|
231
233
|
# Colon part of ternary operator also increases complexity
|
|
232
234
|
self.context.add_condition()
|
|
233
235
|
elif token == 'sub':
|
|
234
|
-
# Check if it's a nested named subroutine or
|
|
236
|
+
# Check if it's a nested named subroutine or anonymou
|
|
235
237
|
self.next(self._state_nested_sub_dec)
|
|
236
238
|
elif token == '(':
|
|
237
239
|
# Track function calls inside function body
|
|
@@ -241,11 +243,11 @@ class PerlStates(CodeStateMachine):
|
|
|
241
243
|
def _state_nested_sub_dec(self, token):
|
|
242
244
|
if token.isspace():
|
|
243
245
|
return
|
|
244
|
-
|
|
246
|
+
if token == '{':
|
|
245
247
|
# Anonymous sub
|
|
246
248
|
self.brace_count += 1
|
|
247
249
|
self.anonymous_count += 1
|
|
248
|
-
anon_name =
|
|
250
|
+
anon_name = "<anonymous>"
|
|
249
251
|
if self.package_name:
|
|
250
252
|
anon_name = f"{self.package_name}::{anon_name}"
|
|
251
253
|
self.context.add_condition() # Count sub as a condition
|
|
@@ -256,10 +258,7 @@ class PerlStates(CodeStateMachine):
|
|
|
256
258
|
full_name = nested_func_name
|
|
257
259
|
if self.package_name:
|
|
258
260
|
full_name = f"{self.package_name}::{nested_func_name}"
|
|
259
|
-
|
|
260
|
-
# Save current function state
|
|
261
|
-
saved_func_context = self.context
|
|
262
|
-
|
|
261
|
+
|
|
263
262
|
# Create a new function for the nested sub
|
|
264
263
|
self.context.try_new_function(full_name)
|
|
265
264
|
self.context.confirm_new_function()
|
|
@@ -295,10 +294,10 @@ class PerlStates(CodeStateMachine):
|
|
|
295
294
|
if token == 'sub':
|
|
296
295
|
# Inline anonymous subroutine as argument
|
|
297
296
|
self.anonymous_count += 1
|
|
298
|
-
full_name =
|
|
297
|
+
full_name = "<anonymous>"
|
|
299
298
|
if self.package_name:
|
|
300
299
|
full_name = f"{self.package_name}::{full_name}"
|
|
301
|
-
|
|
300
|
+
|
|
302
301
|
self.context.try_new_function(full_name)
|
|
303
302
|
self.context.confirm_new_function()
|
|
304
303
|
self.next(self._state_nested_anon_search)
|
|
@@ -327,4 +326,4 @@ class PerlStates(CodeStateMachine):
|
|
|
327
326
|
self.brace_count -= 1
|
|
328
327
|
if self.brace_count == 1: # Back to outer function level
|
|
329
328
|
self.context.end_of_function()
|
|
330
|
-
self.next(self._state_function_body)
|
|
329
|
+
self.next(self._state_function_body)
|