sonicop 26.8.101 → 26.8.103

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 (479) hide show
  1. checksums.yaml +4 -4
  2. data/CONFORMANCE.md +179 -93
  3. data/Cargo.lock +12 -9
  4. data/Cargo.toml +10 -3
  5. data/README.ja.md +60 -7
  6. data/README.md +63 -8
  7. data/lib/sonicop/version.rb +1 -1
  8. data/src/cli.rs +70 -2
  9. data/src/config/mod.rs +55 -1
  10. data/src/config/paths.rs +86 -10
  11. data/src/diagnostic.rs +80 -13
  12. data/src/directives.rs +974 -35
  13. data/src/display_width.rs +58 -0
  14. data/src/engine.rs +1435 -81
  15. data/src/formatter.rs +1661 -106
  16. data/src/lib.rs +4 -0
  17. data/src/magic_comment.rs +295 -0
  18. data/src/nul_bytes.rs +103 -0
  19. data/src/profile.rs +129 -0
  20. data/src/rules/bundler/duplicated_gem.rs +106 -0
  21. data/src/rules/bundler/duplicated_group.rs +105 -0
  22. data/src/rules/bundler/gem_filename.rs +64 -0
  23. data/src/rules/bundler/insecure_protocol_source.rs +46 -0
  24. data/src/rules/bundler/mod.rs +13 -0
  25. data/src/rules/bundler/ordered_gems.rs +30 -0
  26. data/src/rules/bundler/support.rs +32 -0
  27. data/src/rules/gemspec/duplicated_assignment.rs +182 -0
  28. data/src/rules/gemspec/mod.rs +12 -0
  29. data/src/rules/gemspec/ordered_dependencies.rs +63 -0
  30. data/src/rules/gemspec/required_ruby_version.rs +176 -0
  31. data/src/rules/gemspec/ruby_version_globals_usage.rs +53 -0
  32. data/src/rules/gemspec/support.rs +105 -0
  33. data/src/rules/layout/access_modifier_indentation.rs +101 -0
  34. data/src/rules/layout/alignment.rs +409 -0
  35. data/src/rules/layout/argument_alignment.rs +160 -0
  36. data/src/rules/layout/array_alignment.rs +165 -0
  37. data/src/rules/layout/assignment_indentation.rs +76 -0
  38. data/src/rules/layout/begin_end_alignment.rs +34 -0
  39. data/src/rules/layout/block_alignment.rs +496 -0
  40. data/src/rules/layout/block_end_newline.rs +161 -0
  41. data/src/rules/layout/case_indentation.rs +107 -0
  42. data/src/rules/layout/closing_heredoc_indentation.rs +185 -0
  43. data/src/rules/layout/closing_parenthesis_indentation.rs +211 -0
  44. data/src/rules/layout/comment_indentation.rs +193 -0
  45. data/src/rules/layout/condition_position.rs +78 -0
  46. data/src/rules/layout/def_end_alignment.rs +92 -0
  47. data/src/rules/layout/dot_position.rs +144 -0
  48. data/src/rules/layout/else_alignment.rs +248 -0
  49. data/src/rules/layout/empty_comment.rs +92 -0
  50. data/src/rules/layout/empty_line_after_guard_clause.rs +381 -0
  51. data/src/rules/layout/empty_line_after_magic_comment.rs +40 -23
  52. data/src/rules/layout/empty_line_between_defs.rs +254 -0
  53. data/src/rules/layout/empty_lines.rs +85 -0
  54. data/src/rules/layout/empty_lines_around_access_modifier.rs +362 -0
  55. data/src/rules/layout/empty_lines_around_arguments.rs +183 -0
  56. data/src/rules/layout/empty_lines_around_attribute_accessor.rs +178 -0
  57. data/src/rules/layout/empty_lines_around_begin_body.rs +19 -0
  58. data/src/rules/layout/empty_lines_around_block_body.rs +39 -0
  59. data/src/rules/layout/empty_lines_around_body.rs +372 -0
  60. data/src/rules/layout/empty_lines_around_class_body.rs +30 -0
  61. data/src/rules/layout/empty_lines_around_exception_handling_keywords.rs +130 -0
  62. data/src/rules/layout/empty_lines_around_method_body.rs +80 -0
  63. data/src/rules/layout/empty_lines_around_module_body.rs +21 -0
  64. data/src/rules/layout/end_alignment.rs +258 -0
  65. data/src/rules/layout/end_of_line.rs +19 -11
  66. data/src/rules/layout/extra_spacing.rs +297 -0
  67. data/src/rules/layout/first_argument_indentation.rs +364 -0
  68. data/src/rules/layout/first_array_element_indentation.rs +189 -0
  69. data/src/rules/layout/first_hash_element_indentation.rs +229 -0
  70. data/src/rules/layout/first_parameter_indentation.rs +78 -0
  71. data/src/rules/layout/hash_alignment.rs +593 -0
  72. data/src/rules/layout/heredoc_indentation.rs +238 -0
  73. data/src/rules/layout/indentation_consistency.rs +122 -0
  74. data/src/rules/layout/indentation_style.rs +96 -0
  75. data/src/rules/layout/indentation_width.rs +663 -0
  76. data/src/rules/layout/initial_indentation.rs +62 -0
  77. data/src/rules/layout/leading_comment_space.rs +127 -0
  78. data/src/rules/layout/leading_empty_lines.rs +35 -0
  79. data/src/rules/layout/line_length.rs +925 -109
  80. data/src/rules/layout/mod.rs +80 -0
  81. data/src/rules/layout/multiline_array_brace_layout.rs +34 -0
  82. data/src/rules/layout/multiline_block_layout.rs +178 -0
  83. data/src/rules/layout/multiline_brace.rs +365 -0
  84. data/src/rules/layout/multiline_expression.rs +1053 -0
  85. data/src/rules/layout/multiline_hash_brace_layout.rs +43 -0
  86. data/src/rules/layout/multiline_method_call_brace_layout.rs +48 -0
  87. data/src/rules/layout/multiline_method_call_indentation.rs +812 -0
  88. data/src/rules/layout/multiline_method_definition_brace_layout.rs +36 -0
  89. data/src/rules/layout/multiline_operation_indentation.rs +144 -0
  90. data/src/rules/layout/parameter_alignment.rs +50 -0
  91. data/src/rules/layout/rescue_ensure_alignment.rs +232 -0
  92. data/src/rules/layout/space_after_colon.rs +68 -0
  93. data/src/rules/layout/space_after_comma.rs +30 -11
  94. data/src/rules/layout/space_after_method_name.rs +30 -0
  95. data/src/rules/layout/space_after_not.rs +34 -0
  96. data/src/rules/layout/space_after_semicolon.rs +53 -0
  97. data/src/rules/layout/space_around_block_parameters.rs +193 -0
  98. data/src/rules/layout/space_around_equals_in_parameter_default.rs +97 -0
  99. data/src/rules/layout/space_around_keyword.rs +330 -0
  100. data/src/rules/layout/space_around_method_call_operator.rs +102 -0
  101. data/src/rules/layout/space_around_operators.rs +542 -70
  102. data/src/rules/layout/space_before_block_braces.rs +86 -0
  103. data/src/rules/layout/space_before_comma.rs +54 -0
  104. data/src/rules/layout/space_before_comment.rs +34 -0
  105. data/src/rules/layout/space_before_first_arg.rs +105 -0
  106. data/src/rules/layout/space_before_semicolon.rs +67 -0
  107. data/src/rules/layout/space_in_lambda_literal.rs +61 -0
  108. data/src/rules/layout/space_inside_array_literal_brackets.rs +430 -0
  109. data/src/rules/layout/space_inside_array_percent_literal.rs +76 -0
  110. data/src/rules/layout/space_inside_block_braces.rs +382 -0
  111. data/src/rules/layout/space_inside_hash_literal_braces.rs +282 -0
  112. data/src/rules/layout/space_inside_parens.rs +10 -4
  113. data/src/rules/layout/space_inside_percent_literal_delimiters.rs +100 -0
  114. data/src/rules/layout/space_inside_range_literal.rs +157 -0
  115. data/src/rules/layout/space_inside_reference_brackets.rs +205 -0
  116. data/src/rules/layout/space_inside_string_interpolation.rs +117 -0
  117. data/src/rules/layout/support.rs +856 -1
  118. data/src/rules/layout/tokens.rs +358 -0
  119. data/src/rules/layout/trailing_empty_lines.rs +27 -15
  120. data/src/rules/layout/trailing_whitespace.rs +118 -13
  121. data/src/rules/lint/access_modifier.rs +262 -0
  122. data/src/rules/lint/ambiguity.rs +117 -0
  123. data/src/rules/lint/ambiguous_block_association.rs +285 -0
  124. data/src/rules/lint/ambiguous_operator.rs +39 -0
  125. data/src/rules/lint/ambiguous_regexp_literal.rs +18 -0
  126. data/src/rules/lint/assignment_in_condition.rs +234 -0
  127. data/src/rules/lint/big_decimal_new.rs +57 -0
  128. data/src/rules/lint/binary_operator_with_identical_operands.rs +61 -0
  129. data/src/rules/lint/blocks.rs +114 -0
  130. data/src/rules/lint/boolean_symbol.rs +112 -0
  131. data/src/rules/lint/circular_argument_reference.rs +72 -0
  132. data/src/rules/lint/conditions.rs +87 -0
  133. data/src/rules/lint/constant_definition_in_block.rs +62 -0
  134. data/src/rules/lint/cop_directives.rs +168 -0
  135. data/src/rules/lint/debugger.rs +224 -0
  136. data/src/rules/lint/deprecated_class_methods.rs +155 -0
  137. data/src/rules/lint/deprecated_open_ssl_constant.rs +229 -0
  138. data/src/rules/lint/disjunctive_assignment_in_constructor.rs +65 -0
  139. data/src/rules/lint/duplicate_case_condition.rs +43 -0
  140. data/src/rules/lint/duplicate_elsif_condition.rs +33 -0
  141. data/src/rules/lint/duplicate_hash_key.rs +31 -0
  142. data/src/rules/lint/duplicate_methods.rs +768 -41
  143. data/src/rules/lint/duplicate_require.rs +162 -0
  144. data/src/rules/lint/duplicate_rescue_exception.rs +53 -0
  145. data/src/rules/lint/each_with_object_argument.rs +60 -0
  146. data/src/rules/lint/else_layout.rs +114 -0
  147. data/src/rules/lint/empty_conditional_body.rs +166 -0
  148. data/src/rules/lint/empty_ensure.rs +28 -0
  149. data/src/rules/lint/empty_expression.rs +16 -0
  150. data/src/rules/lint/empty_file.rs +22 -0
  151. data/src/rules/lint/empty_interpolation.rs +53 -0
  152. data/src/rules/lint/empty_when.rs +102 -0
  153. data/src/rules/lint/ensure_return.rs +69 -0
  154. data/src/rules/lint/erb_new_arguments.rs +157 -0
  155. data/src/rules/lint/exception_hierarchy.rs +451 -0
  156. data/src/rules/lint/flip_flop.rs +16 -0
  157. data/src/rules/lint/float_comparison.rs +250 -0
  158. data/src/rules/lint/float_out_of_range.rs +40 -0
  159. data/src/rules/lint/flow.rs +167 -0
  160. data/src/rules/lint/format_parameter_mismatch.rs +275 -0
  161. data/src/rules/lint/format_string.rs +328 -0
  162. data/src/rules/lint/hash_compare_by_identity.rs +76 -0
  163. data/src/rules/lint/identity_comparison.rs +83 -0
  164. data/src/rules/lint/implicit_string_concatenation.rs +126 -0
  165. data/src/rules/lint/ineffective_access_modifier.rs +137 -0
  166. data/src/rules/lint/inherit_exception.rs +111 -0
  167. data/src/rules/lint/interpolation_check.rs +132 -0
  168. data/src/rules/lint/literal_as_condition.rs +483 -0
  169. data/src/rules/lint/literal_in_interpolation.rs +485 -0
  170. data/src/rules/lint/literals.rs +252 -0
  171. data/src/rules/lint/locals.rs +65 -0
  172. data/src/rules/lint/loop.rs +76 -0
  173. data/src/rules/lint/missing_cop_enable_directive.rs +62 -0
  174. data/src/rules/lint/missing_super.rs +150 -0
  175. data/src/rules/lint/mixed_regexp_capture_types.rs +21 -0
  176. data/src/rules/lint/mod.rs +135 -0
  177. data/src/rules/lint/multiple_comparison.rs +59 -0
  178. data/src/rules/lint/nested_method_definition.rs +112 -0
  179. data/src/rules/lint/nested_percent_literal.rs +35 -0
  180. data/src/rules/lint/next_without_accumulator.rs +70 -0
  181. data/src/rules/lint/nil_methods.rs +93 -0
  182. data/src/rules/lint/node_equality.rs +265 -0
  183. data/src/rules/lint/non_deterministic_require_order.rs +232 -0
  184. data/src/rules/lint/non_local_exit_from_iterator.rs +95 -0
  185. data/src/rules/lint/ordered_magic_comments.rs +73 -0
  186. data/src/rules/lint/out_of_range_regexp_ref.rs +247 -0
  187. data/src/rules/lint/parameters.rs +68 -0
  188. data/src/rules/lint/parentheses_as_grouped_expression.rs +81 -0
  189. data/src/rules/lint/percent_literal.rs +34 -0
  190. data/src/rules/lint/percent_string_array.rs +73 -0
  191. data/src/rules/lint/percent_symbol_array.rs +52 -0
  192. data/src/rules/lint/raise_exception.rs +108 -0
  193. data/src/rules/lint/rand_one.rs +48 -0
  194. data/src/rules/lint/ranges.rs +29 -0
  195. data/src/rules/lint/redundant_cop_disable_directive.rs +472 -0
  196. data/src/rules/lint/redundant_cop_enable_directive.rs +238 -0
  197. data/src/rules/lint/redundant_require_statement.rs +91 -0
  198. data/src/rules/lint/redundant_safe_navigation.rs +227 -0
  199. data/src/rules/lint/redundant_splat_expansion.rs +266 -0
  200. data/src/rules/lint/redundant_string_coercion.rs +108 -0
  201. data/src/rules/lint/redundant_with_index.rs +94 -0
  202. data/src/rules/lint/redundant_with_object.rs +79 -0
  203. data/src/rules/lint/regexp.rs +130 -0
  204. data/src/rules/lint/regexp_as_condition.rs +45 -0
  205. data/src/rules/lint/require_parentheses.rs +69 -0
  206. data/src/rules/lint/rescue_clause.rs +76 -0
  207. data/src/rules/lint/rescue_exception.rs +24 -0
  208. data/src/rules/lint/rescue_type.rs +68 -0
  209. data/src/rules/lint/return_in_void_context.rs +80 -0
  210. data/src/rules/lint/safe_navigation_chain.rs +306 -0
  211. data/src/rules/lint/safe_navigation_consistency.rs +234 -0
  212. data/src/rules/lint/safe_navigation_with_empty.rs +61 -0
  213. data/src/rules/lint/script_permission.rs +42 -0
  214. data/src/rules/lint/self_assignment.rs +413 -0
  215. data/src/rules/lint/send_with_mixin_argument.rs +81 -0
  216. data/src/rules/lint/shadowed_argument.rs +212 -0
  217. data/src/rules/lint/shadowed_exception.rs +147 -0
  218. data/src/rules/lint/statements.rs +169 -0
  219. data/src/rules/lint/struct_new_override.rs +176 -0
  220. data/src/rules/lint/suppressed_exception.rs +51 -0
  221. data/src/rules/lint/syntax.rs +756 -100
  222. data/src/rules/lint/to_json.rs +38 -0
  223. data/src/rules/lint/top_level_return_with_argument.rs +36 -0
  224. data/src/rules/lint/trailing_comma_in_attribute_declaration.rs +48 -0
  225. data/src/rules/lint/underscore_prefixed_variable_name.rs +32 -0
  226. data/src/rules/lint/unified_integer.rs +51 -0
  227. data/src/rules/lint/unreachable_code.rs +63 -0
  228. data/src/rules/lint/unreachable_loop.rs +313 -0
  229. data/src/rules/lint/unused_block_argument.rs +118 -60
  230. data/src/rules/lint/unused_method_argument.rs +254 -0
  231. data/src/rules/lint/uri_escape_unescape.rs +54 -0
  232. data/src/rules/lint/uri_regexp.rs +61 -0
  233. data/src/rules/lint/useless_access_modifier.rs +299 -0
  234. data/src/rules/lint/useless_assignment.rs +538 -82
  235. data/src/rules/lint/useless_else_without_rescue.rs +39 -0
  236. data/src/rules/lint/useless_method_definition.rs +134 -0
  237. data/src/rules/lint/useless_setter_call.rs +235 -0
  238. data/src/rules/lint/useless_times.rs +288 -0
  239. data/src/rules/lint/variable_force.rs +1736 -0
  240. data/src/rules/lint/void.rs +653 -0
  241. data/src/rules/metrics/abc_size.rs +277 -0
  242. data/src/rules/metrics/block_length.rs +58 -19
  243. data/src/rules/metrics/block_nesting.rs +146 -0
  244. data/src/rules/metrics/class_length.rs +45 -16
  245. data/src/rules/metrics/complexity.rs +1202 -0
  246. data/src/rules/metrics/cyclomatic_complexity.rs +71 -0
  247. data/src/rules/metrics/fragments.rs +198 -0
  248. data/src/rules/metrics/locals.rs +634 -0
  249. data/src/rules/metrics/method_length.rs +62 -2
  250. data/src/rules/metrics/mod.rs +10 -0
  251. data/src/rules/metrics/module_length.rs +49 -8
  252. data/src/rules/metrics/parameter_lists.rs +163 -13
  253. data/src/rules/metrics/perceived_complexity.rs +160 -0
  254. data/src/rules/metrics/support.rs +208 -40
  255. data/src/rules/migration/department_name.rs +196 -0
  256. data/src/rules/migration/mod.rs +6 -0
  257. data/src/rules/mod.rs +220 -10
  258. data/src/rules/naming/accessor_method_name.rs +41 -0
  259. data/src/rules/naming/ascii_identifiers.rs +46 -9
  260. data/src/rules/naming/binary_operator_parameter_name.rs +102 -0
  261. data/src/rules/naming/block_parameter_name.rs +17 -0
  262. data/src/rules/naming/class_and_module_camel_case.rs +32 -0
  263. data/src/rules/naming/constant_name.rs +254 -20
  264. data/src/rules/naming/file_name.rs +98 -0
  265. data/src/rules/naming/heredoc_delimiter_case.rs +49 -0
  266. data/src/rules/naming/heredoc_delimiter_naming.rs +37 -0
  267. data/src/rules/naming/memoized_instance_variable_name.rs +366 -0
  268. data/src/rules/naming/method_name.rs +262 -42
  269. data/src/rules/naming/method_parameter_name.rs +17 -0
  270. data/src/rules/naming/mod.rs +15 -1
  271. data/src/rules/naming/predicate_prefix.rs +129 -0
  272. data/src/rules/naming/rescued_exceptions_variable_name.rs +277 -0
  273. data/src/rules/naming/support.rs +831 -9
  274. data/src/rules/naming/uncommunicative.rs +93 -0
  275. data/src/rules/naming/variable_name.rs +39 -34
  276. data/src/rules/naming/variable_number.rs +212 -0
  277. data/src/rules/node_ext.rs +190 -0
  278. data/src/rules/ordered_gem.rs +165 -0
  279. data/src/rules/regex_cache.rs +59 -0
  280. data/src/rules/security/eval.rs +17 -11
  281. data/src/rules/security/json_load.rs +55 -0
  282. data/src/rules/security/marshal_load.rs +48 -0
  283. data/src/rules/security/mod.rs +4 -0
  284. data/src/rules/security/open.rs +138 -0
  285. data/src/rules/security/yaml_load.rs +42 -0
  286. data/src/rules/send_node.rs +253 -0
  287. data/src/rules/style/access_modifier_declarations.rs +459 -0
  288. data/src/rules/style/accessor_grouping.rs +351 -0
  289. data/src/rules/style/alias.rs +266 -0
  290. data/src/rules/style/and_or.rs +319 -0
  291. data/src/rules/style/array_join.rs +42 -0
  292. data/src/rules/style/attr.rs +98 -0
  293. data/src/rules/style/bare_percent_literals.rs +43 -0
  294. data/src/rules/style/begin_block.rs +13 -0
  295. data/src/rules/style/bisected_attr_accessor.rs +262 -0
  296. data/src/rules/style/block_args.rs +59 -0
  297. data/src/rules/style/block_comments.rs +85 -0
  298. data/src/rules/style/block_delimiters.rs +484 -0
  299. data/src/rules/style/case_equality.rs +167 -0
  300. data/src/rules/style/case_like_if.rs +328 -0
  301. data/src/rules/style/character_literal.rs +50 -0
  302. data/src/rules/style/class_and_module_children.rs +276 -0
  303. data/src/rules/style/class_check.rs +39 -0
  304. data/src/rules/style/class_equality_comparison.rs +218 -0
  305. data/src/rules/style/class_methods.rs +72 -0
  306. data/src/rules/style/class_vars.rs +61 -0
  307. data/src/rules/style/colon_method_call.rs +57 -0
  308. data/src/rules/style/colon_method_definition.rs +31 -0
  309. data/src/rules/style/combinable_loops.rs +215 -0
  310. data/src/rules/style/command_literal.rs +105 -0
  311. data/src/rules/style/comment_annotation.rs +138 -0
  312. data/src/rules/style/commented_keyword.rs +102 -0
  313. data/src/rules/style/comments.rs +225 -0
  314. data/src/rules/style/conditional.rs +138 -0
  315. data/src/rules/style/conditional_assignment.rs +599 -0
  316. data/src/rules/style/def_with_parentheses.rs +58 -0
  317. data/src/rules/style/dir.rs +63 -0
  318. data/src/rules/style/documentation.rs +343 -0
  319. data/src/rules/style/double_cop_disable_directive.rs +65 -0
  320. data/src/rules/style/double_negation.rs +260 -0
  321. data/src/rules/style/each_for_simple_loop.rs +110 -0
  322. data/src/rules/style/each_with_object.rs +242 -0
  323. data/src/rules/style/empty_block_parameter.rs +37 -0
  324. data/src/rules/style/empty_case_condition.rs +224 -0
  325. data/src/rules/style/empty_else.rs +129 -0
  326. data/src/rules/style/empty_lambda_parameter.rs +31 -0
  327. data/src/rules/style/empty_literal.rs +222 -0
  328. data/src/rules/style/empty_method.rs +104 -0
  329. data/src/rules/style/encoding.rs +52 -0
  330. data/src/rules/style/end_block.rs +22 -0
  331. data/src/rules/style/eval_with_location.rs +382 -0
  332. data/src/rules/style/even_odd.rs +110 -0
  333. data/src/rules/style/expand_path_arguments.rs +230 -0
  334. data/src/rules/style/explicit_block_argument.rs +215 -0
  335. data/src/rules/style/exponential_notation.rs +84 -0
  336. data/src/rules/style/float_division.rs +225 -0
  337. data/src/rules/style/for.rs +145 -0
  338. data/src/rules/style/format_sequences.rs +329 -0
  339. data/src/rules/style/format_string.rs +288 -0
  340. data/src/rules/style/format_string_token.rs +357 -0
  341. data/src/rules/style/frozen_string.rs +87 -0
  342. data/src/rules/style/frozen_string_literal_comment.rs +173 -40
  343. data/src/rules/style/global_std_stream.rs +91 -0
  344. data/src/rules/style/global_vars.rs +34 -0
  345. data/src/rules/style/guard_clause.rs +622 -0
  346. data/src/rules/style/hash_as_last_array_item.rs +188 -0
  347. data/src/rules/style/hash_each_methods.rs +403 -0
  348. data/src/rules/style/hash_like_case.rs +164 -0
  349. data/src/rules/style/hash_syntax.rs +96 -26
  350. data/src/rules/style/hash_transform.rs +472 -0
  351. data/src/rules/style/hash_transform_keys.rs +6 -0
  352. data/src/rules/style/hash_transform_values.rs +6 -0
  353. data/src/rules/style/identical_conditional_branches.rs +368 -0
  354. data/src/rules/style/if_inside_else.rs +264 -0
  355. data/src/rules/style/if_unless_modifier.rs +844 -0
  356. data/src/rules/style/if_unless_modifier_of_if_unless.rs +63 -0
  357. data/src/rules/style/if_with_semicolon.rs +327 -0
  358. data/src/rules/style/infinite_loop.rs +199 -0
  359. data/src/rules/style/inverse_methods.rs +390 -0
  360. data/src/rules/style/keyword_parameters_order.rs +159 -0
  361. data/src/rules/style/lambda.rs +352 -0
  362. data/src/rules/style/lambda_call.rs +80 -0
  363. data/src/rules/style/line_end_concatenation.rs +124 -0
  364. data/src/rules/style/line_length_help.rs +262 -0
  365. data/src/rules/style/literal.rs +452 -0
  366. data/src/rules/style/method_call_without_args_parentheses.rs +154 -0
  367. data/src/rules/style/method_def_parentheses.rs +120 -0
  368. data/src/rules/style/min_max.rs +67 -0
  369. data/src/rules/style/missing_respond_to_missing.rs +69 -0
  370. data/src/rules/style/mixin_grouping.rs +163 -0
  371. data/src/rules/style/mixin_usage.rs +66 -0
  372. data/src/rules/style/mod.rs +185 -0
  373. data/src/rules/style/module_function.rs +76 -0
  374. data/src/rules/style/multiline_block_chain.rs +64 -0
  375. data/src/rules/style/multiline_if_modifier.rs +107 -0
  376. data/src/rules/style/multiline_if_then.rs +42 -0
  377. data/src/rules/style/multiline_memoization.rs +107 -0
  378. data/src/rules/style/multiline_ternary_operator.rs +157 -0
  379. data/src/rules/style/multiline_when_then.rs +42 -0
  380. data/src/rules/style/multiple_comparison.rs +231 -0
  381. data/src/rules/style/mutable_constant.rs +509 -0
  382. data/src/rules/style/negated_if.rs +95 -0
  383. data/src/rules/style/negated_unless.rs +54 -0
  384. data/src/rules/style/negated_while.rs +77 -0
  385. data/src/rules/style/nested_modifier.rs +161 -0
  386. data/src/rules/style/nested_parenthesized_calls.rs +133 -0
  387. data/src/rules/style/nested_ternary_operator.rs +112 -0
  388. data/src/rules/style/next.rs +380 -0
  389. data/src/rules/style/nil_comparison.rs +117 -0
  390. data/src/rules/style/nodes.rs +110 -0
  391. data/src/rules/style/non_nil_check.rs +109 -0
  392. data/src/rules/style/not.rs +109 -0
  393. data/src/rules/style/numeric_literal_prefix.rs +136 -0
  394. data/src/rules/style/numeric_literals.rs +158 -21
  395. data/src/rules/style/numeric_predicate.rs +260 -0
  396. data/src/rules/style/one_line_conditional.rs +245 -0
  397. data/src/rules/style/optional_arguments.rs +42 -0
  398. data/src/rules/style/optional_boolean_parameter.rs +43 -0
  399. data/src/rules/style/or_assignment.rs +136 -0
  400. data/src/rules/style/parallel_assignment.rs +375 -0
  401. data/src/rules/style/parameters.rs +97 -0
  402. data/src/rules/style/parens.rs +123 -0
  403. data/src/rules/style/parentheses_around_condition.rs +210 -0
  404. data/src/rules/style/percent.rs +132 -0
  405. data/src/rules/style/percent_array.rs +326 -0
  406. data/src/rules/style/percent_literal_delimiters.rs +99 -0
  407. data/src/rules/style/percent_q_literals.rs +80 -0
  408. data/src/rules/style/perl_backrefs.rs +70 -0
  409. data/src/rules/style/preferred_hash_methods.rs +43 -0
  410. data/src/rules/style/proc.rs +41 -0
  411. data/src/rules/style/raise_args.rs +183 -0
  412. data/src/rules/style/random_with_offset.rs +169 -0
  413. data/src/rules/style/ranges.rs +32 -0
  414. data/src/rules/style/redundant_assignment.rs +165 -0
  415. data/src/rules/style/redundant_begin.rs +463 -0
  416. data/src/rules/style/redundant_capital_w.rs +47 -0
  417. data/src/rules/style/redundant_condition.rs +651 -0
  418. data/src/rules/style/redundant_conditional.rs +97 -0
  419. data/src/rules/style/redundant_exception.rs +133 -0
  420. data/src/rules/style/redundant_fetch_block.rs +145 -0
  421. data/src/rules/style/redundant_file_extension_in_require.rs +60 -0
  422. data/src/rules/style/redundant_freeze.rs +210 -0
  423. data/src/rules/style/redundant_interpolation.rs +171 -0
  424. data/src/rules/style/redundant_parentheses.rs +1104 -0
  425. data/src/rules/style/redundant_percent_q.rs +118 -0
  426. data/src/rules/style/redundant_regexp_character_class.rs +376 -0
  427. data/src/rules/style/redundant_regexp_escape.rs +195 -0
  428. data/src/rules/style/redundant_return.rs +168 -21
  429. data/src/rules/style/redundant_self.rs +523 -0
  430. data/src/rules/style/redundant_self_assignment.rs +137 -0
  431. data/src/rules/style/redundant_sort.rs +172 -0
  432. data/src/rules/style/redundant_sort_by.rs +70 -0
  433. data/src/rules/style/regexp_literal.rs +224 -0
  434. data/src/rules/style/rescue_modifier.rs +109 -0
  435. data/src/rules/style/rescue_standard_error.rs +66 -0
  436. data/src/rules/style/safe_navigation.rs +1007 -0
  437. data/src/rules/style/sample.rs +245 -0
  438. data/src/rules/style/self_assignment.rs +108 -0
  439. data/src/rules/style/semicolon.rs +246 -33
  440. data/src/rules/style/signal_exception.rs +108 -0
  441. data/src/rules/style/single_argument_dig.rs +105 -0
  442. data/src/rules/style/single_line_methods.rs +367 -0
  443. data/src/rules/style/slicing_with_range.rs +263 -0
  444. data/src/rules/style/sole_nested_conditional.rs +503 -0
  445. data/src/rules/style/special_global_vars.rs +329 -0
  446. data/src/rules/style/stabby_lambda_parentheses.rs +71 -0
  447. data/src/rules/style/stderr_puts.rs +46 -0
  448. data/src/rules/style/string_concatenation.rs +503 -0
  449. data/src/rules/style/string_literals.rs +508 -34
  450. data/src/rules/style/string_literals_in_interpolation.rs +45 -0
  451. data/src/rules/style/strip.rs +53 -0
  452. data/src/rules/style/struct_inheritance.rs +178 -0
  453. data/src/rules/style/symbol_array.rs +170 -0
  454. data/src/rules/style/symbol_literal.rs +43 -0
  455. data/src/rules/style/symbol_proc.rs +356 -0
  456. data/src/rules/style/ternary_parentheses.rs +288 -0
  457. data/src/rules/style/trailing_body.rs +103 -0
  458. data/src/rules/style/trailing_body_on_class.rs +10 -0
  459. data/src/rules/style/trailing_body_on_method_definition.rs +20 -0
  460. data/src/rules/style/trailing_body_on_module.rs +10 -0
  461. data/src/rules/style/trailing_comma.rs +119 -0
  462. data/src/rules/style/trailing_comma_in_arguments.rs +72 -0
  463. data/src/rules/style/trailing_comma_in_array_literal.rs +24 -0
  464. data/src/rules/style/trailing_comma_in_hash_literal.rs +25 -0
  465. data/src/rules/style/trailing_method_end_statement.rs +38 -0
  466. data/src/rules/style/trailing_underscore_variable.rs +151 -0
  467. data/src/rules/style/trivial_accessors.rs +339 -0
  468. data/src/rules/style/unless_else.rs +72 -0
  469. data/src/rules/style/unpack_first.rs +91 -0
  470. data/src/rules/style/variable_interpolation.rs +45 -0
  471. data/src/rules/style/when_then.rs +46 -0
  472. data/src/rules/style/while_until_do.rs +39 -0
  473. data/src/rules/style/while_until_modifier.rs +288 -0
  474. data/src/rules/style/word_array.rs +188 -0
  475. data/src/rules/style/yoda_condition.rs +154 -0
  476. data/src/rules/style/zero_length_predicate.rs +185 -0
  477. data/src/rules/support.rs +421 -8
  478. data/src/source.rs +35 -0
  479. metadata +424 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c2383add6601b741d516e8efb4116d217555568a60b08517aa294e42a47fac4
