lizard 1.20.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.20.0.dist-info → lizard-1.21.0.dist-info}/METADATA +11 -2
- {lizard-1.20.0.dist-info → lizard-1.21.0.dist-info}/RECORD +12 -12
- lizard.py +22 -7
- lizard_ext/version.py +1 -1
- lizard_languages/clike.py +46 -22
- lizard_languages/code_reader.py +7 -7
- lizard_languages/java.py +4 -0
- lizard_languages/php.py +12 -1
- {lizard-1.20.0.dist-info → lizard-1.21.0.dist-info}/LICENSE.txt +0 -0
- {lizard-1.20.0.dist-info → lizard-1.21.0.dist-info}/WHEEL +0 -0
- {lizard-1.20.0.dist-info → lizard-1.21.0.dist-info}/entry_points.txt +0 -0
- {lizard-1.20.0.dist-info → lizard-1.21.0.dist-info}/top_level.txt +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
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
|
+
Download-URL: https://pypi.python.org/lizard/
|
|
6
7
|
Author: Terry Yin
|
|
7
8
|
Author-email: terry@odd-e.com
|
|
8
9
|
License: MIT
|
|
9
|
-
Download-URL: https://pypi.python.org/lizard/
|
|
10
10
|
Project-URL: Source, https://github.com/terryyin/lizard
|
|
11
11
|
Platform: any
|
|
12
12
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -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
|
::
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
lizard.py,sha256=
|
|
1
|
+
lizard.py,sha256=L5CBQ47VYvqzUwqrQD5LfzfcwCB5qBuUx4J59-zKOUU,42076
|
|
2
2
|
lizard_ext/__init__.py,sha256=UQ2oZ4ej1CCekgiY2Qj8sGx8HheoYyxjcOxvrwF70kc,871
|
|
3
3
|
lizard_ext/auto_open.py,sha256=byD_RbeVhvSUhR2bJMRitvA3zcKEapFwv0-XaDJ6GFo,1096
|
|
4
4
|
lizard_ext/checkstyleoutput.py,sha256=UzDHg837ErEZepXkR8I8YCoz2r1lkmzGctMA7dpyB-M,1245
|
|
@@ -26,25 +26,25 @@ lizard_ext/lizardns.py,sha256=3PYJrXZeFjzFnbotINZBm-MHeS8MQxVNtkQJd_S5fYA,5161
|
|
|
26
26
|
lizard_ext/lizardoutside.py,sha256=FGm2tbBZ17-2OCgmQlD-vobUCfQKb0FAygf86eM3xuM,336
|
|
27
27
|
lizard_ext/lizardstatementcount.py,sha256=xYk6ixSIItSE1BWQXzrWmduFgGhA3VR817SNKLffyVQ,1182
|
|
28
28
|
lizard_ext/lizardwordcount.py,sha256=2QYXD7-AtkkgAbi9VSidunMbSsGQ7MKYb6IT-bS-cok,7575
|
|
29
|
-
lizard_ext/version.py,sha256
|
|
29
|
+
lizard_ext/version.py,sha256=sIUtJyuNVeq7CiJtEKzI_OdOiYNcDqIVBKdYY_OOYRg,181
|
|
30
30
|
lizard_ext/xmloutput.py,sha256=-cbh0he4O_X-wX56gkv9AnSPNN0qvR7FACqlBeezUS4,5609
|
|
31
31
|
lizard_languages/__init__.py,sha256=DniggHM3SOFlwkP8Tm4csEcisbf7_i1EvfoAkG9Atg0,1611
|
|
32
|
-
lizard_languages/clike.py,sha256=
|
|
33
|
-
lizard_languages/code_reader.py,sha256=
|
|
32
|
+
lizard_languages/clike.py,sha256=BMtIrqPuxmRur-KB7LG6imJtUvnOH2-7p4XFeIFLjME,14892
|
|
33
|
+
lizard_languages/code_reader.py,sha256=I2Kc9F0o4RNX4UUsasUMUieX34u4eOaS73ePhQLC9jg,7844
|
|
34
34
|
lizard_languages/csharp.py,sha256=UDuiG20Ydbb4KMD_mgb82shD37URq3ZvqNLpI77VBPE,3015
|
|
35
35
|
lizard_languages/erlang.py,sha256=tbVPDqi90jV2X1-luDYS32H1zK_2KHwssbJ-tCCOvzg,4249
|
|
36
36
|
lizard_languages/fortran.py,sha256=vwKz1VdwyUtWoECURXuR6e8E2kTR71HtpdbOsmDoyDw,8993
|
|
37
37
|
lizard_languages/gdscript.py,sha256=3mHeCarDq_ilDK_OZ2LOvwfWJqLTzK2lquprLDUnX08,737
|
|
38
38
|
lizard_languages/go.py,sha256=sntz0jOEuj4klPipoTFd16UDK1fAUQfwK7YX_cLMZAc,1346
|
|
39
39
|
lizard_languages/golike.py,sha256=vRIfjTVvc0VmJf27lTOLht55ZF1AQ9wn0Fvu-9WabWk,2858
|
|
40
|
-
lizard_languages/java.py,sha256=
|
|
40
|
+
lizard_languages/java.py,sha256=8t8G6TR-1qxLHGu3m_T5Kuny49WvMfwCQe1eYnvwZmk,6610
|
|
41
41
|
lizard_languages/javascript.py,sha256=vniCNMW-ea9Jpv6c8qCcjLVDYjT8VztjXigp5XRWt0E,317
|
|
42
42
|
lizard_languages/js_style_regex_expression.py,sha256=Xgyogch4xElYtCG4EnBKvalHTl3tjRPcIIcIQRRd61I,1970
|
|
43
43
|
lizard_languages/kotlin.py,sha256=1ao-VOHUrrSluxgGjMcMPIDt_-dqVfT1JOz15PJLJH8,3024
|
|
44
44
|
lizard_languages/lua.py,sha256=3nqBcunBzJrhv4Iqaf8xvbyqxZy3aSxJ-IiHimHFlac,1573
|
|
45
45
|
lizard_languages/objc.py,sha256=2a1teLdaXZBtCeFiIZer1j_sVx9LZ1CbF2XfnqlvLmk,2319
|
|
46
46
|
lizard_languages/perl.py,sha256=lxxdC0KJsr3tmNLMATO7vx3O7yWGgICxHxVTfmu7db8,12111
|
|
47
|
-
lizard_languages/php.py,sha256=
|
|
47
|
+
lizard_languages/php.py,sha256=ebHkNyKDOPLyzey5eDhKuu1AMsmGflxqGGmx1ZrlrGA,10542
|
|
48
48
|
lizard_languages/plsql.py,sha256=Wtny6-YW4Jc6G4EhCgoSuw8D86x6JVREprzaUEyKffw,17261
|
|
49
49
|
lizard_languages/python.py,sha256=-sI7ZzJ1erdmqo1MnCcg5cqn1krUhCHlR_phlhnmd_M,5978
|
|
50
50
|
lizard_languages/r.py,sha256=NHNPDGnWXcl8hMi1VL8OM6nw8F9pP6xZX7EgUIocOec,12930
|
|
@@ -62,9 +62,9 @@ lizard_languages/ttcn.py,sha256=tSkPmtifsuUI5aUKBG-uHvwcmZTWcbtMWoUMF3Cw4cI,2199
|
|
|
62
62
|
lizard_languages/typescript.py,sha256=SOef4ja2GE-Idt9fSF7d0gZTW6bwCNl8K2IT67nIMyA,14805
|
|
63
63
|
lizard_languages/vue.py,sha256=KXUBUo2R1zNF8Pffrz_KsQEN44m5XFRMoGXylxKUeT0,1038
|
|
64
64
|
lizard_languages/zig.py,sha256=RiDZyjnCn97jqFmcl5EQl7pbuH0G1oI2Qo72EXvdtDU,813
|
|
65
|
-
lizard-1.
|
|
66
|
-
lizard-1.
|
|
67
|
-
lizard-1.
|
|
68
|
-
lizard-1.
|
|
69
|
-
lizard-1.
|
|
70
|
-
lizard-1.
|
|
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/version.py
CHANGED
lizard_languages/clike.py
CHANGED
|
@@ -322,53 +322,77 @@ class CLikeStates(CodeStateMachine):
|
|
|
322
322
|
def _state_lambda_check(self, token):
|
|
323
323
|
"""Check if this is a lambda expression or a function attribute."""
|
|
324
324
|
if token == ']':
|
|
325
|
-
# This is
|
|
326
|
-
# Skip the lambda and continue parsing normally
|
|
325
|
+
# This is an empty capture list [](params)
|
|
327
326
|
self._state = self._state_lambda_params
|
|
328
327
|
elif token == '[':
|
|
329
328
|
# This is a function attribute [[attribute]]
|
|
330
329
|
self._state = self._state_attribute
|
|
331
330
|
else:
|
|
332
331
|
# This is a lambda with capture list [capture](params)
|
|
333
|
-
# Skip until we find the closing bracket
|
|
334
332
|
self._state = self._state_lambda_capture
|
|
335
333
|
|
|
336
334
|
def _state_lambda_params(self, token):
|
|
337
335
|
"""Handle lambda parameters and body."""
|
|
338
336
|
if token == '(':
|
|
339
|
-
# Start of parameter list
|
|
337
|
+
# Start of parameter list
|
|
338
|
+
self.bracket_stack.append('(')
|
|
340
339
|
self._state = self._state_lambda_param_list
|
|
341
340
|
else:
|
|
342
|
-
# No parameters, check for body
|
|
341
|
+
# No parameters, check for body or go back to global
|
|
343
342
|
self._state = self._state_lambda_body
|
|
343
|
+
self._state(token)
|
|
344
344
|
|
|
345
345
|
def _state_lambda_param_list(self, token):
|
|
346
|
-
"""Handle lambda parameter list."""
|
|
347
|
-
if token == '
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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()
|
|
351
361
|
|
|
352
362
|
def _state_lambda_body(self, token):
|
|
353
|
-
"""Handle lambda body."""
|
|
363
|
+
"""Handle lambda body and qualifiers."""
|
|
354
364
|
if token == '{':
|
|
355
|
-
# Start of lambda body
|
|
365
|
+
# Start of lambda body
|
|
366
|
+
self.bracket_stack.append('{')
|
|
356
367
|
self._state = self._state_lambda_body_skip
|
|
357
|
-
elif token
|
|
358
|
-
# 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
|
|
359
376
|
self._state = self._state_global
|
|
360
|
-
|
|
377
|
+
self._state(token)
|
|
378
|
+
else:
|
|
379
|
+
# Other tokens (type names, etc.) - stay in state
|
|
380
|
+
pass
|
|
361
381
|
|
|
362
382
|
def _state_lambda_body_skip(self, token):
|
|
363
|
-
"""Skip lambda body
|
|
364
|
-
if token == '
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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
|
|
368
392
|
|
|
369
393
|
def _state_lambda_capture(self, token):
|
|
370
394
|
"""Handle lambda capture list."""
|
|
371
395
|
if token == ']':
|
|
372
|
-
# End of capture list,
|
|
373
|
-
self._state = self.
|
|
396
|
+
# End of capture list, now expect parameters
|
|
397
|
+
self._state = self._state_lambda_params
|
|
374
398
|
# Otherwise, continue in capture list
|
lizard_languages/code_reader.py
CHANGED
|
@@ -94,7 +94,7 @@ class CodeReader:
|
|
|
94
94
|
ext = []
|
|
95
95
|
languages = None
|
|
96
96
|
extra_subclasses = set()
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
# Condition categories - separate types that contribute to cyclomatic complexity
|
|
99
99
|
_control_flow_keywords = {'if', 'for', 'while', 'catch'}
|
|
100
100
|
_logical_operators = {'&&', '||'}
|
|
@@ -104,21 +104,21 @@ class CodeReader:
|
|
|
104
104
|
@classmethod
|
|
105
105
|
def _build_conditions(cls):
|
|
106
106
|
"""Build combined conditions set from separated categories.
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
Returns combined set of all condition types for CCN calculation.
|
|
109
109
|
"""
|
|
110
|
-
return (cls._control_flow_keywords |
|
|
111
|
-
cls._logical_operators |
|
|
112
|
-
cls._case_keywords |
|
|
110
|
+
return (cls._control_flow_keywords |
|
|
111
|
+
cls._logical_operators |
|
|
112
|
+
cls._case_keywords |
|
|
113
113
|
cls._ternary_operators)
|
|
114
114
|
|
|
115
115
|
def __init__(self, context):
|
|
116
116
|
self.parallel_states = []
|
|
117
117
|
self.context = context
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
# Build combined conditions set from separated categories
|
|
120
120
|
self.conditions = copy(self.__class__._build_conditions())
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
# Expose individual categories for extensions
|
|
123
123
|
self.control_flow_keywords = copy(self.__class__._control_flow_keywords)
|
|
124
124
|
self.logical_operators = copy(self.__class__._logical_operators)
|
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/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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|