shirobai 2026.0709.0000

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 (221) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +420 -0
  3. data/Cargo.toml +13 -0
  4. data/LICENSE.txt +21 -0
  5. data/crates/shirobai-core/Cargo.toml +20 -0
  6. data/crates/shirobai-core/src/bin/analysis_profile.rs +447 -0
  7. data/crates/shirobai-core/src/bin/bundle_profile.rs +656 -0
  8. data/crates/shirobai-core/src/bin/hotspot_profile.rs +596 -0
  9. data/crates/shirobai-core/src/bin/walk_profile.rs +191 -0
  10. data/crates/shirobai-core/src/lib.rs +1 -0
  11. data/crates/shirobai-core/src/rules/abc_size.rs +1475 -0
  12. data/crates/shirobai-core/src/rules/access_modifier_indentation.rs +450 -0
  13. data/crates/shirobai-core/src/rules/ambiguous_block_association.rs +528 -0
  14. data/crates/shirobai-core/src/rules/argument_alignment.rs +300 -0
  15. data/crates/shirobai-core/src/rules/arguments_forwarding.rs +1125 -0
  16. data/crates/shirobai-core/src/rules/array_alignment.rs +398 -0
  17. data/crates/shirobai-core/src/rules/assignment_indentation.rs +502 -0
  18. data/crates/shirobai-core/src/rules/block_alignment.rs +869 -0
  19. data/crates/shirobai-core/src/rules/block_delimiters.rs +1876 -0
  20. data/crates/shirobai-core/src/rules/block_length.rs +631 -0
  21. data/crates/shirobai-core/src/rules/block_nesting.rs +390 -0
  22. data/crates/shirobai-core/src/rules/bundle.rs +4433 -0
  23. data/crates/shirobai-core/src/rules/class_length.rs +531 -0
  24. data/crates/shirobai-core/src/rules/closing_parenthesis_indentation.rs +425 -0
  25. data/crates/shirobai-core/src/rules/code_length.rs +801 -0
  26. data/crates/shirobai-core/src/rules/colon_method_call.rs +285 -0
  27. data/crates/shirobai-core/src/rules/complexity.rs +585 -0
  28. data/crates/shirobai-core/src/rules/debugger.rs +438 -0
  29. data/crates/shirobai-core/src/rules/def_end_alignment.rs +485 -0
  30. data/crates/shirobai-core/src/rules/dispatch.rs +276 -0
  31. data/crates/shirobai-core/src/rules/dot_position.rs +276 -0
  32. data/crates/shirobai-core/src/rules/duplicate_magic_comment.rs +1001 -0
  33. data/crates/shirobai-core/src/rules/duplicate_methods.rs +1725 -0
  34. data/crates/shirobai-core/src/rules/else_alignment.rs +837 -0
  35. data/crates/shirobai-core/src/rules/empty_comment.rs +452 -0
  36. data/crates/shirobai-core/src/rules/empty_line_after_guard_clause.rs +1210 -0
  37. data/crates/shirobai-core/src/rules/empty_line_after_magic_comment.rs +166 -0
  38. data/crates/shirobai-core/src/rules/empty_line_between_defs.rs +731 -0
  39. data/crates/shirobai-core/src/rules/empty_lines.rs +432 -0
  40. data/crates/shirobai-core/src/rules/empty_lines_around_arguments.rs +298 -0
  41. data/crates/shirobai-core/src/rules/empty_lines_around_body.rs +1320 -0
  42. data/crates/shirobai-core/src/rules/end_alignment.rs +798 -0
  43. data/crates/shirobai-core/src/rules/file_null.rs +320 -0
  44. data/crates/shirobai-core/src/rules/first_argument_indentation.rs +643 -0
  45. data/crates/shirobai-core/src/rules/first_array_element_indentation.rs +644 -0
  46. data/crates/shirobai-core/src/rules/first_hash_element_indentation.rs +699 -0
  47. data/crates/shirobai-core/src/rules/frozen_string_literal_comment.rs +414 -0
  48. data/crates/shirobai-core/src/rules/hash_alignment.rs +1030 -0
  49. data/crates/shirobai-core/src/rules/hash_each_methods.rs +744 -0
  50. data/crates/shirobai-core/src/rules/hash_syntax.rs +1304 -0
  51. data/crates/shirobai-core/src/rules/hash_transform_keys.rs +909 -0
  52. data/crates/shirobai-core/src/rules/if_unless_modifier.rs +1494 -0
  53. data/crates/shirobai-core/src/rules/indentation_consistency.rs +585 -0
  54. data/crates/shirobai-core/src/rules/indentation_width.rs +1231 -0
  55. data/crates/shirobai-core/src/rules/leading_empty_lines.rs +365 -0
  56. data/crates/shirobai-core/src/rules/line_end_concatenation.rs +330 -0
  57. data/crates/shirobai-core/src/rules/line_index.rs +227 -0
  58. data/crates/shirobai-core/src/rules/line_length.rs +434 -0
  59. data/crates/shirobai-core/src/rules/line_length_breakable.rs +1454 -0
  60. data/crates/shirobai-core/src/rules/method_length.rs +438 -0
  61. data/crates/shirobai-core/src/rules/method_name.rs +565 -0
  62. data/crates/shirobai-core/src/rules/mod.rs +104 -0
  63. data/crates/shirobai-core/src/rules/module_length.rs +283 -0
  64. data/crates/shirobai-core/src/rules/multiline_method_call_brace_layout.rs +594 -0
  65. data/crates/shirobai-core/src/rules/multiline_method_call_indentation.rs +1444 -0
  66. data/crates/shirobai-core/src/rules/multiline_operation_indentation.rs +720 -0
  67. data/crates/shirobai-core/src/rules/nested_parenthesized_calls.rs +486 -0
  68. data/crates/shirobai-core/src/rules/opaque_mask.rs +164 -0
  69. data/crates/shirobai-core/src/rules/parentheses_as_grouped_expression.rs +410 -0
  70. data/crates/shirobai-core/src/rules/parse_cache.rs +116 -0
  71. data/crates/shirobai-core/src/rules/percent_literal_delimiters.rs +835 -0
  72. data/crates/shirobai-core/src/rules/perf_detect.rs +524 -0
  73. data/crates/shirobai-core/src/rules/perf_end_with.rs +368 -0
  74. data/crates/shirobai-core/src/rules/perf_start_with.rs +333 -0
  75. data/crates/shirobai-core/src/rules/perf_string_include.rs +399 -0
  76. data/crates/shirobai-core/src/rules/perf_times_map.rs +570 -0
  77. data/crates/shirobai-core/src/rules/predicate_prefix.rs +397 -0
  78. data/crates/shirobai-core/src/rules/punctuation_spacing.rs +725 -0
  79. data/crates/shirobai-core/src/rules/rails_app.rs +765 -0
  80. data/crates/shirobai-core/src/rules/rails_config.rs +80 -0
  81. data/crates/shirobai-core/src/rules/rails_dynamic_find_by.rs +447 -0
  82. data/crates/shirobai-core/src/rules/rails_pluck.rs +470 -0
  83. data/crates/shirobai-core/src/rules/rails_unknown_env.rs +362 -0
  84. data/crates/shirobai-core/src/rules/redundant_freeze.rs +550 -0
  85. data/crates/shirobai-core/src/rules/redundant_self.rs +691 -0
  86. data/crates/shirobai-core/src/rules/redundant_self_assignment.rs +530 -0
  87. data/crates/shirobai-core/src/rules/require_parentheses.rs +237 -0
  88. data/crates/shirobai-core/src/rules/rspec_dispatcher.rs +2010 -0
  89. data/crates/shirobai-core/src/rules/rspec_empty_line.rs +791 -0
  90. data/crates/shirobai-core/src/rules/rspec_language.rs +233 -0
  91. data/crates/shirobai-core/src/rules/safe_navigation_chain.rs +376 -0
  92. data/crates/shirobai-core/src/rules/self_assignment.rs +976 -0
  93. data/crates/shirobai-core/src/rules/semicolon.rs +492 -0
  94. data/crates/shirobai-core/src/rules/space_around_keyword.rs +881 -0
  95. data/crates/shirobai-core/src/rules/space_around_method_call_operator.rs +398 -0
  96. data/crates/shirobai-core/src/rules/space_before_block_braces.rs +380 -0
  97. data/crates/shirobai-core/src/rules/space_before_first_arg.rs +627 -0
  98. data/crates/shirobai-core/src/rules/space_inside_array_literal_brackets.rs +977 -0
  99. data/crates/shirobai-core/src/rules/space_inside_block_braces.rs +669 -0
  100. data/crates/shirobai-core/src/rules/space_inside_hash_literal_braces.rs +498 -0
  101. data/crates/shirobai-core/src/rules/space_inside_parens.rs +538 -0
  102. data/crates/shirobai-core/src/rules/space_inside_reference_brackets.rs +513 -0
  103. data/crates/shirobai-core/src/rules/space_scan.rs +136 -0
  104. data/crates/shirobai-core/src/rules/stabby_lambda_parentheses.rs +326 -0
  105. data/crates/shirobai-core/src/rules/string_literals.rs +611 -0
  106. data/crates/shirobai-core/src/rules/string_literals_in_interpolation.rs +244 -0
  107. data/crates/shirobai-core/src/rules/trailing_comma.rs +428 -0
  108. data/crates/shirobai-core/src/rules/trailing_comma_in_arguments.rs +502 -0
  109. data/crates/shirobai-core/src/rules/trailing_comma_in_array_literal.rs +210 -0
  110. data/crates/shirobai-core/src/rules/trailing_comma_in_hash_literal.rs +174 -0
  111. data/crates/shirobai-core/src/rules/trailing_empty_lines.rs +307 -0
  112. data/crates/shirobai-core/src/rules/unreachable_code.rs +719 -0
  113. data/crates/shirobai-core/src/rules/useless_access_modifier.rs +1087 -0
  114. data/crates/shirobai-core/src/rules/variable_number.rs +318 -0
  115. data/crates/shirobai-core/src/rules/void.rs +1328 -0
  116. data/ext/shirobai/Cargo.toml +16 -0
  117. data/ext/shirobai/src/lib.rs +4501 -0
  118. data/lib/shirobai/cop/base.rb +45 -0
  119. data/lib/shirobai/cop/layout/access_modifier_indentation.rb +117 -0
  120. data/lib/shirobai/cop/layout/argument_alignment.rb +86 -0
  121. data/lib/shirobai/cop/layout/array_alignment.rb +109 -0
  122. data/lib/shirobai/cop/layout/assignment_indentation.rb +73 -0
  123. data/lib/shirobai/cop/layout/block_alignment.rb +79 -0
  124. data/lib/shirobai/cop/layout/closing_parenthesis_indentation.rb +51 -0
  125. data/lib/shirobai/cop/layout/def_end_alignment.rb +114 -0
  126. data/lib/shirobai/cop/layout/dot_position.rb +63 -0
  127. data/lib/shirobai/cop/layout/else_alignment.rb +86 -0
  128. data/lib/shirobai/cop/layout/empty_comment.rb +75 -0
  129. data/lib/shirobai/cop/layout/empty_line_after_guard_clause.rb +116 -0
  130. data/lib/shirobai/cop/layout/empty_line_after_magic_comment.rb +82 -0
  131. data/lib/shirobai/cop/layout/empty_line_between_defs.rb +78 -0
  132. data/lib/shirobai/cop/layout/empty_lines.rb +70 -0
  133. data/lib/shirobai/cop/layout/empty_lines_around_arguments.rb +55 -0
  134. data/lib/shirobai/cop/layout/empty_lines_around_begin_body.rb +22 -0
  135. data/lib/shirobai/cop/layout/empty_lines_around_block_body.rb +28 -0
  136. data/lib/shirobai/cop/layout/empty_lines_around_body_shared.rb +58 -0
  137. data/lib/shirobai/cop/layout/empty_lines_around_class_body.rb +27 -0
  138. data/lib/shirobai/cop/layout/empty_lines_around_exception_handling_keywords.rb +24 -0
  139. data/lib/shirobai/cop/layout/empty_lines_around_method_body.rb +22 -0
  140. data/lib/shirobai/cop/layout/empty_lines_around_module_body.rb +26 -0
  141. data/lib/shirobai/cop/layout/end_alignment.rb +101 -0
  142. data/lib/shirobai/cop/layout/first_argument_indentation.rb +76 -0
  143. data/lib/shirobai/cop/layout/first_array_element_indentation.rb +89 -0
  144. data/lib/shirobai/cop/layout/first_hash_element_indentation.rb +104 -0
  145. data/lib/shirobai/cop/layout/hash_alignment.rb +135 -0
  146. data/lib/shirobai/cop/layout/indentation_consistency.rb +95 -0
  147. data/lib/shirobai/cop/layout/indentation_width.rb +141 -0
  148. data/lib/shirobai/cop/layout/leading_empty_lines.rb +72 -0
  149. data/lib/shirobai/cop/layout/line_length.rb +227 -0
  150. data/lib/shirobai/cop/layout/multiline_method_call_brace_layout.rb +126 -0
  151. data/lib/shirobai/cop/layout/multiline_method_call_indentation.rb +87 -0
  152. data/lib/shirobai/cop/layout/multiline_operation_indentation.rb +61 -0
  153. data/lib/shirobai/cop/layout/space_after_colon.rb +58 -0
  154. data/lib/shirobai/cop/layout/space_after_comma.rb +64 -0
  155. data/lib/shirobai/cop/layout/space_after_semicolon.rb +57 -0
  156. data/lib/shirobai/cop/layout/space_around_keyword.rb +69 -0
  157. data/lib/shirobai/cop/layout/space_around_method_call_operator.rb +55 -0
  158. data/lib/shirobai/cop/layout/space_before_block_braces.rb +150 -0
  159. data/lib/shirobai/cop/layout/space_before_comma.rb +63 -0
  160. data/lib/shirobai/cop/layout/space_before_comment.rb +56 -0
  161. data/lib/shirobai/cop/layout/space_before_first_arg.rb +65 -0
  162. data/lib/shirobai/cop/layout/space_before_semicolon.rb +57 -0
  163. data/lib/shirobai/cop/layout/space_inside_array_literal_brackets.rb +109 -0
  164. data/lib/shirobai/cop/layout/space_inside_block_braces.rb +116 -0
  165. data/lib/shirobai/cop/layout/space_inside_hash_literal_braces.rb +93 -0
  166. data/lib/shirobai/cop/layout/space_inside_parens.rb +67 -0
  167. data/lib/shirobai/cop/layout/space_inside_reference_brackets.rb +104 -0
  168. data/lib/shirobai/cop/layout/trailing_empty_lines.rb +104 -0
  169. data/lib/shirobai/cop/lint/ambiguous_block_association.rb +117 -0
  170. data/lib/shirobai/cop/lint/debugger.rb +44 -0
  171. data/lib/shirobai/cop/lint/duplicate_magic_comment.rb +67 -0
  172. data/lib/shirobai/cop/lint/duplicate_methods.rb +109 -0
  173. data/lib/shirobai/cop/lint/parentheses_as_grouped_expression.rb +54 -0
  174. data/lib/shirobai/cop/lint/require_parentheses.rb +35 -0
  175. data/lib/shirobai/cop/lint/safe_navigation_chain.rb +52 -0
  176. data/lib/shirobai/cop/lint/self_assignment.rb +105 -0
  177. data/lib/shirobai/cop/lint/unreachable_code.rb +48 -0
  178. data/lib/shirobai/cop/lint/useless_access_modifier.rb +61 -0
  179. data/lib/shirobai/cop/lint/void.rb +60 -0
  180. data/lib/shirobai/cop/metrics/abc_size.rb +61 -0
  181. data/lib/shirobai/cop/metrics/block_length.rb +139 -0
  182. data/lib/shirobai/cop/metrics/block_nesting.rb +62 -0
  183. data/lib/shirobai/cop/metrics/class_length.rb +88 -0
  184. data/lib/shirobai/cop/metrics/complexity_base.rb +35 -0
  185. data/lib/shirobai/cop/metrics/complexity_shared.rb +45 -0
  186. data/lib/shirobai/cop/metrics/cyclomatic_complexity.rb +30 -0
  187. data/lib/shirobai/cop/metrics/method_length.rb +70 -0
  188. data/lib/shirobai/cop/metrics/module_length.rb +83 -0
  189. data/lib/shirobai/cop/metrics/perceived_complexity.rb +30 -0
  190. data/lib/shirobai/cop/naming/method_name.rb +100 -0
  191. data/lib/shirobai/cop/naming/predicate_prefix.rb +107 -0
  192. data/lib/shirobai/cop/naming/variable_number.rb +72 -0
  193. data/lib/shirobai/cop/style/arguments_forwarding.rb +108 -0
  194. data/lib/shirobai/cop/style/block_delimiters.rb +164 -0
  195. data/lib/shirobai/cop/style/colon_method_call.rb +60 -0
  196. data/lib/shirobai/cop/style/file_null.rb +55 -0
  197. data/lib/shirobai/cop/style/frozen_string_literal_comment.rb +118 -0
  198. data/lib/shirobai/cop/style/hash_each_methods.rb +50 -0
  199. data/lib/shirobai/cop/style/hash_syntax.rb +147 -0
  200. data/lib/shirobai/cop/style/hash_transform_keys.rb +46 -0
  201. data/lib/shirobai/cop/style/if_unless_modifier.rb +207 -0
  202. data/lib/shirobai/cop/style/line_end_concatenation.rb +42 -0
  203. data/lib/shirobai/cop/style/nested_parenthesized_calls.rb +88 -0
  204. data/lib/shirobai/cop/style/percent_literal_delimiters.rb +113 -0
  205. data/lib/shirobai/cop/style/redundant_freeze.rb +75 -0
  206. data/lib/shirobai/cop/style/redundant_self.rb +50 -0
  207. data/lib/shirobai/cop/style/redundant_self_assignment.rb +62 -0
  208. data/lib/shirobai/cop/style/semicolon.rb +190 -0
  209. data/lib/shirobai/cop/style/stabby_lambda_parentheses.rb +75 -0
  210. data/lib/shirobai/cop/style/string_literals.rb +135 -0
  211. data/lib/shirobai/cop/style/string_literals_in_interpolation.rb +130 -0
  212. data/lib/shirobai/cop/style/trailing_comma_in_arguments.rb +127 -0
  213. data/lib/shirobai/cop/style/trailing_comma_in_array_literal.rb +127 -0
  214. data/lib/shirobai/cop/style/trailing_comma_in_hash_literal.rb +127 -0
  215. data/lib/shirobai/dispatch.rb +481 -0
  216. data/lib/shirobai/inject.rb +123 -0
  217. data/lib/shirobai/node_locator.rb +124 -0
  218. data/lib/shirobai/source_offsets.rb +43 -0
  219. data/lib/shirobai/version.rb +5 -0
  220. data/lib/shirobai.rb +5 -0
  221. metadata +271 -0