4
- data.tar.gz: 65fcd7157dbd75afe11b9f89d069c5c89a34a907517b1c83b022716477d6e373
3
+ metadata.gz: 11d1ded7e100a86c067bfd508cf4b7b3b402e2c964fb5bcbabb4f95e800551dc
4
+ data.tar.gz: 2e06b2ad86065842b5fda6dd9b41b798175a87b70633a205fd73fddaa611e1e2
5
5
  SHA512:
6
- metadata.gz: 410780fd39347da7d2b707a0573ec6ad26d3ed852929abc37d88d492fd499eaf4286790ad09137fb737b173358c8073804aa8d4afd7e6b631630f5a740d6e0c2
7
- data.tar.gz: c0905dee79e639abd4cb472b7d7a18c60c198b34f20cef3f84d6ac6a878ce95a2c6fc7e65c7b9e6f90b6e4c32b7da4f0ca7d325ca0f7f4a49cd94c3260434417
6
+ metadata.gz: e52e6bd4a26c68bd2ed6bda10bfba3772e6760c1573d81c6212391cd0bf1acdccece6b102eed1d9bbf9c2e652556772f12fa626628bb6266d9eee7d94826c314
7
+ data.tar.gz: 240b544b1e90d355b92025d226d4cdd4efd358e81e9bc26449d5e9f641505230c510b4389487efc936d8e6fcb8fcf281c7b18180ade2a6c0b66fe18c980912df
data/CONFORMANCE.md CHANGED
@@ -1,109 +1,195 @@
1
1
  # RuboCop conformance
