lizard 1.17.30__tar.gz → 1.18.0__tar.gz

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.
Files changed (104) hide show
  1. {lizard-1.17.30 → lizard-1.18.0}/PKG-INFO +6 -1
  2. {lizard-1.17.30 → lizard-1.18.0}/README.rst +3 -0
  3. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/PKG-INFO +6 -1
  4. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/SOURCES.txt +3 -2
  5. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/entry_points.txt +1 -0
  6. {lizard-1.17.30 → lizard-1.18.0}/lizard.py +43 -22
  7. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/__init__.py +14 -0
  8. lizard-1.18.0/lizard_ext/checkstyleoutput.py +33 -0
  9. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/version.py +1 -1
  10. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/__init__.py +4 -2
  11. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/clike.py +60 -1
  12. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/code_reader.py +19 -0
  13. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/go.py +1 -1
  14. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/java.py +6 -3
  15. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/javascript.py +1 -1
  16. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/perl.py +20 -21
  17. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/php.py +14 -14
  18. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/python.py +37 -1
  19. lizard-1.18.0/lizard_languages/r.py +290 -0
  20. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/rust.py +7 -2
  21. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/script_language.py +2 -2
  22. lizard-1.18.0/lizard_languages/st.py +139 -0
  23. lizard-1.18.0/lizard_languages/tsx.py +469 -0
  24. lizard-1.18.0/lizard_languages/typescript.py +375 -0
  25. {lizard-1.17.30 → lizard-1.18.0}/test/testFilesFilter.py +3 -5
  26. {lizard-1.17.30 → lizard-1.18.0}/test/testHelpers.py +12 -0
  27. {lizard-1.17.30 → lizard-1.18.0}/test/testLanguages.py +4 -1
  28. {lizard-1.17.30 → lizard-1.18.0}/test/testOutput.py +23 -4
  29. {lizard-1.17.30 → lizard-1.18.0}/test/testOutputFile.py +31 -0
  30. lizard-1.17.30/lizard_languages/js_style_language_states.py +0 -184
  31. lizard-1.17.30/lizard_languages/jsx.py +0 -337
  32. lizard-1.17.30/lizard_languages/tsx.py +0 -35
  33. lizard-1.17.30/lizard_languages/typescript.py +0 -175
  34. {lizard-1.17.30 → lizard-1.18.0}/LICENSE.txt +0 -0
  35. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/dependency_links.txt +0 -0
  36. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/requires.txt +0 -0
  37. {lizard-1.17.30 → lizard-1.18.0}/lizard.egg-info/top_level.txt +0 -0
  38. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/auto_open.py +0 -0
  39. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/csvoutput.py +0 -0
  40. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/default_ordered_dict.py +0 -0
  41. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/extension_base.py +0 -0
  42. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/htmloutput.py +0 -0
  43. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/keywords.py +0 -0
  44. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardboolcount.py +0 -0
  45. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardcomplextags.py +0 -0
  46. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardcpre.py +0 -0
  47. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizarddependencycount.py +0 -0
  48. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizarddumpcomments.py +0 -0
  49. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardduplicate.py +0 -0
  50. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardduplicated_param_list.py +0 -0
  51. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardexitcount.py +0 -0
  52. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardgotocount.py +0 -0
  53. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardignoreassert.py +0 -0
  54. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardio.py +0 -0
  55. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardmccabe.py +0 -0
  56. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardmodified.py +0 -0
  57. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardnd.py +0 -0
  58. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardnonstrict.py +0 -0
  59. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardns.py +0 -0
  60. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardoutside.py +0 -0
  61. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardstatementcount.py +0 -0
  62. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/lizardwordcount.py +0 -0
  63. {lizard-1.17.30 → lizard-1.18.0}/lizard_ext/xmloutput.py +0 -0
  64. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/csharp.py +0 -0
  65. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/erlang.py +0 -0
  66. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/fortran.py +0 -0
  67. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/gdscript.py +0 -0
  68. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/golike.py +0 -0
  69. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/js_style_regex_expression.py +0 -0
  70. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/kotlin.py +0 -0
  71. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/lua.py +0 -0
  72. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/objc.py +0 -0
  73. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/ruby.py +0 -0
  74. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/rubylike.py +0 -0
  75. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/scala.py +0 -0
  76. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/solidity.py +0 -0
  77. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/swift.py +0 -0
  78. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/tnsdl.py +0 -0
  79. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/ttcn.py +0 -0
  80. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/vue.py +0 -0
  81. {lizard-1.17.30 → lizard-1.18.0}/lizard_languages/zig.py +0 -0
  82. {lizard-1.17.30 → lizard-1.18.0}/setup.cfg +0 -0
  83. {lizard-1.17.30 → lizard-1.18.0}/setup.py +0 -0
  84. {lizard-1.17.30 → lizard-1.18.0}/test/testApplication.py +0 -0
  85. {lizard-1.17.30 → lizard-1.18.0}/test/testAssertionExtension.py +0 -0
  86. {lizard-1.17.30 → lizard-1.18.0}/test/testBasicFunctionInfo.py +0 -0
  87. {lizard-1.17.30 → lizard-1.18.0}/test/testCOutsideComplexity.py +0 -0
  88. {lizard-1.17.30 → lizard-1.18.0}/test/testCPreprocessorExtension.py +0 -0
  89. {lizard-1.17.30 → lizard-1.18.0}/test/testCommentOptions.py +0 -0
  90. {lizard-1.17.30 → lizard-1.18.0}/test/testCyclomaticComplexity.py +0 -0
  91. {lizard-1.17.30 → lizard-1.18.0}/test/testExtension.py +0 -0
  92. {lizard-1.17.30 → lizard-1.18.0}/test/testFunctionDependencyCount.py +0 -0
  93. {lizard-1.17.30 → lizard-1.18.0}/test/testFunctionExitCount.py +0 -0
  94. {lizard-1.17.30 → lizard-1.18.0}/test/testFunctionGotoCount.py +0 -0
  95. {lizard-1.17.30 → lizard-1.18.0}/test/testFunctionStatementCount.py +0 -0
  96. {lizard-1.17.30 → lizard-1.18.0}/test/testMcCabe.py +0 -0
  97. {lizard-1.17.30 → lizard-1.18.0}/test/testNestedStructures.py +0 -0
  98. {lizard-1.17.30 → lizard-1.18.0}/test/testNestingDepth.py +0 -0
  99. {lizard-1.17.30 → lizard-1.18.0}/test/testOutputCSV.py +0 -0
  100. {lizard-1.17.30 → lizard-1.18.0}/test/testOutputHTML.py +0 -0
  101. {lizard-1.17.30 → lizard-1.18.0}/test/testTokenizer.py +0 -0
  102. {lizard-1.17.30 → lizard-1.18.0}/test/test_analyzer.py +0 -0
  103. {lizard-1.17.30 → lizard-1.18.0}/test/test_auto_open.py +0 -0
  104. {lizard-1.17.30 → lizard-1.18.0}/test/test_options.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lizard