@@ -0,0 +1,447 @@
1
+ //! Standalone CPU-time profiler for the pure-Rust analysis cost.
2
+ //!
3
+ //! Companion to `walk_profile.rs`. Where `walk_profile` measures a *generic*
4
+ //! traversal, this binary calls the **real** `shirobai_core::rules::*::check_*`
5
+ //! functions — the same pure-Rust analysis bodies the magnus wrappers in
6
+ //! `ext/shirobai/src/lib.rs` invoke — directly, with **no magnus boundary**.
7
+ //! Comparing the sum of these against the e2e Rust cost isolates the magnus
8
+ //! marshaling / value-copy overhead.
9
+ //!
10
+ //! Each `check_*` re-parses its source via `parse_cache::with_parsed` (cache
11
+ //! miss per distinct source), so a per-cop timing includes parse. The
12
+ //! `parse` mode measures parse-only (P); per-cop pure analysis = timing − P.
13
+ //!
14
+ //! Config values are RuboCop defaults (see `vendor/rubocop/config/default.yml`
15
+ //! and the Ruby cop wrappers in `lib/shirobai/cop/**`). The goal is cost
16
+ //! measurement, not offense exactness; values are chosen to exercise the real
17
+ //! analysis path, not to short-circuit it.
18
+ //!
19
+ //! Usage: analysis_profile <mode> <corpus_dir> [iterations]
20
+ //! mode = parse | <cop-name> | all
21
+ //! Prints median CPU time (s) to stdout; min/max/sink to stderr.
22
+ //! CPU time is `CLOCK_PROCESS_CPUTIME_ID`.
23
+
24
+ use std::path::PathBuf;
25
+
26
+ use ruby_prism::parse;
27
+ use shirobai_core::rules;
28
+
29
+ /// All cop modes this profiler understands, in a stable order.
30
+ const COPS: &[&str] = &[
31
+ "debugger",
32
+ "block_length",
33
+ "block_nesting",
34
+ "complexity",
35
+ "variable_number",
36
+ "method_name",
37
+ "safe_navigation_chain",
38
+ "dot_position",
39
+ "line_length",
40
+ "line_length_breakables",
41
+ "argument_alignment",
42
+ "first_argument_indentation",
43
+ "redundant_self",
44
+ "indentation_width",
45
+ "multiline_bundle",
46
+ ];
47
+
48
+ // ----- config values (RuboCop defaults) -----
49
+
50
+ fn debugger_methods() -> Vec<String> {
51
+ // Flattened Lint/Debugger DebuggerMethods (all groups).
52
+ [
53
+ "binding.irb",
54
+ "Kernel.binding.irb",
55
+ "byebug",
56
+ "remote_byebug",
57
+ "Kernel.byebug",
58
+ "Kernel.remote_byebug",
59
+ "page.save_and_open_page",
60
+ "page.save_and_open_screenshot",
61
+ "page.save_page",
62
+ "page.save_screenshot",
63
+ "save_and_open_page",
64
+ "save_and_open_screenshot",
65
+ "save_page",
66
+ "save_screenshot",
67
+ "binding.b",
68
+ "binding.break",
69
+ "Kernel.binding.b",
70
+ "Kernel.binding.break",
71
+ "binding.pry",
72
+ "binding.remote_pry",
73
+ "binding.pry_remote",
74
+ "Kernel.binding.pry",
75
+ "Kernel.binding.remote_pry",
76
+ "Kernel.binding.pry_remote",
77
+ "Pry.rescue",
78
+ "pry",
79
+ "debugger",
80
+ "Kernel.debugger",
81
+ "jard",
82
+ "binding.console",
83
+ ]
84
+ .iter()
85
+ .map(|s| s.to_string())
86
+ .collect()
87
+ }
88
+
89
+ fn debugger_requires() -> Vec<String> {
90
+ ["debug/open", "debug/start"]
91
+ .iter()
92
+ .map(|s| s.to_string())
93
+ .collect()
94
+ }
95
+
96
+ fn variable_number_allowed() -> Vec<String> {
97
+ [
98
+ "TLS1_1",
99
+ "TLS1_2",
100
+ "capture3",
101
+ "iso8601",
102
+ "rfc1123_date",
103
+ "rfc822",
104
+ "rfc2822",
105
+ "rfc3339",
106
+ "x86_64",
107
+ ]
108
+ .iter()
109
+ .map(|s| s.to_string())
110
+ .collect()
111
+ }
112
+
113
+ fn safe_nav_nil_methods() -> Vec<String> {
114
+ ["present?", "blank?", "presence", "try", "try!", "in?"]
115
+ .iter()
116
+ .map(|s| s.to_string())
117
+ .collect()
118
+ }
119
+
120
+ fn kernel_methods() -> Vec<String> {
121
+ // Snapshot of `Kernel.methods(false)` (Ruby 4.0). RedundantSelf supplies
122
+ // this allow-list; a representative list keeps the analysis path live.
123
+ [
124
+ "Array",
125
+ "Complex",
126
+ "Float",
127
+ "Hash",
128
+ "Integer",
129
+ "Pathname",
130
+ "Rational",
131
+ "String",
132
+ "__callee__",
133
+ "__dir__",
134
+ "__method__",
135
+ "`",
136
+ "abort",
137
+ "at_exit",
138
+ "autoload",
139
+ "autoload?",
140
+ "binding",
141
+ "block_given?",
142
+ "caller",
143
+ "caller_locations",
144
+ "catch",
145
+ "eval",
146
+ "exec",
147
+ "exit",
148
+ "exit!",
149
+ "fail",
150
+ "fork",
151
+ "format",
152
+ "gets",
153
+ "global_variables",
154
+ "iterator?",
155
+ "lambda",
156
+ "load",
157
+ "local_variables",
158
+ "loop",
159
+ "open",
160
+ "p",
161
+ "print",
162
+ "printf",
163
+ "proc",
164
+ "putc",
165
+ "puts",
166
+ "raise",
167
+ "rand",
168
+ "readline",
169
+ "readlines",
170
+ "require",
171
+ "require_relative",
172
+ "select",
173
+ "set_trace_func",
174
+ "sleep",
175
+ "spawn",
176
+ "sprintf",
177
+ "srand",
178
+ "syscall",
179
+ "system",
180
+ "test",
181
+ "throw",
182
+ "trace_var",
183
+ "trap",
184
+ "untrace_var",
185
+ "warn",
186
+ ]
187
+ .iter()
188
+ .map(|s| s.to_string())
189
+ .collect()
190
+ }
191
+
192
+ /// Run one cop's `check_*` over every source, returning an accumulated sink so
193
+ /// the optimizer cannot drop the call.
194
+ fn run_cop(cop: &str, sources: &[Vec<u8>]) -> usize {
195
+ let mut sink = 0usize;
196
+ match cop {
197
+ "debugger" => {
198
+ let m = debugger_methods();
199
+ let r = debugger_requires();
200
+ for s in sources {
201
+ sink = sink.wrapping_add(rules::debugger::check_debugger(s, &m, &r).len());
202
+ }
203
+ }
204
+ "block_length" => {
205
+ let cao: Vec<String> = Vec::new();
206
+ for s in sources {
207
+ sink = sink.wrapping_add(
208
+ rules::block_length::check_block_length(s, 25, false, &cao).len(),
209
+ );
210
+ }
211
+ }
212
+ "block_nesting" => {
213
+ for s in sources {
214
+ let (off, deep) = rules::block_nesting::check_block_nesting(s, 3, false, false);
215
+ sink = sink.wrapping_add(off.len()).wrapping_add(deep);
216
+ }
217
+ }
218
+ "complexity" => {
219
+ for s in sources {
220
+ sink = sink.wrapping_add(rules::complexity::check_complexity(s).len());
221
+ }
222
+ }
223
+ "variable_number" => {
224
+ let allowed = variable_number_allowed();
225
+ // style=1 normalcase, flags = CheckMethodNames(2) | CheckSymbols(1) = 3
226
+ for s in sources {
227
+ let (off, had) = rules::variable_number::check_variable_number(s, 1, 3, &allowed);
228
+ sink = sink.wrapping_add(off.len()).wrapping_add(had as usize);
229
+ }
230
+ }
231
+ "method_name" => {
232
+ // style=0 snake_case
233
+ for s in sources {
234
+ sink = sink.wrapping_add(rules::method_name::check_method_name(s, 0).len());
235
+ }
236
+ }
237
+ "safe_navigation_chain" => {
238
+ let nm = safe_nav_nil_methods();
239
+ for s in sources {
240
+ sink = sink.wrapping_add(
241
+ rules::safe_navigation_chain::check_safe_navigation_chain(s, &nm).len(),
242
+ );
243
+ }
244
+ }
245
+ "dot_position" => {
246
+ // style=0 leading
247
+ for s in sources {
248
+ sink = sink.wrapping_add(rules::dot_position::check_dot_position(s, 0).len());
249
+ }
250
+ }
251
+ "line_length" => {
252
+ // max=120, tab_width=0
253
+ for s in sources {
254
+ sink = sink.wrapping_add(rules::line_length::check_line_length(s, 120, 0).len());
255
+ }
256
+ }
257
+ "line_length_breakables" => {
258
+ // max=120, split_strings=false. Mirror the cop: derive the
259
+ // candidate-line set (length > Max) first, then restrict the
260
+ // breakable computation to those lines.
261
+ for s in sources {
262
+ let candidates: std::collections::HashSet<usize> =
263
+ rules::line_length::check_line_length(s, 120, 0)
264
+ .into_iter()
265
+ .map(|c| c.line_index)
266
+ .collect();
267
+ sink = sink.wrapping_add(
268
+ rules::line_length_breakable::compute_breakables_filtered(
269
+ s,
270
+ 120,
271
+ false,
272
+ Some(&candidates),
273
+ )
274
+ .len(),
275
+ );
276
+ }
277
+ }
278
+ "argument_alignment" => {
279
+ // style=0 with_first_argument, indent=2, incompatible=false
280
+ for s in sources {
281
+ sink = sink.wrapping_add(
282
+ rules::argument_alignment::check_argument_alignment(s, 0, 2, false).len(),
283
+ );
284
+ }
285
+ }
286
+ "first_argument_indentation" => {
287
+ // style=0 special_for_inner_method_call_in_parentheses, indent=2, enforce=false
288
+ for s in sources {
289
+ sink = sink.wrapping_add(
290
+ rules::first_argument_indentation::check_first_argument_indentation(
291
+ s, 0, 2, false,
292
+ )
293
+ .len(),
294
+ );
295
+ }
296
+ }
297
+ "redundant_self" => {
298
+ let km = kernel_methods();
299
+ for s in sources {
300
+ sink = sink.wrapping_add(rules::redundant_self::check_redundant_self(s, &km).len());
301
+ }
302
+ }
303
+ "indentation_width" => {
304
+ // Config: width=2, relative_to_receiver=false, access_modifier_outdent=false,
305
+ // indented_internal_methods=false, end_align=2(start_of_line),
306
+ // def_end_align_def=false, use_tabs=false
307
+ let cfg = rules::indentation_width::Config {
308
+ width: 2,
309
+ relative_to_receiver: false,
310
+ access_modifier_outdent: false,
311
+ indented_internal_methods: false,
312
+ end_align: 2,
313
+ def_end_align_def: false,
314
+ use_tabs: false,
315
+ };
316
+ let allowed: Vec<usize> = Vec::new();
317
+ let prior: Vec<(usize, usize)> = Vec::new();
318
+ for s in sources {
319
+ sink = sink.wrapping_add(
320
+ rules::indentation_width::check_indentation_width(s, cfg, &allowed, &prior)
321
+ .len(),
322
+ );
323
+ }
324
+ }
325
+ "multiline_bundle" => {
326
+ // op aligned(0)/indent2/base2, mc aligned(0)/indent2/base2
327
+ for s in sources {
328
+ let (op, mc) = rules::bundle::check_multiline_bundle(s, 0, 2, 2, 0, 2, 2);
329
+ sink = sink.wrapping_add(op.len()).wrapping_add(mc.len());
330
+ }
331
+ }
332
+ other => {
333
+ eprintln!("unknown cop: {other}");
334
+ std::process::exit(2);
335
+ }
336
+ }
337
+ sink
338
+ }
339
+
340
+ fn run_parse(sources: &[Vec<u8>]) -> usize {
341
+ let mut sink = 0usize;
342
+ for s in sources {
343
+ let r = parse(s);
344
+ sink = sink.wrapping_add(r.node().location().end_offset());
345
+ }
346
+ sink
347
+ }
348
+
349
+ fn collect_rb_files(dir: &str) -> Vec<Vec<u8>> {
350
+ let mut out = Vec::new();
351
+ let mut stack = vec![PathBuf::from(dir)];
352
+ while let Some(p) = stack.pop() {
353
+ let Ok(rd) = std::fs::read_dir(&p) else {
354
+ continue;
355
+ };
356
+ for entry in rd.flatten() {
357
+ let path = entry.path();
358
+ if path.is_dir() {
359
+ stack.push(path);
360
+ } else if path.extension().is_some_and(|e| e == "rb")
361
+ && let Ok(bytes) = std::fs::read(&path)
362
+ {
363
+ out.push(bytes);
364
+ }
365
+ }
366
+ }
367
+ out
368
+ }
369
+
370
+ #[cfg(unix)]
371
+ fn cpu_time_secs() -> f64 {
372
+ let mut ts = libc::timespec {
373
+ tv_sec: 0,
374
+ tv_nsec: 0,
375
+ };
376
+ // SAFETY: ts is a valid timespec.
377
+ unsafe {
378
+ libc::clock_gettime(libc::CLOCK_PROCESS_CPUTIME_ID, &mut ts);
379
+ }
380
+ ts.tv_sec as f64 + ts.tv_nsec as f64 / 1e9
381
+ }
382
+
383
+ #[cfg(not(unix))]
384
+ fn cpu_time_secs() -> f64 {
385
+ use std::time::{SystemTime, UNIX_EPOCH};
386
+ SystemTime::now()
387
+ .duration_since(UNIX_EPOCH)
388
+ .unwrap()
389
+ .as_secs_f64()
390
+ }
391
+
392
+ fn run_mode(mode: &str, sources: &[Vec<u8>]) -> usize {
393
+ if mode == "parse" {
394
+ run_parse(sources)
395
+ } else {
396
+ run_cop(mode, sources)
397
+ }
398
+ }
399
+
400
+ fn time_mode(mode: &str, sources: &[Vec<u8>], iters: usize) -> (f64, f64, f64) {
401
+ let mut times = Vec::with_capacity(iters);
402
+ let mut sink = 0usize;
403
+ for _ in 0..iters {
404
+ let t0 = cpu_time_secs();
405
+ sink = sink.wrapping_add(run_mode(mode, sources));
406
+ let t1 = cpu_time_secs();
407
+ times.push(t1 - t0);
408
+ }
409
+ times.sort_by(|a, b| a.partial_cmp(b).unwrap());
410
+ let median = times[times.len() / 2];
411
+ let min = times[0];
412
+ let max = times[times.len() - 1];
413
+ eprintln!("mode={mode} median={median:.6} min={min:.6} max={max:.6} sink={sink}");
414
+ (median, min, max)
415
+ }
416
+
417
+ fn main() {
418
+ let args: Vec<String> = std::env::args().collect();
419
+ if args.len() < 3 {
420
+ eprintln!("usage: analysis_profile <parse|<cop>|all> <corpus_dir> [iterations]");
421
+ eprintln!("cops: parse {}", COPS.join(" "));
422
+ std::process::exit(2);
423
+ }
424
+ let mode = args[1].clone();
425
+ let dir = &args[2];
426
+ let iters: usize = args.get(3).and_then(|s| s.parse().ok()).unwrap_or(1);
427
+
428
+ let sources = collect_rb_files(dir);
429
+ if sources.is_empty() {
430
+ eprintln!("no .rb files found under {dir}");
431
+ std::process::exit(1);
432
+ }
433
+ eprintln!("files={} iterations={iters}", sources.len());
434
+
435
+ if mode == "all" {
436
+ // Convenience: run parse + every cop in one process (one timing each).
437
+ let (p, _, _) = time_mode("parse", &sources, iters);
438
+ println!("parse\t{p:.6}");
439
+ for cop in COPS {
440
+ let (m, _, _) = time_mode(cop, &sources, iters);
441
+ println!("{cop}\t{m:.6}\t{:.6}", m - p);
442
+ }
443
+ } else {
444
+ let (median, _, _) = time_mode(&mode, &sources, iters);
445
+ println!("{median:.6}");
446
+ }
447
+ }