2
2
 
3
- Snapshot date: 2026-08-10
4
- Reference: RuboCop 1.89.0
5
- Corpus: the complete 1,759-file RuboCop source tree
6
- Configuration: RuboCop 1.89.0 built-in defaults (`--force-default-config`)
3
+ Snapshot date: 2026-08-14
4
+ Reference: RuboCop 1.89.0 on Ruby 4.0.6
5
+ Configuration: RuboCop 1.89.0 built-in defaults (`--force-default-config`) on both sides
7
6
 
8
7
  The bundled `config/default.yml` records the upstream version it was vendored from in its header.
9
8
  Re-fetch it with `scripts/sync_default_yml.sh <rubocop-version>`.
10
9
 
11
- The comparison covers every cop Sonicop implemented at the snapshot date, listed verbatim in the
12
- `cops` variable of the command below. Each JSON offense is normalized by path, cop, line, column,
13
- length, severity, correctability, and message. Run `sonicop --show-cops` for the current set.
14
-
15
- | Result | Count |
16
- |---|---:|
17
- | Reference offense locations | 4,052 |
18
- | Sonicop offense locations | 4,052 |
19
- | Matching locations | 4,052 |
20
- | False positives | **0** |
21
- | False negatives | **0** |
22
- | Location recall | **100%** |
23
- | Message/severity/correctability differences at matching locations | **0** |
24
-
25
- All offenses from the implemented cops match RuboCop by location, message, severity, and
26
- correctability. This includes RuboCop's source-line indexing for metric cops, line-length
27
- correctability and exemptions, directive lexing around heredocs, and target-version syntax errors.
28
-
29
- Run the harness against a local RuboCop and corpus. It defaults to `rubocop` on `PATH`
30
- (`gem install rubocop`) as the reference and `./target/release/sonicop` (`make release`) as the
31
- candidate, so both only need `--reference` / `--candidate` when they live elsewhere — for example
32
- `--reference "bundle exec rubocop"` inside a bundled project:
10
+ ## What is measured
33
11
 
