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,656 @@
1
+ //! Phase-decomposition profiler for the 68-cop bundle (MEASUREMENT ONLY).
2
+ //!
3
+ //! Splits the pure-Rust cost of `check_all_bundle` into phases over a corpus:
4
+ //!
5
+ //! * `parse` — prism parse only (P).
6
+ //! * `walk0` — parse + shared walk driving 1 noop rule (W ≈ walk mechanics).
7
+ //! * `walkN:<n>` — parse + shared walk driving n noop rules (dyn-dispatch
8
+ //! scaling; per-rule fan-out cost = slope over n).
9
+ //! * `bundle` — the real `check_all_bundle` with a packed config read from a
10
+ //! file dumped by the Ruby side (same values e2e uses).
11
+ //! * `guard` — `check_empty_line_after_guard_clause` standalone (its own
12
+ //! AST walk today; candidate to fold into the shared walk).
13
+ //! * `mname` — `check_method_name_filtered` standalone (own pruned walk).
14
+ //! * `linelen` — the `LineLength` source scan (no heredocs).
15
+ //!
16
+ //! Usage: bundle_profile <mode> <corpus_dir> <packed_config_file> [iterations]
17
+ //! Prints median CPU seconds over iterations to stdout; min/max to stderr.
18
+
19
+ use std::path::PathBuf;
20
+
21
+ use ruby_prism::{Node, parse};
22
+ use shirobai_core::rules;
23
+ use shirobai_core::rules::bundle::BundleConfig;
24
+ use shirobai_core::rules::dispatch::{self, Rule};
25
+
26
+ #[cfg(unix)]
27
+ fn cpu_time_secs() -> f64 {
28
+ let mut ts = libc::timespec {
29
+ tv_sec: 0,
30
+ tv_nsec: 0,
31
+ };
32
+ // SAFETY: ts is a valid timespec.
33
+ unsafe { libc::clock_gettime(libc::CLOCK_PROCESS_CPUTIME_ID, &mut ts) };
34
+ ts.tv_sec as f64 + ts.tv_nsec as f64 / 1e9
35
+ }
36
+
37
+ fn collect_rb_files(dir: &PathBuf, out: &mut Vec<PathBuf>) {
38
+ let Ok(entries) = std::fs::read_dir(dir) else {
39
+ return;
40
+ };
41
+ for entry in entries.flatten() {
42
+ let path = entry.path();
43
+ if path.is_dir() {
44
+ collect_rb_files(&path, out);
45
+ } else if path.extension().is_some_and(|e| e == "rb") {
46
+ out.push(path);
47
+ }
48
+ }
49
+ }
50
+
51
+ struct NoopRule {
52
+ sink: usize,
53
+ }
54
+
55
+ impl Rule for NoopRule {
56
+ fn enter(&mut self, node: &Node<'_>) {
57
+ // Touch the node so the call cannot be optimized out entirely.
58
+ self.sink = self.sink.wrapping_add(node.location().start_offset());
59
+ }
60
+
61
+ fn leave(&mut self) {
62
+ self.sink = self.sink.wrapping_add(1);
63
+ }
64
+ }
65
+
66
+ /// Reads one or more packed segments (nums line, list count, lists), one per
67
+ /// origin, matching the per-origin wire format. A dump with fewer segments
68
+ /// than `N_ORIGINS` (e.g. a core-only config) gets the dormant plugin
69
+ /// segments appended so old dump scripts keep working after regeneration.
70
+ fn read_packed(path: &str) -> (Vec<Vec<i64>>, Vec<Vec<Vec<String>>>) {
71
+ let text = std::fs::read_to_string(path).expect("packed config file");
72
+ let mut lines = text.lines().peekable();
73
+ let mut packed_nums = Vec::new();
74
+ let mut packed_lists = Vec::new();
75
+ while lines.peek().is_some_and(|l| !l.trim().is_empty()) {
76
+ let nums: Vec<i64> = lines
77
+ .next()
78
+ .expect("nums line")
79
+ .split_whitespace()
80
+ .map(|t| t.parse().expect("num"))
81
+ .collect();
82
+ let list_count: usize = lines.next().expect("list count").parse().expect("count");
83
+ let mut lists = Vec::with_capacity(list_count);
84
+ for _ in 0..list_count {
85
+ let k: usize = lines.next().expect("list len").parse().expect("len");
86
+ let mut items = Vec::with_capacity(k);
87
+ for _ in 0..k {
88
+ items.push(lines.next().expect("list item").to_string());
89
+ }
90
+ lists.push(items);
91
+ }
92
+ packed_nums.push(nums);
93
+ packed_lists.push(lists);
94
+ }
95
+ if packed_nums.len() < 2 {
96
+ // Dormant performance segment.
97
+ packed_nums.push(vec![0, 0, 0]);
98
+ packed_lists.push(vec![vec![]]);
99
+ }
100
+ if packed_nums.len() < 3 {
101
+ // Dormant rspec segment: enable flag off + 16 empty role lists.
102
+ packed_nums.push(vec![0]);
103
+ packed_lists.push(vec![vec![]; 16]);
104
+ }
105
+ if packed_nums.len() < 4 {
106
+ // Dormant rails segment: enable flag off + no lists.
107
+ packed_nums.push(vec![0]);
108
+ packed_lists.push(vec![]);
109
+ }
110
+ (packed_nums, packed_lists)
111
+ }
112
+
113
+ /// Per-rule attribution: each standalone `check_*` entry drives its own walk
114
+ /// over the (cache-hot) parse. Reported per-rule seconds therefore include one
115
+ /// walk's traversal mechanics each; subtract the `walk0` phase median (whole
116
+ /// corpus) for the pure rule body. Ordering matches `Dispatch::SLOTS` roughly.
117
+ fn run_perrule(sources: &[Vec<u8>], cfg: &BundleConfig, iterations: usize) {
118
+ use shirobai_core::rules as r;
119
+ type Entry<'c> = (&'static str, Box<dyn Fn(&[u8]) + 'c>);
120
+ macro_rules! e {
121
+ ($name:literal, $body:expr) => {
122
+ ($name, Box::new($body) as Box<dyn Fn(&[u8])>)
123
+ };
124
+ }
125
+ let entries: Vec<Entry> = vec![
126
+ e!("debugger", |s: &[u8]| {
127
+ std::hint::black_box(r::debugger::check_debugger(
128
+ s,
129
+ &cfg.debugger_methods,
130
+ &cfg.debugger_requires,
131
+ ));
132
+ }),
133
+ e!("block_length", |s: &[u8]| {
134
+ std::hint::black_box(r::block_length::check_block_length_filtered(
135
+ s,
136
+ cfg.block_length_max,
137
+ cfg.block_length_count_comments,
138
+ &cfg.block_length_count_as_one,
139
+ &cfg.block_length_allowed_methods,
140
+ cfg.block_length_filtered,
141
+ ));
142
+ }),
143
+ e!("block_nesting", |s: &[u8]| {
144
+ std::hint::black_box(r::block_nesting::check_block_nesting(
145
+ s,
146
+ cfg.block_nesting_max,
147
+ cfg.block_nesting_count_blocks,
148
+ cfg.block_nesting_count_modifier_forms,
149
+ ));
150
+ }),
151
+ e!("complexity", |s: &[u8]| {
152
+ std::hint::black_box(r::complexity::check_complexity_exceeding(
153
+ s,
154
+ cfg.max_cyclomatic,
155
+ cfg.max_perceived,
156
+ ));
157
+ }),
158
+ e!("variable_number", |s: &[u8]| {
159
+ std::hint::black_box(r::variable_number::check_variable_number(
160
+ s,
161
+ cfg.variable_number_style,
162
+ cfg.variable_number_flags,
163
+ &cfg.variable_number_allowed_identifiers,
164
+ ));
165
+ }),
166
+ e!("method_name", |s: &[u8]| {
167
+ std::hint::black_box(r::method_name::check_method_name_filtered(
168
+ s,
169
+ cfg.method_name_style,
170
+ true,
171
+ ));
172
+ }),
173
+ e!("safe_navigation_chain", |s: &[u8]| {
174
+ std::hint::black_box(r::safe_navigation_chain::check_safe_navigation_chain(
175
+ s,
176
+ &cfg.safe_navigation_nil_methods,
177
+ ));
178
+ }),
179
+ e!("multiline_operation", |s: &[u8]| {
180
+ let c = cfg.multiline_operation;
181
+ std::hint::black_box(
182
+ r::multiline_operation_indentation::check_multiline_operation_indentation(
183
+ s, c.0, c.1, c.2,
184
+ ),
185
+ );
186
+ }),
187
+ e!("multiline_method_call", |s: &[u8]| {
188
+ let c = cfg.multiline_method_call;
189
+ std::hint::black_box(
190
+ r::multiline_method_call_indentation::check_multiline_method_call_indentation(
191
+ s, c.0, c.1, c.2,
192
+ ),
193
+ );
194
+ }),
195
+ e!("dot_position", |s: &[u8]| {
196
+ std::hint::black_box(r::dot_position::check_dot_position(
197
+ s,
198
+ cfg.dot_position_style,
199
+ ));
200
+ }),
201
+ e!("line_length", |s: &[u8]| {
202
+ std::hint::black_box(r::line_length::check_line_length(
203
+ s,
204
+ cfg.line_length_max,
205
+ cfg.line_length_tab_width,
206
+ ));
207
+ }),
208
+ e!("line_end_concatenation", |s: &[u8]| {
209
+ std::hint::black_box(r::line_end_concatenation::check_line_end_concatenation(s));
210
+ }),
211
+ e!("argument_alignment", |s: &[u8]| {
212
+ std::hint::black_box(r::argument_alignment::check_argument_alignment(
213
+ s,
214
+ cfg.argument_alignment_style,
215
+ cfg.argument_alignment_indent,
216
+ cfg.argument_alignment_incompatible,
217
+ ));
218
+ }),
219
+ e!("first_argument_indentation", |s: &[u8]| {
220
+ std::hint::black_box(r::first_argument_indentation::check_first_argument_indentation(
221
+ s,
222
+ cfg.first_argument_style,
223
+ cfg.first_argument_indent,
224
+ cfg.first_argument_enforce_fixed_no_line_break,
225
+ ));
226
+ }),
227
+ e!("redundant_self", |s: &[u8]| {
228
+ std::hint::black_box(r::redundant_self::check_redundant_self(
229
+ s,
230
+ &cfg.redundant_self_kernel_methods,
231
+ ));
232
+ }),
233
+ e!("indentation_width", |s: &[u8]| {
234
+ std::hint::black_box(r::indentation_width::check_indentation_width(
235
+ s,
236
+ cfg.indentation_width,
237
+ &[],
238
+ &[],
239
+ ));
240
+ }),
241
+ e!("predicate_prefix", |s: &[u8]| {
242
+ std::hint::black_box(r::predicate_prefix::check_predicate_prefix(
243
+ s,
244
+ &cfg.predicate_prefix_name_prefixes,
245
+ &cfg.predicate_prefix_macros,
246
+ ));
247
+ }),
248
+ e!("closing_parenthesis_indentation", |s: &[u8]| {
249
+ std::hint::black_box(
250
+ r::closing_parenthesis_indentation::check_closing_parenthesis_indentation(
251
+ s,
252
+ cfg.closing_paren_indent,
253
+ ),
254
+ );
255
+ }),
256
+ e!("first_array_element_indentation", |s: &[u8]| {
257
+ std::hint::black_box(
258
+ r::first_array_element_indentation::check_first_array_element_indentation(
259
+ s,
260
+ cfg.first_array_element_style,
261
+ cfg.first_array_element_indent,
262
+ cfg.first_array_element_enforce_fixed,
263
+ ),
264
+ );
265
+ }),
266
+ e!("hash_each_methods", |s: &[u8]| {
267
+ std::hint::black_box(r::hash_each_methods::check_hash_each_methods(
268
+ s,
269
+ &cfg.hash_each_allowed_receivers,
270
+ ));
271
+ }),
272
+ e!("void", |s: &[u8]| {
273
+ std::hint::black_box(r::void::check_void(s, cfg.void_check_nonmutating));
274
+ }),
275
+ e!("useless_access_modifier", |s: &[u8]| {
276
+ std::hint::black_box(r::useless_access_modifier::check_useless_access_modifier(
277
+ s,
278
+ &cfg.useless_access_modifier_context_creating,
279
+ &cfg.useless_access_modifier_method_creating,
280
+ cfg.useless_access_modifier_active_support,
281
+ ));
282
+ }),
283
+ e!("empty_lines_around_body", |s: &[u8]| {
284
+ std::hint::black_box(r::empty_lines_around_body::check_empty_lines_around_body(
285
+ s,
286
+ cfg.empty_lines_around_body,
287
+ ));
288
+ }),
289
+ e!("block_delimiters", |s: &[u8]| {
290
+ std::hint::black_box(r::block_delimiters::check_block_delimiters_events(
291
+ s,
292
+ &cfg.block_delimiters,
293
+ ));
294
+ }),
295
+ e!("abc_size", |s: &[u8]| {
296
+ std::hint::black_box(r::abc_size::check_abc_size(
297
+ s,
298
+ cfg.abc_size_max_floor,
299
+ cfg.abc_size_discount_repeated,
300
+ ));
301
+ }),
302
+ e!("indentation_consistency", |s: &[u8]| {
303
+ std::hint::black_box(r::indentation_consistency::check_indentation_consistency(
304
+ s,
305
+ cfg.indentation_consistency,
306
+ ));
307
+ }),
308
+ e!("empty_line_between_defs", |s: &[u8]| {
309
+ std::hint::black_box(r::empty_line_between_defs::check_empty_line_between_defs(
310
+ s,
311
+ cfg.empty_line_between_defs.clone(),
312
+ ));
313
+ }),
314
+ e!("end_alignment", |s: &[u8]| {
315
+ std::hint::black_box(r::end_alignment::check_end_alignment(s, cfg.end_alignment));
316
+ }),
317
+ e!("block_alignment", |s: &[u8]| {
318
+ std::hint::black_box(r::block_alignment::check_block_alignment(
319
+ s,
320
+ cfg.block_alignment,
321
+ ));
322
+ }),
323
+ e!("else_alignment", |s: &[u8]| {
324
+ std::hint::black_box(r::else_alignment::check_else_alignment(
325
+ s,
326
+ cfg.else_alignment,
327
+ ));
328
+ }),
329
+ e!("first_hash_element_indentation", |s: &[u8]| {
330
+ std::hint::black_box(
331
+ r::first_hash_element_indentation::check_first_hash_element_indentation(
332
+ s,
333
+ cfg.first_hash_element_style,
334
+ cfg.first_hash_element_indent,
335
+ cfg.first_hash_element_enforce_fixed,
336
+ cfg.first_hash_element_separators,
337
+ ),
338
+ );
339
+ }),
340
+ e!("hash_alignment", |s: &[u8]| {
341
+ std::hint::black_box(r::hash_alignment::check_hash_alignment(
342
+ s,
343
+ &cfg.hash_alignment,
344
+ ));
345
+ }),
346
+ e!("empty_lines_around_arguments", |s: &[u8]| {
347
+ std::hint::black_box(
348
+ r::empty_lines_around_arguments::check_empty_lines_around_arguments(s),
349
+ );
350
+ }),
351
+ e!("hash_syntax", |s: &[u8]| {
352
+ std::hint::black_box(r::hash_syntax::check_hash_syntax(s, &cfg.hash_syntax));
353
+ }),
354
+ e!("string_literals", |s: &[u8]| {
355
+ std::hint::black_box(r::string_literals::check_string_literals(
356
+ s,
357
+ &cfg.string_literals,
358
+ ));
359
+ }),
360
+ e!("trailing_comma_in_arguments", |s: &[u8]| {
361
+ std::hint::black_box(
362
+ r::trailing_comma_in_arguments::check_trailing_comma_in_arguments(
363
+ s,
364
+ &cfg.trailing_comma_in_arguments,
365
+ ),
366
+ );
367
+ }),
368
+ e!("string_literals_in_interpolation", |s: &[u8]| {
369
+ std::hint::black_box(
370
+ r::string_literals_in_interpolation::check_string_literals_in_interpolation(
371
+ s,
372
+ &cfg.string_literals_in_interpolation,
373
+ ),
374
+ );
375
+ }),
376
+ e!("trailing_empty_lines", |s: &[u8]| {
377
+ std::hint::black_box(r::trailing_empty_lines::check_trailing_empty_lines(
378
+ s,
379
+ &cfg.trailing_empty_lines,
380
+ ));
381
+ }),
382
+ e!("space_around_method_call_operator", |s: &[u8]| {
383
+ std::hint::black_box(
384
+ r::space_around_method_call_operator::check_space_around_method_call_operator(s),
385
+ );
386
+ }),
387
+ e!("space_around_keyword", |s: &[u8]| {
388
+ std::hint::black_box(r::space_around_keyword::check_space_around_keyword(s));
389
+ }),
390
+ e!("space_inside_block_braces", |s: &[u8]| {
391
+ std::hint::black_box(r::space_inside_block_braces::check_space_inside_block_braces(
392
+ s,
393
+ cfg.space_inside_block_braces,
394
+ ));
395
+ }),
396
+ e!("space_inside_hash_literal_braces", |s: &[u8]| {
397
+ std::hint::black_box(
398
+ r::space_inside_hash_literal_braces::check_space_inside_hash_literal_braces(
399
+ s,
400
+ cfg.space_inside_hash_literal_braces,
401
+ ),
402
+ );
403
+ }),
404
+ e!("space_inside_array_literal_brackets", |s: &[u8]| {
405
+ std::hint::black_box(
406
+ r::space_inside_array_literal_brackets::check_space_inside_array_literal_brackets(
407
+ s,
408
+ cfg.space_inside_array_literal_brackets,
409
+ ),
410
+ );
411
+ }),
412
+ e!("space_before_block_braces", |s: &[u8]| {
413
+ std::hint::black_box(r::space_before_block_braces::check_space_before_block_braces(
414
+ s,
415
+ cfg.space_before_block_braces,
416
+ ));
417
+ }),
418
+ e!("method_length", |s: &[u8]| {
419
+ std::hint::black_box(r::method_length::check_method_length(
420
+ s,
421
+ cfg.method_length_max,
422
+ cfg.method_length_count_comments,
423
+ &cfg.method_length_count_as_one,
424
+ ));
425
+ }),
426
+ e!("def_end_alignment", |s: &[u8]| {
427
+ std::hint::black_box(r::def_end_alignment::check_def_end_alignment(
428
+ s,
429
+ cfg.def_end_alignment,
430
+ ));
431
+ }),
432
+ e!("require_parentheses", |s: &[u8]| {
433
+ std::hint::black_box(r::require_parentheses::check_require_parentheses(s));
434
+ }),
435
+ e!("self_assignment", |s: &[u8]| {
436
+ std::hint::black_box(r::self_assignment::check_self_assignment(s));
437
+ }),
438
+ e!("nested_parenthesized_calls", |s: &[u8]| {
439
+ std::hint::black_box(r::nested_parenthesized_calls::check_nested_parenthesized_calls(
440
+ s,
441
+ &cfg.nested_parenthesized_calls_allowed_methods,
442
+ ));
443
+ }),
444
+ e!("parentheses_as_grouped_expression", |s: &[u8]| {
445
+ std::hint::black_box(
446
+ r::parentheses_as_grouped_expression::check_parentheses_as_grouped_expression(s),
447
+ );
448
+ }),
449
+ e!("percent_literal_delimiters", |s: &[u8]| {
450
+ std::hint::black_box(r::percent_literal_delimiters::check_percent_literal_delimiters(
451
+ s,
452
+ &cfg.percent_literal_delimiters,
453
+ ));
454
+ }),
455
+ e!("multiline_method_call_brace_layout", |s: &[u8]| {
456
+ std::hint::black_box(
457
+ r::multiline_method_call_brace_layout::check_multiline_method_call_brace_layout(
458
+ s,
459
+ cfg.multiline_method_call_brace_style,
460
+ ),
461
+ );
462
+ }),
463
+ e!("access_modifier_indentation", |s: &[u8]| {
464
+ std::hint::black_box(
465
+ r::access_modifier_indentation::check_access_modifier_indentation(
466
+ s,
467
+ cfg.access_modifier_indentation,
468
+ ),
469
+ );
470
+ }),
471
+ e!("assignment_indentation", |s: &[u8]| {
472
+ std::hint::black_box(r::assignment_indentation::check_assignment_indentation(
473
+ s,
474
+ cfg.assignment_indentation,
475
+ ));
476
+ }),
477
+ e!("redundant_self_assignment", |s: &[u8]| {
478
+ std::hint::black_box(r::redundant_self_assignment::check_redundant_self_assignment(s));
479
+ }),
480
+ e!("colon_method_call", |s: &[u8]| {
481
+ std::hint::black_box(r::colon_method_call::check_colon_method_call(s));
482
+ }),
483
+ e!("stabby_lambda_parentheses", |s: &[u8]| {
484
+ std::hint::black_box(r::stabby_lambda_parentheses::check_stabby_lambda_parentheses(
485
+ s,
486
+ cfg.stabby_lambda_parentheses,
487
+ ));
488
+ }),
489
+ e!("unreachable_code", |s: &[u8]| {
490
+ std::hint::black_box(r::unreachable_code::check_unreachable_code(s));
491
+ }),
492
+ e!("hash_transform_keys", |s: &[u8]| {
493
+ std::hint::black_box(r::hash_transform_keys::check_hash_transform_keys(s));
494
+ }),
495
+ e!("ambiguous_block_association", |s: &[u8]| {
496
+ let c = &cfg.ambiguous_block_association;
497
+ std::hint::black_box(r::ambiguous_block_association::check_ambiguous_block_association(
498
+ s,
499
+ &c.allowed_methods,
500
+ &[],
501
+ ));
502
+ }),
503
+ e!("empty_line_after_guard_clause", |s: &[u8]| {
504
+ std::hint::black_box(
505
+ r::empty_line_after_guard_clause::check_empty_line_after_guard_clause(s),
506
+ );
507
+ }),
508
+ e!("empty_comment", |s: &[u8]| {
509
+ std::hint::black_box(r::empty_comment::check_empty_comment(s, cfg.empty_comment));
510
+ }),
511
+ e!("empty_line_after_magic_comment", |s: &[u8]| {
512
+ std::hint::black_box(
513
+ r::empty_line_after_magic_comment::check_empty_line_after_magic_comment(s),
514
+ );
515
+ }),
516
+ e!("empty_lines", |s: &[u8]| {
517
+ std::hint::black_box(r::empty_lines::check_empty_lines(s));
518
+ }),
519
+ e!("leading_empty_lines", |s: &[u8]| {
520
+ std::hint::black_box(r::leading_empty_lines::check_leading_empty_lines(s));
521
+ }),
522
+ ];
523
+
524
+ let mut totals = vec![f64::MAX; entries.len()];
525
+ for _ in 0..iterations {
526
+ let mut iter_totals = vec![0.0f64; entries.len()];
527
+ for s in sources {
528
+ // Warm the parse cache so every entry below hits it.
529
+ shirobai_core::rules::parse_cache::with_parsed(s, |_, _| ());
530
+ for (i, (_, f)) in entries.iter().enumerate() {
531
+ let t0 = cpu_time_secs();
532
+ f(s);
533
+ iter_totals[i] += cpu_time_secs() - t0;
534
+ }
535
+ }
536
+ for (t, it) in totals.iter_mut().zip(&iter_totals) {
537
+ *t = t.min(*it);
538
+ }
539
+ }
540
+ let mut rows: Vec<(&str, f64)> = entries
541
+ .iter()
542
+ .map(|(n, _)| *n)
543
+ .zip(totals.iter().copied())
544
+ .collect();
545
+ rows.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
546
+ let sum: f64 = totals.iter().sum();
547
+ println!("perrule total (min over {iterations} iters): {sum:.3}s");
548
+ for (name, t) in rows {
549
+ println!("{t:8.3} {name}");
550
+ }
551
+ }
552
+
553
+ fn run_pass(mode: &str, n_rules: usize, sources: &[Vec<u8>], cfg: &BundleConfig) -> usize {
554
+ let mut sink = 0usize;
555
+ match mode {
556
+ "parse" => {
557
+ for s in sources {
558
+ let result = parse(s);
559
+ sink = sink.wrapping_add(result.node().location().end_offset());
560
+ }
561
+ }
562
+ "walk" => {
563
+ for s in sources {
564
+ let mut noops: Vec<NoopRule> = (0..n_rules).map(|_| NoopRule { sink: 0 }).collect();
565
+ let mut refs: Vec<&mut dyn Rule> =
566
+ noops.iter_mut().map(|r| r as &mut dyn Rule).collect();
567
+ dispatch::run(s, &mut refs);
568
+ sink = sink.wrapping_add(noops.iter().map(|r| r.sink).sum::<usize>());
569
+ }
570
+ }
571
+ "bundle" => {
572
+ for s in sources {
573
+ let result = rules::bundle::check_all_bundle(s, cfg);
574
+ sink = sink.wrapping_add(result.line_length.len());
575
+ sink = sink.wrapping_add(result.indentation_width.len());
576
+ }
577
+ }
578
+ "guard" => {
579
+ for s in sources {
580
+ let out = rules::empty_line_after_guard_clause::check_empty_line_after_guard_clause(s);
581
+ sink = sink.wrapping_add(out.len());
582
+ }
583
+ }
584
+ "mname" => {
585
+ for s in sources {
586
+ let out = rules::method_name::check_method_name_filtered(s, 0, true);
587
+ sink = sink.wrapping_add(out.0.len());
588
+ }
589
+ }
590
+ "linelen" => {
591
+ for s in sources {
592
+ let out = rules::line_length::check_line_length(s, 120, 2);
593
+ sink = sink.wrapping_add(out.len());
594
+ }
595
+ }
596
+ other => {
597
+ eprintln!("unknown mode: {other}");
598
+ std::process::exit(2);
599
+ }
600
+ }
601
+ sink
602
+ }
603
+
604
+ fn main() {
605
+ let args: Vec<String> = std::env::args().collect();
606
+ if args.len() < 4 {
607
+ eprintln!("usage: bundle_profile <mode> <corpus_dir> <packed_config_file> [iterations]");
608
+ std::process::exit(2);
609
+ }
610
+ let mode_arg = args[1].as_str();
611
+ let corpus = PathBuf::from(&args[2]);
612
+ let (nums, lists) = read_packed(&args[3]);
613
+ let iterations: usize = args.get(4).map_or(3, |v| v.parse().expect("iterations"));
614
+ let cfg = BundleConfig::from_packed(&nums, lists).expect("valid packed config");
615
+
616
+ let (mode, n_rules) = match mode_arg.strip_prefix("walkN:") {
617
+ Some(n) => ("walk", n.parse().expect("walkN count")),
618
+ None if mode_arg == "walk0" => ("walk", 1),
619
+ None => (mode_arg, 0),
620
+ };
621
+
622
+ let mut files = Vec::new();
623
+ collect_rb_files(&corpus, &mut files);
624
+ files.sort();
625
+ let sources: Vec<Vec<u8>> = files
626
+ .iter()
627
+ .filter_map(|p| std::fs::read(p).ok())
628
+ .collect();
629
+ eprintln!("files: {}", sources.len());
630
+
631
+ if mode == "perrule" {
632
+ run_perrule(&sources, &cfg, iterations);
633
+ return;
634
+ }
635
+
636
+ // Warm-up pass (untimed): touch every source once.
637
+ let warm = run_pass(mode, n_rules, &sources, &cfg);
638
+
639
+ let mut times: Vec<f64> = (0..iterations)
640
+ .map(|_| {
641
+ let t0 = cpu_time_secs();
642
+ let sink = run_pass(mode, n_rules, &sources, &cfg);
643
+ let dt = cpu_time_secs() - t0;
644
+ eprintln!(" iter: {dt:.3}s (sink {sink})");
645
+ dt
646
+ })
647
+ .collect();
648
+ times.sort_by(|a, b| a.partial_cmp(b).unwrap());
649
+ let median = times[times.len() / 2];
650
+ eprintln!(
651
+ "min {:.3}s / max {:.3}s / warm sink {warm}",
652
+ times[0],
653
+ times[times.len() - 1]
654
+ );
655
+ println!("{mode_arg}\t{median:.3}");
656
+ }