3
- Version: 1.17.30
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/
@@ -71,6 +71,8 @@ A list of supported languages:
71
71
  - Erlang
72
72
  - Zig
73
73
  - Perl
74
+ - Structured Text (St)
75
+ - R
74
76
 
75
77
  By default lizard will search for any source code that it knows and mix
76
78
  all the results together. This might not be what you want. You can use
@@ -194,6 +196,7 @@ Options
194
196
  generate report in Jenkins server
195
197
  --csv Generate CSV output as a transform of the default output
196
198
  -H, --html Output HTML report
199
+ --checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
197
200
  -m, --modified Calculate modified cyclomatic complexity number , which count a
198
201
  switch/case with multiple cases as one CCN.
199
202
  -E EXTENSIONS, --extension EXTENSIONS
@@ -410,3 +413,5 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
410
413
  - `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
411
414
  - `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
412
415
 
416
+
417
+
@@ -40,6 +40,8 @@ A list of supported languages:
40
40
  - Erlang
41
41
  - Zig
42
42
  - Perl
43
+ - Structured Text (St)
44
+ - R
43
45
 
44
46
  By default lizard will search for any source code that it knows and mix
45
47
  all the results together. This might not be what you want. You can use
@@ -163,6 +165,7 @@ Options
163
165
  generate report in Jenkins server
164
166
  --csv Generate CSV output as a transform of the default output
165
167
  -H, --html Output HTML report
168
+ --checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
166
169
  -m, --modified Calculate modified cyclomatic complexity number , which count a
167
170
  switch/case with multiple cases as one CCN.
168
171
  -E EXTENSIONS, --extension EXTENSIONS
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lizard
3
- Version: 1.17.30
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/
@@ -71,6 +71,8 @@ A list of supported languages:
71
71
  - Erlang
72
72
  - Zig
73
73
  - Perl
74
+ - Structured Text (St)
75
+ - R
74
76
 
75
77
  By default lizard will search for any source code that it knows and mix
76
78
  all the results together. This might not be what you want. You can use
@@ -194,6 +196,7 @@ Options
194
196
  generate report in Jenkins server
195
197
  --csv Generate CSV output as a transform of the default output
196
198
  -H, --html Output HTML report