34
- ```bash
35
- cops="Layout/EmptyLineAfterMagicComment,Layout/EndOfLine,Layout/LineLength,Layout/SpaceAfterComma,Layout/SpaceAroundOperators,Layout/SpaceInsideParens,Layout/TrailingEmptyLines,Layout/TrailingWhitespace,Lint/DuplicateMethods,Lint/Syntax,Lint/UnusedBlockArgument,Lint/UselessAssignment,Metrics/BlockLength,Metrics/ClassLength,Metrics/MethodLength,Metrics/ModuleLength,Metrics/ParameterLists,Naming/AsciiIdentifiers,Naming/ConstantName,Naming/MethodName,Naming/VariableName,Security/Eval,Style/FrozenStringLiteralComment,Style/HashSyntax,Style/NumericLiterals,Style/RedundantReturn,Style/Semicolon,Style/StringLiterals"
36
-
37
- scripts/conformance_diff.sh \
38
- --force-default-config \
39
- --cop "${cops}" \
40
- -- /path/to/rubocop
41
- ```
42
-
43
- `Lint/Syntax` is also run independently over the corpus. The latest owayo tree-sitter grammar
44
- accepts the modern syntax in all 1,759 files, while Sonicop resolves Ruby 2.6 from the corpus
45
- gemspec and applies a post-parse syntax feature gate. Its four fatal offenses, including the legacy
46
- parser recovery diagnostics, exactly match RuboCop.
47
-
48
- ## Limits of this measurement
12
+ Five Ruby projects, 18,246 target files between them, are linted by both tools and compared offense
13
+ by offense. An offense is keyed by cop name, path, line and column; at each shared key the last
14
+ line, last column, length, message, severity and correctability are compared as well.
49
15
 
