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,318 @@
1
+ //! `Naming/VariableNumber`.
2
+
3
+ use ruby_prism::{Node, Visit};
4
+
5
+ /// An identifier whose numbering does not match the configured style.
6
+ pub struct VariableNumberOffense {
7
+ pub start_offset: usize,
8
+ pub end_offset: usize,
9
+ /// 0 = variable, 1 = method name, 2 = symbol.
10
+ pub identifier_type: u8,
11
+ pub name: String,
12
+ /// First alternative style (0=snake_case,1=normalcase,2=non_integer) that
13
+ /// the name matches, or 255 when none (unrecognized).
14
+ pub alternative: u8,
15
+ }
16
+
17
+ const SNAKE_CASE: u8 = 0;
18
+ const NORMALCASE: u8 = 1;
19
+ const NON_INTEGER: u8 = 2;
20
+
21
+ /// Port of `ConfigurableNumbering::FORMATS`. A name is valid for a style when
22
+ /// its trailing numbering matches; the leading sigil (`@`, `$`) never affects
23
+ /// the result since all patterns are anchored at the end.
24
+ fn valid_for(name: &str, style: u8) -> bool {
25
+ let chars: Vec<char> = name.chars().collect();
26
+ let n = chars.len();
27
+ if n == 0 {
28
+ return true;
29
+ }
30
+ let trailing_digits = chars
31
+ .iter()
32
+ .rev()
33
+ .take_while(|c| c.is_ascii_digit())
34
+ .count();
35
+ let ends_non_digit = trailing_digits == 0; // `\D\z`
36
+ let all_digits = trailing_digits == n; // `\A\d+\z`
37
+ let before = (trailing_digits < n).then(|| chars[n - trailing_digits - 1]);
38
+ // `_\d+\z`: trailing digits immediately preceded by `_`.
39
+ let underscore_digits = trailing_digits > 0 && before == Some('_');
40
+ // `[^_\d]\d+\z`: trailing digits preceded by a non-underscore non-digit.
41
+ let normal_digits = trailing_digits > 0 && before.is_some_and(|c| c != '_');
42
+ // `\A_\d+\z`: the whole name is `_` followed by digits (implicit param).
43
+ let implicit_param = n >= 2 && chars[0] == '_' && trailing_digits == n - 1;
44
+
45
+ match style {
46
+ SNAKE_CASE => ends_non_digit || underscore_digits || all_digits,
47
+ NORMALCASE => ends_non_digit || normal_digits || all_digits || implicit_param,
48
+ NON_INTEGER => ends_non_digit || all_digits || implicit_param,
49
+ _ => true,
50
+ }
51
+ }
52
+
53
+ /// First alternative style (in `SupportedStyles` order) the name matches, or
54
+ /// 255 (unrecognized), mirroring `report_opposing_styles`.
55
+ fn first_alternative(name: &str, current: u8) -> u8 {
56
+ for style in [SNAKE_CASE, NORMALCASE, NON_INTEGER] {
57
+ if style != current && valid_for(name, style) {
58
+ return style;
59
+ }
60
+ }
61
+ 255
62
+ }
63
+
64
+ fn strip_sigils(name: &str) -> String {
65
+ name.chars().filter(|c| *c != '@' && *c != '$').collect()
66
+ }
67
+
68
+ pub fn check_variable_number(
69
+ source: &[u8],
70
+ style: u8,
71
+ flags: u8,
72
+ allowed_identifiers: &[String],
73
+ ) -> (Vec<VariableNumberOffense>, bool) {
74
+ let mut visitor = build_rule(source, style, flags, allowed_identifiers);
75
+ super::parse_cache::with_parsed(source, |_source, node| visitor.visit(node));
76
+ (visitor.offenses, visitor.had_correct)
77
+ }
78
+
79
+ /// Build the rule for use standalone or in a shared-walk bundle.
80
+ pub(crate) fn build_rule<'a>(
81
+ source: &'a [u8],
82
+ style: u8,
83
+ flags: u8,
84
+ allowed_identifiers: &'a [String],
85
+ ) -> Visitor<'a> {
86
+ Visitor {
87
+ source,
88
+ style,
89
+ check_symbols: flags & 1 != 0,
90
+ check_method_names: flags & 2 != 0,
91
+ allowed_identifiers,
92
+ offenses: Vec::new(),
93
+ had_correct: false,
94
+ }
95
+ }
96
+
97
+ pub(crate) struct Visitor<'a> {
98
+ source: &'a [u8],
99
+ style: u8,
100
+ check_symbols: bool,
101
+ check_method_names: bool,
102
+ allowed_identifiers: &'a [String],
103
+ pub(crate) offenses: Vec<VariableNumberOffense>,
104
+ pub(crate) had_correct: bool,
105
+ }
106
+
107
+ impl Visitor<'_> {
108
+ fn text(&self, start: usize, end: usize) -> String {
109
+ String::from_utf8_lossy(&self.source[start..end]).into_owned()
110
+ }
111
+
112
+ /// Run the numbering check for one identifier. `name` is the identifier
113
+ /// (with any sigil); `[start, end)` is the offense range.
114
+ fn check(&mut self, name: String, start: usize, end: usize, identifier_type: u8) {
115
+ let stripped = strip_sigils(&name);
116
+ if self.allowed_identifiers.contains(&stripped) {
117
+ return;
118
+ }
119
+ if valid_for(&name, self.style) {
120
+ self.had_correct = true;
121
+ } else {
122
+ let alternative = first_alternative(&name, self.style);
123
+ self.offenses.push(VariableNumberOffense {
124
+ start_offset: start,
125
+ end_offset: end,
126
+ identifier_type,
127
+ name,
128
+ alternative,
129
+ });
130
+ }
131
+ }
132
+
133
+ fn check_loc(&mut self, loc: ruby_prism::Location<'_>, identifier_type: u8) {
134
+ let (start, end) = (loc.start_offset(), loc.end_offset());
135
+ self.check(self.text(start, end), start, end, identifier_type);
136
+ }
137
+
138
+ fn process(&mut self, node: &Node<'_>) {
139
+ if let Some(n) = node.as_local_variable_write_node() {
140
+ self.check_loc(n.name_loc(), 0);
141
+ } else if let Some(n) = node.as_instance_variable_write_node() {
142
+ self.check_loc(n.name_loc(), 0);
143
+ } else if let Some(n) = node.as_class_variable_write_node() {
144
+ self.check_loc(n.name_loc(), 0);
145
+ } else if let Some(n) = node.as_global_variable_write_node() {
146
+ self.check_loc(n.name_loc(), 0);
147
+ } else if node.as_local_variable_target_node().is_some()
148
+ || node.as_instance_variable_target_node().is_some()
149
+ || node.as_class_variable_target_node().is_some()
150
+ || node.as_global_variable_target_node().is_some()
151
+ || node.as_required_parameter_node().is_some()
152
+ {
153
+ self.check_loc(node.location(), 0);
154
+ } else if self.check_method_names
155
+ && let Some(n) = node.as_def_node()
156
+ {
157
+ self.check_loc(n.name_loc(), 1);
158
+ } else if self.check_symbols
159
+ && let Some(n) = node.as_symbol_node()
160
+ {
161
+ let loc = node.location();
162
+ let name = String::from_utf8_lossy(n.unescaped()).into_owned();
163
+ self.check(name, loc.start_offset(), loc.end_offset(), 2);
164
+ }
165
+ }
166
+ }
167
+
168
+ impl super::dispatch::Rule for Visitor<'_> {
169
+ fn interest(&self) -> super::dispatch::Interest {
170
+ use super::dispatch::Interest;
171
+ Interest(
172
+ Interest::ENTER_ALL
173
+ | Interest::LEAF,
174
+ )
175
+ }
176
+
177
+ fn enter(&mut self, node: &Node<'_>) {
178
+ self.process(node);
179
+ }
180
+
181
+ fn leave(&mut self) {}
182
+
183
+ fn enter_leaf(&mut self, node: &Node<'_>) {
184
+ self.process(node);
185
+ }
186
+ }
187
+
188
+ impl<'pr> Visit<'pr> for Visitor<'_> {
189
+ fn visit_branch_node_enter(&mut self, node: Node<'pr>) {
190
+ super::dispatch::Rule::enter(self, &node);
191
+ }
192
+
193
+ fn visit_leaf_node_enter(&mut self, node: Node<'pr>) {
194
+ super::dispatch::Rule::enter_leaf(self, &node);
195
+ }
196
+ }
197
+
198
+ #[cfg(test)]
199
+ mod tests {
200
+ use super::*;
201
+
202
+ fn names(source: &str, style: u8) -> Vec<(String, u8)> {
203
+ let (offenses, _) = check_variable_number(source.as_bytes(), style, 0b11, &[]);
204
+ offenses
205
+ .into_iter()
206
+ .map(|o| (o.name, o.alternative))
207
+ .collect()
208
+ }
209
+
210
+ #[test]
211
+ fn valid_for_snake_case() {
212
+ for ok in [
213
+ "local_1", "local_12", "local_", "aB_1", "a_1_b", "_", "_foo", "@foo", "_1", "42",
214
+ ] {
215
+ assert!(valid_for(ok, SNAKE_CASE), "{ok} should be valid snake_case");
216
+ }
217
+ for bad in ["local1", "@local1", "camelCase1", "aB1", "a1"] {
218
+ assert!(
219
+ !valid_for(bad, SNAKE_CASE),
220
+ "{bad} should be invalid snake_case"
221
+ );
222
+ }
223
+ }
224
+
225
+ #[test]
226
+ fn valid_for_normalcase() {
227
+ for ok in ["local1", "_1", "x42", "localFoo"] {
228
+ assert!(valid_for(ok, NORMALCASE), "{ok} should be valid normalcase");
229
+ }
230
+ for bad in ["local_1", "sha_256", "myAttribute_1"] {
231
+ assert!(
232
+ !valid_for(bad, NORMALCASE),
233
+ "{bad} should be invalid normalcase"
234
+ );
235
+ }
236
+ }
237
+
238
+ #[test]
239
+ fn valid_for_non_integer() {
240
+ for ok in ["localone", "_1", "42"] {
241
+ assert!(
242
+ valid_for(ok, NON_INTEGER),
243
+ "{ok} should be valid non_integer"
244
+ );
245
+ }
246
+ for bad in ["local1", "local_1"] {
247
+ assert!(
248
+ !valid_for(bad, NON_INTEGER),
249
+ "{bad} should be invalid non_integer"
250
+ );
251
+ }
252
+ }
253
+
254
+ #[test]
255
+ fn detects_local_variable() {
256
+ assert_eq!(
257
+ names("local1 = 1", SNAKE_CASE),
258
+ vec![("local1".to_string(), NORMALCASE)]
259
+ );
260
+ }
261
+
262
+ #[test]
263
+ fn detects_instance_and_global_with_sigil() {
264
+ let got = names("@local1 = 1\n$g1 = 1", SNAKE_CASE);
265
+ assert_eq!(
266
+ got,
267
+ vec![
268
+ ("@local1".to_string(), NORMALCASE),
269
+ ("$g1".to_string(), NORMALCASE)
270
+ ]
271
+ );
272
+ }
273
+
274
+ #[test]
275
+ fn detects_method_param_and_name() {
276
+ let got = names("def method1(arg1); end", SNAKE_CASE);
277
+ assert!(got.iter().any(|(n, _)| n == "method1"));
278
+ assert!(got.iter().any(|(n, _)| n == "arg1"));
279
+ }
280
+
281
+ #[test]
282
+ fn detects_symbol() {
283
+ assert_eq!(
284
+ names(":sym1", SNAKE_CASE),
285
+ vec![("sym1".to_string(), NORMALCASE)]
286
+ );
287
+ }
288
+
289
+ #[test]
290
+ fn integer_symbols_accepted() {
291
+ assert!(names(":\"42\"", SNAKE_CASE).is_empty());
292
+ assert!(names("%i[1 2 3]", SNAKE_CASE).is_empty());
293
+ }
294
+
295
+ #[test]
296
+ fn allowed_identifier_skipped() {
297
+ let (offenses, _) = check_variable_number(
298
+ "@capture3 = 1".as_bytes(),
299
+ SNAKE_CASE,
300
+ 0b11,
301
+ &["capture3".to_string()],
302
+ );
303
+ assert!(offenses.is_empty());
304
+ }
305
+
306
+ #[test]
307
+ fn had_correct_flag() {
308
+ let (offenses, had_correct) =
309
+ check_variable_number("a1 = 1\na_2 = 1".as_bytes(), SNAKE_CASE, 0b11, &[]);
310
+ assert_eq!(offenses.len(), 1);
311
+ assert!(had_correct);
312
+ }
313
+
314
+ #[test]
315
+ fn masgn_targets_checked() {
316
+ assert_eq!(names("a1, b2 = 1, 2", SNAKE_CASE).len(), 2);
317
+ }
318
+ }