lizard 1.19.0__py2.py3-none-any.whl → 1.21.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.19.0.dist-info → lizard-1.21.0.dist-info}/METADATA +10 -1
- lizard-1.21.0.dist-info/RECORD +70 -0
- lizard.py +22 -7
- lizard_ext/__init__.py +0 -1
- lizard_ext/lizardcomplextags.py +6 -0
- lizard_ext/lizardmccabe.py +6 -1
- lizard_ext/lizardmodified.py +7 -2
- lizard_ext/lizardnonstrict.py +2 -1
- lizard_ext/lizardns.py +31 -8
- lizard_ext/version.py +1 -1
- lizard_languages/clike.py +51 -24
- lizard_languages/code_reader.py +26 -2
- lizard_languages/csharp.py +21 -2
- lizard_languages/erlang.py +9 -1
- lizard_languages/fortran.py +5 -6
- lizard_languages/gdscript.py +6 -2
- lizard_languages/java.py +4 -0
- lizard_languages/kotlin.py +6 -3
- lizard_languages/perl.py +8 -4
- lizard_languages/php.py +18 -3
- lizard_languages/plsql.py +5 -2
- lizard_languages/python.py +6 -4
- lizard_languages/r.py +9 -6
- lizard_languages/rubylike.py +6 -3
- lizard_languages/rust.py +7 -2
- lizard_languages/scala.py +6 -2
- lizard_languages/solidity.py +6 -1
- lizard_languages/st.py +9 -5
- lizard_languages/swift.py +6 -2
- lizard_languages/tnsdl.py +6 -1
- lizard_languages/tsx.py +2 -2
- lizard_languages/ttcn.py +5 -3
- lizard_languages/typescript.py +6 -2
- lizard_languages/zig.py +7 -1
- lizard-1.19.0.dist-info/RECORD +0 -70
- {lizard-1.19.0.dist-info → lizard-1.21.0.dist-info}/LICENSE.txt +0 -0
- {lizard-1.19.0.dist-info → lizard-1.21.0.dist-info}/WHEEL +0 -0
- {lizard-1.19.0.dist-info → lizard-1.21.0.dist-info}/entry_points.txt +0 -0
- {lizard-1.19.0.dist-info → lizard-1.21.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.21.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/
|
|
@@ -351,6 +351,15 @@ behavior of lizard. There are two types of forgiveness comments:
|
|
|
351
351
|
...
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
Selective forgiveness: Use "#lizard forgives(metric1, metric2)" to forgive only specific metrics (e.g. length, cyclomatic_complexity, parameter_count, nloc, token_count).
|
|
355
|
+
|
|
356
|
+
::
|
|
357
|
+
|
|
358
|
+
int foo() {
|
|
359
|
+
// #lizard forgives(length) // Forgive only length violations
|
|
360
|
+
...
|
|
361
|
+
}
|
|
362
|
+
|
|
354
363
|
2. Global code forgiveness: Put "#lizard forgive global" before global code to suppress warnings for all code outside of functions.
|
|
355
364
|
|
|
356
365
|
::
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
lizard.py,sha256=L5CBQ47VYvqzUwqrQD5LfzfcwCB5qBuUx4J59-zKOUU,42076
|
|
2
|
+
lizard_ext/__init__.py,sha256=UQ2oZ4ej1CCekgiY2Qj8sGx8HheoYyxjcOxvrwF70kc,871
|
|
3
|
+
lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
|
|
4
|
+
lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
|
|
5
|
+
lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
|
|
6
|
+
lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
|
|
7
|
+
lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
|
|
8
|
+
lizard_ext/htmloutput.py,sha256=G7MdLD7AwuR0LLCAkEpyHx4N_ssCcFQdXd7Xg5jZWX0,6537
|
|
9
|
+
lizard_ext/keywords.py,sha256=VxsxoATtKV-8egMKd7I8sd2qbZMtEFEpsszk__6rmjQ,893
|
|
10
|
+
lizard_ext/lizardboolcount.py,sha256=abmMA9X3VFRO5mziicUxWKmHldHNC0jBEe7NKAKA5fs,1062
|
|
11
|
+
lizard_ext/lizardcomplextags.py,sha256=z4Jsz8HNxbZXhDRuUeVnNfgt5Zv4FYEW2kRPO4G3F3E,1027
|
|
12
|
+
lizard_ext/lizardcpre.py,sha256=bVrMXffGUZlydv_zwIVtp-Ij9XyXMFEcap2R4DmLXPU,1277
|
|
13
|
+
lizard_ext/lizarddependencycount.py,sha256=6Nt2z69cyG8P3pceTUCTU6yh4h73lezL2awRFh7K77Y,2423
|
|
14
|
+
lizard_ext/lizarddumpcomments.py,sha256=-c46U3CP_beDfBuB9QhObpiB6wK7xq1u9jeRjgAyU90,683
|
|
15
|
+
lizard_ext/lizardduplicate.py,sha256=tlU-UTB_d_qjekiO4MSaXPBT1FSX8uTaOcfeXnjBzU8,9762
|
|
16
|
+
lizard_ext/lizardduplicated_param_list.py,sha256=_ApeSbjA-NU_rmICsivyfGEEg0-O94wWgWpeeE8F3Zc,1811
|
|
17
|
+
lizard_ext/lizardexitcount.py,sha256=ziAVFi5RgjxJOwAQy6LzdZZflkvvxTiXOIp0FAL0wKQ,692
|
|
18
|
+
lizard_ext/lizardgotocount.py,sha256=w2GWWwqVh4j7Fum41Wvg172b70JvtCm5BCzZUmmTlcM,514
|
|
19
|
+
lizard_ext/lizardignoreassert.py,sha256=sqLwcnJQ06SYqIk901ib4NQ8ECwjIe_qL4T6z1wLXAk,644
|
|
20
|
+
lizard_ext/lizardio.py,sha256=xQN-AgLGLKJarJkgfaqX_TKyupbb7GTcwPxrL2B1J1w,3357
|
|
21
|
+
lizard_ext/lizardmccabe.py,sha256=nLwfYjnAWn1DSoDc-k3iPvltiSX_YmAEdAfM3rOdLSQ,1538
|
|
22
|
+
lizard_ext/lizardmodified.py,sha256=51EY-HM840k_-jFiJugp_fOPBKvovX2Wu0s4ghLM3us,1002
|
|
23
|
+
lizard_ext/lizardnd.py,sha256=h4MaSsFAY5rK8kOSr61LkWntwJWV1KnhaEOWZgp3-c0,7496
|
|
24
|
+
lizard_ext/lizardnonstrict.py,sha256=DWdE2kTn39Gf8UkaRwIO9ogWTDuqA4lXNlQIBT3uLRM,457
|
|
25
|
+
lizard_ext/lizardns.py,sha256=3PYJrXZeFjzFnbotINZBm-MHeS8MQxVNtkQJd_S5fYA,5161
|
|
26
|
+
lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
|
|
27
|
+
lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
|
|
28
|
+
lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
|
|
29
|
+
lizard_ext/version.py,sha256=sIUtJyuNVeq7CiJtEKzI_OdOiYNcDqIVBKdYY_OOYRg,181
|
|
30
|
+
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
31
|
+
lizard_languages/__init__.py,sha256=DniggHM3SOFlwkP8Tm4csEcisbf7_i1EvfoAkG9Atg0,1611
|
|
32
|
+
lizard_languages/clike.py,sha256=BMtIrqPuxmRur-KB7LG6imJtUvnOH2-7p4XFeIFLjME,14892
|
|
33
|
+
lizard_languages/code_reader.py,sha256=I2Kc9F0o4RNX4UUsasUMUieX34u4eOaS73ePhQLC9jg,7844
|
|
34
|
+
lizard_languages/csharp.py,sha256=UDuiG20Ydbb4KMD_mgb82shD37URq3ZvqNLpI77VBPE,3015
|
|
35
|
+
lizard_languages/erlang.py,sha256=tbVPDqi90jV2X1-luDYS32H1zK_2KHwssbJ-tCCOvzg,4249
|
|
36
|
+
lizard_languages/fortran.py,sha256=vwKz1VdwyUtWoECURXuR6e8E2kTR71HtpdbOsmDoyDw,8993
|
|
37
|
+
lizard_languages/gdscript.py,sha256=3mHeCarDq_ilDK_OZ2LOvwfWJqLTzK2lquprLDUnX08,737
|
|
38
|
+
lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
|
|
39
|
+
lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
|
|
40
|
+
lizard_languages/java.py,sha256=8t8G6TR-1qxLHGu3m_T5Kuny49WvMfwCQe1eYnvwZmk,6610
|
|
41
|
+
lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
|
|
42
|
+
lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
|
|
43
|
+
lizard_languages/kotlin.py,sha256=1ao-VOHUrrSluxgGjMcMPIDt_-dqVfT1JOz15PJLJH8,3024
|
|
44
|
+
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
45
|
+
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
46
|
+
lizard_languages/perl.py,sha256=lxxdC0KJsr3tmNLMATO7vx3O7yWGgICxHxVTfmu7db8,12111
|
|
47
|
+
lizard_languages/php.py,sha256=ebHkNyKDOPLyzey5eDhKuu1AMsmGflxqGGmx1ZrlrGA,10542
|
|
48
|
+
lizard_languages/plsql.py,sha256=Wtny6-YW4Jc6G4EhCgoSuw8D86x6JVREprzaUEyKffw,17261
|
|
49
|
+
lizard_languages/python.py,sha256=-sI7ZzJ1erdmqo1MnCcg5cqn1krUhCHlR_phlhnmd_M,5978
|
|
50
|
+
lizard_languages/r.py,sha256=NHNPDGnWXcl8hMi1VL8OM6nw8F9pP6xZX7EgUIocOec,12930
|
|
51
|
+
lizard_languages/ruby.py,sha256=HL1ZckeuUUJU3QSVAOPsG_Zsl0C6X2PX5_VaWqclzkM,2277
|
|
52
|
+
lizard_languages/rubylike.py,sha256=vnfY2lSeIYEgfjJWrfNL6fg1ISHGiwo32H7bf7Stwos,3604
|
|
53
|
+
lizard_languages/rust.py,sha256=7ioyo-kpy5zSQ6Fwua6GcYopkLUdfyExg6kLP0Br3p0,1027
|
|
54
|
+
lizard_languages/scala.py,sha256=onEuS8IsT7aCB-HV0arVDjmgpezXWndk1RCOgoySUes,1436
|
|
55
|
+
lizard_languages/script_language.py,sha256=SKe45AbO6Z-axbN8KW_g7jf9g7YTXZ6dWzJj4ubDsM8,1172
|
|
56
|
+
lizard_languages/solidity.py,sha256=eg7HQ7cuV1d0Awwj4lTySu9f63F1vrK8czsPt_W5Kqw,680
|
|
57
|
+
lizard_languages/st.py,sha256=QiiGNYE8CzmfmZAF_wLQKovXk1Fyqa8QFGTXM1meNNk,4385
|
|
58
|
+
lizard_languages/swift.py,sha256=JcHmBV-p916K2o2XrTKf98U6Bt9-jAEOS9JqA1W5tkA,2570
|
|
59
|
+
lizard_languages/tnsdl.py,sha256=y2dcZI_PmriNd7UTP1Db9QEpWBzKeoH8odRzkiN-P1k,3009
|
|
60
|
+
lizard_languages/tsx.py,sha256=fY9qzPcSvhHSs-62ZFh0mMGyKVaSCEfsYZxBS_uHzww,17161
|
|
61
|
+
lizard_languages/ttcn.py,sha256=tSkPmtifsuUI5aUKBG-uHvwcmZTWcbtMWoUMF3Cw4cI,2199
|
|
62
|
+
lizard_languages/typescript.py,sha256=SOef4ja2GE-Idt9fSF7d0gZTW6bwCNl8K2IT67nIMyA,14805
|
|
63
|
+
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
64
|
+
lizard_languages/zig.py,sha256=RiDZyjnCn97jqFmcl5EQl7pbuH0G1oI2Qo72EXvdtDU,813
|
|
65
|
+
lizard-1.21.0.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
|
|
66
|
+
lizard-1.21.0.dist-info/METADATA,sha256=jLq4TDqRDAQd2RimhMDLBNUlv7ULP0vJ6pqybAtClPg,16978
|
|
67
|
+
lizard-1.21.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
|
68
|
+
lizard-1.21.0.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
|
|
69
|
+
lizard-1.21.0.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
|
|
70
|
+
lizard-1.21.0.dist-info/RECORD,,
|
lizard.py
CHANGED
|
@@ -291,6 +291,7 @@ class FunctionInfo(Nesting): # pylint: disable=R0902
|
|
|
291
291
|
self.fan_out = 0
|
|
292
292
|
self.general_fan_out = 0
|
|
293
293
|
self.max_nesting_depth = 0 # Initialize max_nesting_depth to 0
|
|
294
|
+
self.forgiven_metrics = set()
|
|
294
295
|
|
|
295
296
|
@property
|
|
296
297
|
def name_in_space(self):
|
|
@@ -509,9 +510,15 @@ def comment_counter(tokens, reader):
|
|
|
509
510
|
if comment is not None:
|
|
510
511
|
for _ in comment.splitlines()[1:]:
|
|
511
512
|
yield '\n'
|
|
512
|
-
|
|
513
|
+
stripped = comment.strip()
|
|
514
|
+
if stripped.startswith("#lizard forgive global"):
|
|
513
515
|
reader.context.forgive_global = True
|
|
514
|
-
elif
|
|
516
|
+
elif stripped.startswith("#lizard forgives("):
|
|
517
|
+
match = re.search(r'#lizard forgives?\(([^)]*)\)', stripped)
|
|
518
|
+
if match:
|
|
519
|
+
metrics = {m.strip() for m in match.group(1).split(',') if m.strip()}
|
|
520
|
+
reader.context.current_function.forgiven_metrics.update(metrics)
|
|
521
|
+
elif stripped.startswith("#lizard forgive"):
|
|
515
522
|
reader.context.forgive = True
|
|
516
523
|
if "GENERATED CODE" in comment:
|
|
517
524
|
return
|
|
@@ -597,9 +604,16 @@ def warning_filter(option, module_infos):
|
|
|
597
604
|
for file_info in module_infos:
|
|
598
605
|
if file_info:
|
|
599
606
|
for fun in file_info.function_list:
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
607
|
+
violated_metrics = [
|
|
608
|
+
attr for attr, limit in option.thresholds.items()
|
|
609
|
+
if getattr(fun, attr) > limit
|
|
610
|
+
]
|
|
611
|
+
if not violated_metrics:
|
|
612
|
+
continue
|
|
613
|
+
forgiven = getattr(fun, 'forgiven_metrics', set())
|
|
614
|
+
if all(metric in forgiven for metric in violated_metrics):
|
|
615
|
+
continue
|
|
616
|
+
yield fun
|
|
603
617
|
|
|
604
618
|
|
|
605
619
|
def whitelist_filter(warnings, script=None, whitelist=None):
|
|
@@ -1109,7 +1123,8 @@ def main(argv=None):
|
|
|
1109
1123
|
sys.stdout = original_stdout
|
|
1110
1124
|
output_file.close()
|
|
1111
1125
|
if 0 <= options.number < warning_count:
|
|
1112
|
-
|
|
1126
|
+
return 1
|
|
1127
|
+
return 0
|
|
1113
1128
|
|
|
1114
1129
|
|
|
1115
1130
|
def print_extension_results(extensions):
|
|
@@ -1119,4 +1134,4 @@ def print_extension_results(extensions):
|
|
|
1119
1134
|
|
|
1120
1135
|
|
|
1121
1136
|
if __name__ == "__main__":
|
|
1122
|
-
main()
|
|
1137
|
+
sys.exit(main())
|
lizard_ext/__init__.py
CHANGED
|
@@ -21,7 +21,6 @@ def print_csv(results, options, _, total_factory):
|
|
|
21
21
|
|
|
22
22
|
def print_checkstyle(results, options, _, total_factory, file=None):
|
|
23
23
|
import sys
|
|
24
|
-
print("DEBUG: print_checkstyle called", file=sys.stderr)
|
|
25
24
|
output = checkstyle_output(total_factory(list(results)), options.verbose)
|
|
26
25
|
if file is None:
|
|
27
26
|
file = sys.stdout
|
lizard_ext/lizardcomplextags.py
CHANGED
|
@@ -5,10 +5,16 @@ that adding the complexity and the line numbers of the keywords appear.
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class LizardExtension(object): # pylint: disable=R0903
|
|
8
|
+
"""
|
|
9
|
+
Complex tags extension: records all complexity-adding keywords and their line numbers.
|
|
10
|
+
Uses reader.conditions (combined set of all condition types) to track all
|
|
11
|
+
complexity contributors: control flow, logical operators, case labels, and ternary.
|
|
12
|
+
"""
|
|
8
13
|
|
|
9
14
|
# pylint: disable=W0221
|
|
10
15
|
def __call__(self, tokens, reader):
|
|
11
16
|
context = reader.context
|
|
17
|
+
# Use combined conditions set - intentionally includes all types
|
|
12
18
|
conditions = reader.conditions
|
|
13
19
|
for token in tokens:
|
|
14
20
|
yield token
|
lizard_ext/lizardmccabe.py
CHANGED
|
@@ -14,9 +14,14 @@ from .extension_base import ExtensionBase
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
17
|
+
"""
|
|
18
|
+
McCabe extension: only counts the first 'case' in a switch statement.
|
|
19
|
+
Consecutive cases without code between them don't add to complexity.
|
|
20
|
+
Works by detecting case tokens (conceptually from reader.case_keywords).
|
|
21
|
+
"""
|
|
17
22
|
|
|
18
23
|
def _state_global(self, token):
|
|
19
|
-
if token == "case":
|
|
24
|
+
if token == "case": # Detect case keywords
|
|
20
25
|
self._state = self._in_case
|
|
21
26
|
|
|
22
27
|
def _in_case(self, token):
|
lizard_ext/lizardmodified.py
CHANGED
|
@@ -6,14 +6,19 @@ where the whole switch/case will be counted as 1.
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class LizardExtension(object): # pylint: disable=R0903
|
|
9
|
+
"""
|
|
10
|
+
Modified CCN extension: counts entire switch/case as 1 complexity.
|
|
11
|
+
Adds +1 for 'switch', subtracts -1 for each 'case'.
|
|
12
|
+
Works with switch/case keywords (conceptually from reader.case_keywords).
|
|
13
|
+
"""
|
|
9
14
|
|
|
10
15
|
def __call__(self, tokens, reader):
|
|
11
16
|
for token in tokens:
|
|
12
|
-
if token == 'switch':
|
|
17
|
+
if token == 'switch': # Add complexity for switch statement
|
|
13
18
|
reader.context.add_condition()
|
|
14
19
|
if hasattr(reader.context, "add_nd_condition"):
|
|
15
20
|
reader.context.add_nd_condition()
|
|
16
|
-
elif token == 'case':
|
|
21
|
+
elif token == 'case': # Subtract complexity for each case
|
|
17
22
|
reader.context.add_condition(-1)
|
|
18
23
|
if hasattr(reader.context, "add_nd_condition"):
|
|
19
24
|
reader.context.add_nd_condition(-1)
|
lizard_ext/lizardnonstrict.py
CHANGED
|
@@ -9,5 +9,6 @@ class LizardExtension(object): # pylint: disable=R0903
|
|
|
9
9
|
|
|
10
10
|
# pylint: disable=W0221
|
|
11
11
|
def __call__(self, tokens, reader):
|
|
12
|
-
|
|
12
|
+
# Remove logical operators from conditions (non-strict mode)
|
|
13
|
+
reader.conditions -= reader.logical_operators
|
|
13
14
|
return tokens
|
lizard_ext/lizardns.py
CHANGED
|
@@ -67,10 +67,35 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
67
67
|
|
|
68
68
|
def __init__(self):
|
|
69
69
|
super(LizardExtension, self).__init__(None)
|
|
70
|
-
self.structure_piles = [0]
|
|
70
|
+
self.structure_piles = [0] # Invariant: must always have at least one element
|
|
71
71
|
|
|
72
|
-
def
|
|
72
|
+
def _push_scope(self):
|
|
73
|
+
"""Push a new scope level. Safe to call anytime."""
|
|
74
|
+
self.structure_piles.append(0)
|
|
75
|
+
|
|
76
|
+
def _pop_scope(self):
|
|
77
|
+
"""Pop a scope level. Maintains invariant of at least one element."""
|
|
78
|
+
if len(self.structure_piles) > 1:
|
|
79
|
+
self.structure_piles.pop()
|
|
80
|
+
|
|
81
|
+
def _increment_current_scope(self):
|
|
82
|
+
"""Increment structure count in current scope. Safe even if piles corrupted."""
|
|
83
|
+
if not self.structure_piles:
|
|
84
|
+
self.structure_piles = [0] # Restore invariant
|
|
73
85
|
self.structure_piles[-1] += 1
|
|
86
|
+
|
|
87
|
+
def _reset_or_decrement_current_scope(self, decrement=False):
|
|
88
|
+
"""Reset or decrement current scope counter. Safe even if piles corrupted."""
|
|
89
|
+
if not self.structure_piles:
|
|
90
|
+
self.structure_piles = [0] # Restore invariant
|
|
91
|
+
return
|
|
92
|
+
if decrement:
|
|
93
|
+
self.structure_piles[-1] -= 1
|
|
94
|
+
else:
|
|
95
|
+
self.structure_piles[-1] = 0
|
|
96
|
+
|
|
97
|
+
def pile_up_within_block(self):
|
|
98
|
+
self._increment_current_scope()
|
|
74
99
|
cur_level = sum(self.structure_piles)
|
|
75
100
|
# Is there a path around _state_global?
|
|
76
101
|
if not hasattr(self.context.current_function, "max_nested_structures"):
|
|
@@ -83,10 +108,10 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
83
108
|
if not hasattr(self.context.current_function, "max_nested_structures"):
|
|
84
109
|
self.context.current_function.max_nested_structures = 0
|
|
85
110
|
if token == '{':
|
|
86
|
-
self.
|
|
111
|
+
self._push_scope()
|
|
87
112
|
elif token in ';}':
|
|
88
113
|
if token == '}':
|
|
89
|
-
self.
|
|
114
|
+
self._pop_scope()
|
|
90
115
|
self._state = self._block_ending
|
|
91
116
|
elif token in self.structures:
|
|
92
117
|
self._state = self._in_structure_head
|
|
@@ -98,10 +123,8 @@ class LizardExtension(ExtensionBase): # pylint: disable=R0903
|
|
|
98
123
|
self._state(token)
|
|
99
124
|
|
|
100
125
|
def _block_ending(self, token):
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
else:
|
|
104
|
-
self.structure_piles[-1] = 0
|
|
126
|
+
decrement = token in self.matching_structures
|
|
127
|
+
self._reset_or_decrement_current_scope(decrement=decrement)
|
|
105
128
|
self._state = self._state_global
|
|
106
129
|
self._state(token)
|
|
107
130
|
|
lizard_ext/version.py
CHANGED
lizard_languages/clike.py
CHANGED
|
@@ -32,8 +32,11 @@ class CLikeReader(CodeReader, CCppCommentsMixin):
|
|
|
32
32
|
|
|
33
33
|
@staticmethod
|
|
34
34
|
def generate_tokens(source_code, addition='', token_class=None):
|
|
35
|
-
# Add pattern for
|
|
36
|
-
|
|
35
|
+
# Add pattern for C++ raw string literals R"delimiter(content)delimiter"
|
|
36
|
+
# The delimiter can be empty or up to 16 chars (excluding parentheses, backslash, whitespace)
|
|
37
|
+
# Using a simplified pattern that handles most cases
|
|
38
|
+
addition = r"|R\"[^(\\]*\((?:[^)]|\)[^\"])*\)[^(\\]*\"" + \
|
|
39
|
+
r"|(?:\d*\.\d+(?:[eE][-+]?\d+)?)" + \
|
|
37
40
|
r"|(?:\d+\.(?:\d+)?(?:[eE][-+]?\d+)?)" + \
|
|
38
41
|
addition
|
|
39
42
|
return CodeReader.generate_tokens(source_code, addition, token_class)
|
|
@@ -319,53 +322,77 @@ class CLikeStates(CodeStateMachine):
|
|
|
319
322
|
def _state_lambda_check(self, token):
|
|
320
323
|
"""Check if this is a lambda expression or a function attribute."""
|
|
321
324
|
if token == ']':
|
|
322
|
-
# This is
|
|
323
|
-
# Skip the lambda and continue parsing normally
|
|
325
|
+
# This is an empty capture list [](params)
|
|
324
326
|
self._state = self._state_lambda_params
|
|
325
327
|
elif token == '[':
|
|
326
328
|
# This is a function attribute [[attribute]]
|
|
327
329
|
self._state = self._state_attribute
|
|
328
330
|
else:
|
|
329
331
|
# This is a lambda with capture list [capture](params)
|
|
330
|
-
# Skip until we find the closing bracket
|
|
331
332
|
self._state = self._state_lambda_capture
|
|
332
333
|
|
|
333
334
|
def _state_lambda_params(self, token):
|
|
334
335
|
"""Handle lambda parameters and body."""
|
|
335
336
|
if token == '(':
|
|
336
|
-
# Start of parameter list
|
|
337
|
+
# Start of parameter list
|
|
338
|
+
self.bracket_stack.append('(')
|
|
337
339
|
self._state = self._state_lambda_param_list
|
|
338
340
|
else:
|
|
339
|
-
# No parameters, check for body
|
|
341
|
+
# No parameters, check for body or go back to global
|
|
340
342
|
self._state = self._state_lambda_body
|
|
343
|
+
self._state(token)
|
|
341
344
|
|
|
342
345
|
def _state_lambda_param_list(self, token):
|
|
343
|
-
"""Handle lambda parameter list."""
|
|
344
|
-
if token == '
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
"""Handle lambda parameter list with proper bracket tracking."""
|
|
347
|
+
if token == '(':
|
|
348
|
+
self.bracket_stack.append('(')
|
|
349
|
+
elif token == ')':
|
|
350
|
+
if self.bracket_stack and self.bracket_stack[-1] == '(':
|
|
351
|
+
self.bracket_stack.pop()
|
|
352
|
+
if not self.bracket_stack:
|
|
353
|
+
# End of parameter list, check for body
|
|
354
|
+
self._state = self._state_lambda_body
|
|
355
|
+
elif token in ('<', '['):
|
|
356
|
+
self.bracket_stack.append(token)
|
|
357
|
+
elif token == '>' and self.bracket_stack and self.bracket_stack[-1] == '<':
|
|
358
|
+
self.bracket_stack.pop()
|
|
359
|
+
elif token == ']' and self.bracket_stack and self.bracket_stack[-1] == '[':
|
|
360
|
+
self.bracket_stack.pop()
|
|
348
361
|
|
|
349
362
|
def _state_lambda_body(self, token):
|
|
350
|
-
"""Handle lambda body."""
|
|
363
|
+
"""Handle lambda body and qualifiers."""
|
|
351
364
|
if token == '{':
|
|
352
|
-
# Start of lambda body
|
|
365
|
+
# Start of lambda body
|
|
366
|
+
self.bracket_stack.append('{')
|
|
353
367
|
self._state = self._state_lambda_body_skip
|
|
354
|
-
elif token
|
|
355
|
-
# Lambda
|
|
368
|
+
elif token in ('mutable', 'noexcept', 'constexpr', 'consteval'):
|
|
369
|
+
# Lambda qualifiers, stay in this state
|
|
370
|
+
pass
|
|
371
|
+
elif token == '->':
|
|
372
|
+
# Trailing return type, stay in this state until we find '{'
|
|
373
|
+
pass
|
|
374
|
+
elif token in (';', ',', ')'):
|
|
375
|
+
# Lambda declaration ended, return to global
|
|
356
376
|
self._state = self._state_global
|
|
357
|
-
|
|
377
|
+
self._state(token)
|
|
378
|
+
else:
|
|
379
|
+
# Other tokens (type names, etc.) - stay in state
|
|
380
|
+
pass
|
|
358
381
|
|
|
359
382
|
def _state_lambda_body_skip(self, token):
|
|
360
|
-
"""Skip lambda body
|
|
361
|
-
if token == '
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
383
|
+
"""Skip lambda body with proper brace tracking."""
|
|
384
|
+
if token == '{':
|
|
385
|
+
self.bracket_stack.append('{')
|
|
386
|
+
elif token == '}':
|
|
387
|
+
if self.bracket_stack and self.bracket_stack[-1] == '{':
|
|
388
|
+
self.bracket_stack.pop()
|
|
389
|
+
if not self.bracket_stack:
|
|
390
|
+
# End of lambda body
|
|
391
|
+
self._state = self._state_global
|
|
365
392
|
|
|
366
393
|
def _state_lambda_capture(self, token):
|
|
367
394
|
"""Handle lambda capture list."""
|
|
368
395
|
if token == ']':
|
|
369
|
-
# End of capture list,
|
|
370
|
-
self._state = self.
|
|
396
|
+
# End of capture list, now expect parameters
|
|
397
|
+
self._state = self._state_lambda_params
|
|
371
398
|
# Otherwise, continue in capture list
|
lizard_languages/code_reader.py
CHANGED
|
@@ -94,12 +94,36 @@ class CodeReader:
|
|
|
94
94
|
ext = []
|
|
95
95
|
languages = None
|
|
96
96
|
extra_subclasses = set()
|
|
97
|
-
|
|
97
|
+
|
|
98
|
+
# Condition categories - separate types that contribute to cyclomatic complexity
|
|
99
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch'}
|
|
100
|
+
_logical_operators = {'&&', '||'}
|
|
101
|
+
_case_keywords = {'case'}
|
|
102
|
+
_ternary_operators = {'?'}
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def _build_conditions(cls):
|
|
106
|
+
"""Build combined conditions set from separated categories.
|
|
107
|
+
|
|
108
|
+
Returns combined set of all condition types for CCN calculation.
|
|
109
|
+
"""
|
|
110
|
+
return (cls._control_flow_keywords |
|
|
111
|
+
cls._logical_operators |
|
|
112
|
+
cls._case_keywords |
|
|
113
|
+
cls._ternary_operators)
|
|
98
114
|
|
|
99
115
|
def __init__(self, context):
|
|
100
116
|
self.parallel_states = []
|
|
101
117
|
self.context = context
|
|
102
|
-
|
|
118
|
+
|
|
119
|
+
# Build combined conditions set from separated categories
|
|
120
|
+
self.conditions = copy(self.__class__._build_conditions())
|
|
121
|
+
|
|
122
|
+
# Expose individual categories for extensions
|
|
123
|
+
self.control_flow_keywords = copy(self.__class__._control_flow_keywords)
|
|
124
|
+
self.logical_operators = copy(self.__class__._logical_operators)
|
|
125
|
+
self.case_keywords = copy(self.__class__._case_keywords)
|
|
126
|
+
self.ternary_operators = copy(self.__class__._ternary_operators)
|
|
103
127
|
|
|
104
128
|
@classmethod
|
|
105
129
|
def match_filename(cls, filename):
|
lizard_languages/csharp.py
CHANGED
|
@@ -11,8 +11,11 @@ class CSharpReader(CLikeReader):
|
|
|
11
11
|
ext = ['cs']
|
|
12
12
|
language_names = ['csharp']
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# Separated condition categories
|
|
15
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch'}
|
|
16
|
+
_logical_operators = {'&&', '||'}
|
|
17
|
+
_case_keywords = {'case'}
|
|
18
|
+
_ternary_operators = {'?', '??'} # C# has both ?: and ?? (null-coalescing)
|
|
16
19
|
|
|
17
20
|
def __init__(self, context):
|
|
18
21
|
super(CSharpReader, self).__init__(context)
|
|
@@ -39,6 +42,22 @@ class CSharpStates(CLikeStates):
|
|
|
39
42
|
if self.class_name and self.context.current_function:
|
|
40
43
|
self.context.current_function.name = f"{self.class_name}::{name}"
|
|
41
44
|
|
|
45
|
+
def _state_dec_to_imp(self, token):
|
|
46
|
+
"""Override to handle C# expression-bodied members (=>)"""
|
|
47
|
+
if token == '=>':
|
|
48
|
+
# Expression-bodied member: confirm function and enter expression body
|
|
49
|
+
self.context.confirm_new_function()
|
|
50
|
+
self._state = self._state_expression_body
|
|
51
|
+
else:
|
|
52
|
+
super(CSharpStates, self)._state_dec_to_imp(token)
|
|
53
|
+
|
|
54
|
+
def _state_expression_body(self, token):
|
|
55
|
+
"""Read the expression body until semicolon, processing tokens for complexity"""
|
|
56
|
+
if token == ';':
|
|
57
|
+
# End of expression-bodied method, finalize function and return to global state
|
|
58
|
+
self.context.end_of_function()
|
|
59
|
+
self._state = self._state_global
|
|
60
|
+
|
|
42
61
|
def _state_global(self, token):
|
|
43
62
|
if token in ("class", "struct", "record"):
|
|
44
63
|
self.class_name = None
|
lizard_languages/erlang.py
CHANGED
|
@@ -13,7 +13,15 @@ class ErlangReader(CodeReader):
|
|
|
13
13
|
|
|
14
14
|
ext = ['erl', 'hrl', 'es', 'escript']
|
|
15
15
|
language_names = ['erlang']
|
|
16
|
-
|
|
16
|
+
|
|
17
|
+
# Separated condition categories
|
|
18
|
+
_control_flow_keywords = {'if', 'catch', 'when'} # when is used in guards
|
|
19
|
+
_logical_operators = {'and', 'or', 'not'}
|
|
20
|
+
_case_keywords = {'case'}
|
|
21
|
+
# Note: '?' in Erlang is a macro expansion operator (e.g., ?MODULE, ?EMPTY_NODE)
|
|
22
|
+
# Unlike C-style ternary, it's for compile-time macro substitution
|
|
23
|
+
# Included in ternary_operators because macro usage adds to code complexity
|
|
24
|
+
_ternary_operators = {'?'}
|
|
17
25
|
|
|
18
26
|
def __init__(self, context):
|
|
19
27
|
super(ErlangReader, self).__init__(context)
|
lizard_languages/fortran.py
CHANGED
|
@@ -19,12 +19,11 @@ class FortranReader(CodeReader, FortranCommentsMixin):
|
|
|
19
19
|
ext = ['f70', 'f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp']
|
|
20
20
|
language_names = ['fortran']
|
|
21
21
|
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
22
|
+
# Separated condition categories (case-insensitive language)
|
|
23
|
+
_control_flow_keywords = {'IF', 'DO', 'if', 'do'}
|
|
24
|
+
_logical_operators = {'.AND.', '.OR.', '.and.', '.or.'}
|
|
25
|
+
_case_keywords = {'CASE', 'case'}
|
|
26
|
+
_ternary_operators = set()
|
|
28
27
|
_blocks = [
|
|
29
28
|
'PROGRAM', 'MODULE', 'SUBMODULE', 'SUBROUTINE', 'FUNCTION', 'TYPE',
|
|
30
29
|
'INTERFACE', 'BLOCK', 'IF', 'DO', 'FORALL', 'WHERE', 'SELECT', 'ASSOCIATE'
|
lizard_languages/gdscript.py
CHANGED
|
@@ -9,8 +9,12 @@ class GDScriptReader(PythonReader):
|
|
|
9
9
|
|
|
10
10
|
ext = ['gd']
|
|
11
11
|
language_names = ['GDScript']
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
|
|
13
|
+
# Separated condition categories
|
|
14
|
+
_control_flow_keywords = {'if', 'elif', 'for', 'while', 'catch', 'do'}
|
|
15
|
+
_logical_operators = {'&&', '||'}
|
|
16
|
+
_case_keywords = {'case'}
|
|
17
|
+
_ternary_operators = {'?'}
|
|
14
18
|
|
|
15
19
|
def __init__(self, context):
|
|
16
20
|
super(GDScriptReader, self).__init__(context)
|
lizard_languages/java.py
CHANGED
|
@@ -54,11 +54,15 @@ class JavaStates(CLikeStates): # pylint: disable=R0903
|
|
|
54
54
|
|
|
55
55
|
def _try_start_a_class(self, token):
|
|
56
56
|
if token in ("class", "record", "enum"):
|
|
57
|
+
# "record" inside method bodies is a variable name, not a keyword
|
|
58
|
+
if token == "record" and self.in_method_body:
|
|
59
|
+
return False
|
|
57
60
|
self.class_name = None
|
|
58
61
|
self.is_record = token == "record"
|
|
59
62
|
self.in_record_constructor = False
|
|
60
63
|
self._state = self._state_class_declaration
|
|
61
64
|
return True
|
|
65
|
+
return False
|
|
62
66
|
|
|
63
67
|
def _state_global(self, token):
|
|
64
68
|
if token == '@':
|
lizard_languages/kotlin.py
CHANGED
|
@@ -13,9 +13,12 @@ class KotlinReader(CodeReader, CCppCommentsMixin, SwiftReplaceLabel):
|
|
|
13
13
|
|
|
14
14
|
ext = ['kt', 'kts']
|
|
15
15
|
language_names = ['kotlin']
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
16
|
+
|
|
17
|
+
# Separated condition categories
|
|
18
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch'}
|
|
19
|
+
_logical_operators = {'&&', '||'}
|
|
20
|
+
_case_keywords = set() # Kotlin uses 'when' expressions, not case
|
|
21
|
+
_ternary_operators = {'?:'} # Elvis operator
|
|
19
22
|
|
|
20
23
|
def __init__(self, context):
|
|
21
24
|
super(KotlinReader, self).__init__(context)
|
lizard_languages/perl.py
CHANGED
|
@@ -19,8 +19,13 @@ class PerlReader(CodeReader, ScriptLanguageMixIn):
|
|
|
19
19
|
|
|
20
20
|
ext = ['pl', 'pm']
|
|
21
21
|
language_names = ['perl']
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
|
|
23
|
+
# Separated condition categories
|
|
24
|
+
_control_flow_keywords = {'if', 'elsif', 'unless', 'while', 'until', 'for',
|
|
25
|
+
'foreach', 'when', 'given', 'default', 'do'}
|
|
26
|
+
_logical_operators = {'&&', '||'} # Perl also has 'and', 'or' with different precedence
|
|
27
|
+
_case_keywords = set()
|
|
28
|
+
_ternary_operators = {'?', ':'} # Both parts of ternary operator
|
|
24
29
|
|
|
25
30
|
def __init__(self, context):
|
|
26
31
|
super(PerlReader, self).__init__(context)
|
|
@@ -65,8 +70,7 @@ class PerlReader(CodeReader, ScriptLanguageMixIn):
|
|
|
65
70
|
|
|
66
71
|
|
|
67
72
|
class PerlStates(CodeStateMachine):
|
|
68
|
-
|
|
69
|
-
'&&', '||', '?', ':', 'when', 'given', 'default', 'do'])
|
|
73
|
+
# Note: _conditions removed - now inherited from PerlReader
|
|
70
74
|
|
|
71
75
|
def __init__(self, context):
|
|
72
76
|
super(PerlStates, self).__init__(context)
|
lizard_languages/php.py
CHANGED
|
@@ -31,11 +31,15 @@ class PHPLanguageStates(CodeStateMachine):
|
|
|
31
31
|
self.match_case_count = 0
|
|
32
32
|
|
|
33
33
|
def _state_global(self, token):
|
|
34
|
-
if token == '
|
|
34
|
+
if token == 'use':
|
|
35
|
+
# Enter use statement state
|
|
36
|
+
self._state = self._state_use
|
|
37
|
+
elif token == 'class':
|
|
35
38
|
self._state = self._class_declaration
|
|
36
39
|
elif token == 'trait':
|
|
37
40
|
self._state = self._trait_declaration
|
|
38
41
|
elif token == 'function':
|
|
42
|
+
# Treat 'function' as function declaration
|
|
39
43
|
self.is_function_declaration = True
|
|
40
44
|
self._state = self._function_name
|
|
41
45
|
elif token == 'fn':
|
|
@@ -88,6 +92,13 @@ class PHPLanguageStates(CodeStateMachine):
|
|
|
88
92
|
else:
|
|
89
93
|
self.last_tokens = ''
|
|
90
94
|
|
|
95
|
+
def _state_use(self, token):
|
|
96
|
+
"""Handle use statements (use function, use const, etc.)"""
|
|
97
|
+
if token == ';':
|
|
98
|
+
# End of use statement, return to global state
|
|
99
|
+
self._state = self._state_global
|
|
100
|
+
# Ignore all other tokens in use statements
|
|
101
|
+
|
|
91
102
|
def _trait_declaration(self, token):
|
|
92
103
|
if token and not token.isspace() and token not in ['{', '(']:
|
|
93
104
|
self.trait_name = token
|
|
@@ -231,8 +242,12 @@ class PHPReader(CodeReader, CCppCommentsMixin):
|
|
|
231
242
|
|
|
232
243
|
ext = ['php']
|
|
233
244
|
language_names = ['php']
|
|
234
|
-
|
|
235
|
-
|
|
245
|
+
|
|
246
|
+
# Separated condition categories
|
|
247
|
+
_control_flow_keywords = {'if', 'elseif', 'for', 'foreach', 'while', 'catch', 'match'}
|
|
248
|
+
_logical_operators = {'&&', '||'} # PHP also has 'and', 'or' with different precedence
|
|
249
|
+
_case_keywords = {'case'}
|
|
250
|
+
_ternary_operators = {'?'}
|
|
236
251
|
|
|
237
252
|
@staticmethod
|
|
238
253
|
def generate_tokens(source_code, addition='', token_class=None):
|
lizard_languages/plsql.py
CHANGED
|
@@ -45,11 +45,14 @@ class PLSQLReader(CodeReader, CCppCommentsMixin):
|
|
|
45
45
|
ext = ["sql", "pks", "pkb", "pls", "plb", "pck"]
|
|
46
46
|
language_names = ["plsql", "pl/sql"]
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# Separated condition categories
|
|
49
49
|
# Note: 'loop' is NOT in this set because LOOP has special handling:
|
|
50
50
|
# - standalone LOOP adds +1
|
|
51
51
|
# - LOOP after WHILE/FOR should not add (it's part of the compound statement)
|
|
52
|
-
|
|
52
|
+
_control_flow_keywords = {"if", "elsif", "when", "while", "for"}
|
|
53
|
+
_logical_operators = {"and", "or"}
|
|
54
|
+
_case_keywords = set() # PL/SQL uses 'when' in case expressions
|
|
55
|
+
_ternary_operators = set()
|
|
53
56
|
|
|
54
57
|
def __init__(self, context):
|
|
55
58
|
super(PLSQLReader, self).__init__(context)
|
lizard_languages/python.py
CHANGED
|
@@ -29,10 +29,12 @@ class PythonReader(CodeReader, ScriptLanguageMixIn):
|
|
|
29
29
|
|
|
30
30
|
ext = ['py']
|
|
31
31
|
language_names = ['python']
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
|
|
33
|
+
# Separated condition categories
|
|
34
|
+
_control_flow_keywords = {'if', 'elif', 'for', 'while', 'except', 'finally'}
|
|
35
|
+
_logical_operators = {'and', 'or'}
|
|
36
|
+
_case_keywords = set() # Python uses if/elif, not case
|
|
37
|
+
_ternary_operators = set() # Python uses 'x if c else y' syntax, not ?
|
|
36
38
|
|
|
37
39
|
def __init__(self, context):
|
|
38
40
|
super(PythonReader, self).__init__(context)
|
lizard_languages/r.py
CHANGED
|
@@ -12,12 +12,15 @@ class RReader(CodeReader, ScriptLanguageMixIn):
|
|
|
12
12
|
ext = ['r', 'R']
|
|
13
13
|
language_names = ['r', 'R']
|
|
14
14
|
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
# Separated condition categories
|
|
16
|
+
_control_flow_keywords = {'if', 'else if', 'for', 'while', 'repeat', 'switch',
|
|
17
|
+
'tryCatch', 'try', 'ifelse'} # ifelse is a vectorized control function
|
|
18
|
+
# R has both short-circuit (&&, ||) and element-wise (&, |) operators
|
|
19
|
+
# Both types count toward CCN as they represent conditional logic (vectorized or not)
|
|
20
|
+
# Users can use -Enonstrict to exclude logical operators if desired
|
|
21
|
+
_logical_operators = {'&&', '||', '&', '|'}
|
|
22
|
+
_case_keywords = set()
|
|
23
|
+
_ternary_operators = set()
|
|
21
24
|
|
|
22
25
|
def __init__(self, context):
|
|
23
26
|
super(RReader, self).__init__(context)
|
lizard_languages/rubylike.py
CHANGED
|
@@ -97,9 +97,12 @@ class RubylikeStateMachine(CodeStateMachine):
|
|
|
97
97
|
class RubylikeReader(CodeReader, ScriptLanguageMixIn):
|
|
98
98
|
# pylint: disable=R0903
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
# Separated condition categories
|
|
101
|
+
_control_flow_keywords = {'if', 'elsif', 'elseif', 'until', 'for', 'while',
|
|
102
|
+
'rescue', 'ensure', 'when'}
|
|
103
|
+
_logical_operators = {'and', 'or', '||', '&&'} # Both word and symbol forms
|
|
104
|
+
_case_keywords = set() # Ruby uses 'when' for case expressions
|
|
105
|
+
_ternary_operators = {'?'}
|
|
103
106
|
|
|
104
107
|
def __init__(self, context):
|
|
105
108
|
super(RubylikeReader, self).__init__(context)
|
lizard_languages/rust.py
CHANGED
|
@@ -12,8 +12,13 @@ class RustReader(CodeReader, CCppCommentsMixin):
|
|
|
12
12
|
|
|
13
13
|
ext = ['rs']
|
|
14
14
|
language_names = ['rust']
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
# Separated condition categories
|
|
17
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch', 'match', 'where'}
|
|
18
|
+
_logical_operators = {'&&', '||'}
|
|
19
|
+
_case_keywords = set() # Rust uses match arms, not case keyword
|
|
20
|
+
# Note: '?' in Rust is the error propagation operator, not ternary
|
|
21
|
+
_ternary_operators = {'?'}
|
|
17
22
|
|
|
18
23
|
def __init__(self, context):
|
|
19
24
|
super().__init__(context)
|
lizard_languages/scala.py
CHANGED
|
@@ -12,8 +12,12 @@ class ScalaReader(CodeReader, CCppCommentsMixin):
|
|
|
12
12
|
|
|
13
13
|
ext = ['scala']
|
|
14
14
|
language_names = ['scala']
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
# Separated condition categories
|
|
17
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch', 'do'}
|
|
18
|
+
_logical_operators = {'&&', '||'}
|
|
19
|
+
_case_keywords = {'case'} # Pattern matching
|
|
20
|
+
_ternary_operators = {'?'}
|
|
17
21
|
|
|
18
22
|
def __init__(self, context):
|
|
19
23
|
super(ScalaReader, self).__init__(context)
|
lizard_languages/solidity.py
CHANGED
|
@@ -11,7 +11,12 @@ class SolidityReader(CodeReader, CCppCommentsMixin):
|
|
|
11
11
|
|
|
12
12
|
ext = ['sol']
|
|
13
13
|
language_names = ['solidity']
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
# Separated condition categories
|
|
16
|
+
_control_flow_keywords = {'if', 'for', 'while'}
|
|
17
|
+
_logical_operators = {'&&', '||'}
|
|
18
|
+
_case_keywords = set()
|
|
19
|
+
_ternary_operators = {'?'}
|
|
15
20
|
|
|
16
21
|
def __init__(self, context):
|
|
17
22
|
super(SolidityReader, self).__init__(context)
|
lizard_languages/st.py
CHANGED
|
@@ -22,11 +22,15 @@ class StReader(CodeReader, StCommentsMixin):
|
|
|
22
22
|
language_names = ['st']
|
|
23
23
|
macro_pattern = re.compile(r"#\s*(\w+)\s*(.*)", re.M | re.S)
|
|
24
24
|
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
'if', 'elsif', '
|
|
28
|
-
'IF', 'ELSIF', '
|
|
29
|
-
|
|
25
|
+
# Separated condition categories (case-insensitive language)
|
|
26
|
+
_control_flow_keywords = {
|
|
27
|
+
'if', 'elsif', 'for', 'while', 'repeat',
|
|
28
|
+
'IF', 'ELSIF', 'FOR', 'WHILE', 'REPEAT'
|
|
29
|
+
}
|
|
30
|
+
# ST is case-insensitive, so include both lowercase and uppercase forms
|
|
31
|
+
_logical_operators = {'and', 'or', 'AND', 'OR'}
|
|
32
|
+
_case_keywords = {'case', 'CASE'}
|
|
33
|
+
_ternary_operators = set()
|
|
30
34
|
|
|
31
35
|
_functions = set([
|
|
32
36
|
'FUNCTION_BLOCK', 'FUNCTION', 'ACTION'
|
lizard_languages/swift.py
CHANGED
|
@@ -30,8 +30,12 @@ class SwiftReader(CodeReader, CCppCommentsMixin, SwiftReplaceLabel):
|
|
|
30
30
|
FUNC_KEYWORD = 'def'
|
|
31
31
|
ext = ['swift']
|
|
32
32
|
language_names = ['swift']
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
|
|
34
|
+
# Separated condition categories
|
|
35
|
+
_control_flow_keywords = {'if', 'for', 'while', 'catch', 'guard'}
|
|
36
|
+
_logical_operators = {'&&', '||'}
|
|
37
|
+
_case_keywords = {'case'} # Pattern matching
|
|
38
|
+
_ternary_operators = {'?'}
|
|
35
39
|
|
|
36
40
|
def __init__(self, context):
|
|
37
41
|
super(SwiftReader, self).__init__(context)
|
lizard_languages/tnsdl.py
CHANGED
|
@@ -84,7 +84,12 @@ class SDLReader(CodeReader, CCppCommentsMixin):
|
|
|
84
84
|
if condition:
|
|
85
85
|
return self.context.CONDITION()
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
# Separated condition categories
|
|
88
|
+
_control_flow_keywords = {'WHILE', '#if'}
|
|
89
|
+
_logical_operators = {'AND', 'OR'}
|
|
90
|
+
_case_keywords = set()
|
|
91
|
+
_ternary_operators = set()
|
|
92
|
+
# Note: No need to define _conditions - base class builds it automatically
|
|
88
93
|
|
|
89
94
|
def _is_condition(self, token, last_token):
|
|
90
95
|
if token == ':' and last_token == ')':
|
lizard_languages/tsx.py
CHANGED
|
@@ -48,8 +48,8 @@ class JSXTypeScriptStates(CodeStateMachine):
|
|
|
48
48
|
self.started_function = None
|
|
49
49
|
self.pending_function_name = ''
|
|
50
50
|
self._ts_declare = False
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
# Note: Condition categories come from TypeScriptReader class level,
|
|
52
|
+
# no need to define instance-level duplicates
|
|
53
53
|
self.inside_function = inside_function # Track if we're already inside a function
|
|
54
54
|
|
|
55
55
|
def statemachine_before_return(self):
|
lizard_languages/ttcn.py
CHANGED
|
@@ -10,9 +10,11 @@ class TTCNReader(CLikeReader): # pylint: disable=R0903
|
|
|
10
10
|
ext = ['ttcn', 'ttcnpp']
|
|
11
11
|
language_names = ['ttcn', 'ttcn3']
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
# Separated condition categories
|
|
14
|
+
_control_flow_keywords = {'if', 'for', 'while', 'altstep', 'alt', 'interleave', 'goto'}
|
|
15
|
+
_logical_operators = {'and', 'or', 'xor'}
|
|
16
|
+
_case_keywords = {'case'}
|
|
17
|
+
_ternary_operators = set()
|
|
16
18
|
|
|
17
19
|
def __init__(self, context):
|
|
18
20
|
super(TTCNReader, self).__init__(context)
|
lizard_languages/typescript.py
CHANGED
|
@@ -51,8 +51,12 @@ class TypeScriptReader(CodeReader, CCppCommentsMixin):
|
|
|
51
51
|
|
|
52
52
|
ext = ['ts']
|
|
53
53
|
language_names = ['typescript', 'ts']
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
# Separated condition categories
|
|
56
|
+
_control_flow_keywords = {'if', 'elseif', 'for', 'while', 'catch'}
|
|
57
|
+
_logical_operators = {'&&', '||'}
|
|
58
|
+
_case_keywords = {'case'}
|
|
59
|
+
_ternary_operators = {'?'}
|
|
56
60
|
|
|
57
61
|
def __init__(self, context):
|
|
58
62
|
super().__init__(context)
|
lizard_languages/zig.py
CHANGED
|
@@ -12,7 +12,13 @@ from .golike import GoLikeStates
|
|
|
12
12
|
class ZigReader(CodeReader, CCppCommentsMixin):
|
|
13
13
|
ext = ["zig"]
|
|
14
14
|
language_names = ["zig"]
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
# Separated condition categories
|
|
17
|
+
_control_flow_keywords = {"if", "for", "while", "try", "catch"}
|
|
18
|
+
_logical_operators = {"and", "or", "orelse"} # orelse is Zig's null coalescing
|
|
19
|
+
_case_keywords = set()
|
|
20
|
+
# Note: '=>' is for error union and switch cases in Zig
|
|
21
|
+
_ternary_operators = {"=>"}
|
|
16
22
|
|
|
17
23
|
def __init__(self, context):
|
|
18
24
|
super().__init__(context)
|
lizard-1.19.0.dist-info/RECORD
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
lizard.py,sha256=B0g5lEp5me31bQpjfMKVayF0iUfH0PalVecOKeUX3-A,41365
|
|
2
|
-
lizard_ext/__init__.py,sha256=AkZYVqCgd7XX1hMwLMyh_ABm9geIHmobEch-thYXZz8,932
|
|
3
|
-
lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
|
|
4
|
-
lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
|
|
5
|
-
lizard_ext/csvoutput.py,sha256=43fhmo8kB85qcdujCwySGNuTC4FkKUPLqIApPeljPnA,2663
|
|
6
|
-
lizard_ext/default_ordered_dict.py,sha256=YbVz6nPlQ6DjWc_EOFBz6AJN2XLo9dpnUdeyejQvUDE,831
|
|
7
|
-
lizard_ext/extension_base.py,sha256=rnjUL2mqSGToUVYydju7fa8ZwynLPY8S1F17gIJP55I,346
|
|
8
|
-
lizard_ext/htmloutput.py,sha256=G7MdLD7AwuR0LLCAkEpyHx4N_ssCcFQdXd7Xg5jZWX0,6537
|
|
9
|
-
lizard_ext/keywords.py,sha256=VxsxoATtKV-8egMKd7I8sd2qbZMtEFEpsszk__6rmjQ,893
|
|
10
|
-
lizard_ext/lizardboolcount.py,sha256=abmMA9X3VFRO5mziicUxWKmHldHNC0jBEe7NKAKA5fs,1062
|
|
11
|
-
lizard_ext/lizardcomplextags.py,sha256=flrwYg24P5DoDsBO3gdcK9SxkugX_brhfjuu8zgPnOc,681
|
|
12
|
-
lizard_ext/lizardcpre.py,sha256=bVrMXffGUZlydv_zwIVtp-Ij9XyXMFEcap2R4DmLXPU,1277
|
|
13
|
-
lizard_ext/lizarddependencycount.py,sha256=6Nt2z69cyG8P3pceTUCTU6yh4h73lezL2awRFh7K77Y,2423
|
|
14
|
-
lizard_ext/lizarddumpcomments.py,sha256=-c46U3CP_beDfBuB9QhObpiB6wK7xq1u9jeRjgAyU90,683
|
|
15
|
-
lizard_ext/lizardduplicate.py,sha256=tlU-UTB_d_qjekiO4MSaXPBT1FSX8uTaOcfeXnjBzU8,9762
|
|
16
|
-
lizard_ext/lizardduplicated_param_list.py,sha256=_ApeSbjA-NU_rmICsivyfGEEg0-O94wWgWpeeE8F3Zc,1811
|
|
17
|
-
lizard_ext/lizardexitcount.py,sha256=ziAVFi5RgjxJOwAQy6LzdZZflkvvxTiXOIp0FAL0wKQ,692
|
|
18
|
-
lizard_ext/lizardgotocount.py,sha256=w2GWWwqVh4j7Fum41Wvg172b70JvtCm5BCzZUmmTlcM,514
|
|
19
|
-
lizard_ext/lizardignoreassert.py,sha256=sqLwcnJQ06SYqIk901ib4NQ8ECwjIe_qL4T6z1wLXAk,644
|
|
20
|
-
lizard_ext/lizardio.py,sha256=xQN-AgLGLKJarJkgfaqX_TKyupbb7GTcwPxrL2B1J1w,3357
|
|
21
|
-
lizard_ext/lizardmccabe.py,sha256=RiO8ASmQUah4udOH8SbE2OOMxwShIPByW93TlFxXlQU,1274
|
|
22
|
-
lizard_ext/lizardmodified.py,sha256=4Ld7yy1D2m2biMtx-g0DtjXwLa-9mG2togS2IRDAF3k,705
|
|
23
|
-
lizard_ext/lizardnd.py,sha256=h4MaSsFAY5rK8kOSr61LkWntwJWV1KnhaEOWZgp3-c0,7496
|
|
24
|
-
lizard_ext/lizardnonstrict.py,sha256=pPG22up2uh9rEkdRFtTWdiuOaiBNe0ZUjaZQpSTX5LE,394
|
|
25
|
-
lizard_ext/lizardns.py,sha256=8pztUoRS_UWN24MawwxeHEJgYh49id5PWODUBb6O72U,4184
|
|
26
|
-
lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
|
|
27
|
-
lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
|
|
28
|
-
lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
|
|
29
|
-
lizard_ext/version.py,sha256=gScBFBQFiyhdj77Gb-x1hY6wABdKwZlb_aaNdiiADQ0,181
|
|
30
|
-
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
31
|
-
lizard_languages/__init__.py,sha256=DniggHM3SOFlwkP8Tm4csEcisbf7_i1EvfoAkG9Atg0,1611
|
|
32
|
-
lizard_languages/clike.py,sha256=_oDUbA1hEIPujKw3wyAtC1uFPvjmg4TBgKbHLa-S1wg,13597
|
|
33
|
-
lizard_languages/code_reader.py,sha256=O0nZkI4xEHIWGgFBDwczj8-ay7aKllPnzRsblQtL3Gc,6874
|
|
34
|
-
lizard_languages/csharp.py,sha256=EfFAIOIcJXUUhXTlZApXGSlzG34NZvHM9OSe6m7hpv0,2141
|
|
35
|
-
lizard_languages/erlang.py,sha256=7YJS2cMyXDKEV_kpH8DzBARxFCFcjKuTOPSQ3K52auU,3860
|
|
36
|
-
lizard_languages/fortran.py,sha256=KATDsnfjob5W3579A_VxbwrbTkK7Rx3p0eXdBgjx25I,8973
|
|
37
|
-
lizard_languages/gdscript.py,sha256=KwlGoODilQnFgXvODpq_XlA6fV3hGbN9fd7bsiEUn78,637
|
|
38
|
-
lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
|
|
39
|
-
lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
|
|
40
|
-
lizard_languages/java.py,sha256=HQBTZjUKbUJwgmtLYIzJrWtPpFP3ZdBP_NJK7YOXZC0,6424
|
|
41
|
-
lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
|
|
42
|
-
lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
|
|
43
|
-
lizard_languages/kotlin.py,sha256=v_o2orEzA5gB9vM_0h-E4QXjrc5Yum-0K6W6_laOThc,2844
|
|
44
|
-
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
45
|
-
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
46
|
-
lizard_languages/perl.py,sha256=136w620eECe_t-kmlRUGrsZSxQNo2JQ_PZTSQfCSmHY,11987
|
|
47
|
-
lizard_languages/php.py,sha256=UV40p8WzNC64NQ5qElPKzcFTjVt5kenLMz-eKYlcnMY,9940
|
|
48
|
-
lizard_languages/plsql.py,sha256=17zf0AkDLFW7mA3ngUNrU7z6U6DkmA96CKOcEhYNC5Q,17137
|
|
49
|
-
lizard_languages/python.py,sha256=AsL0SmQ73zhNS1iGi4Z8VtuUE0VjqBzo9W8W0mjqL0E,5790
|
|
50
|
-
lizard_languages/r.py,sha256=IoyMhmFtUmTji6rm6-fqss_j_kvIHu3JjABRh6RNys0,12583
|
|
51
|
-
lizard_languages/ruby.py,sha256=HL1ZckeuUUJU3QSVAOPsG_Zsl0C6X2PX5_VaWqclzkM,2277
|
|
52
|
-
lizard_languages/rubylike.py,sha256=dAGZ2wqW8nqaESMU8HkeR9gwQ-q9fmZqE6AANvVZD1Q,3426
|
|
53
|
-
lizard_languages/rust.py,sha256=WarDHnFZv99Yu3_C5DpZfLS8dVWz6AcOzo2dzLW94rA,817
|
|
54
|
-
lizard_languages/scala.py,sha256=6Jr_TG945VYqB3o5weD7jN7S4beHt4aVj3r-fmKeMAM,1316
|
|
55
|
-
lizard_languages/script_language.py,sha256=SKe45AbO6Z-axbN8KW_g7jf9g7YTXZ6dWzJj4ubDsM8,1172
|
|
56
|
-
lizard_languages/solidity.py,sha256=Z0GD7U5bI5eUikdy7m_iKWeFD5yXRYq4r3zycscOhJQ,553
|
|
57
|
-
lizard_languages/st.py,sha256=7fpOfNAoUjNY8RCHSYLufnOzZTUkKwjVvcyRyM1xP2Y,4160
|
|
58
|
-
lizard_languages/swift.py,sha256=p8S2OAkQOx9YQ02yhoVXFkr7pMqUH1Nb3RVXPHRU_9M,2450
|
|
59
|
-
lizard_languages/tnsdl.py,sha256=pGcalA_lHY362v2wwPS86seYBOOBBjvmU6vd4Yy3A9g,2803
|
|
60
|
-
lizard_languages/tsx.py,sha256=1oOVCcz5yHkmYLYGhSarCMSXfGVasweklAqqapkuNR4,17160
|
|
61
|
-
lizard_languages/ttcn.py,sha256=ygjw_raBmPF-4mgoM8m6CAdyEMpTI-n1kZJK1RL4Vxo,2131
|
|
62
|
-
lizard_languages/typescript.py,sha256=unCDj040dY9fTOw9iIykqjt2j5tZWJ2Bm9fHYjOWY5I,14706
|
|
63
|
-
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
64
|
-
lizard_languages/zig.py,sha256=NX1iyBstBuJFeAGBOAIaRfrmeBREne2HX6Pt4fXZZTQ,586
|
|
65
|
-
lizard-1.19.0.dist-info/LICENSE.txt,sha256=05ZjgQ8Cl1dD9p0BhW-Txzkc5rhCogGJVEuf1GT2Y_M,1303
|
|
66
|
-
lizard-1.19.0.dist-info/METADATA,sha256=DNAVbhrtdVrI5tn2rWZoSBwCfZ5ArhMQ0XNmHvOqbh4,16697
|
|
67
|
-
lizard-1.19.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
|
68
|
-
lizard-1.19.0.dist-info/entry_points.txt,sha256=pPMMwoHAltzGHqR2WeJQItLeeyR7pbX5R2S_POC-xoo,40
|
|
69
|
-
lizard-1.19.0.dist-info/top_level.txt,sha256=5NTrTaOLhHuTzXaGcZPKfuaOgUv7WafNGe0Zl5aycpg,35
|
|
70
|
-
lizard-1.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|