50
- The 100% above is a property of *this corpus*, not a general accuracy claim. RuboCop's own source
51
- tree is RuboCop-clean and stylistically uniform, so entire classes of input never occur in it and
52
- the comparison cannot exercise them. A cop that is never triggered contributes nothing to the
53
- match count, and its absence is indistinguishable from agreement.
16
+ | Corpus | Commit | Target files | Reference offenses |
17
+ |---|---|---:|---:|
18
+ | rubocop/rubocop | `f009b33` | 1,765 | 5,766 |
19
+ | rails/rails | `ce95deb` | 3,550 | 167,535 |
20
+ | ruby/ruby | `a9c656e` | 7,465 | 756,427 |
21
+ | Homebrew/brew | `6cf9b12` | 2,176 | 49,733 |
22
+ | mastodon/mastodon | `3f5c32f` | 3,290 | 15,286 |
54
23
 
55
- Two implemented cops are confirmed to produce **zero offenses on both sides** over this corpus, so
56
- this run says nothing about them:
24
+ ruby/ruby's reference had to be assembled from chunked runs, and 15 files under `test/ruby/` are
25
+ excluded from it: RuboCop finds offenses in them whose text is not valid UTF-8, and its JSON
26
+ formatter raises `source sequence is illegal/malformed utf-8` rather than emitting a document. The
27
+ comparison for that corpus therefore covers 7,450 of its 7,465 files.
57
28
 
58
- | Cop | Offenses in this corpus | Verified elsewhere |
59
- |---|---:|---|
60
- | `Layout/TrailingEmptyLines` | 0 | Diverges on line, column, length, and message for a file ending in a blank line |
61
- | `Naming/AsciiIdentifiers` | 0 | Rails contains exactly two occurrences; both diverge on message casing |
29
+ The **target file lists match exactly** on all five — not just the counts but the paths. That is
30
+ worth stating separately because file discovery is where a port silently diverges first: RuboCop
31
+ never reads `.gitignore`, applies a shebang test only to extensionless files, descends through
32
+ directory symlinks, and treats a hidden path by its *first* component rather than by any dot in it.
62
33
 
63
- Running the same harness over application code rather than linter source surfaces divergences this
64
- corpus does not. Treat a clean run on any single corpus as evidence about that corpus.
34
+ Sonicop implements **394 of RuboCop's 609 cops**: every cop RuboCop enables by default, matched name
35
+ for name, with nothing extra on either side. The other 215 are the ones RuboCop ships switched off —
36
+ 159 `Enabled: pending` and 56 `Enabled: false` — and are not implemented yet.
65
37
 
66
- Two properties of the harness itself also bound what a run can tell you:
38
+ Because the two enabled sets are identical, neither side is restricted with `--only`; the plain run
39
+ is already like for like. RuboCop's extension plugins are not installed, so this measures the range
40
+ that can be checked without them.
67
41
 
68
- - Only the cops passed to `--cop` are compared. Cops that RuboCop reports and Sonicop has not
69
- implemented show up as false negatives, which is expected rather than a defect.
70
- - RuboCop emits **only** `Lint/Syntax` for a file it considers fatally unparseable and suppresses
71
- every other cop for that file, while Sonicop keeps inspecting it. Whenever the two disagree
72
- about syntax validity — most easily triggered by a `TargetRubyVersion` the source does not
73
- satisfy — the false-positive count is dominated by that single behaviour rather than by cop
74
- logic. Check the reference output for `"severity": "fatal"` before reading a large false-positive
75
- count as a cop problem.
76
-
77
- Closing these gaps properly means migrating RuboCop's own spec suite, which exercises each cop
78
- against inputs written to break it. Until that lands, this document records what was measured, not
79
- an upper bound on what could differ.
80
-
81
- ## Rails configuration and scale
82
-
83
- Rails source snapshot: `adf307b03b4241cbc0ed3821faf3b153ca6cd5cd` (Rails 8.2.0.alpha).
84
- The reference process uses RuboCop 1.89.0 with the five plugins declared by Rails:
85
- rubocop-minitest 0.40.0, rubocop-packaging 0.6.0, rubocop-performance 1.26.1,
86
- rubocop-rails 2.36.0, and rubocop-md 2.0.4.
42
+ ```bash
43
+ rubocop --force-default-config --cache false -f json
44
+ sonicop --force-default-config --format json
45
+ ```
87
46
 
88
- This corpus exercises `AllCops/DisabledByDefault`, external plugin cops, recursive excludes,
89
- hidden paths, and the inherited `guides/.rubocop.yml` configuration.
47
+ ## Results
90
48
 
91
- | Compatibility check | RuboCop | Sonicop | Result |
49
+ | Corpus | Excess | Missing | Field differences |
92
50
  |---|---:|---:|---|
