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,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubbyCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks that comment annotation keywords are written according
7
+ # to guidelines.
8
+ class CommentAnnotation < Cop
9
+ include AnnotationComment
10
+
11
+ MSG = 'Annotation keywords like `%s` should be all upper case, ' \
12
+ 'followed by a colon, and a space, ' \
13
+ 'then a note describing the problem.'.freeze
14
+ MISSING_NOTE = 'Annotation comment, with keyword `%s`, ' \
15
+ 'is missing a note.'.freeze
16
+
17
+ def investigate(processed_source)
18
+ processed_source.comments.each_with_index do |comment, ix|
19
+ next unless first_comment_line?(processed_source.comments, ix)
20
+
21
+ margin, first_word, colon, space, note = split_comment(comment)
22
+ next unless annotation?(comment) &&
23
+ !correct_annotation?(first_word, colon, space, note)
24
+
25
+ length = concat_length(first_word, colon, space)
26
+ add_offense(comment, annotation_range(comment, margin, length),
27
+ format(note ? MSG : MISSING_NOTE, first_word))
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def first_comment_line?(comments, ix)
34
+ ix.zero? || comments[ix - 1].loc.line < comments[ix].loc.line - 1
35
+ end
36
+
37
+ def autocorrect(comment)
38
+ margin, first_word, colon, space, note = split_comment(comment)
39
+ return if note.nil?
40
+
41
+ length = concat_length(first_word, colon, space)
42
+ range = annotation_range(comment, margin, length)
43
+
44
+ ->(corrector) { corrector.replace(range, "#{first_word.upcase}: ") }
45
+ end
46
+
47
+ def annotation_range(comment, margin, length)
48
+ start = comment.loc.expression.begin_pos + margin.length
49
+ range_between(start, start + length)
50
+ end
51
+
52
+ def concat_length(*args)
53
+ args.reduce(0) { |acc, elem| acc + elem.to_s.length }
54
+ end
55
+
56
+ def correct_annotation?(first_word, colon, space, note)
57
+ keyword?(first_word) && (colon && space && note || !colon && !note)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,691 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubbyCop
4
+ module Cop
5
+ module Style
6
+ # Helper module to provide common methods to classes needed for the
7
+ # ConditionalAssignment Cop.
8
+ module ConditionalAssignmentHelper
9
+ EQUAL = '='.freeze
10
+ END_ALIGNMENT = 'Lint/EndAlignment'.freeze
11
+ ALIGN_WITH = 'EnforcedStyleAlignWith'.freeze
12
+ KEYWORD = 'keyword'.freeze
13
+
14
+ # `elsif` branches show up in the `node` as an `else`. We need
15
+ # to recursively iterate over all `else` branches and consider all
16
+ # but the last `node` an `elsif` branch and consider the last `node`
17
+ # the actual `else` branch.
18
+ def expand_elses(branch)
19
+ elsif_branches = expand_elsif(branch)
20
+ else_branch = elsif_branches.any? ? elsif_branches.pop : branch
21
+ [elsif_branches, else_branch]
22
+ end
23
+
24
+ # `when` nodes contain the entire branch including the condition.
25
+ # We only need the contents of the branch, not the condition.
26
+ def expand_when_branches(when_branches)
27
+ when_branches.map { |branch| branch.children[1] }
28
+ end
29
+
30
+ def tail(branch)
31
+ branch.begin_type? ? [*branch].last : branch
32
+ end
33
+
34
+ def lhs(node)
35
+ case node.type
36
+ when :send
37
+ lhs_for_send(node)
38
+ when :op_asgn
39
+ "#{node.children[0].source} #{node.children[1]}= "
40
+ when :and_asgn, :or_asgn
41
+ "#{node.children[0].source} #{node.loc.operator.source} "
42
+ when :casgn
43
+ "#{node.children[1]} = "
44
+ when *ConditionalAssignment::VARIABLE_ASSIGNMENT_TYPES
45
+ "#{node.children[0]} = "
46
+ else
47
+ node.source
48
+ end
49
+ end
50
+
51
+ def indent(cop, source)
52
+ conf = cop.config.for_cop(END_ALIGNMENT)
53
+ if conf[ALIGN_WITH] == KEYWORD
54
+ ' ' * source.length
55
+ else
56
+ ''
57
+ end
58
+ end
59
+
60
+ def end_with_eq?(sym)
61
+ sym.to_s.end_with?(EQUAL)
62
+ end
63
+
64
+ private
65
+
66
+ def expand_elsif(node, elsif_branches = [])
67
+ return [] if node.nil? || !node.if_type?
68
+ _condition, elsif_branch, else_branch = *node
69
+ elsif_branches << elsif_branch
70
+ if else_branch && else_branch.if_type?
71
+ expand_elsif(else_branch, elsif_branches)
72
+ else
73
+ elsif_branches << else_branch
74
+ end
75
+ elsif_branches
76
+ end
77
+
78
+ def lhs_for_send(node)
79
+ receiver = node.receiver ? node.receiver.source : ''
80
+
81
+ if node.method?(:[]=)
82
+ indices = node.arguments[0...-1].map(&:source).join(', ')
83
+ "#{receiver}[#{indices}] = "
84
+ elsif node.setter_method?
85
+ "#{receiver}.#{node.method_name[0...-1]} = "
86
+ else
87
+ "#{receiver} #{node.method_name} "
88
+ end
89
+ end
90
+
91
+ def setter_method?(method_name)
92
+ method_name.to_s.end_with?(EQUAL) &&
93
+ !%i[!= == === >= <=].include?(method_name)
94
+ end
95
+
96
+ def assignment_rhs_exist?(node)
97
+ parent = node.parent
98
+ return true unless parent
99
+ !(parent.mlhs_type? || parent.resbody_type?)
100
+ end
101
+ end
102
+
103
+ # Check for `if` and `case` statements where each branch is used for
104
+ # assignment to the same variable when using the return of the
105
+ # condition can be used instead.
106
+ #
107
+ # @example
108
+ # EnforcedStyle: assign_to_condition
109
+ #
110
+ # # bad
111
+ # if foo
112
+ # bar = 1
113
+ # else
114
+ # bar = 2
115
+ # end
116
+ #
117
+ # case foo
118
+ # when 'a'
119
+ # bar += 1
120
+ # else
121
+ # bar += 2
122
+ # end
123
+ #
124
+ # if foo
125
+ # some_method
126
+ # bar = 1
127
+ # else
128
+ # some_other_method
129
+ # bar = 2
130
+ # end
131
+ #
132
+ # # good
133
+ # bar = if foo
134
+ # 1
135
+ # else
136
+ # 2
137
+ # end
138
+ #
139
+ # bar += case foo
140
+ # when 'a'
141
+ # 1
142
+ # else
143
+ # 2
144
+ # end
145
+ #
146
+ # bar << if foo
147
+ # some_method
148
+ # 1
149
+ # else
150
+ # some_other_method
151
+ # 2
152
+ # end
153
+ #
154
+ # EnforcedStyle: assign_inside_condition
155
+ # # bad
156
+ # bar = if foo
157
+ # 1
158
+ # else
159
+ # 2
160
+ # end
161
+ #
162
+ # bar += case foo
163
+ # when 'a'
164
+ # 1
165
+ # else
166
+ # 2
167
+ # end
168
+ #
169
+ # bar << if foo
170
+ # some_method
171
+ # 1
172
+ # else
173
+ # some_other_method
174
+ # 2
175
+ # end
176
+ #
177
+ # # good
178
+ # if foo
179
+ # bar = 1
180
+ # else
181
+ # bar = 2
182
+ # end
183
+ #
184
+ # case foo
185
+ # when 'a'
186
+ # bar += 1
187
+ # else
188
+ # bar += 2
189
+ # end
190
+ #
191
+ # if foo
192
+ # some_method
193
+ # bar = 1
194
+ # else
195
+ # some_other_method
196
+ # bar = 2
197
+ # end
198
+ class ConditionalAssignment < Cop
199
+ include ConditionalAssignmentHelper
200
+ include ConfigurableEnforcedStyle
201
+ include IgnoredNode
202
+
203
+ MSG = 'Use the return of the conditional for variable assignment ' \
204
+ 'and comparison.'.freeze
205
+ ASSIGN_TO_CONDITION_MSG =
206
+ 'Assign variables inside of conditionals'.freeze
207
+ VARIABLE_ASSIGNMENT_TYPES =
208
+ %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
209
+ ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES +
210
+ %i[and_asgn or_asgn op_asgn masgn].freeze
211
+ LINE_LENGTH = 'Metrics/LineLength'.freeze
212
+ INDENTATION_WIDTH = 'Layout/IndentationWidth'.freeze
213
+ ENABLED = 'Enabled'.freeze
214
+ MAX = 'Max'.freeze
215
+ SINGLE_LINE_CONDITIONS_ONLY = 'SingleLineConditionsOnly'.freeze
216
+ WIDTH = 'Width'.freeze
217
+
218
+ def_node_matcher :condition?, '{if case}'
219
+
220
+ # The shovel operator `<<` does not have its own type. It is a `send`
221
+ # type.
222
+ def_node_matcher :assignment_type?, <<-END
223
+ {
224
+ #{ASSIGNMENT_TYPES.join(' ')}
225
+ (send _recv {:[]= :<< :=~ :!~ :<=> #end_with_eq?} ...)
226
+ }
227
+ END
228
+
229
+ ASSIGNMENT_TYPES.each do |type|
230
+ define_method "on_#{type}" do |node|
231
+ return if part_of_ignored_node?(node)
232
+
233
+ check_assignment_to_condition(node)
234
+ end
235
+ end
236
+
237
+ def on_send(node)
238
+ return unless assignment_type?(node)
239
+
240
+ check_assignment_to_condition(node)
241
+ end
242
+
243
+ def on_if(node)
244
+ return unless style == :assign_to_condition
245
+ return if node.elsif?
246
+
247
+ else_branch = node.else_branch
248
+ elsif_branches, else_branch = expand_elses(else_branch)
249
+
250
+ return unless else_branch
251
+
252
+ branches = [node.if_branch, *elsif_branches, else_branch]
253
+
254
+ check_node(node, branches)
255
+ end
256
+
257
+ def on_case(node)
258
+ return unless style == :assign_to_condition
259
+ return unless node.else_branch
260
+
261
+ when_branches = expand_when_branches(node.when_branches)
262
+ branches = [*when_branches, node.else_branch]
263
+
264
+ check_node(node, branches)
265
+ end
266
+
267
+ private
268
+
269
+ def check_assignment_to_condition(node)
270
+ return unless style == :assign_inside_condition
271
+ return unless assignment_rhs_exist?(node)
272
+ ignore_node(node)
273
+
274
+ assignment = assignment_node(node)
275
+ return unless condition?(assignment)
276
+ return if allowed_ternary?(assignment)
277
+
278
+ _condition, *branches, else_branch = *assignment
279
+ return unless else_branch # empty else
280
+ return if allowed_single_line?([*branches, else_branch])
281
+
282
+ add_offense(node, :expression, ASSIGN_TO_CONDITION_MSG)
283
+ end
284
+
285
+ def allowed_ternary?(assignment)
286
+ assignment.if_type? && assignment.ternary? && !include_ternary?
287
+ end
288
+
289
+ def allowed_single_line?(branches)
290
+ single_line_conditions_only? && branches.any?(&:begin_type?)
291
+ end
292
+
293
+ def autocorrect(node)
294
+ if assignment_type?(node)
295
+ move_assignment_inside_condition(node)
296
+ else
297
+ move_assignment_outside_condition(node)
298
+ end
299
+ end
300
+
301
+ def assignment_node(node)
302
+ *_variable, assignment = *node
303
+
304
+ if assignment.begin_type? && assignment.children.one?
305
+ assignment, = *assignment
306
+ end
307
+
308
+ assignment
309
+ end
310
+
311
+ def move_assignment_outside_condition(node)
312
+ if node.case_type?
313
+ CaseCorrector.correct(self, node)
314
+ elsif node.ternary?
315
+ TernaryCorrector.correct(node)
316
+ elsif node.if?
317
+ IfCorrector.correct(self, node)
318
+ elsif node.unless?
319
+ UnlessCorrector.correct(self, node)
320
+ end
321
+ end
322
+
323
+ def move_assignment_inside_condition(node)
324
+ *_assignment, condition = *node
325
+
326
+ if ternary_condition?(condition)
327
+ TernaryCorrector.move_assignment_inside_condition(node)
328
+ elsif condition.case_type?
329
+ CaseCorrector.move_assignment_inside_condition(node)
330
+ elsif condition.if_type?
331
+ IfCorrector.move_assignment_inside_condition(node)
332
+ end
333
+ end
334
+
335
+ def ternary_condition?(node)
336
+ [node, node.children.first].any? { |n| n.if_type? && n.ternary? }
337
+ end
338
+
339
+ def lhs_all_match?(branches)
340
+ first_lhs = lhs(branches.first)
341
+ branches.all? { |branch| lhs(branch) == first_lhs }
342
+ end
343
+
344
+ def assignment_types_match?(*nodes)
345
+ return unless assignment_type?(nodes.first)
346
+
347
+ nodes.map(&:type).uniq.one?
348
+ end
349
+
350
+ def check_node(node, branches)
351
+ return if allowed_ternary?(node)
352
+ return unless allowed_statements?(branches)
353
+ return if allowed_single_line?(branches)
354
+ return if correction_exceeds_line_limit?(node, branches)
355
+
356
+ add_offense(node, :expression)
357
+ end
358
+
359
+ def allowed_statements?(branches)
360
+ return false unless branches.all?
361
+
362
+ statements = branches.map { |branch| tail(branch) }
363
+
364
+ lhs_all_match?(statements) && statements.none?(&:masgn_type?) &&
365
+ assignment_types_match?(*statements)
366
+ end
367
+
368
+ # If `Metrics/LineLength` is enabled, we do not want to introduce an
369
+ # offense by auto-correcting this cop. Find the max configured line
370
+ # length. Find the longest line of condition. Remove the assignment
371
+ # from lines that contain the offending assignment because after
372
+ # correcting, this will not be on the line anymore. Check if the length
373
+ # of the longest line + the length of the corrected assignment is
374
+ # greater than the max configured line length
375
+ def correction_exceeds_line_limit?(node, branches)
376
+ return false unless line_length_cop_enabled?
377
+
378
+ assignment = lhs(tail(branches[0]))
379
+
380
+ longest_rhs_exceeds_line_limit?(branches, assignment) ||
381
+ longest_line_exceeds_line_limit?(node, assignment)
382
+ end
383
+
384
+ def longest_rhs_exceeds_line_limit?(branches, assignment)
385
+ longest_rhs_full_length(branches, assignment) > max_line_length
386
+ end
387
+
388
+ def longest_line_exceeds_line_limit?(node, assignment)
389
+ longest_line(node, assignment).length > max_line_length
390
+ end
391
+
392
+ def longest_rhs_full_length(branches, assignment)
393
+ longest_rhs(branches) + indentation_width + assignment.length
394
+ end
395
+
396
+ def longest_line(node, assignment)
397
+ assignment_regex = /#{Regexp.escape(assignment).gsub(' ', '\s*')}/
398
+ lines = node.source.lines.map do |line|
399
+ line.chomp.sub(assignment_regex, '')
400
+ end
401
+ longest_line = lines.max_by(&:length)
402
+ longest_line + assignment
403
+ end
404
+
405
+ def longest_rhs(branches)
406
+ line_lengths = branches.flat_map do |branch|
407
+ branch.children.last.source.split("\n").map(&:length)
408
+ end
409
+ line_lengths.max
410
+ end
411
+
412
+ def line_length_cop_enabled?
413
+ config.for_cop(LINE_LENGTH)[ENABLED]
414
+ end
415
+
416
+ def max_line_length
417
+ config.for_cop(LINE_LENGTH)[MAX]
418
+ end
419
+
420
+ def indentation_width
421
+ config.for_cop(INDENTATION_WIDTH)[WIDTH] || 2
422
+ end
423
+
424
+ def single_line_conditions_only?
425
+ cop_config[SINGLE_LINE_CONDITIONS_ONLY]
426
+ end
427
+
428
+ def include_ternary?
429
+ cop_config['IncludeTernaryExpressions']
430
+ end
431
+ end
432
+
433
+ # Helper module to provide common methods to ConditionalAssignment
434
+ # correctors
435
+ module ConditionalCorrectorHelper
436
+ def remove_whitespace_in_branches(corrector, branch, condition, column)
437
+ branch.each_node do |child|
438
+ white_space = white_space_range(child, column)
439
+ corrector.remove(white_space) if white_space.source.strip.empty?
440
+ end
441
+
442
+ [condition.loc.else, condition.loc.end].each do |loc|
443
+ corrector.remove_preceding(loc, loc.column - column)
444
+ end
445
+ end
446
+
447
+ def white_space_range(node, column)
448
+ expression = node.loc.expression
449
+ begin_pos = expression.begin_pos - (expression.column - column - 2)
450
+
451
+ Parser::Source::Range.new(expression.source_buffer,
452
+ begin_pos,
453
+ expression.begin_pos)
454
+ end
455
+
456
+ def assignment(node)
457
+ *_, condition = *node
458
+ Parser::Source::Range.new(node.loc.expression.source_buffer,
459
+ node.loc.expression.begin_pos,
460
+ condition.loc.expression.begin_pos)
461
+ end
462
+
463
+ def correct_if_branches(corrector, cop, node)
464
+ if_branch, elsif_branches, else_branch = extract_tail_branches(node)
465
+
466
+ corrector.insert_before(node.source_range, lhs(if_branch))
467
+ replace_branch_assignment(corrector, if_branch)
468
+ correct_branches(corrector, elsif_branches)
469
+ replace_branch_assignment(corrector, else_branch)
470
+ corrector.insert_before(node.loc.end, indent(cop, lhs(if_branch)))
471
+ end
472
+
473
+ def replace_branch_assignment(corrector, branch)
474
+ _variable, *_operator, assignment = *branch
475
+ corrector.replace(branch.source_range, assignment.source)
476
+ end
477
+
478
+ def correct_branches(corrector, branches)
479
+ branches.each do |branch|
480
+ *_, assignment = *branch
481
+ corrector.replace(branch.source_range, assignment.source)
482
+ end
483
+ end
484
+ end
485
+
486
+ # Corrector to correct conditional assignment in ternary conditions.
487
+ class TernaryCorrector
488
+ class << self
489
+ include ConditionalAssignmentHelper
490
+ include ConditionalCorrectorHelper
491
+
492
+ def correct(node)
493
+ lambda do |corrector|
494
+ corrector.replace(node.source_range, correction(node))
495
+ end
496
+ end
497
+
498
+ def move_assignment_inside_condition(node)
499
+ *_var, rhs = *node
500
+ if_branch, else_branch = extract_branches(node)
501
+ assignment = assignment(node)
502
+
503
+ lambda do |corrector|
504
+ remove_parentheses(corrector, rhs) if Util.parentheses?(rhs)
505
+ corrector.remove(assignment)
506
+
507
+ move_branch_inside_condition(corrector, if_branch, assignment)
508
+ move_branch_inside_condition(corrector, else_branch, assignment)
509
+ end
510
+ end
511
+
512
+ private
513
+
514
+ def correction(node)
515
+ condition, if_branch, else_branch = *node
516
+
517
+ "#{lhs(if_branch)}#{ternary(condition, if_branch, else_branch)}"
518
+ end
519
+
520
+ def ternary(condition, if_branch, else_branch)
521
+ _variable, *_operator, if_rhs = *if_branch
522
+ _else_variable, *_operator, else_rhs = *else_branch
523
+
524
+ expr = "#{condition.source} ? #{if_rhs.source} : #{else_rhs.source}"
525
+
526
+ element_assignment?(if_branch) ? "(#{expr})" : expr
527
+ end
528
+
529
+ def element_assignment?(node)
530
+ node.send_type? && node.method_name != :[]=
531
+ end
532
+
533
+ def extract_branches(node)
534
+ *_var, rhs = *node
535
+ condition, = *rhs if rhs.begin_type? && rhs.children.one?
536
+ _condition, if_branch, else_branch = *(condition || rhs)
537
+
538
+ [if_branch, else_branch]
539
+ end
540
+
541
+ def remove_parentheses(corrector, node)
542
+ corrector.remove(node.loc.begin)
543
+ corrector.remove(node.loc.end)
544
+ end
545
+
546
+ def move_branch_inside_condition(corrector, branch, assignment)
547
+ corrector.insert_before(branch.loc.expression, assignment.source)
548
+ end
549
+ end
550
+ end
551
+
552
+ # Corrector to correct conditional assignment in `if` statements.
553
+ class IfCorrector
554
+ class << self
555
+ include ConditionalAssignmentHelper
556
+ include ConditionalCorrectorHelper
557
+
558
+ def correct(cop, node)
559
+ ->(corrector) { correct_if_branches(corrector, cop, node) }
560
+ end
561
+
562
+ def move_assignment_inside_condition(node)
563
+ column = node.loc.expression.column
564
+ *_var, condition = *node
565
+ assignment = assignment(node)
566
+
567
+ lambda do |corrector|
568
+ corrector.remove(assignment)
569
+
570
+ extract_branches(condition).flatten.each do |branch|
571
+ move_branch_inside_condition(corrector, branch, condition,
572
+ assignment, column)
573
+ end
574
+ end
575
+ end
576
+
577
+ private
578
+
579
+ def extract_tail_branches(node)
580
+ if_branch, elsif_branches, else_branch = extract_branches(node)
581
+ elsif_branches.map! { |branch| tail(branch) }
582
+
583
+ [tail(if_branch), elsif_branches, tail(else_branch)]
584
+ end
585
+
586
+ def extract_branches(node)
587
+ _condition, if_branch, else_branch = *node
588
+ elsif_branches, else_branch = expand_elses(else_branch)
589
+
590
+ [if_branch, elsif_branches, else_branch]
591
+ end
592
+
593
+ def move_branch_inside_condition(corrector, branch, condition,
594
+ assignment, column)
595
+ branch_assignment = tail(branch)
596
+ corrector.insert_before(branch_assignment.loc.expression,
597
+ assignment.source)
598
+
599
+ remove_whitespace_in_branches(corrector, branch, condition, column)
600
+
601
+ branch_else = branch.parent.loc.else
602
+ corrector.remove_preceding(branch_else, branch_else.column - column)
603
+ end
604
+ end
605
+ end
606
+
607
+ # Corrector to correct conditional assignment in `case` statements.
608
+ class CaseCorrector
609
+ class << self
610
+ include ConditionalAssignmentHelper
611
+ include ConditionalCorrectorHelper
612
+
613
+ def correct(cop, node)
614
+ when_branches, else_branch = extract_tail_branches(node)
615
+
616
+ lambda do |corrector|
617
+ corrector.insert_before(node.source_range, lhs(else_branch))
618
+ correct_branches(corrector, when_branches)
619
+ replace_branch_assignment(corrector, else_branch)
620
+
621
+ corrector.insert_before(node.loc.end,
622
+ indent(cop, lhs(else_branch)))
623
+ end
624
+ end
625
+
626
+ def move_assignment_inside_condition(node)
627
+ column = node.loc.expression.column
628
+ *_var, condition = *node
629
+ assignment = assignment(node)
630
+
631
+ lambda do |corrector|
632
+ corrector.remove(assignment)
633
+
634
+ extract_branches(condition).flatten.each do |branch|
635
+ move_branch_inside_condition(corrector, branch, condition,
636
+ assignment, column)
637
+ end
638
+ end
639
+ end
640
+
641
+ private
642
+
643
+ def extract_tail_branches(node)
644
+ when_branches, else_branch = extract_branches(node)
645
+ when_branches.map! { |branch| tail(branch) }
646
+ [when_branches, tail(else_branch)]
647
+ end
648
+
649
+ def extract_branches(node)
650
+ _condition, *when_branches, else_branch = *node
651
+ when_branches = expand_when_branches(when_branches)
652
+ [when_branches, else_branch]
653
+ end
654
+
655
+ def move_branch_inside_condition(corrector, branch, condition,
656
+ assignment, column)
657
+ branch_assignment = tail(branch)
658
+ corrector.insert_before(branch_assignment.loc.expression,
659
+ assignment.source)
660
+
661
+ remove_whitespace_in_branches(corrector, branch, condition, column)
662
+
663
+ parent_keyword = branch.parent.loc.keyword
664
+ corrector.remove_preceding(parent_keyword,
665
+ parent_keyword.column - column)
666
+ end
667
+ end
668
+ end
669
+
670
+ # Corrector to correct conditional assignment in `unless` statements.
671
+ class UnlessCorrector
672
+ class << self
673
+ include ConditionalAssignmentHelper
674
+ include ConditionalCorrectorHelper
675
+
676
+ def correct(cop, node)
677
+ ->(corrector) { correct_if_branches(corrector, cop, node) }
678
+ end
679
+
680
+ private
681
+
682
+ def extract_tail_branches(node)
683
+ _condition, else_branch, if_branch = *node
684
+
685
+ [tail(if_branch), [], tail(else_branch)]
686
+ end
687
+ end
688
+ end
689
+ end
690
+ end
691
+ end