rubbycop 0.49.0

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 (499) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +211 -0
  4. data/assets/logo.png +0 -0
  5. data/assets/output.html.erb +261 -0
  6. data/bin/rubbycop +17 -0
  7. data/config/default.yml +1548 -0
  8. data/config/disabled.yml +119 -0
  9. data/config/enabled.yml +1734 -0
  10. data/lib/rubbycop.rb +510 -0
  11. data/lib/rubbycop/ast/builder.rb +64 -0
  12. data/lib/rubbycop/ast/node.rb +610 -0
  13. data/lib/rubbycop/ast/node/and_node.rb +37 -0
  14. data/lib/rubbycop/ast/node/array_node.rb +48 -0
  15. data/lib/rubbycop/ast/node/case_node.rb +64 -0
  16. data/lib/rubbycop/ast/node/ensure_node.rb +25 -0
  17. data/lib/rubbycop/ast/node/for_node.rb +53 -0
  18. data/lib/rubbycop/ast/node/hash_node.rb +109 -0
  19. data/lib/rubbycop/ast/node/if_node.rb +138 -0
  20. data/lib/rubbycop/ast/node/keyword_splat_node.rb +45 -0
  21. data/lib/rubbycop/ast/node/mixin/binary_operator_node.rb +23 -0
  22. data/lib/rubbycop/ast/node/mixin/conditional_node.rb +45 -0
  23. data/lib/rubbycop/ast/node/mixin/hash_element_node.rb +125 -0
  24. data/lib/rubbycop/ast/node/mixin/modifier_node.rb +17 -0
  25. data/lib/rubbycop/ast/node/mixin/predicate_operator_node.rb +35 -0
  26. data/lib/rubbycop/ast/node/or_node.rb +37 -0
  27. data/lib/rubbycop/ast/node/pair_node.rb +64 -0
  28. data/lib/rubbycop/ast/node/resbody_node.rb +25 -0
  29. data/lib/rubbycop/ast/node/send_node.rb +209 -0
  30. data/lib/rubbycop/ast/node/until_node.rb +43 -0
  31. data/lib/rubbycop/ast/node/when_node.rb +61 -0
  32. data/lib/rubbycop/ast/node/while_node.rb +43 -0
  33. data/lib/rubbycop/ast/sexp.rb +16 -0
  34. data/lib/rubbycop/ast/traversal.rb +171 -0
  35. data/lib/rubbycop/cached_data.rb +63 -0
  36. data/lib/rubbycop/cli.rb +199 -0
  37. data/lib/rubbycop/comment_config.rb +155 -0
  38. data/lib/rubbycop/config.rb +444 -0
  39. data/lib/rubbycop/config_loader.rb +244 -0
  40. data/lib/rubbycop/config_loader_resolver.rb +43 -0
  41. data/lib/rubbycop/config_store.rb +48 -0
  42. data/lib/rubbycop/cop/autocorrect_logic.rb +26 -0
  43. data/lib/rubbycop/cop/badge.rb +73 -0
  44. data/lib/rubbycop/cop/bundler/duplicated_gem.rb +69 -0
  45. data/lib/rubbycop/cop/bundler/ordered_gems.rb +113 -0
  46. data/lib/rubbycop/cop/commissioner.rb +118 -0
  47. data/lib/rubbycop/cop/cop.rb +222 -0
  48. data/lib/rubbycop/cop/corrector.rb +135 -0
  49. data/lib/rubbycop/cop/force.rb +41 -0
  50. data/lib/rubbycop/cop/ignored_node.rb +38 -0
  51. data/lib/rubbycop/cop/layout/access_modifier_indentation.rb +109 -0
  52. data/lib/rubbycop/cop/layout/align_array.rb +35 -0
  53. data/lib/rubbycop/cop/layout/align_hash.rb +235 -0
  54. data/lib/rubbycop/cop/layout/align_parameters.rb +97 -0
  55. data/lib/rubbycop/cop/layout/block_end_newline.rb +56 -0
  56. data/lib/rubbycop/cop/layout/case_indentation.rb +163 -0
  57. data/lib/rubbycop/cop/layout/closing_parenthesis_indentation.rb +88 -0
  58. data/lib/rubbycop/cop/layout/comment_indentation.rb +71 -0
  59. data/lib/rubbycop/cop/layout/dot_position.rb +84 -0
  60. data/lib/rubbycop/cop/layout/else_alignment.rb +105 -0
  61. data/lib/rubbycop/cop/layout/empty_line_after_magic_comment.rb +63 -0
  62. data/lib/rubbycop/cop/layout/empty_line_between_defs.rb +143 -0
  63. data/lib/rubbycop/cop/layout/empty_lines.rb +60 -0
  64. data/lib/rubbycop/cop/layout/empty_lines_around_access_modifier.rb +90 -0
  65. data/lib/rubbycop/cop/layout/empty_lines_around_begin_body.rb +42 -0
  66. data/lib/rubbycop/cop/layout/empty_lines_around_block_body.rb +41 -0
  67. data/lib/rubbycop/cop/layout/empty_lines_around_class_body.rb +39 -0
  68. data/lib/rubbycop/cop/layout/empty_lines_around_exception_handling_keywords.rb +127 -0
  69. data/lib/rubbycop/cop/layout/empty_lines_around_method_body.rb +41 -0
  70. data/lib/rubbycop/cop/layout/empty_lines_around_module_body.rb +44 -0
  71. data/lib/rubbycop/cop/layout/end_of_line.rb +52 -0
  72. data/lib/rubbycop/cop/layout/extra_spacing.rb +237 -0
  73. data/lib/rubbycop/cop/layout/first_array_element_line_break.rb +41 -0
  74. data/lib/rubbycop/cop/layout/first_hash_element_line_break.rb +33 -0
  75. data/lib/rubbycop/cop/layout/first_method_argument_line_break.rb +49 -0
  76. data/lib/rubbycop/cop/layout/first_method_parameter_line_break.rb +42 -0
  77. data/lib/rubbycop/cop/layout/first_parameter_indentation.rb +109 -0
  78. data/lib/rubbycop/cop/layout/indent_array.rb +114 -0
  79. data/lib/rubbycop/cop/layout/indent_assignment.rb +42 -0
  80. data/lib/rubbycop/cop/layout/indent_hash.rb +134 -0
  81. data/lib/rubbycop/cop/layout/indent_heredoc.rb +173 -0
  82. data/lib/rubbycop/cop/layout/indentation_consistency.rb +51 -0
  83. data/lib/rubbycop/cop/layout/indentation_width.rb +303 -0
  84. data/lib/rubbycop/cop/layout/initial_indentation.rb +42 -0
  85. data/lib/rubbycop/cop/layout/leading_comment_space.rb +43 -0
  86. data/lib/rubbycop/cop/layout/multiline_array_brace_layout.rb +81 -0
  87. data/lib/rubbycop/cop/layout/multiline_assignment_layout.rb +88 -0
  88. data/lib/rubbycop/cop/layout/multiline_block_layout.rb +134 -0
  89. data/lib/rubbycop/cop/layout/multiline_hash_brace_layout.rb +81 -0
  90. data/lib/rubbycop/cop/layout/multiline_method_call_brace_layout.rb +97 -0
  91. data/lib/rubbycop/cop/layout/multiline_method_call_indentation.rb +215 -0
  92. data/lib/rubbycop/cop/layout/multiline_method_definition_brace_layout.rb +82 -0
  93. data/lib/rubbycop/cop/layout/multiline_operation_indentation.rb +89 -0
  94. data/lib/rubbycop/cop/layout/rescue_ensure_alignment.rb +86 -0
  95. data/lib/rubbycop/cop/layout/space_after_colon.rb +40 -0
  96. data/lib/rubbycop/cop/layout/space_after_comma.rb +21 -0
  97. data/lib/rubbycop/cop/layout/space_after_method_name.rb +37 -0
  98. data/lib/rubbycop/cop/layout/space_after_not.rb +38 -0
  99. data/lib/rubbycop/cop/layout/space_after_semicolon.rb +21 -0
  100. data/lib/rubbycop/cop/layout/space_around_block_parameters.rb +109 -0
  101. data/lib/rubbycop/cop/layout/space_around_equals_in_parameter_default.rb +68 -0
  102. data/lib/rubbycop/cop/layout/space_around_keyword.rb +224 -0
  103. data/lib/rubbycop/cop/layout/space_around_operators.rb +142 -0
  104. data/lib/rubbycop/cop/layout/space_before_block_braces.rb +54 -0
  105. data/lib/rubbycop/cop/layout/space_before_comma.rb +16 -0
  106. data/lib/rubbycop/cop/layout/space_before_comment.rb +27 -0
  107. data/lib/rubbycop/cop/layout/space_before_first_arg.rb +64 -0
  108. data/lib/rubbycop/cop/layout/space_before_semicolon.rb +16 -0
  109. data/lib/rubbycop/cop/layout/space_in_lambda_literal.rb +87 -0
  110. data/lib/rubbycop/cop/layout/space_inside_array_percent_literal.rb +53 -0
  111. data/lib/rubbycop/cop/layout/space_inside_block_braces.rb +158 -0
  112. data/lib/rubbycop/cop/layout/space_inside_brackets.rb +20 -0
  113. data/lib/rubbycop/cop/layout/space_inside_hash_literal_braces.rb +150 -0
  114. data/lib/rubbycop/cop/layout/space_inside_parens.rb +16 -0
  115. data/lib/rubbycop/cop/layout/space_inside_percent_literal_delimiters.rb +64 -0
  116. data/lib/rubbycop/cop/layout/space_inside_range_literal.rb +63 -0
  117. data/lib/rubbycop/cop/layout/space_inside_string_interpolation.rb +65 -0
  118. data/lib/rubbycop/cop/layout/tab.rb +57 -0
  119. data/lib/rubbycop/cop/layout/trailing_blank_lines.rb +78 -0
  120. data/lib/rubbycop/cop/layout/trailing_whitespace.rb +28 -0
  121. data/lib/rubbycop/cop/lint/ambiguous_block_association.rb +66 -0
  122. data/lib/rubbycop/cop/lint/ambiguous_operator.rb +55 -0
  123. data/lib/rubbycop/cop/lint/ambiguous_regexp_literal.rb +43 -0
  124. data/lib/rubbycop/cop/lint/assignment_in_condition.rb +80 -0
  125. data/lib/rubbycop/cop/lint/block_alignment.rb +229 -0
  126. data/lib/rubbycop/cop/lint/circular_argument_reference.rb +83 -0
  127. data/lib/rubbycop/cop/lint/condition_position.rb +52 -0
  128. data/lib/rubbycop/cop/lint/debugger.rb +72 -0
  129. data/lib/rubbycop/cop/lint/def_end_alignment.rb +78 -0
  130. data/lib/rubbycop/cop/lint/deprecated_class_methods.rb +90 -0
  131. data/lib/rubbycop/cop/lint/duplicate_case_condition.rb +53 -0
  132. data/lib/rubbycop/cop/lint/duplicate_methods.rb +151 -0
  133. data/lib/rubbycop/cop/lint/duplicated_key.rb +38 -0
  134. data/lib/rubbycop/cop/lint/each_with_object_argument.rb +39 -0
  135. data/lib/rubbycop/cop/lint/else_layout.rb +65 -0
  136. data/lib/rubbycop/cop/lint/empty_ensure.rb +60 -0
  137. data/lib/rubbycop/cop/lint/empty_expression.rb +42 -0
  138. data/lib/rubbycop/cop/lint/empty_interpolation.rb +36 -0
  139. data/lib/rubbycop/cop/lint/empty_when.rb +38 -0
  140. data/lib/rubbycop/cop/lint/end_alignment.rb +157 -0
  141. data/lib/rubbycop/cop/lint/end_in_method.rb +40 -0
  142. data/lib/rubbycop/cop/lint/ensure_return.rb +43 -0
  143. data/lib/rubbycop/cop/lint/float_out_of_range.rb +35 -0
  144. data/lib/rubbycop/cop/lint/format_parameter_mismatch.rb +182 -0
  145. data/lib/rubbycop/cop/lint/handle_exceptions.rb +56 -0
  146. data/lib/rubbycop/cop/lint/implicit_string_concatenation.rb +95 -0
  147. data/lib/rubbycop/cop/lint/ineffective_access_modifier.rb +143 -0
  148. data/lib/rubbycop/cop/lint/inherit_exception.rb +83 -0
  149. data/lib/rubbycop/cop/lint/invalid_character_literal.rb +41 -0
  150. data/lib/rubbycop/cop/lint/literal_in_condition.rb +127 -0
  151. data/lib/rubbycop/cop/lint/literal_in_interpolation.rb +76 -0
  152. data/lib/rubbycop/cop/lint/loop.rb +63 -0
  153. data/lib/rubbycop/cop/lint/multiple_compare.rb +48 -0
  154. data/lib/rubbycop/cop/lint/nested_method_definition.rb +105 -0
  155. data/lib/rubbycop/cop/lint/next_without_accumulator.rb +50 -0
  156. data/lib/rubbycop/cop/lint/non_local_exit_from_iterator.rb +85 -0
  157. data/lib/rubbycop/cop/lint/parentheses_as_grouped_expression.rb +60 -0
  158. data/lib/rubbycop/cop/lint/percent_string_array.rb +84 -0
  159. data/lib/rubbycop/cop/lint/percent_symbol_array.rb +66 -0
  160. data/lib/rubbycop/cop/lint/rand_one.rb +39 -0
  161. data/lib/rubbycop/cop/lint/require_parentheses.rb +61 -0
  162. data/lib/rubbycop/cop/lint/rescue_exception.rb +45 -0
  163. data/lib/rubbycop/cop/lint/safe_navigation_chain.rb +70 -0
  164. data/lib/rubbycop/cop/lint/shadowed_exception.rb +132 -0
  165. data/lib/rubbycop/cop/lint/shadowing_outer_local_variable.rb +53 -0
  166. data/lib/rubbycop/cop/lint/string_conversion_in_interpolation.rb +58 -0
  167. data/lib/rubbycop/cop/lint/syntax.rb +55 -0
  168. data/lib/rubbycop/cop/lint/underscore_prefixed_variable_name.rb +62 -0
  169. data/lib/rubbycop/cop/lint/unified_integer.rb +42 -0
  170. data/lib/rubbycop/cop/lint/unneeded_disable.rb +231 -0
  171. data/lib/rubbycop/cop/lint/unneeded_splat_expansion.rb +141 -0
  172. data/lib/rubbycop/cop/lint/unreachable_code.rb +52 -0
  173. data/lib/rubbycop/cop/lint/unused_block_argument.rb +145 -0
  174. data/lib/rubbycop/cop/lint/unused_method_argument.rb +61 -0
  175. data/lib/rubbycop/cop/lint/useless_access_modifier.rb +229 -0
  176. data/lib/rubbycop/cop/lint/useless_assignment.rb +132 -0
  177. data/lib/rubbycop/cop/lint/useless_comparison.rb +28 -0
  178. data/lib/rubbycop/cop/lint/useless_else_without_rescue.rb +46 -0
  179. data/lib/rubbycop/cop/lint/useless_setter_call.rb +162 -0
  180. data/lib/rubbycop/cop/lint/void.rb +108 -0
  181. data/lib/rubbycop/cop/message_annotator.rb +116 -0
  182. data/lib/rubbycop/cop/metrics/abc_size.rb +39 -0
  183. data/lib/rubbycop/cop/metrics/block_length.rb +32 -0
  184. data/lib/rubbycop/cop/metrics/block_nesting.rb +64 -0
  185. data/lib/rubbycop/cop/metrics/class_length.rb +24 -0
  186. data/lib/rubbycop/cop/metrics/cyclomatic_complexity.rb +31 -0
  187. data/lib/rubbycop/cop/metrics/line_length.rb +168 -0
  188. data/lib/rubbycop/cop/metrics/method_length.rb +27 -0
  189. data/lib/rubbycop/cop/metrics/module_length.rb +24 -0
  190. data/lib/rubbycop/cop/metrics/parameter_lists.rb +45 -0
  191. data/lib/rubbycop/cop/metrics/perceived_complexity.rb +61 -0
  192. data/lib/rubbycop/cop/mixin/access_modifier_node.rb +41 -0
  193. data/lib/rubbycop/cop/mixin/annotation_comment.rb +36 -0
  194. data/lib/rubbycop/cop/mixin/array_hash_indentation.rb +82 -0
  195. data/lib/rubbycop/cop/mixin/array_min_size.rb +59 -0
  196. data/lib/rubbycop/cop/mixin/array_syntax.rb +15 -0
  197. data/lib/rubbycop/cop/mixin/autocorrect_alignment.rb +149 -0
  198. data/lib/rubbycop/cop/mixin/check_assignment.rb +40 -0
  199. data/lib/rubbycop/cop/mixin/classish_length.rb +36 -0
  200. data/lib/rubbycop/cop/mixin/code_length.rb +32 -0
  201. data/lib/rubbycop/cop/mixin/configurable_enforced_style.rb +97 -0
  202. data/lib/rubbycop/cop/mixin/configurable_formatting.rb +48 -0
  203. data/lib/rubbycop/cop/mixin/configurable_max.rb +19 -0
  204. data/lib/rubbycop/cop/mixin/configurable_naming.rb +16 -0
  205. data/lib/rubbycop/cop/mixin/configurable_numbering.rb +17 -0
  206. data/lib/rubbycop/cop/mixin/def_node.rb +27 -0
  207. data/lib/rubbycop/cop/mixin/documentation_comment.rb +46 -0
  208. data/lib/rubbycop/cop/mixin/duplication.rb +46 -0
  209. data/lib/rubbycop/cop/mixin/empty_lines_around_body.rb +161 -0
  210. data/lib/rubbycop/cop/mixin/end_keyword_alignment.rb +85 -0
  211. data/lib/rubbycop/cop/mixin/enforce_superclass.rb +36 -0
  212. data/lib/rubbycop/cop/mixin/first_element_line_break.rb +41 -0
  213. data/lib/rubbycop/cop/mixin/frozen_string_literal.rb +37 -0
  214. data/lib/rubbycop/cop/mixin/hash_alignment.rb +116 -0
  215. data/lib/rubbycop/cop/mixin/ignored_pattern.rb +27 -0
  216. data/lib/rubbycop/cop/mixin/integer_node.rb +12 -0
  217. data/lib/rubbycop/cop/mixin/match_range.rb +22 -0
  218. data/lib/rubbycop/cop/mixin/method_complexity.rb +30 -0
  219. data/lib/rubbycop/cop/mixin/method_preference.rb +30 -0
  220. data/lib/rubbycop/cop/mixin/min_body_length.rb +19 -0
  221. data/lib/rubbycop/cop/mixin/multiline_expression_indentation.rb +183 -0
  222. data/lib/rubbycop/cop/mixin/multiline_literal_brace_layout.rb +152 -0
  223. data/lib/rubbycop/cop/mixin/negative_conditional.rb +43 -0
  224. data/lib/rubbycop/cop/mixin/on_method_def.rb +44 -0
  225. data/lib/rubbycop/cop/mixin/on_normal_if_unless.rb +14 -0
  226. data/lib/rubbycop/cop/mixin/parentheses.rb +22 -0
  227. data/lib/rubbycop/cop/mixin/parser_diagnostic.rb +34 -0
  228. data/lib/rubbycop/cop/mixin/percent_literal.rb +100 -0
  229. data/lib/rubbycop/cop/mixin/preceding_following_alignment.rb +89 -0
  230. data/lib/rubbycop/cop/mixin/rescue_node.rb +21 -0
  231. data/lib/rubbycop/cop/mixin/safe_assignment.rb +20 -0
  232. data/lib/rubbycop/cop/mixin/safe_mode.rb +22 -0
  233. data/lib/rubbycop/cop/mixin/space_after_punctuation.rb +55 -0
  234. data/lib/rubbycop/cop/mixin/space_before_punctuation.rb +48 -0
  235. data/lib/rubbycop/cop/mixin/space_inside.rb +76 -0
  236. data/lib/rubbycop/cop/mixin/statement_modifier.rb +69 -0
  237. data/lib/rubbycop/cop/mixin/string_help.rb +33 -0
  238. data/lib/rubbycop/cop/mixin/string_literals_help.rb +33 -0
  239. data/lib/rubbycop/cop/mixin/surrounding_space.rb +40 -0
  240. data/lib/rubbycop/cop/mixin/target_rails_version.rb +16 -0
  241. data/lib/rubbycop/cop/mixin/target_ruby_version.rb +16 -0
  242. data/lib/rubbycop/cop/mixin/too_many_lines.rb +39 -0
  243. data/lib/rubbycop/cop/mixin/trailing_comma.rb +161 -0
  244. data/lib/rubbycop/cop/mixin/unused_argument.rb +42 -0
  245. data/lib/rubbycop/cop/offense.rb +188 -0
  246. data/lib/rubbycop/cop/performance/caller.rb +41 -0
  247. data/lib/rubbycop/cop/performance/case_when_splat.rb +176 -0
  248. data/lib/rubbycop/cop/performance/casecmp.rb +107 -0
  249. data/lib/rubbycop/cop/performance/compare_with_block.rb +107 -0
  250. data/lib/rubbycop/cop/performance/count.rb +98 -0
  251. data/lib/rubbycop/cop/performance/detect.rb +107 -0
  252. data/lib/rubbycop/cop/performance/double_start_end_with.rb +102 -0
  253. data/lib/rubbycop/cop/performance/end_with.rb +55 -0
  254. data/lib/rubbycop/cop/performance/fixed_size.rb +56 -0
  255. data/lib/rubbycop/cop/performance/flat_map.rb +73 -0
  256. data/lib/rubbycop/cop/performance/hash_each_methods.rb +84 -0
  257. data/lib/rubbycop/cop/performance/lstrip_rstrip.rb +41 -0
  258. data/lib/rubbycop/cop/performance/range_include.rb +41 -0
  259. data/lib/rubbycop/cop/performance/redundant_block_call.rb +93 -0
  260. data/lib/rubbycop/cop/performance/redundant_match.rb +55 -0
  261. data/lib/rubbycop/cop/performance/redundant_merge.rb +149 -0
  262. data/lib/rubbycop/cop/performance/redundant_sort_by.rb +45 -0
  263. data/lib/rubbycop/cop/performance/regexp_match.rb +215 -0
  264. data/lib/rubbycop/cop/performance/reverse_each.rb +40 -0
  265. data/lib/rubbycop/cop/performance/sample.rb +140 -0
  266. data/lib/rubbycop/cop/performance/size.rb +71 -0
  267. data/lib/rubbycop/cop/performance/start_with.rb +58 -0
  268. data/lib/rubbycop/cop/performance/string_replacement.rb +170 -0
  269. data/lib/rubbycop/cop/performance/times_map.rb +61 -0
  270. data/lib/rubbycop/cop/rails/action_filter.rb +96 -0
  271. data/lib/rubbycop/cop/rails/active_support_aliases.rb +68 -0
  272. data/lib/rubbycop/cop/rails/application_job.rb +32 -0
  273. data/lib/rubbycop/cop/rails/application_record.rb +32 -0
  274. data/lib/rubbycop/cop/rails/blank.rb +138 -0
  275. data/lib/rubbycop/cop/rails/date.rb +127 -0
  276. data/lib/rubbycop/cop/rails/delegate.rb +106 -0
  277. data/lib/rubbycop/cop/rails/delegate_allow_blank.rb +51 -0
  278. data/lib/rubbycop/cop/rails/dynamic_find_by.rb +81 -0
  279. data/lib/rubbycop/cop/rails/enum_uniqueness.rb +43 -0
  280. data/lib/rubbycop/cop/rails/exit.rb +61 -0
  281. data/lib/rubbycop/cop/rails/file_path.rb +75 -0
  282. data/lib/rubbycop/cop/rails/find_by.rb +51 -0
  283. data/lib/rubbycop/cop/rails/find_each.rb +47 -0
  284. data/lib/rubbycop/cop/rails/has_and_belongs_to_many.rb +18 -0
  285. data/lib/rubbycop/cop/rails/http_positional_arguments.rb +106 -0
  286. data/lib/rubbycop/cop/rails/not_null_column.rb +67 -0
  287. data/lib/rubbycop/cop/rails/output.rb +23 -0
  288. data/lib/rubbycop/cop/rails/output_safety.rb +58 -0
  289. data/lib/rubbycop/cop/rails/pluralization_grammar.rb +106 -0
  290. data/lib/rubbycop/cop/rails/present.rb +143 -0
  291. data/lib/rubbycop/cop/rails/read_write_attribute.rb +65 -0
  292. data/lib/rubbycop/cop/rails/relative_date_constant.rb +88 -0
  293. data/lib/rubbycop/cop/rails/request_referer.rb +56 -0
  294. data/lib/rubbycop/cop/rails/reversible_migration.rb +216 -0
  295. data/lib/rubbycop/cop/rails/safe_navigation.rb +91 -0
  296. data/lib/rubbycop/cop/rails/save_bang.rb +160 -0
  297. data/lib/rubbycop/cop/rails/scope_args.rb +29 -0
  298. data/lib/rubbycop/cop/rails/skips_model_validations.rb +63 -0
  299. data/lib/rubbycop/cop/rails/time_zone.rb +197 -0
  300. data/lib/rubbycop/cop/rails/uniq_before_pluck.rb +93 -0
  301. data/lib/rubbycop/cop/rails/validation.rb +64 -0
  302. data/lib/rubbycop/cop/registry.rb +171 -0
  303. data/lib/rubbycop/cop/security/eval.rb +30 -0
  304. data/lib/rubbycop/cop/security/json_load.rb +44 -0
  305. data/lib/rubbycop/cop/security/marshal_load.rb +37 -0
  306. data/lib/rubbycop/cop/security/yaml_load.rb +37 -0
  307. data/lib/rubbycop/cop/severity.rb +76 -0
  308. data/lib/rubbycop/cop/style/accessor_method_name.rb +45 -0
  309. data/lib/rubbycop/cop/style/alias.rb +119 -0
  310. data/lib/rubbycop/cop/style/and_or.rb +125 -0
  311. data/lib/rubbycop/cop/style/array_join.rb +30 -0
  312. data/lib/rubbycop/cop/style/ascii_comments.rb +38 -0
  313. data/lib/rubbycop/cop/style/ascii_identifiers.rb +36 -0
  314. data/lib/rubbycop/cop/style/attr.rb +50 -0
  315. data/lib/rubbycop/cop/style/auto_resource_cleanup.rb +42 -0
  316. data/lib/rubbycop/cop/style/bare_percent_literals.rb +57 -0
  317. data/lib/rubbycop/cop/style/begin_block.rb +16 -0
  318. data/lib/rubbycop/cop/style/block_comments.rb +46 -0
  319. data/lib/rubbycop/cop/style/block_delimiters.rb +228 -0
  320. data/lib/rubbycop/cop/style/braces_around_hash_parameters.rb +138 -0
  321. data/lib/rubbycop/cop/style/case_equality.rb +18 -0
  322. data/lib/rubbycop/cop/style/character_literal.rb +43 -0
  323. data/lib/rubbycop/cop/style/class_and_module_camel_case.rb +29 -0
  324. data/lib/rubbycop/cop/style/class_and_module_children.rb +69 -0
  325. data/lib/rubbycop/cop/style/class_check.rb +40 -0
  326. data/lib/rubbycop/cop/style/class_methods.rb +67 -0
  327. data/lib/rubbycop/cop/style/class_vars.rb +23 -0
  328. data/lib/rubbycop/cop/style/collection_methods.rb +51 -0
  329. data/lib/rubbycop/cop/style/colon_method_call.rb +33 -0
  330. data/lib/rubbycop/cop/style/command_literal.rb +119 -0
  331. data/lib/rubbycop/cop/style/comment_annotation.rb +62 -0
  332. data/lib/rubbycop/cop/style/conditional_assignment.rb +691 -0
  333. data/lib/rubbycop/cop/style/constant_name.rb +29 -0
  334. data/lib/rubbycop/cop/style/copyright.rb +89 -0
  335. data/lib/rubbycop/cop/style/def_with_parentheses.rb +31 -0
  336. data/lib/rubbycop/cop/style/documentation.rb +79 -0
  337. data/lib/rubbycop/cop/style/documentation_method.rb +80 -0
  338. data/lib/rubbycop/cop/style/double_negation.rb +35 -0
  339. data/lib/rubbycop/cop/style/each_for_simple_loop.rb +57 -0
  340. data/lib/rubbycop/cop/style/each_with_object.rb +91 -0
  341. data/lib/rubbycop/cop/style/empty_case_condition.rb +84 -0
  342. data/lib/rubbycop/cop/style/empty_else.rb +138 -0
  343. data/lib/rubbycop/cop/style/empty_literal.rb +108 -0
  344. data/lib/rubbycop/cop/style/empty_method.rb +102 -0
  345. data/lib/rubbycop/cop/style/encoding.rb +92 -0
  346. data/lib/rubbycop/cop/style/end_block.rb +17 -0
  347. data/lib/rubbycop/cop/style/even_odd.rb +56 -0
  348. data/lib/rubbycop/cop/style/file_name.rb +183 -0
  349. data/lib/rubbycop/cop/style/flip_flop.rb +20 -0
  350. data/lib/rubbycop/cop/style/for.rb +50 -0
  351. data/lib/rubbycop/cop/style/format_string.rb +46 -0
  352. data/lib/rubbycop/cop/style/format_string_token.rb +141 -0
  353. data/lib/rubbycop/cop/style/frozen_string_literal_comment.rb +96 -0
  354. data/lib/rubbycop/cop/style/global_vars.rb +70 -0
  355. data/lib/rubbycop/cop/style/guard_clause.rb +90 -0
  356. data/lib/rubbycop/cop/style/hash_syntax.rb +214 -0
  357. data/lib/rubbycop/cop/style/identical_conditional_branches.rb +130 -0
  358. data/lib/rubbycop/cop/style/if_inside_else.rb +45 -0
  359. data/lib/rubbycop/cop/style/if_unless_modifier.rb +80 -0
  360. data/lib/rubbycop/cop/style/if_unless_modifier_of_if_unless.rb +38 -0
  361. data/lib/rubbycop/cop/style/if_with_semicolon.rb +20 -0
  362. data/lib/rubbycop/cop/style/implicit_runtime_error.rb +31 -0
  363. data/lib/rubbycop/cop/style/infinite_loop.rb +91 -0
  364. data/lib/rubbycop/cop/style/inline_comment.rb +32 -0
  365. data/lib/rubbycop/cop/style/inverse_methods.rb +130 -0
  366. data/lib/rubbycop/cop/style/lambda.rb +209 -0
  367. data/lib/rubbycop/cop/style/lambda_call.rb +66 -0
  368. data/lib/rubbycop/cop/style/line_end_concatenation.rb +115 -0
  369. data/lib/rubbycop/cop/style/method_call_with_args_parentheses.rb +107 -0
  370. data/lib/rubbycop/cop/style/method_call_without_args_parentheses.rb +75 -0
  371. data/lib/rubbycop/cop/style/method_called_on_do_end_block.rb +44 -0
  372. data/lib/rubbycop/cop/style/method_def_parentheses.rb +83 -0
  373. data/lib/rubbycop/cop/style/method_missing.rb +81 -0
  374. data/lib/rubbycop/cop/style/method_name.rb +28 -0
  375. data/lib/rubbycop/cop/style/missing_else.rb +100 -0
  376. data/lib/rubbycop/cop/style/mixin_grouping.rb +135 -0
  377. data/lib/rubbycop/cop/style/module_function.rb +64 -0
  378. data/lib/rubbycop/cop/style/multiline_block_chain.rb +42 -0
  379. data/lib/rubbycop/cop/style/multiline_if_modifier.rb +63 -0
  380. data/lib/rubbycop/cop/style/multiline_if_then.rb +47 -0
  381. data/lib/rubbycop/cop/style/multiline_memoization.rb +77 -0
  382. data/lib/rubbycop/cop/style/multiline_ternary_operator.rb +19 -0
  383. data/lib/rubbycop/cop/style/mutable_constant.rb +68 -0
  384. data/lib/rubbycop/cop/style/negated_if.rb +103 -0
  385. data/lib/rubbycop/cop/style/negated_while.rb +32 -0
  386. data/lib/rubbycop/cop/style/nested_modifier.rb +87 -0
  387. data/lib/rubbycop/cop/style/nested_parenthesized_calls.rb +61 -0
  388. data/lib/rubbycop/cop/style/nested_ternary_operator.rb +21 -0
  389. data/lib/rubbycop/cop/style/next.rb +225 -0
  390. data/lib/rubbycop/cop/style/nil_comparison.rb +35 -0
  391. data/lib/rubbycop/cop/style/non_nil_check.rb +121 -0
  392. data/lib/rubbycop/cop/style/not.rb +69 -0
  393. data/lib/rubbycop/cop/style/numeric_literal_prefix.rb +97 -0
  394. data/lib/rubbycop/cop/style/numeric_literals.rb +101 -0
  395. data/lib/rubbycop/cop/style/numeric_predicate.rb +140 -0
  396. data/lib/rubbycop/cop/style/one_line_conditional.rb +75 -0
  397. data/lib/rubbycop/cop/style/op_method.rb +41 -0
  398. data/lib/rubbycop/cop/style/option_hash.rb +58 -0
  399. data/lib/rubbycop/cop/style/optional_arguments.rb +62 -0
  400. data/lib/rubbycop/cop/style/parallel_assignment.rb +287 -0
  401. data/lib/rubbycop/cop/style/parentheses_around_condition.rb +56 -0
  402. data/lib/rubbycop/cop/style/percent_literal_delimiters.rb +100 -0
  403. data/lib/rubbycop/cop/style/percent_q_literals.rb +52 -0
  404. data/lib/rubbycop/cop/style/perl_backrefs.rb +31 -0
  405. data/lib/rubbycop/cop/style/predicate_name.rb +67 -0
  406. data/lib/rubbycop/cop/style/preferred_hash_methods.rb +78 -0
  407. data/lib/rubbycop/cop/style/proc.rb +26 -0
  408. data/lib/rubbycop/cop/style/raise_args.rb +140 -0
  409. data/lib/rubbycop/cop/style/redundant_begin.rb +47 -0
  410. data/lib/rubbycop/cop/style/redundant_exception.rb +55 -0
  411. data/lib/rubbycop/cop/style/redundant_freeze.rb +45 -0
  412. data/lib/rubbycop/cop/style/redundant_parentheses.rb +199 -0
  413. data/lib/rubbycop/cop/style/redundant_return.rb +121 -0
  414. data/lib/rubbycop/cop/style/redundant_self.rb +144 -0
  415. data/lib/rubbycop/cop/style/regexp_literal.rb +114 -0
  416. data/lib/rubbycop/cop/style/rescue_modifier.rb +37 -0
  417. data/lib/rubbycop/cop/style/safe_navigation.rb +145 -0
  418. data/lib/rubbycop/cop/style/self_assignment.rb +93 -0
  419. data/lib/rubbycop/cop/style/semicolon.rb +70 -0
  420. data/lib/rubbycop/cop/style/send.rb +21 -0
  421. data/lib/rubbycop/cop/style/signal_exception.rb +109 -0
  422. data/lib/rubbycop/cop/style/single_line_block_params.rb +68 -0
  423. data/lib/rubbycop/cop/style/single_line_methods.rb +77 -0
  424. data/lib/rubbycop/cop/style/special_global_vars.rb +156 -0
  425. data/lib/rubbycop/cop/style/stabby_lambda_parentheses.rb +113 -0
  426. data/lib/rubbycop/cop/style/string_literals.rb +102 -0
  427. data/lib/rubbycop/cop/style/string_literals_in_interpolation.rb +30 -0
  428. data/lib/rubbycop/cop/style/string_methods.rb +34 -0
  429. data/lib/rubbycop/cop/style/struct_inheritance.rb +32 -0
  430. data/lib/rubbycop/cop/style/symbol_array.rb +109 -0
  431. data/lib/rubbycop/cop/style/symbol_literal.rb +32 -0
  432. data/lib/rubbycop/cop/style/symbol_proc.rb +143 -0
  433. data/lib/rubbycop/cop/style/ternary_parentheses.rb +200 -0
  434. data/lib/rubbycop/cop/style/trailing_comma_in_arguments.rb +64 -0
  435. data/lib/rubbycop/cop/style/trailing_comma_in_literal.rb +56 -0
  436. data/lib/rubbycop/cop/style/trailing_underscore_variable.rb +113 -0
  437. data/lib/rubbycop/cop/style/trivial_accessors.rb +176 -0
  438. data/lib/rubbycop/cop/style/unless_else.rb +39 -0
  439. data/lib/rubbycop/cop/style/unneeded_capital_w.rb +41 -0
  440. data/lib/rubbycop/cop/style/unneeded_interpolation.rb +98 -0
  441. data/lib/rubbycop/cop/style/unneeded_percent_q.rb +96 -0
  442. data/lib/rubbycop/cop/style/variable_interpolation.rb +44 -0
  443. data/lib/rubbycop/cop/style/variable_name.rb +39 -0
  444. data/lib/rubbycop/cop/style/variable_number.rb +78 -0
  445. data/lib/rubbycop/cop/style/when_then.rb +24 -0
  446. data/lib/rubbycop/cop/style/while_until_do.rb +36 -0
  447. data/lib/rubbycop/cop/style/while_until_modifier.rb +41 -0
  448. data/lib/rubbycop/cop/style/word_array.rb +114 -0
  449. data/lib/rubbycop/cop/style/zero_length_predicate.rb +90 -0
  450. data/lib/rubbycop/cop/team.rb +193 -0
  451. data/lib/rubbycop/cop/util.rb +309 -0
  452. data/lib/rubbycop/cop/variable_force.rb +458 -0
  453. data/lib/rubbycop/cop/variable_force/assignment.rb +90 -0
  454. data/lib/rubbycop/cop/variable_force/branch.rb +318 -0
  455. data/lib/rubbycop/cop/variable_force/branchable.rb +21 -0
  456. data/lib/rubbycop/cop/variable_force/reference.rb +49 -0
  457. data/lib/rubbycop/cop/variable_force/scope.rb +107 -0
  458. data/lib/rubbycop/cop/variable_force/variable.rb +103 -0
  459. data/lib/rubbycop/cop/variable_force/variable_table.rb +128 -0
  460. data/lib/rubbycop/error.rb +11 -0
  461. data/lib/rubbycop/formatter/base_formatter.rb +123 -0
  462. data/lib/rubbycop/formatter/clang_style_formatter.rb +54 -0
  463. data/lib/rubbycop/formatter/colorizable.rb +41 -0
  464. data/lib/rubbycop/formatter/disabled_config_formatter.rb +181 -0
  465. data/lib/rubbycop/formatter/disabled_lines_formatter.rb +57 -0
  466. data/lib/rubbycop/formatter/emacs_style_formatter.rb +24 -0
  467. data/lib/rubbycop/formatter/file_list_formatter.rb +19 -0
  468. data/lib/rubbycop/formatter/formatter_set.rb +102 -0
  469. data/lib/rubbycop/formatter/fuubar_style_formatter.rb +80 -0
  470. data/lib/rubbycop/formatter/html_formatter.rb +134 -0
  471. data/lib/rubbycop/formatter/json_formatter.rb +74 -0
  472. data/lib/rubbycop/formatter/offense_count_formatter.rb +55 -0
  473. data/lib/rubbycop/formatter/progress_formatter.rb +63 -0
  474. data/lib/rubbycop/formatter/simple_text_formatter.rb +136 -0
  475. data/lib/rubbycop/formatter/text_util.rb +20 -0
  476. data/lib/rubbycop/formatter/worst_offenders_formatter.rb +60 -0
  477. data/lib/rubbycop/magic_comment.rb +210 -0
  478. data/lib/rubbycop/name_similarity.rb +21 -0
  479. data/lib/rubbycop/node_pattern.rb +543 -0
  480. data/lib/rubbycop/options.rb +355 -0
  481. data/lib/rubbycop/path_util.rb +36 -0
  482. data/lib/rubbycop/platform.rb +11 -0
  483. data/lib/rubbycop/processed_source.rb +151 -0
  484. data/lib/rubbycop/rake_task.rb +86 -0
  485. data/lib/rubbycop/remote_config.rb +78 -0
  486. data/lib/rubbycop/result_cache.rb +176 -0
  487. data/lib/rubbycop/rspec/cop_helper.rb +98 -0
  488. data/lib/rubbycop/rspec/host_environment_simulation_helper.rb +32 -0
  489. data/lib/rubbycop/rspec/shared_contexts.rb +98 -0
  490. data/lib/rubbycop/rspec/shared_examples.rb +92 -0
  491. data/lib/rubbycop/rspec/support.rb +8 -0
  492. data/lib/rubbycop/runner.rb +338 -0
  493. data/lib/rubbycop/string_interpreter.rb +57 -0
  494. data/lib/rubbycop/string_util.rb +156 -0
  495. data/lib/rubbycop/target_finder.rb +201 -0
  496. data/lib/rubbycop/token.rb +25 -0
  497. data/lib/rubbycop/version.rb +19 -0
  498. data/lib/rubbycop/warning.rb +11 -0
  499. metadata +663 -0
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubbyCop
4
+ module Formatter
5
+ # Common logic for UI texts.
6
+ module TextUtil
7
+ module_function
8
+
9
+ def pluralize(number, thing, options = {})
10
+ if number.zero? && options[:no_for_zero]
11
+ "no #{thing}s"
12
+ elsif number == 1
13
+ "1 #{thing}"
14
+ else
15
+ "#{number} #{thing}s"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+
5
+ module RubbyCop
6
+ module Formatter
7
+ # This formatter displays the list of offensive files, sorted by number of
8
+ # offenses with the worst offenders first.
9
+ #
10
+ # Here's the format:
11
+ #
12
+ # 26 this/file/is/really/bad.rb
13
+ # 3 just/ok.rb
14
+ # --
15
+ # 29 Total
16
+ class WorstOffendersFormatter < BaseFormatter
17
+ attr_reader :offense_counts
18
+
19
+ def started(target_files)
20
+ super
21
+ @offense_counts = {}
22
+ end
23
+
24
+ def file_finished(file, offenses)
25
+ return if offenses.empty?
26
+
27
+ path = Pathname.new(file).relative_path_from(Pathname.new(Dir.pwd))
28
+ @offense_counts[path] = offenses.size
29
+ end
30
+
31
+ def finished(_inspected_files)
32
+ report_summary(@offense_counts)
33
+ end
34
+
35
+ def report_summary(offense_counts)
36
+ per_file_counts = ordered_offense_counts(offense_counts)
37
+ total_count = total_offense_count(offense_counts)
38
+
39
+ output.puts
40
+
41
+ per_file_counts.each do |file_name, count|
42
+ output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}" \
43
+ "#{file_name}\n"
44
+ end
45
+ output.puts '--'
46
+ output.puts "#{total_count} Total"
47
+
48
+ output.puts
49
+ end
50
+
51
+ def ordered_offense_counts(offense_counts)
52
+ Hash[offense_counts.sort_by { |k, v| [-v, k] }]
53
+ end
54
+
55
+ def total_offense_count(offense_counts)
56
+ offense_counts.values.inject(0, :+)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubbyCop
4
+ # Parse different formats of magic comments.
5
+ #
6
+ # @abstract parent of three different magic comment handlers
7
+ class MagicComment
8
+ # @see https://git.io/vMC1C IRB's pattern for matching magic comment tokens
9
+ TOKEN = /[[:alnum:]\-_]+/
10
+
11
+ # Detect magic comment format and pass it to the appropriate wrapper.
12
+ #
13
+ # @param comment [String]
14
+ #
15
+ # @return [RubbyCop::MagicComment]
16
+ def self.parse(comment)
17
+ case comment
18
+ when EmacsComment::FORMAT then EmacsComment.new(comment)
19
+ when VimComment::FORMAT then VimComment.new(comment)
20
+ else
21
+ SimpleComment.new(comment)
22
+ end
23
+ end
24
+
25
+ def initialize(comment)
26
+ @comment = comment
27
+ end
28
+
29
+ def any?
30
+ frozen_string_literal_specified? || encoding_specified?
31
+ end
32
+
33
+ # Does the magic comment enable the frozen string literal feature.
34
+ #
35
+ # Test whether the frozen string literal value is `true`. Cannot
36
+ # just return `frozen_string_literal` since an invalid magic comment
37
+ # like `# frozen_string_literal: yes` is possible and the truthy value
38
+ # `'yes'` does not actually enable the feature
39
+ #
40
+ # @return [Boolean]
41
+ def frozen_string_literal?
42
+ frozen_string_literal == true
43
+ end
44
+
45
+ # Was a magic comment for the frozen string literal found?
46
+ #
47
+ # @return [Boolean]
48
+ def frozen_string_literal_specified?
49
+ specified?(frozen_string_literal)
50
+ end
51
+
52
+ # Expose the `frozen_string_literal` value coerced to a boolean if possible.
53
+ #
54
+ # @return [Boolean] if value is `true` or `false`
55
+ # @return [nil] if frozen_string_literal comment isn't found
56
+ # @return [String] if comment is found but isn't true or false
57
+ def frozen_string_literal
58
+ return unless (setting = extract_frozen_string_literal)
59
+
60
+ case setting
61
+ when 'true' then true
62
+ when 'false' then false
63
+ else
64
+ setting
65
+ end
66
+ end
67
+
68
+ def encoding_specified?
69
+ specified?(encoding)
70
+ end
71
+
72
+ private
73
+
74
+ def specified?(value)
75
+ !value.nil?
76
+ end
77
+
78
+ # Match the entire comment string with a pattern and take the first capture.
79
+ #
80
+ # @param pattern [Regexp]
81
+ #
82
+ # @return [String] if pattern matched
83
+ # @return [nil] otherwise
84
+ def extract(pattern)
85
+ @comment[pattern, 1]
86
+ end
87
+
88
+ # Parent to Vim and Emacs magic comment handling.
89
+ #
90
+ # @abstract
91
+ class EditorComment < MagicComment
92
+ private
93
+
94
+ # Find a token starting with the provided keyword and extract its value.
95
+ #
96
+ # @param keyword [String]
97
+ #
98
+ # @return [String] extracted value if it is found
99
+ # @return [nil] otherwise
100
+ def match(keyword)
101
+ pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*(#{TOKEN})\z/
102
+
103
+ tokens.each do |token|
104
+ next unless (value = token[pattern, 1])
105
+
106
+ return value.downcase
107
+ end
108
+
109
+ nil
110
+ end
111
+
112
+ # Individual tokens composing an editor specific comment string.
113
+ #
114
+ # @return [Array<String>]
115
+ def tokens
116
+ extract(self.class::FORMAT).split(self.class::SEPARATOR).map(&:strip)
117
+ end
118
+ end
119
+
120
+ # Wrapper for Emacs style magic comments.
121
+ #
122
+ # @example Emacs style comment
123
+ # comment = RubbyCop::MagicComment.parse(
124
+ # '# -*- encoding: ASCII-8BIT -*-'
125
+ # )
126
+ #
127
+ # comment.encoding # => 'ascii-8bit'
128
+ #
129
+ # @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
130
+ # @see https://git.io/vMCXh Emacs handling in Ruby's parse.y
131
+ class EmacsComment < EditorComment
132
+ FORMAT = /\-\*\-(.+)\-\*\-/
133
+ SEPARATOR = ';'.freeze
134
+ OPERATOR = ':'.freeze
135
+
136
+ def encoding
137
+ match('encoding')
138
+ end
139
+
140
+ private
141
+
142
+ def extract_frozen_string_literal
143
+ match('frozen_string_literal')
144
+ end
145
+ end
146
+
147
+ # Wrapper for Vim style magic comments.
148
+ #
149
+ # @example Vim style comment
150
+ # comment = RubbyCop::MagicComment.parse(
151
+ # '# vim: filetype=ruby, fileencoding=ascii-8bit'
152
+ # )
153
+ #
154
+ # comment.encoding # => 'ascii-8bit'
155
+ class VimComment < EditorComment
156
+ FORMAT = /#\s*vim:\s*(.+)/
157
+ SEPARATOR = ', '.freeze
158
+ OPERATOR = '='.freeze
159
+
160
+ # For some reason the fileencoding keyword only works if there
161
+ # is at least one other token included in the string. For example
162
+ #
163
+ # # works
164
+ # # vim: foo=bar, fileencoding=ascii-8bit
165
+ #
166
+ # # does nothing
167
+ # # vim: foo=bar, fileencoding=ascii-8bit
168
+ #
169
+ def encoding
170
+ match('fileencoding') if tokens.size > 1
171
+ end
172
+
173
+ # Vim comments cannot specify frozen string literal behavior.
174
+ def frozen_string_literal; end
175
+ end
176
+
177
+ # Wrapper for regular magic comments not bound to an editor.
178
+ #
179
+ # Simple comments can only specify one setting per comment.
180
+ #
181
+ # @example frozen string literal comments
182
+ # comment1 = RubbyCop::MagicComment.parse('# frozen_string_literal: true')
183
+ # comment1.frozen_string_literal # => true
184
+ # comment1.encoding # => nil
185
+ #
186
+ # @example encoding comments
187
+ # comment2 = RubbyCop::MagicComment.parse('# encoding: utf-8')
188
+ # comment2.frozen_string_literal # => nil
189
+ # comment2.encoding # => 'utf-8'
190
+ class SimpleComment < MagicComment
191
+ # Match `encoding` or `coding`
192
+ def encoding
193
+ extract(/\b(?:en)?coding: (#{TOKEN})/i)
194
+ end
195
+
196
+ private
197
+
198
+ # Extract `frozen_string_literal`.
199
+ #
200
+ # The `frozen_string_literal` magic comment only works if it
201
+ # is the only text in the comment.
202
+ #
203
+ # Case-insensitive and dashes/underscores are acceptable.
204
+ # @see https://git.io/vM7Mg
205
+ def extract_frozen_string_literal
206
+ extract(/\A#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/i)
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubbyCop
4
+ # Common functionality for finding names that are similar to a given name.
5
+ module NameSimilarity
6
+ MINIMUM_SIMILARITY_TO_SUGGEST = 0.9
7
+
8
+ def find_similar_name(target_name, scope)
9
+ names = collect_variable_like_names(scope)
10
+ names.delete(target_name)
11
+
12
+ scores = names.each_with_object({}) do |name, hash|
13
+ score = StringUtil.similarity(target_name, name)
14
+ hash[name] = score if score >= MINIMUM_SIMILARITY_TO_SUGGEST
15
+ end
16
+
17
+ most_similar_name, _max_score = scores.max_by { |_, score| score }
18
+ most_similar_name
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,543 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubbycop:disable Metrics/ClassLength
4
+ # rubbycop:disable Metrics/CyclomaticComplexity
5
+
6
+ module RubbyCop
7
+ # This class performs a pattern-matching operation on an AST node.
8
+ #
9
+ # Initialize a new `NodePattern` with `NodePattern.new(pattern_string)`, then
10
+ # pass an AST node to `NodePattern#match`. Alternatively, use one of the class
11
+ # macros in `NodePattern::Macros` to define your own pattern-matching method.
12
+ #
13
+ # If the match fails, `nil` will be returned. If the match succeeds, the
14
+ # return value depends on whether a block was provided to `#match`, and
15
+ # whether the pattern contained any "captures" (values which are extracted
16
+ # from a matching AST.)
17
+ #
18
+ # - With block: #match yields the captures (if any) and passes the return
19
+ # value of the block through.
20
+ # - With no block, but one capture: the capture is returned.
21
+ # - With no block, but multiple captures: captures are returned as an array.
22
+ # - With no block and no captures: #match returns `true`.
23
+ #
24
+ # ## Pattern string format examples
25
+ #
26
+ # ':sym' # matches a literal symbol
27
+ # '1' # matches a literal integer
28
+ # 'nil' # matches a literal nil
29
+ # 'send' # matches (send ...)
30
+ # '(send)' # matches (send)
31
+ # '(send ...)' # matches (send ...)
32
+ # '(op-asgn)' # node types with hyphenated names also work
33
+ # '{send class}' # matches (send ...) or (class ...)
34
+ # '({send class})' # matches (send) or (class)
35
+ # '(send const)' # matches (send (const ...))
36
+ # '(send _ :new)' # matches (send <anything> :new)
37
+ # '(send $_ :new)' # as above, but whatever matches the $_ is captured
38
+ # '(send $_ $_)' # you can use as many captures as you want
39
+ # '(send !const ...)' # ! negates the next part of the pattern
40
+ # '$(send const ...)' # arbitrary matching can be performed on a capture
41
+ # '(send _recv _msg)' # wildcards can be named (for readability)
42
+ # '(send ... :new)' # you can specifically match against the last child
43
+ # # (this only works for the very last)
44
+ # '(send $...)' # capture all the children as an array
45
+ # '(send $... int)' # capture all children but the last as an array
46
+ # '(send _x :+ _x)' # unification is performed on named wildcards
47
+ # # (like Prolog variables...)
48
+ # # (#== is used to see if values unify)
49
+ # '(int odd?)' # words which end with a ? are predicate methods,
50
+ # # are are called on the target to see if it matches
51
+ # # any Ruby method which the matched object supports
52
+ # # can be used
53
+ # # if a truthy value is returned, the match succeeds
54
+ # '(int [!1 !2])' # [] contains multiple patterns, ALL of which must
55
+ # # match in that position
56
+ # # in other words, while {} is pattern union (logical
57
+ # # OR), [] is intersection (logical AND)
58
+ # '(send %1 _)' # % stands for a parameter which must be supplied to
59
+ # # #match at matching time
60
+ # # it will be compared to the corresponding value in
61
+ # # the AST using #==
62
+ # # a bare '%' is the same as '%1'
63
+ # # the number of extra parameters passed to #match
64
+ # # must equal the highest % value in the pattern
65
+ # # for consistency, %0 is the 'root node' which is
66
+ # # passed as the 1st argument to #match, where the
67
+ # # matching process starts
68
+ # '^^send' # each ^ ascends one level in the AST
69
+ # # so this matches against the grandparent node
70
+ # '#method' # we call this a 'funcall'; it calls a method in the
71
+ # # context where a pattern-matching method is defined
72
+ # # if that returns a truthy value, the match succeeds
73
+ # 'equal?(%1)' # predicates can be given 1 or more extra args
74
+ # '#method(%0, 1)' # funcalls can also be given 1 or more extra args
75
+ #
76
+ # You can nest arbitrarily deep:
77
+ #
78
+ # # matches node parsed from 'Const = Class.new' or 'Const = Module.new':
79
+ # '(casgn nil const (send (const nil {:Class :Module}) :new)))'
80
+ # # matches a node parsed from an 'if', with a '==' comparison,
81
+ # # and no 'else' branch:
82
+ # '(if (send _ :== _) _ nil)'
83
+ #
84
+ # Note that patterns like 'send' are implemented by calling `#send_type?` on
85
+ # the node being matched, 'const' by `#const_type?`, 'int' by `#int_type?`,
86
+ # and so on. Therefore, if you add methods which are named like
87
+ # `#prefix_type?` to the AST node class, then 'prefix' will become usable as
88
+ # a pattern.
89
+ #
90
+ # Also note that if you need a "guard clause" to protect against possible nils
91
+ # in a certain place in the AST, you can do it like this: `[!nil <pattern>]`
92
+ #
93
+ # The compiler code is very simple; don't be afraid to read through it!
94
+ class NodePattern
95
+ # @private
96
+ Invalid = Class.new(StandardError)
97
+
98
+ # @private
99
+ # Builds Ruby code which implements a pattern
100
+ class Compiler
101
+ RSYM = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}
102
+ ID_CHAR = /[a-zA-Z_-]/
103
+ META = /\(|\)|\{|\}|\[|\]|\$\.\.\.|\$|!|\^|\.\.\./
104
+ NUMBER = /-?\d+(?:\.\d+)?/
105
+ TOKEN =
106
+ /\G(?:[\s,]+|#{META}|%\d*|#{NUMBER}|\#?#{ID_CHAR}+[\!\?]?\(?|#{RSYM}|.)/
107
+
108
+ NODE = /\A#{ID_CHAR}+\Z/
109
+ PREDICATE = /\A#{ID_CHAR}+\?\(?\Z/
110
+ WILDCARD = /\A_#{ID_CHAR}*\Z/
111
+ FUNCALL = /\A\##{ID_CHAR}+[\!\?]?\(?\Z/
112
+ LITERAL = /\A(?:#{RSYM}|#{NUMBER}|nil)\Z/
113
+ PARAM = /\A%\d*\Z/
114
+ CLOSING = /\A(?:\)|\}|\])\Z/
115
+
116
+ attr_reader :match_code
117
+
118
+ def initialize(str, node_var = 'node0')
119
+ @string = str
120
+ @root = node_var
121
+
122
+ @temps = 0 # avoid name clashes between temp variables
123
+ @captures = 0 # number of captures seen
124
+ @unify = {} # named wildcard -> temp variable number
125
+ @params = 0 # highest % (param) number seen
126
+
127
+ run(node_var)
128
+ end
129
+
130
+ def run(node_var)
131
+ tokens = @string.scan(TOKEN)
132
+ tokens.reject! { |token| token =~ /\A[\s,]+\Z/ } # drop whitespace
133
+ @match_code = compile_expr(tokens, node_var, false)
134
+ fail_due_to('unbalanced pattern') unless tokens.empty?
135
+ end
136
+
137
+ # rubbycop:disable Metrics/MethodLength, Metrics/AbcSize
138
+ def compile_expr(tokens, cur_node, seq_head)
139
+ # read a single pattern-matching expression from the token stream,
140
+ # return Ruby code which performs the corresponding matching operation
141
+ # on 'cur_node' (which is Ruby code which evaluates to an AST node)
142
+ #
143
+ # the 'pattern-matching' expression may be a composite which
144
+ # contains an arbitrary number of sub-expressions
145
+ token = tokens.shift
146
+ case token
147
+ when '(' then compile_seq(tokens, cur_node, seq_head)
148
+ when '{' then compile_union(tokens, cur_node, seq_head)
149
+ when '[' then compile_intersect(tokens, cur_node, seq_head)
150
+ when '!' then compile_negation(tokens, cur_node, seq_head)
151
+ when '$' then compile_capture(tokens, cur_node, seq_head)
152
+ when '^' then compile_ascend(tokens, cur_node, seq_head)
153
+ when WILDCARD then compile_wildcard(cur_node, token[1..-1], seq_head)
154
+ when FUNCALL then compile_funcall(tokens, cur_node, token, seq_head)
155
+ when LITERAL then compile_literal(cur_node, token, seq_head)
156
+ when PREDICATE then compile_predicate(tokens, cur_node, token, seq_head)
157
+ when NODE then compile_nodetype(cur_node, token)
158
+ when PARAM then compile_param(cur_node, token[1..-1], seq_head)
159
+ when CLOSING then fail_due_to("#{token} in invalid position")
160
+ when nil then fail_due_to('pattern ended prematurely')
161
+ else fail_due_to("invalid token #{token.inspect}")
162
+ end
163
+ end
164
+ # rubbycop:enable Metrics/MethodLength, Metrics/AbcSize
165
+
166
+ def compile_seq(tokens, cur_node, seq_head)
167
+ fail_due_to('empty parentheses') if tokens.first == ')'
168
+ fail_due_to('parentheses at sequence head') if seq_head
169
+
170
+ # 'cur_node' is a Ruby expression which evaluates to an AST node,
171
+ # but we don't know how expensive it is
172
+ # to be safe, cache the node in a temp variable and then use the
173
+ # temp variable as 'cur_node'
174
+ with_temp_node(cur_node) do |init, temp_node|
175
+ terms = compile_seq_terms(tokens, temp_node)
176
+
177
+ join_terms(init, terms, ' && ')
178
+ end
179
+ end
180
+
181
+ def compile_seq_terms(tokens, cur_node)
182
+ ret, size =
183
+ compile_seq_terms_with_size(tokens, cur_node) do |token, terms, index|
184
+ case token
185
+ when '...'.freeze
186
+ return compile_ellipsis(tokens, cur_node, terms, index)
187
+ when '$...'.freeze
188
+ return compile_capt_ellip(tokens, cur_node, terms, index)
189
+ end
190
+ end
191
+
192
+ ret << "(#{cur_node}.children.size == #{size})"
193
+ end
194
+
195
+ def compile_seq_terms_with_size(tokens, cur_node)
196
+ index = nil
197
+ terms = []
198
+ until tokens.first == ')'
199
+ yield tokens.first, terms, index || 0
200
+ term, index = compile_expr_with_index(tokens, cur_node, index)
201
+ terms << term
202
+ end
203
+
204
+ tokens.shift # drop concluding )
205
+ [terms, index]
206
+ end
207
+
208
+ def compile_expr_with_index(tokens, cur_node, index)
209
+ if index.nil?
210
+ # in 'sequence head' position; some expressions are compiled
211
+ # differently at 'sequence head' (notably 'node type' expressions)
212
+ # grep for seq_head to see where it makes a difference
213
+ [compile_expr(tokens, cur_node, true), 0]
214
+ else
215
+ child_node = "#{cur_node}.children[#{index}]"
216
+ [compile_expr(tokens, child_node, false), index + 1]
217
+ end
218
+ end
219
+
220
+ def compile_ellipsis(tokens, cur_node, terms, index)
221
+ if (term = compile_seq_tail(tokens, "#{cur_node}.children.last"))
222
+ terms << "(#{cur_node}.children.size > #{index})"
223
+ terms << term
224
+ elsif index > 0
225
+ terms << "(#{cur_node}.children.size >= #{index})"
226
+ end
227
+ terms
228
+ end
229
+
230
+ def compile_capt_ellip(tokens, cur_node, terms, index)
231
+ capture = next_capture
232
+ if (term = compile_seq_tail(tokens, "#{cur_node}.children.last"))
233
+ terms << "(#{cur_node}.children.size > #{index})"
234
+ terms << term
235
+ terms << "(#{capture} = #{cur_node}.children[#{index}..-2])"
236
+ else
237
+ terms << "(#{cur_node}.children.size >= #{index})" if index > 0
238
+ terms << "(#{capture} = #{cur_node}.children[#{index}..-1])"
239
+ end
240
+ terms
241
+ end
242
+
243
+ def compile_seq_tail(tokens, cur_node)
244
+ tokens.shift
245
+ if tokens.first == ')'
246
+ tokens.shift
247
+ nil
248
+ else
249
+ expr = compile_expr(tokens, cur_node, false)
250
+ fail_due_to('missing )') unless tokens.shift == ')'
251
+ expr
252
+ end
253
+ end
254
+
255
+ def compile_union(tokens, cur_node, seq_head)
256
+ fail_due_to('empty union') if tokens.first == '}'
257
+
258
+ with_temp_node(cur_node) do |init, temp_node|
259
+ terms = union_terms(tokens, temp_node, seq_head)
260
+ join_terms(init, terms, ' || ')
261
+ end
262
+ end
263
+
264
+ def union_terms(tokens, temp_node, seq_head)
265
+ # we need to ensure that each branch of the {} contains the same
266
+ # number of captures (since only one branch of the {} can actually
267
+ # match, the same variables are used to hold the captures for each
268
+ # branch)
269
+ compile_expr_with_captures(tokens,
270
+ temp_node, seq_head) do |term, before, after|
271
+ terms = [term]
272
+ until tokens.first == '}'
273
+ terms << compile_expr_with_capture_check(tokens, temp_node,
274
+ seq_head, before, after)
275
+ end
276
+ tokens.shift
277
+
278
+ terms
279
+ end
280
+ end
281
+
282
+ def compile_expr_with_captures(tokens, temp_node, seq_head)
283
+ captures_before = @captures
284
+ expr = compile_expr(tokens, temp_node, seq_head)
285
+
286
+ yield expr, captures_before, @captures
287
+ end
288
+
289
+ def compile_expr_with_capture_check(tokens, temp_node, seq_head, before,
290
+ after)
291
+ @captures = before
292
+ expr = compile_expr(tokens, temp_node, seq_head)
293
+ if @captures != after
294
+ fail_due_to('each branch of {} must have same # of captures')
295
+ end
296
+
297
+ expr
298
+ end
299
+
300
+ def compile_intersect(tokens, cur_node, seq_head)
301
+ fail_due_to('empty intersection') if tokens.first == ']'
302
+
303
+ with_temp_node(cur_node) do |init, temp_node|
304
+ terms = []
305
+ until tokens.first == ']'
306
+ terms << compile_expr(tokens, temp_node, seq_head)
307
+ end
308
+ tokens.shift
309
+
310
+ join_terms(init, terms, ' && ')
311
+ end
312
+ end
313
+
314
+ def compile_capture(tokens, cur_node, seq_head)
315
+ "(#{next_capture} = #{cur_node}#{'.type' if seq_head}; " \
316
+ "#{compile_expr(tokens, cur_node, seq_head)})"
317
+ end
318
+
319
+ def compile_negation(tokens, cur_node, seq_head)
320
+ "(!#{compile_expr(tokens, cur_node, seq_head)})"
321
+ end
322
+
323
+ def compile_ascend(tokens, cur_node, seq_head)
324
+ "(#{cur_node}.parent && " \
325
+ "#{compile_expr(tokens, "#{cur_node}.parent", seq_head)})"
326
+ end
327
+
328
+ def compile_wildcard(cur_node, name, seq_head)
329
+ if name.empty?
330
+ 'true'
331
+ elsif @unify.key?(name)
332
+ # we have already seen a wildcard with this name before
333
+ # so the value it matched the first time will already be stored
334
+ # in a temp. check if this value matches the one stored in the temp
335
+ "(#{cur_node}#{'.type' if seq_head} == temp#{@unify[name]})"
336
+ else
337
+ n = @unify[name] = next_temp_value
338
+ # double assign to temp#{n} to avoid "assigned but unused variable"
339
+ "(temp#{n} = temp#{n} = #{cur_node}#{'.type' if seq_head}; true)"
340
+ end
341
+ end
342
+
343
+ def compile_literal(cur_node, literal, seq_head)
344
+ "(#{cur_node}#{'.type' if seq_head} == #{literal})"
345
+ end
346
+
347
+ def compile_predicate(tokens, cur_node, predicate, seq_head)
348
+ if predicate.end_with?('(') # is there an arglist?
349
+ args = compile_args(tokens)
350
+ predicate = predicate[0..-2] # drop the trailing (
351
+ "(#{cur_node}#{'.type' if seq_head}.#{predicate}(#{args.join(',')}))"
352
+ else
353
+ "(#{cur_node}#{'.type' if seq_head}.#{predicate})"
354
+ end
355
+ end
356
+
357
+ def compile_funcall(tokens, cur_node, method, seq_head)
358
+ # call a method in the context which this pattern-matching
359
+ # code is used in. pass target value as an argument
360
+ method = method[1..-1] # drop the leading #
361
+ if method.end_with?('(') # is there an arglist?
362
+ args = compile_args(tokens)
363
+ method = method[0..-2] # drop the trailing (
364
+ "(#{method}(#{cur_node}#{'.type' if seq_head}),#{args.join(',')})"
365
+ else
366
+ "(#{method}(#{cur_node}#{'.type' if seq_head}))"
367
+ end
368
+ end
369
+
370
+ def compile_nodetype(cur_node, type)
371
+ "(#{cur_node} && #{cur_node}.#{type.tr('-', '_')}_type?)"
372
+ end
373
+
374
+ def compile_param(cur_node, number, seq_head)
375
+ "(#{cur_node}#{'.type' if seq_head} == #{get_param(number)})"
376
+ end
377
+
378
+ def compile_args(tokens)
379
+ args = []
380
+ args << compile_arg(tokens.shift) until tokens.first == ')'
381
+ tokens.shift # drop the )
382
+ args
383
+ end
384
+
385
+ def compile_arg(token)
386
+ case token
387
+ when WILDCARD then
388
+ name = token[1..-1]
389
+ number = @unify[name] || fail_due_to('invalid in arglist: ' + token)
390
+ "temp#{number}"
391
+ when LITERAL then token
392
+ when PARAM then get_param(token[1..-1])
393
+ when CLOSING then fail_due_to("#{token} in invalid position")
394
+ when nil then fail_due_to('pattern ended prematurely')
395
+ else fail_due_to("invalid token in arglist: #{token.inspect}")
396
+ end
397
+ end
398
+
399
+ def next_capture
400
+ "capture#{@captures += 1}"
401
+ end
402
+
403
+ def get_param(number)
404
+ number = number.empty? ? 1 : Integer(number)
405
+ @params = number if number > @params
406
+ number.zero? ? @root : "param#{number}"
407
+ end
408
+
409
+ def join_terms(init, terms, operator)
410
+ "(#{init};#{terms.join(operator)})"
411
+ end
412
+
413
+ def emit_capture_list
414
+ (1..@captures).map { |n| "capture#{n}" }.join(',')
415
+ end
416
+
417
+ def emit_retval
418
+ if @captures.zero?
419
+ 'true'
420
+ elsif @captures == 1
421
+ 'capture1'
422
+ else
423
+ "[#{emit_capture_list}]"
424
+ end
425
+ end
426
+
427
+ def emit_param_list
428
+ (1..@params).map { |n| "param#{n}" }.join(',')
429
+ end
430
+
431
+ def emit_trailing_params
432
+ params = emit_param_list
433
+ params.empty? ? '' : ",#{params}"
434
+ end
435
+
436
+ def emit_method_code
437
+ <<-CODE
438
+ return nil unless #{@match_code}
439
+ block_given? ? yield(#{emit_capture_list}) : (return #{emit_retval})
440
+ CODE
441
+ end
442
+
443
+ def fail_due_to(message)
444
+ raise Invalid, "Couldn't compile due to #{message}. Pattern: #{@string}"
445
+ end
446
+
447
+ def with_temp_node(cur_node)
448
+ with_temp_variable do |temp_var|
449
+ # double assign to temp#{n} to avoid "assigned but unused variable"
450
+ yield "#{temp_var} = #{temp_var} = #{cur_node}", temp_var
451
+ end
452
+ end
453
+
454
+ def with_temp_variable
455
+ yield "temp#{next_temp_value}"
456
+ end
457
+
458
+ def next_temp_value
459
+ @temps += 1
460
+ end
461
+ end
462
+
463
+ # Helpers for defining methods based on a pattern string
464
+ module Macros
465
+ # Define a method which applies a pattern to an AST node
466
+ #
467
+ # The new method will return nil if the node does not match
468
+ # If the node matches, and a block is provided, the new method will
469
+ # yield to the block (passing any captures as block arguments).
470
+ # If the node matches, and no block is provided, the new method will
471
+ # return the captures, or `true` if there were none.
472
+ def def_node_matcher(method_name, pattern_str)
473
+ compiler = RubbyCop::NodePattern::Compiler.new(pattern_str, 'node')
474
+ src = "def #{method_name}(node" \
475
+ "#{compiler.emit_trailing_params});" \
476
+ "#{compiler.emit_method_code};end"
477
+
478
+ location = caller_locations(1, 1).first
479
+ class_eval(src, location.path, location.lineno)
480
+ end
481
+
482
+ # Define a method which recurses over the descendants of an AST node,
483
+ # checking whether any of them match the provided pattern
484
+ #
485
+ # If the method name ends with '?', the new method will return `true`
486
+ # as soon as it finds a descendant which matches. Otherwise, it will
487
+ # yield all descendants which match.
488
+ def def_node_search(method_name, pattern_str)
489
+ compiler = RubbyCop::NodePattern::Compiler.new(pattern_str, 'node')
490
+ called_from = caller(1..1).first.split(':')
491
+
492
+ if method_name.to_s.end_with?('?')
493
+ node_search_first(method_name, compiler, called_from)
494
+ else
495
+ node_search_all(method_name, compiler, called_from)
496
+ end
497
+ end
498
+
499
+ def node_search_first(method_name, compiler, called_from)
500
+ node_search(method_name, compiler, 'return true', '', called_from)
501
+ end
502
+
503
+ def node_search_all(method_name, compiler, called_from)
504
+ yieldval = compiler.emit_capture_list
505
+ yieldval = 'node' if yieldval.empty?
506
+ prelude = "return enum_for(:#{method_name}, node0" \
507
+ "#{compiler.emit_trailing_params}) unless block_given?"
508
+
509
+ node_search(method_name, compiler, "yield(#{yieldval})", prelude,
510
+ called_from)
511
+ end
512
+
513
+ def node_search(method_name, compiler, on_match, prelude, called_from)
514
+ src = node_search_body(method_name, compiler.emit_trailing_params,
515
+ prelude, compiler.match_code, on_match)
516
+ filename, lineno = *called_from
517
+ class_eval(src, filename, lineno.to_i)
518
+ end
519
+
520
+ def node_search_body(method_name, trailing_params, prelude, match_code,
521
+ on_match)
522
+ <<-END
523
+ def #{method_name}(node0#{trailing_params})
524
+ #{prelude}
525
+ node0.each_node do |node|
526
+ if #{match_code}
527
+ #{on_match}
528
+ end
529
+ end
530
+ nil
531
+ end
532
+ END
533
+ end
534
+ end
535
+
536
+ def initialize(str)
537
+ compiler = Compiler.new(str)
538
+ src = "def match(node0#{compiler.emit_trailing_params});" \
539
+ "#{compiler.emit_method_code}end"
540
+ instance_eval(src)
541
+ end
542
+ end
543
+ end