93
- | Ruby target files from repository root | 3,453 | 3,453 | Exact paths and order |
94
- | Enabled cops for `activerecord/lib/active_record.rb` | 107 | 107 | Exact names and order |
95
- | Enabled cops for a file under `guides/` | 102 | 102 | Exact names and order |
96
- | Full-tree false positives | — | 0 | Pass |
97
-
98
- RuboCop also discovers 75 Markdown targets through rubocop-md. Markdown extraction is outside
99
- Sonicop's current parser scope, so those files are removed before comparing `-L` output.
100
-
101
- The full 3,453-Ruby-file offense comparison reports 10 reference locations and no Sonicop
102
- locations: zero false positives and 10 visible false negatives. The missing locations are three
103
- `Style/RedundantPercentQ`, two `Lint/RedundantCopDisableDirective`, two
104
- `Layout/IndentationWidth`, two unsupported `Lint/DuplicateMethods` cases, and one
105
- `Lint/Debugger`. Artifacts: `/tmp/sonicop-conformance-rails-full-2`.
106
-
107
- On the 402-file `activerecord/lib` subset, a warmed single run with cache and color disabled took
108
- 4.37 seconds for RuboCop and 0.35 seconds for Sonicop (`/usr/bin/time -p`): approximately
109
- **12.5x faster** on the validation machine.
51
+ | rubocop/rubocop | 0 | 0 | none |
52
+ | rails/rails | 0 | 0 | none |
53
+ | mastodon/mastodon | 0 | 0 | none |
54
+ | Homebrew/brew | 254 | 999 | none |
55
+ | ruby/ruby | 156 | 616 | 5 |
56
+
57
+ Three of the five agree offense for offense — RuboCop's own tree, Rails and Mastodon — with no
58
+ excess, no shortfall and no metadata differences anywhere.
59
+
60
+ Homebrew's remaining difference is entirely `Lint/Syntax`; every other cop matches exactly. Much of
61
+ ruby/ruby's is too, with 116 of the 616 missing and 44 of the 156 excess being `Lint/Syntax` itself,
62
+ and the rest follows from it: a file the two disagree about is inspected by one tool and skipped by
63
+ the other, so every offense in it lands on one side of the ledger. That is why the residue there is
64
+ spread thinly across the Layout department instead of sitting in one cop. Both are explained under
65
+ *Known divergences*.
66
+
67
+ ruby/ruby also carries five differences at positions both tools reported: two `correctable` flags and
68
+ one `last_column`/`length` pair, all on indentation cops inside files the two parse differently, plus
69
+ one `Lint/Syntax` message naming a different token (`tLCURLY` where RuboCop says `tLAMBEG`).
70
+
71
+ Autocorrect is compared the same way, byte for byte over the whole tree: run `-A` with both tools
72
+ from a clean checkout and diff the results.
73
+
74
+ | Corpus | Corrected tree |
75
+ |---|---|
76
+ | rubocop/rubocop | **identical** |
77
+ | mastodon/mastodon | **identical** |
78
+ | Homebrew/brew | 2 files differ |
79
+ | rails/rails | 25 files differ |
80
+ | ruby/ruby | not measurable |
81
+
82
+ RuboCop's own tree and Mastodon are the hard line: a change that breaks byte equality on either is a
83
+ regression to be fixed, not a new known divergence to be recorded.
84
+
85
+ The rails/rails residue is one shape, and Homebrew contributes one more of it: the body of a
86
+ `begin`/`rescue`/`end` ends up indented two columns off. It appears only in a full `-A` run — the
87
+ first-pass detection matches exactly, and reducing the case to a single file reproduces nothing —
88
+ because what differs is which correction pass a nested indentation fix lands in. Homebrew's other
89
+ file pairs a `disable`/`enable` around `Lint/EmptyBlock`, a cop RuboCop ships as pending; the
90
+ upstream run leaves both comments alone and Sonicop removes them.
91
+
92
+ ruby/ruby cannot be measured this way at all: RuboCop's own run does not finish on it (see *Reading a
93
+ measurement*), so there is no complete reference tree to diff against.
94
+
95
+ Run the comparison on a copy of the corpus. Autocorrect rewrites the tree in place, so a run that
96
+ shares a checkout with anything else — another comparison, a lint measurement — has both tools
97
+ reading different files and reports differences that are not there.
98
+
99
+ ## Reading a measurement
100
+
101
+ Two traps make a run look better or worse than it is, and both have produced wrong conclusions here.
102
+
103
+ **Counts cancel out.** Comparing offense totals hides an excess and a shortfall of the same size.
104
+ Compare the *set* of locations, then compare fields at the locations both tools produced.
105
+
106
+ **RuboCop can stop early and still emit valid JSON.** On ruby/ruby its parser raises
107
+ `invalid byte sequence in UTF-8` on `test/ruby/test_regexp.rb` and the whole run unwinds; the JSON
108
+ formatter still writes a well-formed document from an `ensure` block. The result reports 6,924 of
109
+ 7,465 files inspected, and the 541 files after that one in sort order were never looked at — so
110
+ every offense Sonicop found in them counts as "excess". Check `summary.inspected_file_count`
111
+ against `summary.target_file_count` on every run. To get a complete reference for that corpus, split
112
+ the file list into chunks and re-run each chunk past the file that killed it.
113
+
114
+ **And sometimes it emits nothing at all.** Fifteen files under `test/ruby/` hold offenses whose text
115
+ is not valid UTF-8, and `JSONFormatter#finished` raises `source sequence is illegal/malformed utf-8`
116
+ while serializing — before a single byte reaches stdout. The failure therefore looks like an empty
117
+ file rather than a truncated one, and a chunked reference run has to tell it apart from the case
118
+ above: retrying one file at a time is right when a file killed the parser, and useless when the run
119
+ never had a chance to start. Two other stdout notices break JSON the same way — `--parallel` being
120
+ ignored under `--cache false`, and the plugin suggestions RuboCop prints after a run — so a reference
121
+ reader should skip to the first `{` rather than trust byte zero.
122
+
123
+ ## Known divergences
124
+
125
+ `tests/conformance/known_divergences.yml` is the machine-checked record: a divergence that is listed
126
+ but no longer reproduces fails the test, and so does one that appears without being listed.
127
+
128
+ ### Error recovery after a syntax error
129
+
130
+ RuboCop parses with `parser`, an LALR parser that recovers from an error and keeps going, emitting
131
+ further diagnostics from the recovered state. Sonicop parses with tree-sitter, which does not model
132
+ that recovery, so the follow-on diagnostics cannot be reproduced. On Homebrew this accounts for the
133
+ 999 missing `Lint/Syntax` offenses: `class definition in method body`, `dynamic constant assignment`,
134
+ `cannot assign to a keyword`, and repeated `unexpected token` inside one multi-line hash.
135
+
136
+ What the divergence does not change is **which** files are held to be unparseable, and that is what
137
+ decides whether a file is inspected at all: RuboCop runs no cop other than `Lint/Syntax` on a file
138
+ that does not parse, and Sonicop does the same. On Homebrew both tools flag the same 568 files —
139
+ neither has one the other lacks. This is why every cop other than `Lint/Syntax` matches exactly there
140
+ despite the 1,253 offenses of difference.
141
+
142
+ Within those 568 files the agreement is partial, as recovery cannot be reproduced: 498 report the
143
+ same first diagnostic (position and message), and 223 report an identical list end to end. The 70
144
+ files whose first diagnostic differs follow one shape — an endless method definition (`def to_s =
145
+ to_str`, valid from Ruby 3.0, rejected by the default `TargetRubyVersion: 2.7`) leaves `parser`'s
146
+ method context open, so the enclosing `class` emits `class definition in method body` when it is
147
+ finally reduced. The diagnostic is reported at the `class` keyword, far above the line that actually
148
+ failed, which puts it first in source order.
149
+
150
+ ### Grammar gaps
151
+
152
+ Where tree-sitter rejects code that Ruby accepts, Sonicop reports a syntax error and — following the
153
+ rule above — reports nothing else for that file. One such gap costs every offense in the file at
154
+ once, which makes them worth hunting: fixing eight lexer rules in the grammar fork took ruby/ruby
155
+ from 24 affected files to 5, and cut the offenses Sonicop was missing there by more than sevenfold.
156
+ What is left moves with how many cops are implemented — one unparseable file costs every offense
157
+ every cop would have reported in it — so the shortfall grows as coverage does.
158
+
159
+ What remains are constructs whose ambiguity Ruby resolves with information a grammar does not have.
160
+ `$a?0:1` is the clearest: whether `?0` is a character literal or the start of a ternary depends on
161
+ whether the token before it completed an expression, which in turn depends on knowing that `a` is a
162
+ local variable rather than a method call.
163
+
164
+ ### Encoding — the one deliberate difference
165
+
166
+ RuboCop's autocorrect ends in a plain `File.write`, so a corrected Shift_JIS file is written back as
167
+ UTF-8 while its magic comment still claims Shift_JIS. The result no longer loads: read it back as
168
+ cp932 and you get mojibake, not the source you had.
169
+
170
+ **Sonicop writes the correction back in the encoding the file declares**, and refuses to write at all
171
+ when the correction holds a character that encoding cannot represent, leaving the file untouched
172
+ rather than substituting something else. Drop-in compatibility reaches a long way, but not as far as
173
+ reproducing data loss on purpose. This is the only place Sonicop knowingly differs, and it only
174
+ shows up on files that declare a non-UTF-8 encoding — 8 of the 18,246 files measured here.
175
+
176
+ A source declaring `ASCII-8BIT` or `binary` needs no such treatment: Ruby measures it one byte at a
177
+ time, so Sonicop reads it that way too and the bytes go back out unchanged.
178
+
179
+ ## Limits
180
+
181
+ A clean run is a property of the corpora, not a general claim. `known_divergences.yml` carries the
182
+ current list of what these corpora never exercise; the main ones are non-default configuration
183
+ values, extension plugins, and Windows line endings. Cops that never fire contribute nothing to a
184
+ match count, and their silence is indistinguishable from agreement.
185
+
186
+ The 215 cops RuboCop ships switched off are a limit of a different kind. Sonicop recognizes them —
187
+ they appear in `--show-cops` and a configuration naming them still loads — but it does not implement
188
+ them, so enabling one through `.rubocop.yml` or `--only` checks nothing at all. Sonicop prints a
189
+ warning to stderr in that case rather than letting an unchecked file look clean, but a project whose
190
+ configuration leans on those cops is outside what this document measures.
191
+
192
+ Closing that gap properly means porting RuboCop's own spec suite, which exercises each cop against
193
+ inputs written to break it. Until that lands, this document records what was measured.
194
+
195
+ Timings live in the README's Performance section.
data/Cargo.lock CHANGED
@@ -243,6 +243,15 @@ version = "1.17.0"
243
243
  source = "registry+https://github.com/rust-lang/crates.io-index"