199
+ --checkstyle Generate Checkstyle XML output for integration with Jenkins and other tools
197
200
  -m, --modified Calculate modified cyclomatic complexity number , which count a
198
201
  switch/case with multiple cases as one CCN.
199
202
  -E EXTENSIONS, --extension EXTENSIONS
@@ -410,3 +413,5 @@ Lizard is also used as a plugin for fastlane to help check code complexity and s
410
413
  - `European research project FASTEN (Fine-grained Analysis of SofTware Ecosystems as Networks, <http://fasten-project.eu/)>`_
411
414
  - `for a quality analyzer <https://github.com/fasten-project/quality-analyzer>`_
412
415
 
416
+
417
+
@@ -11,6 +11,7 @@ lizard.egg-info/requires.txt
11
11
  lizard.egg-info/top_level.txt
12
12
  lizard_ext/__init__.py
13
13
  lizard_ext/auto_open.py
14
+ lizard_ext/checkstyleoutput.py
14
15
  lizard_ext/csvoutput.py
15
16
  lizard_ext/default_ordered_dict.py
16
17
  lizard_ext/extension_base.py
@@ -48,21 +49,21 @@ lizard_languages/go.py
48
49
  lizard_languages/golike.py
49
50
  lizard_languages/java.py
50
51
  lizard_languages/javascript.py
51
- lizard_languages/js_style_language_states.py
52
52
  lizard_languages/js_style_regex_expression.py
53
- lizard_languages/jsx.py
54
53
  lizard_languages/kotlin.py
55
54
  lizard_languages/lua.py
56
55
  lizard_languages/objc.py
57
56
  lizard_languages/perl.py
58
57
  lizard_languages/php.py
59
58
  lizard_languages/python.py
59
+ lizard_languages/r.py
60
60
  lizard_languages/ruby.py
61
61
  lizard_languages/rubylike.py
62
62
  lizard_languages/rust.py
63
63
  lizard_languages/scala.py
64
64
  lizard_languages/script_language.py
65
65
  lizard_languages/solidity.py
66
+ lizard_languages/st.py
66
67
  lizard_languages/swift.py
67
68
  lizard_languages/tnsdl.py
68
69
  lizard_languages/tsx.py
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  lizard = lizard:main
3
+
@@ -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 open(whitelist, mode='r').read()
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
- with open(gitignore_path, 'r') as gitignore_file:
921
- # Read lines and strip whitespace and empty lines
922
- patterns = [line.strip() for line in gitignore_file.readlines()]
923
- patterns = [p for p in patterns if p and not p.startswith('#')]
924
- gitignore_spec = pathspec.PathSpec.from_lines('gitwildmatch', patterns)
925
- base_path = path
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
- if not opt.printer:
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
- mapping = {
1013
- '.csv': print_csv,
1014
- '.htm': html_output,
1015
- '.html': html_output,
1016
- '.xml': print_xml
1017
- }
1018
- _, ext = os.path.splitext(path)
1019
- printer = mapping.get(ext)
1020
- return printer
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 = printer(result, options, schema, AllResult)
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:
@@ -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=" ")
@@ -3,4 +3,4 @@
3
3
  #
4
4
  # pylint: disable=missing-docstring,invalid-name
5
5
 
6
- version = "1.17.30"
6
+ version = "1.18.0"
@@ -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
 
@@ -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", "mm", "cxx", "h", "hpp"]
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
@@ -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
@@ -31,7 +31,7 @@ class GoReader(CodeReader, CCppCommentsMixin):
31
31
  state(token)
32
32
  yield token
33
33
  continue
34
-
34
+
35
35
  # For non-backtick tokens, process normally
36
36
  for state in self.parallel_states:
37
37
  state(token)
@@ -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)
@@ -2,7 +2,7 @@
2
2
  Language parser for JavaScript
3
3
  '''
4
4
 
5
- from .typescript import TypeScriptReader
5
+ from .typescript import TypeScriptReader
6
6
 
7
7
 
8
8
  class JavaScriptReader(TypeScriptReader):
@@ -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', '&&', '||', '?', ':', 'when', 'given', 'default', 'do'])
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 cases
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', '&&', '||', '?', ':', 'when', 'given', 'default', 'do'])
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 == '$' or token == 'my' or token == 'our' or token == 'local':
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 names
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 = f"<anonymous>"
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 names
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 = f"<anonymous>"
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 anonymous
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
- elif token == '{':
246
+ if token == '{':
245
247
  # Anonymous sub
246
248
  self.brace_count += 1
247
249
  self.anonymous_count += 1
248
- anon_name = f"<anonymous>"
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 = f"<anonymous>"
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)