244
244
  checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
245
245
 
246
+ [[package]]
247
+ name = "encoding_rs"
248
+ version = "0.8.35"
249
+ source = "registry+https://github.com/rust-lang/crates.io-index"
250
+ checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
251
+ dependencies = [
252
+ "cfg-if",
253
+ ]
254
+
246
255
  [[package]]
247
256
  name = "equivalent"
248
257
  version = "1.0.2"
@@ -733,11 +742,12 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
733
742
 
734
743
  [[package]]
735
744
  name = "sonicop"
736
- version = "26.8.101"
745
+ version = "26.8.103"
737
746
  dependencies = [
738
747
  "anyhow",
739
748
  "assert_cmd",
740
749
  "clap",
750
+ "encoding_rs",
741
751
  "globset",
742
752
  "ignore",
743
753
  "rayon",
@@ -749,7 +759,6 @@ dependencies = [
749
759
  "tempfile",
750
760
  "tree-sitter",
751
761
  "tree-sitter-ruby",
752
- "unicode-width",
753
762
  "ureq",
754
763
  ]
755
764
 
@@ -839,7 +848,7 @@ checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782"
839
848
  [[package]]
840
849
  name = "tree-sitter-ruby"
841
850
  version = "26.8.100"
842
- source = "git+https://github.com/owayo/tree-sitter-ruby.git?rev=88a64c6c37c4c23d8d4d804c4ef9c4eca3c88b96#88a64c6c37c4c23d8d4d804c4ef9c4eca3c88b96"
851
+ source = "git+https://github.com/owayo/tree-sitter-ruby.git?rev=6408d8b7adff44f0b346517b56071460efd323b6#6408d8b7adff44f0b346517b56071460efd323b6"
843
852
  dependencies = [
844
853
  "cc",
845
854
  "tree-sitter-language",
@@ -851,12 +860,6 @@ version = "1.0.24"
851
860
  source = "registry+https://github.com/rust-lang/crates.io-index"
852
861
  checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
853
862
 
854
- [[package]]
855
- name = "unicode-width"
856
- version = "0.2.2"
857
- source = "registry+https://github.com/rust-lang/crates.io-index"
858
- checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
859
-
860
863
  [[package]]
861
864
  name = "unsafe-libyaml"
862
865
  version = "0.2.11"
data/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sonicop"
3
- version = "26.8.101"
3
+ version = "26.8.103"
4
4
  edition = "2024"
5
5
  rust-version = "1.85"
6
6
  description = "A fast, native RuboCop-compatible Ruby linter and formatter"
@@ -17,6 +17,7 @@ path = "src/main.rs"
17
17
  [dependencies]
18
18
  anyhow = "1.0.104"
19
19
  clap = { version = "4.6.4", features = ["derive", "wrap_help"] }
20
+ encoding_rs = "0.8.35"
20
21
  globset = "0.4.19"
21
22
  ignore = "0.4.31"
22
23
  rayon = "1.12.0"
@@ -27,8 +28,7 @@ serde_yaml_ng = "0.10"
27
28
  shell-words = "1.1.1"
28
29
  tempfile = "3.27.0"
29
30
  tree-sitter = "0.26.11"
30
- tree-sitter-ruby = { git = "https://github.com/owayo/tree-sitter-ruby.git", rev = "88a64c6c37c4c23d8d4d804c4ef9c4eca3c88b96" }
31
- unicode-width = "0.2.2"
31
+ tree-sitter-ruby = { git = "https://github.com/owayo/tree-sitter-ruby.git", rev = "6408d8b7adff44f0b346517b56071460efd323b6" }
32
32
  ureq = { version = "3.4", default-features = false, features = ["native-tls"] }
33
33
 
34
34
  [dev-dependencies]
@@ -38,3 +38,10 @@ assert_cmd = "2.2.2"
38
38
  lto = "thin"
39
39
  codegen-units = 1
40
40
  strip = true
41
+
42
+ # `cargo build --profile profiling` keeps the symbols a sampling profiler needs while
43
+ # generating exactly the code `--release` does.
44
+ [profile.profiling]
45
+ inherits = "release"
46
+ debug = 1
47
+ strip = "none"
data/README.ja.md CHANGED
@@ -32,7 +32,7 @@ CI へ最小限の変更で導入できます。
32
32
 
33
33
  ## 主な機能
34
34
 
35
- Layout、Lint、Metrics、Naming、Security、Style の各部門の Cop を実装しています。
35
+ Bundler、Gemspec、Layout、Lint、Metrics、Migration、Naming、Security、Style の各部門の Cop を実装しています。
36
36
  実装済みの Cop はリリースごとに増えるため、一覧はバイナリ自身が正本です。
37
37
 
38
38
  ```bash
@@ -107,12 +107,65 @@ Ruby プラグイン実行、キャッシュ再利用、カスタム Cop、実
107
107
 
108
108
  ### 適合性
109
109
 
110
- 実装済み Cop は、正規化した JSON offense を使って RuboCop 1.89 との一致を検証済みです。
111
- 本家の全 1,759 ファイルを本家既定設定で検査した現在のスナップショットは、参照 4,052 件中
112
- 4,052 件すべての位置が一致(**recall 100%**)し、**誤検出とメタデータ差もともに 0 件**です。コマンド、比較範囲、測定結果は
113
- [CONFORMANCE.md](CONFORMANCE.md) にまとめています。Rails では Ruby 対象 3,453 ファイルと
114
- ルート/`guides/` の有効 Cop 一覧が完全一致し、全体の誤検出は 0 件です。Active Record
115
- 402 ファイルの無キャッシュ実行は本家比で約 12.5 倍高速でした。
110
+ 実装済み Cop は、RuboCop 自身・Rails・Ruby・Homebrew・Mastodon の 5 プロジェクト
111
+ 計 18,246 ファイルに対して、両者とも本家既定設定で検証しています。offense は Cop 名・パス・
112
+ 行・桁・終端行・終端桁・長さ・メッセージ・重大度・修正可否のすべてで突き合わせます。
113
+
114
+ 5 つのうち 3 つが**完全一致**です。RuboCop 自身のツリー(5,766 件)、Rails(167,535 件)、
115
+ Mastodon(15,286 件)で、過剰も不足もメタデータ差もありません。対象ファイル一覧は 5 つすべてで
116
+ 一致します。残る差分は `Lint/Syntax` に集中しており、本家の LALR パーサが構文エラーから回復して
117
+ 出す追加診断を tree-sitter では再現できないことによるものです。autocorrect は RuboCop 自身のツリーと
118
+ Mastodon でバイト単位に一致します。この 2 つは死守ラインとして扱い、バイト一致が崩れた場合は
119
+ 既知差分ではなく退行として直します。
120
+
121
+ コマンド、コーパスごとの数値、この種の計測が誤った結論を導く 2 つの罠は
122
+ [CONFORMANCE.md](CONFORMANCE.md) にまとめています。
123
+
124
+ ### 性能
125
+
126
+ 適合性検証に使う 5 コーパスすべてで測定しました。両者とも同梱の既定設定
127
+ (`--force-default-config`)で走らせているためプロジェクト側の `.rubocop.yml` は読まず、
128
+ どのコーパスでも**対象ファイル一覧は完全に一致**しています。
129
+
130
+ 両者とも既定の全 Cop で走らせています。**同じ 394 Cop** が名前まで一致しているため、
131
+ どちらも絞る必要がなく、素の実行がそのまま対等な比較になります。
132
+ 各値は暖機後 2 回の最速値です。
133
+
134
+ | コーパス | ファイル | RuboCop 並列 | Sonicop 並列 | RuboCop 単一 | Sonicop 単一 |
135
+ |---|---:|---:|---:|---:|---:|
136
+ | rubocop/rubocop | 1,765 | 10.64 秒 | **2.84 秒** | 40.66 秒 | **13.60 秒** |
137
+ | mastodon/mastodon | 3,290 | 10.78 秒 | **3.13 秒** | 33.22 秒 | **13.57 秒** |
138
+ | Homebrew/brew | 2,176 | 11.13 秒 | **4.61 秒** | 37.89 秒 | **10.81 秒** |
139
+ | rails/rails | 3,550 | 23.84 秒 | **10.15 秒** | 84.93 秒 | **33.69 秒** |
140
+ | ruby/ruby | 7,465 | 95.71 秒 | **35.74 秒** | 258.87 秒 | **107.37 秒** |
141
+
142
+ 差は並列で 2.4〜3.8 倍、単一プロセスで 2.4〜3.5 倍と幅があり、1 コーパスでは代表できません。
143
+ 単一プロセスの列のほうが安定しています。エンジンそのものを測っており、
144
+ 各ツールの並列化が対象ツリーにたまたま噛み合ったかどうかに左右されないためです。
145
+
146
+ 仕事を省いて速いわけではありません。この同じ 394 Cop について、RuboCop 自身のツリーと
147
+ Mastodon では**すべての offense が一致**し、autocorrect もバイト単位で一致します。
148
+
149
+ 再現時に注意が必要な点が 2 つあります。RuboCop は **`--cache false` と併用すると
150
+ `--parallel` を黙って無効化します**。そのためここでの並列実行はキャッシュを有効にしたうえで
151
+ 実行ごとにキャッシュディレクトリを消しており、`--cache false --parallel` で計測すると
152
+ 単一プロセスを測ることになり差が過大に出ます。また RuboCop の既定は単一プロセス、
153
+ Sonicop は `--no-parallel` を渡さない限り並列です。
154
+
155
+ ```bash
156
+ # RuboCop(並列・キャッシュは毎回空・既定の全 394 Cop)
157
+ rubocop --force-default-config --cache true --cache-root "$(mktemp -d)" \
158
+ --no-color --parallel -f quiet
159
+
160
+ # Sonicop
161
+ sonicop --force-default-config --format quiet
162
+ ```
163
+
164
+ 測定機は Apple M2(8 コア)、Ruby 4.0.6(YJIT 利用可)、RubyGems 導入の RuboCop 1.89.0。
165
+ 上の値は 1 分平均のロードアベレージが 4〜29 の状態で採ったもので、**アイドル状態ではありません**。
166
+ 両者を同じ条件で測っているため倍率は保たれますが、秒数そのものは下限ではなく、静かなマシンなら
167
+ より速く出ます。コアを奪い合うものが動いていると両者とも膨らみ、その度合いは一致しないため、
168
+ 秒数そのものが重要なときは他に負荷のない状態で測ってください。
116
169
 
117
170
  ## 開発
118
171
 
data/README.md CHANGED
@@ -32,7 +32,8 @@ existing editor and CI integrations with minimal changes.
32
32
 
33
33
  ## Features
34
34
 
35
- Sonicop implements cops in the Layout, Lint, Metrics, Naming, Security, and Style departments.
35
+ Sonicop implements cops in the Bundler, Gemspec, Layout, Lint, Metrics, Migration, Naming,
36
+ Security, and Style departments.
36
37
  The implemented set grows with each release, so the binary itself is the authoritative list:
37
38
 
38
39
  ```bash
@@ -116,13 +117,67 @@ reuse, custom Ruby cops, or cops outside the implemented set.
116
117
 
117
118
  ### Conformance
118
119
 
119
- The implemented cops are verified against RuboCop 1.89 using normalized JSON offenses.
120
- On RuboCop's complete 1,759-file source tree with the upstream default configuration, the current
121
- snapshot matches all 4,052 reference offense locations (**100% recall**) with **zero false
122
- positives and zero metadata differences**. On Rails, all 3,453 Ruby target paths and the root/nested enabled-cop lists match
123
- exactly, the full tree has zero false positives, and the 402-file Active Record subset runs about
124
- 12.5x faster without cache. See [CONFORMANCE.md](CONFORMANCE.md) for the commands, scope, and
125
- measurements.
120
+ The implemented cops are verified against RuboCop 1.89.0 over five Ruby projects — RuboCop itself,
121
+ Rails, Ruby, Homebrew and Mastodon — totalling 18,246 files, with the upstream default
122
+ configuration on both sides. Every offense is compared by cop, path, line, column, last line, last
123
+ column, length, message, severity and correctability.
124
+
125
+ Three of the five match **exactly**: RuboCop's own tree (5,766 offenses), Rails (167,535) and
126
+ Mastodon (15,286), with no excess, no shortfall and no metadata differences. The target file lists
127
+ match exactly on all five. What remains is concentrated in `Lint/Syntax`, where RuboCop's LALR
128
+ parser recovers from an error and emits diagnostics a tree-sitter parse cannot reconstruct.
129
+ Autocorrect is byte-identical on RuboCop's own tree and on Mastodon, the two corpora held as a hard
130
+ line: a change that breaks byte equality there is a regression, not a new known divergence.
131
+
132
+ See [CONFORMANCE.md](CONFORMANCE.md) for the commands, the per-corpus numbers, and the two ways a
133
+ measurement of this kind can mislead you.
134
+
135
+ ### Performance
136
+
137
+ Measured over all five conformance corpora. Both tools were given their own bundled default
138
+ configuration (`--force-default-config`), so neither reads the project's `.rubocop.yml`, and on every
139
+ corpus the two resolve **the same file list**.
140
+
141
+ Both tools run their full default set — **the same 394 cops**, matched name for name — so neither
142
+ side is restricted and the comparison is like-for-like as it stands. Times are the fastest of two
143
+ warmed runs.
144
+
145
+ | Corpus | Files | RuboCop parallel | Sonicop parallel | RuboCop single | Sonicop single |
146
+ |---|---:|---:|---:|---:|---:|
147
+ | rubocop/rubocop | 1,765 | 10.64 s | **2.84 s** | 40.66 s | **13.60 s** |
148
+ | mastodon/mastodon | 3,290 | 10.78 s | **3.13 s** | 33.22 s | **13.57 s** |
149
+ | Homebrew/brew | 2,176 | 11.13 s | **4.61 s** | 37.89 s | **10.81 s** |
150
+ | rails/rails | 3,550 | 23.84 s | **10.15 s** | 84.93 s | **33.69 s** |
151
+ | ruby/ruby | 7,465 | 95.71 s | **35.74 s** | 258.87 s | **107.37 s** |
152
+
153
+ The gap is 2.4x to 3.8x in parallel and 2.4x to 3.5x single-process, so no single corpus summarizes
154
+ it. The single-process column is the steadier of the two — it measures the engines rather than how
155
+ well each one's parallelism happens to fit the tree it was pointed at.
156
+
157
+ The speed is not bought by skipping work: over those same 394 cops the two agree on **every offense**
158
+ on RuboCop's own tree and on Mastodon, and autocorrect there is byte-identical.
159
+
160
+ Two details matter for reproducing this. RuboCop **silently turns `--parallel` off when combined
161
+ with `--cache false`**, so its parallel runs here use a cache directory that is deleted before each
162
+ run rather than disabled; timing it with `--cache false --parallel` measures a single process and
163
+ overstates the difference. RuboCop's default is a single process, while Sonicop is parallel unless
164
+ `--no-parallel` is passed.
165
+
166
+ ```bash
167
+ # RuboCop, parallel, cold cache, its full default set of 394 cops
168
+ rubocop --force-default-config --cache true --cache-root "$(mktemp -d)" \
169
+ --no-color --parallel -f quiet
170
+
171
+ # Sonicop
172
+ sonicop --force-default-config --format quiet
173
+ ```
174
+
175
+ Machine: Apple M2 (8 cores), Ruby 4.0.6 with YJIT available, RubyGems-installed RuboCop 1.89.0.
176
+ The numbers above were taken with a one-minute load average between 4 and 29 — the machine was in
177
+ use, not idle. Both tools ran under the same conditions, so the ratios hold, but the absolute
178
+ seconds are not a floor: expect better on a quiet machine. Anything competing for cores inflates
179
+ both sides, and not by the same factor on each, so measure on an otherwise idle machine when the
180
+ absolute numbers matter to you.
126
181
 
127
182
  ## Development
128
183
 
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Generated from Cargo.toml by `rake version:sync`. Do not edit by hand.
4
4
  module Sonicop
5
- VERSION = '26.8.101'
5
+ VERSION = '26.8.103'
6
6
  end