rubocop 0.76.0 → 0.83.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 (289) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +6 -6
  4. data/config/default.yml +466 -306
  5. data/lib/rubocop/ast/builder.rb +45 -42
  6. data/lib/rubocop/ast/node/array_node.rb +13 -0
  7. data/lib/rubocop/ast/node/block_node.rb +7 -1
  8. data/lib/rubocop/ast/node/case_match_node.rb +56 -0
  9. data/lib/rubocop/ast/node/def_node.rb +11 -0
  10. data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
  11. data/lib/rubocop/ast/node/mixin/method_dispatch_node.rb +8 -0
  12. data/lib/rubocop/ast/node/regexp_node.rb +2 -4
  13. data/lib/rubocop/ast/node/send_node.rb +4 -0
  14. data/lib/rubocop/ast/node.rb +13 -20
  15. data/lib/rubocop/ast/traversal.rb +29 -10
  16. data/lib/rubocop/cli/command/auto_genenerate_config.rb +105 -0
  17. data/lib/rubocop/cli/command/base.rb +33 -0
  18. data/lib/rubocop/cli/command/execute_runner.rb +76 -0
  19. data/lib/rubocop/cli/command/init_dotfile.rb +45 -0
  20. data/lib/rubocop/cli/command/show_cops.rb +80 -0
  21. data/lib/rubocop/cli/command/version.rb +17 -0
  22. data/lib/rubocop/cli/command.rb +21 -0
  23. data/lib/rubocop/cli/environment.rb +21 -0
  24. data/lib/rubocop/cli.rb +20 -233
  25. data/lib/rubocop/comment_config.rb +6 -1
  26. data/lib/rubocop/config.rb +41 -11
  27. data/lib/rubocop/config_loader.rb +54 -44
  28. data/lib/rubocop/config_loader_resolver.rb +28 -1
  29. data/lib/rubocop/config_obsoletion.rb +67 -11
  30. data/lib/rubocop/config_validator.rb +74 -99
  31. data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
  32. data/lib/rubocop/cop/badge.rb +5 -5
  33. data/lib/rubocop/cop/bundler/gem_comment.rb +4 -4
  34. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +3 -3
  35. data/lib/rubocop/cop/cop.rb +21 -0
  36. data/lib/rubocop/cop/corrector.rb +48 -24
  37. data/lib/rubocop/cop/correctors/alignment_corrector.rb +2 -2
  38. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -2
  39. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  40. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +3 -3
  41. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  42. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  43. data/lib/rubocop/cop/correctors/space_corrector.rb +1 -2
  44. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +2 -2
  45. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  46. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  47. data/lib/rubocop/cop/generator.rb +6 -6
  48. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +59 -0
  49. data/lib/rubocop/cop/internal_affairs/offense_location_keyword.rb +1 -1
  50. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  51. data/lib/rubocop/cop/layout/{align_arguments.rb → argument_alignment.rb} +1 -1
  52. data/lib/rubocop/cop/layout/array_alignment.rb +82 -0
  53. data/lib/rubocop/cop/layout/{indent_assignment.rb → assignment_indentation.rb} +1 -1
  54. data/lib/rubocop/cop/layout/block_end_newline.rb +5 -3
  55. data/lib/rubocop/cop/layout/condition_position.rb +12 -2
  56. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  57. data/lib/rubocop/cop/layout/else_alignment.rb +8 -0
  58. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  59. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +68 -0
  60. data/lib/rubocop/cop/layout/end_of_line.rb +2 -2
  61. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  62. data/lib/rubocop/cop/layout/{indent_first_argument.rb → first_argument_indentation.rb} +5 -5
  63. data/lib/rubocop/cop/layout/{indent_first_array_element.rb → first_array_element_indentation.rb} +20 -14
  64. data/lib/rubocop/cop/layout/{indent_first_hash_element.rb → first_hash_element_indentation.rb} +4 -4
  65. data/lib/rubocop/cop/layout/{indent_first_parameter.rb → first_parameter_indentation.rb} +3 -3
  66. data/lib/rubocop/cop/layout/{align_hash.rb → hash_alignment.rb} +10 -6
  67. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  68. data/lib/rubocop/cop/layout/{indent_heredoc.rb → heredoc_indentation.rb} +7 -7
  69. data/lib/rubocop/cop/layout/{tab.rb → indentation_style.rb} +48 -6
  70. data/lib/rubocop/cop/layout/leading_comment_space.rb +34 -3
  71. data/lib/rubocop/cop/layout/{leading_blank_lines.rb → leading_empty_lines.rb} +1 -1
  72. data/lib/rubocop/cop/{metrics → layout}/line_length.rb +72 -110
  73. data/lib/rubocop/cop/layout/multiline_block_layout.rb +15 -6
  74. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
  75. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +1 -1
  76. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  77. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +13 -4
  78. data/lib/rubocop/cop/layout/{align_parameters.rb → parameter_alignment.rb} +1 -1
  79. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +3 -3
  80. data/lib/rubocop/cop/layout/space_around_keyword.rb +12 -0
  81. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +133 -0
  82. data/lib/rubocop/cop/layout/space_around_operators.rb +69 -9
  83. data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
  84. data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
  85. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -1
  86. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -2
  87. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
  88. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +2 -2
  89. data/lib/rubocop/cop/layout/{trailing_blank_lines.rb → trailing_empty_lines.rb} +1 -1
  90. data/lib/rubocop/cop/layout/trailing_whitespace.rb +2 -2
  91. data/lib/rubocop/cop/lint/ambiguous_operator.rb +38 -0
  92. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +14 -0
  93. data/lib/rubocop/cop/lint/boolean_symbol.rb +12 -0
  94. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  95. data/lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb +1 -1
  96. data/lib/rubocop/cop/lint/{duplicated_key.rb → duplicate_hash_key.rb} +1 -1
  97. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -5
  98. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  99. data/lib/rubocop/cop/lint/empty_when.rb +29 -6
  100. data/lib/rubocop/cop/lint/ensure_return.rb +18 -1
  101. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  102. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  103. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -1
  104. data/lib/rubocop/cop/lint/inherit_exception.rb +1 -1
  105. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  106. data/lib/rubocop/cop/lint/literal_as_condition.rb +10 -13
  107. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  108. data/lib/rubocop/cop/lint/loop.rb +6 -4
  109. data/lib/rubocop/cop/lint/{multiple_compare.rb → multiple_comparison.rb} +2 -2
  110. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  111. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
  112. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  113. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -9
  114. data/lib/rubocop/cop/lint/percent_string_array.rb +2 -2
  115. data/lib/rubocop/cop/lint/raise_exception.rb +75 -0
  116. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -9
  117. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +13 -8
  118. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  119. data/lib/rubocop/cop/lint/{string_conversion_in_interpolation.rb → redundant_string_coercion.rb} +2 -2
  120. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  121. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  122. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -1
  123. data/lib/rubocop/cop/lint/struct_new_override.rb +58 -0
  124. data/lib/rubocop/cop/lint/{handle_exceptions.rb → suppressed_exception.rb} +13 -29
  125. data/lib/rubocop/cop/lint/unified_integer.rb +0 -2
  126. data/lib/rubocop/cop/lint/unused_method_argument.rb +32 -6
  127. data/lib/rubocop/cop/lint/uri_regexp.rb +4 -4
  128. data/lib/rubocop/cop/lint/useless_access_modifier.rb +69 -23
  129. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -2
  130. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +5 -0
  131. data/lib/rubocop/cop/lint/useless_setter_call.rb +5 -1
  132. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  133. data/lib/rubocop/cop/migration/department_name.rb +47 -6
  134. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  135. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
  136. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +6 -1
  137. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -5
  138. data/lib/rubocop/cop/mixin/{hash_alignment.rb → hash_alignment_styles.rb} +1 -1
  139. data/lib/rubocop/cop/mixin/hash_transform_method.rb +178 -0
  140. data/lib/rubocop/cop/mixin/line_length_help.rb +89 -0
  141. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -0
  142. data/lib/rubocop/cop/mixin/nil_methods.rb +4 -4
  143. data/lib/rubocop/cop/mixin/parser_diagnostic.rb +1 -1
  144. data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
  145. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -24
  146. data/lib/rubocop/cop/mixin/target_ruby_version.rb +5 -1
  147. data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -13
  148. data/lib/rubocop/cop/naming/{uncommunicative_block_param_name.rb → block_parameter_name.rb} +3 -3
  149. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +1 -1
  150. data/lib/rubocop/cop/naming/constant_name.rb +2 -1
  151. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +6 -6
  152. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  153. data/lib/rubocop/cop/naming/method_name.rb +26 -0
  154. data/lib/rubocop/cop/naming/{uncommunicative_method_param_name.rb → method_parameter_name.rb} +4 -4
  155. data/lib/rubocop/cop/naming/predicate_name.rb +6 -6
  156. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  157. data/lib/rubocop/cop/offense.rb +11 -0
  158. data/lib/rubocop/cop/registry.rb +15 -3
  159. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -6
  160. data/lib/rubocop/cop/style/alias.rb +5 -5
  161. data/lib/rubocop/cop/style/and_or.rb +5 -6
  162. data/lib/rubocop/cop/style/array_join.rb +2 -2
  163. data/lib/rubocop/cop/style/attr.rb +8 -0
  164. data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
  165. data/lib/rubocop/cop/style/case_equality.rb +24 -1
  166. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  167. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  168. data/lib/rubocop/cop/style/conditional_assignment.rb +10 -10
  169. data/lib/rubocop/cop/style/copyright.rb +1 -1
  170. data/lib/rubocop/cop/style/dir.rb +1 -1
  171. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -0
  172. data/lib/rubocop/cop/style/documentation.rb +43 -5
  173. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +1 -1
  174. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  175. data/lib/rubocop/cop/style/each_with_object.rb +3 -3
  176. data/lib/rubocop/cop/style/empty_method.rb +1 -5
  177. data/lib/rubocop/cop/style/end_block.rb +6 -0
  178. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  179. data/lib/rubocop/cop/style/even_odd.rb +2 -2
  180. data/lib/rubocop/cop/style/expand_path_arguments.rb +3 -3
  181. data/lib/rubocop/cop/style/exponential_notation.rb +119 -0
  182. data/lib/rubocop/cop/style/format_string.rb +2 -2
  183. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
  184. data/lib/rubocop/cop/style/guard_clause.rb +28 -4
  185. data/lib/rubocop/cop/style/hash_each_methods.rb +89 -0
  186. data/lib/rubocop/cop/style/hash_syntax.rb +3 -5
  187. data/lib/rubocop/cop/style/hash_transform_keys.rb +83 -0
  188. data/lib/rubocop/cop/style/hash_transform_values.rb +80 -0
  189. data/lib/rubocop/cop/style/if_unless_modifier.rb +61 -6
  190. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -0
  191. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  192. data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
  193. data/lib/rubocop/cop/style/ip_addresses.rb +4 -4
  194. data/lib/rubocop/cop/style/lambda.rb +3 -2
  195. data/lib/rubocop/cop/style/lambda_call.rb +1 -21
  196. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +169 -0
  197. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
  198. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -205
  199. data/lib/rubocop/cop/style/mixin_grouping.rb +2 -2
  200. data/lib/rubocop/cop/style/module_function.rb +58 -12
  201. data/lib/rubocop/cop/style/multiline_if_modifier.rb +1 -1
  202. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  203. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  204. data/lib/rubocop/cop/style/multiline_when_then.rb +21 -2
  205. data/lib/rubocop/cop/style/mutable_constant.rb +2 -4
  206. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -9
  207. data/lib/rubocop/cop/style/next.rb +7 -7
  208. data/lib/rubocop/cop/style/nil_comparison.rb +1 -1
  209. data/lib/rubocop/cop/style/non_nil_check.rb +4 -4
  210. data/lib/rubocop/cop/style/not.rb +1 -1
  211. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  212. data/lib/rubocop/cop/style/numeric_literals.rb +8 -4
  213. data/lib/rubocop/cop/style/numeric_predicate.rb +5 -4
  214. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  215. data/lib/rubocop/cop/style/option_hash.rb +3 -3
  216. data/lib/rubocop/cop/style/optional_arguments.rb +1 -1
  217. data/lib/rubocop/cop/style/or_assignment.rb +4 -3
  218. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
  219. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  220. data/lib/rubocop/cop/style/perl_backrefs.rb +2 -2
  221. data/lib/rubocop/cop/style/proc.rb +1 -1
  222. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  223. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  224. data/lib/rubocop/cop/style/redundant_condition.rb +18 -6
  225. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
  226. data/lib/rubocop/cop/style/redundant_exception.rb +3 -3
  227. data/lib/rubocop/cop/style/redundant_interpolation.rb +2 -2
  228. data/lib/rubocop/cop/style/redundant_parentheses.rb +3 -3
  229. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -2
  230. data/lib/rubocop/cop/style/redundant_return.rb +7 -15
  231. data/lib/rubocop/cop/style/redundant_self.rb +1 -1
  232. data/lib/rubocop/cop/style/redundant_sort.rb +3 -3
  233. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  234. data/lib/rubocop/cop/style/return_nil.rb +1 -1
  235. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  236. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  237. data/lib/rubocop/cop/style/slicing_with_range.rb +39 -0
  238. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  239. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -4
  240. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -1
  241. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  242. data/lib/rubocop/cop/style/symbol_literal.rb +2 -2
  243. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -3
  244. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +35 -22
  245. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +41 -0
  246. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +88 -0
  247. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +44 -0
  248. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
  249. data/lib/rubocop/cop/style/trivial_accessors.rb +6 -6
  250. data/lib/rubocop/cop/style/unpack_first.rb +0 -4
  251. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -1
  252. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -2
  253. data/lib/rubocop/cop/style/word_array.rb +1 -1
  254. data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
  255. data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
  256. data/lib/rubocop/cop/team.rb +5 -0
  257. data/lib/rubocop/cop/util.rb +24 -0
  258. data/lib/rubocop/cop/variable_force/assignment.rb +1 -0
  259. data/lib/rubocop/cop/variable_force/scope.rb +1 -0
  260. data/lib/rubocop/cop/variable_force/variable.rb +1 -0
  261. data/lib/rubocop/cop/variable_force.rb +4 -1
  262. data/lib/rubocop/formatter/base_formatter.rb +2 -2
  263. data/lib/rubocop/formatter/clang_style_formatter.rb +0 -2
  264. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  265. data/lib/rubocop/formatter/json_formatter.rb +6 -5
  266. data/lib/rubocop/formatter/junit_formatter.rb +74 -0
  267. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  268. data/lib/rubocop/formatter/tap_formatter.rb +0 -2
  269. data/lib/rubocop/name_similarity.rb +12 -9
  270. data/lib/rubocop/node_pattern.rb +97 -11
  271. data/lib/rubocop/options.rb +26 -13
  272. data/lib/rubocop/processed_source.rb +1 -4
  273. data/lib/rubocop/rake_task.rb +1 -0
  274. data/lib/rubocop/result_cache.rb +23 -7
  275. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  276. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  277. data/lib/rubocop/rspec/shared_contexts.rb +5 -4
  278. data/lib/rubocop/runner.rb +25 -4
  279. data/lib/rubocop/target_finder.rb +6 -4
  280. data/lib/rubocop/target_ruby.rb +151 -0
  281. data/lib/rubocop/version.rb +1 -1
  282. data/lib/rubocop.rb +53 -27
  283. metadata +73 -48
  284. data/lib/rubocop/cop/layout/align_array.rb +0 -39
  285. data/lib/rubocop/cop/lint/end_in_method.rb +0 -40
  286. data/lib/rubocop/cop/mixin/safe_mode.rb +0 -24
  287. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
  288. data/lib/rubocop/formatter/disabled_lines_formatter.rb +0 -57
  289. data/lib/rubocop/string_util.rb +0 -14
@@ -78,6 +78,8 @@ module RuboCop
78
78
  # # matching process starts
79
79
  # '^^send' # each ^ ascends one level in the AST
80
80
  # # so this matches against the grandparent node
81
+ # '`send' # descends any number of level in the AST
82
+ # # so this matches against any descendant node
81
83
  # '#method' # we call this a 'funcall'; it calls a method in the
82
84
  # # context where a pattern-matching method is defined
83
85
  # # if that returns a truthy value, the match succeeds
@@ -112,7 +114,7 @@ module RuboCop
112
114
  SYMBOL = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}.freeze
113
115
  IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/.freeze
114
116
  META = Regexp.union(
115
- %w"( ) { } [ ] $< < > $... $ ! ^ ... + * ?"
117
+ %w"( ) { } [ ] $< < > $... $ ! ^ ` ... + * ?"
116
118
  ).freeze
117
119
  NUMBER = /-?\d+(?:\.\d+)?/.freeze
118
120
  STRING = /".+?"/.freeze
@@ -188,7 +190,7 @@ module RuboCop
188
190
 
189
191
  @temps = 0 # avoid name clashes between temp variables
190
192
  @captures = 0 # number of captures seen
191
- @unify = {} # named wildcard -> temp variable number
193
+ @unify = {} # named wildcard -> temp variable
192
194
  @params = 0 # highest % (param) number seen
193
195
  run(node_var)
194
196
  end
@@ -223,6 +225,7 @@ module RuboCop
223
225
  when '!' then compile_negation
224
226
  when '$' then compile_capture
225
227
  when '^' then compile_ascend
228
+ when '`' then compile_descend
226
229
  when WILDCARD then compile_wildcard(token[1..-1])
227
230
  when FUNCALL then compile_funcall(token)
228
231
  when LITERAL then compile_literal(token)
@@ -380,7 +383,7 @@ module RuboCop
380
383
  def compile_seq_head
381
384
  return unless seq_head?
382
385
 
383
- fail_due_to 'sequences can not start with <' \
386
+ fail_due_to 'sequences cannot start with <' \
384
387
  if @terms[0].respond_to? :call
385
388
 
386
389
  with_seq_head_context(@terms[0])
@@ -466,12 +469,67 @@ module RuboCop
466
469
  end
467
470
  end
468
471
 
472
+ def access_unify(name)
473
+ var = @unify[name]
474
+
475
+ if var == :forbidden_unification
476
+ fail_due_to "Wildcard #{name} was first seen in a subset of a" \
477
+ " union and can't be used outside that union"
478
+ end
479
+ var
480
+ end
481
+
482
+ def forbid_unification(*names)
483
+ names.each do |name|
484
+ @unify[name] = :forbidden_unification
485
+ end
486
+ end
487
+
488
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
489
+ def unify_in_union(enum)
490
+ # We need to reset @unify before each branch is processed.
491
+ # Moreover we need to keep track of newly encountered wildcards.
492
+ # Var `new_unify_intersection` will hold those that are encountered
493
+ # in all branches; these are not a problem.
494
+ # Var `partial_unify` will hold those encountered in only a subset
495
+ # of the branches; these can't be used outside of the union.
496
+
497
+ return to_enum __method__, enum unless block_given?
498
+
499
+ new_unify_intersection = nil
500
+ partial_unify = []
501
+ unify_before = @unify.dup
502
+
503
+ result = enum.each do |e|
504
+ @unify = unify_before.dup if new_unify_intersection
505
+ yield e
506
+ new_unify = @unify.keys - unify_before.keys
507
+ if new_unify_intersection.nil?
508
+ # First iteration
509
+ new_unify_intersection = new_unify
510
+ else
511
+ union = new_unify_intersection | new_unify
512
+ new_unify_intersection &= new_unify
513
+ partial_unify |= union - new_unify_intersection
514
+ end
515
+ end
516
+
517
+ # At this point, all members of `new_unify_intersection` can be used
518
+ # for unification outside of the union, but partial_unify may not
519
+
520
+ forbid_unification(*partial_unify)
521
+
522
+ result
523
+ end
524
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
525
+
469
526
  def compile_union
470
527
  # we need to ensure that each branch of the {} contains the same
471
528
  # number of captures (since only one branch of the {} can actually
472
529
  # match, the same variables are used to hold the captures for each
473
530
  # branch)
474
531
  enum = tokens_until('}', 'union')
532
+ enum = unify_in_union(enum)
475
533
  terms = insure_same_captures(enum, 'branch of {}')
476
534
  .map { compile_expr }
477
535
 
@@ -496,6 +554,19 @@ module RuboCop
496
554
  with_context("#{CUR_NODE} && #{compile_expr}", "#{CUR_NODE}.parent")
497
555
  end
498
556
 
557
+ def compile_descend
558
+ with_temp_variables do |descendant|
559
+ pattern = with_context(compile_expr, descendant,
560
+ use_temp_node: false)
561
+ [
562
+ "RuboCop::NodePattern.descend(#{CUR_ELEMENT}).",
563
+ "any? do |#{descendant}|",
564
+ " #{pattern}",
565
+ 'end'
566
+ ].join("\n")
567
+ end
568
+ end
569
+
499
570
  def compile_wildcard(name)
500
571
  if name.empty?
501
572
  'true'
@@ -503,12 +574,12 @@ module RuboCop
503
574
  # we have already seen a wildcard with this name before
504
575
  # so the value it matched the first time will already be stored
505
576
  # in a temp. check if this value matches the one stored in the temp
506
- "#{CUR_ELEMENT} == temp#{@unify[name]}"
577
+ "#{CUR_ELEMENT} == #{access_unify(name)}"
507
578
  else
508
- n = @unify[name] = next_temp_value
509
- # double assign to temp#{n} to avoid "assigned but unused variable"
510
- "(temp#{n} = #{CUR_ELEMENT}; " \
511
- "temp#{n} = temp#{n}; true)"
579
+ n = @unify[name] = "unify_#{name.gsub('-', '__')}"
580
+ # double assign to avoid "assigned but unused variable"
581
+ "(#{n} = #{CUR_ELEMENT}; " \
582
+ "#{n} = #{n}; true)"
512
583
  end
513
584
  end
514
585
 
@@ -560,9 +631,8 @@ module RuboCop
560
631
  def compile_arg(token)
561
632
  case token
562
633
  when WILDCARD then
563
- name = token[1..-1]
564
- number = @unify[name] || fail_due_to('invalid in arglist: ' + token)
565
- "temp#{number}"
634
+ name = token[1..-1]
635
+ access_unify(name) || fail_due_to('invalid in arglist: ' + token)
566
636
  when LITERAL then token
567
637
  when PARAM then get_param(token[1..-1])
568
638
  when CLOSING then fail_due_to("#{token} in invalid position")
@@ -796,6 +866,22 @@ module RuboCop
796
866
  def to_s
797
867
  "#<#{self.class} #{pattern}>"
798
868
  end
869
+
870
+ # Yields its argument and any descendants, depth-first.
871
+ #
872
+ def self.descend(element, &block)
873
+ return to_enum(__method__, element) unless block_given?
874
+
875
+ yield element
876
+
877
+ if element.is_a?(::RuboCop::AST::Node)
878
+ element.children.each do |child|
879
+ descend(child, &block)
880
+ end
881
+ end
882
+
883
+ nil
884
+ end
799
885
  end
800
886
  end
801
887
  # rubocop:enable Metrics/ClassLength, Metrics/CyclomaticComplexity
@@ -83,6 +83,12 @@ module RuboCop
83
83
 
84
84
  def add_cop_selection_csv_option(option, opts)
85
85
  option(opts, "--#{option} [COP1,COP2,...]") do |list|
86
+ unless list
87
+ message = "--#{option} argument should be [COP1,COP2,...]."
88
+
89
+ raise OptionArgumentError, message
90
+ end
91
+
86
92
  @options[:"#{option}"] =
87
93
  if list.empty?
88
94
  ['']
@@ -97,6 +103,7 @@ module RuboCop
97
103
  def add_configuration_options(opts)
98
104
  option(opts, '-c', '--config FILE')
99
105
  option(opts, '--force-exclusion')
106
+ option(opts, '--only-recognized-file-types')
100
107
  option(opts, '--ignore-parent-exclusion')
101
108
  option(opts, '--force-default-config')
102
109
  add_auto_gen_options(opts)
@@ -154,6 +161,7 @@ module RuboCop
154
161
  end
155
162
  end
156
163
 
164
+ # rubocop:disable Metrics/MethodLength
157
165
  def add_boolean_flags(opts)
158
166
  option(opts, '-F', '--fail-fast')
159
167
  option(opts, '-C', '--cache FLAG')
@@ -162,6 +170,8 @@ module RuboCop
162
170
  option(opts, '-E', '--extra-details')
163
171
  option(opts, '-S', '--display-style-guide')
164
172
  option(opts, '-a', '--auto-correct')
173
+ option(opts, '--disable-pending-cops')
174
+ option(opts, '--enable-pending-cops')
165
175
  option(opts, '--ignore-disable-comments')
166
176
 
167
177
  option(opts, '--safe')
@@ -172,6 +182,7 @@ module RuboCop
172
182
  option(opts, '-V', '--verbose-version')
173
183
  option(opts, '-P', '--parallel')
174
184
  end
185
+ # rubocop:enable Metrics/MethodLength
175
186
 
176
187
  def add_aliases(opts)
177
188
  option(opts, '-l', '--lint') do
@@ -237,10 +248,7 @@ module RuboCop
237
248
  def format_message_from(name, cop_names)
238
249
  message = 'Unrecognized cop or department: %<name>s.'
239
250
  message_with_candidate = "%<message>s\nDid you mean? %<candidate>s"
240
- corrections = cop_names.select do |cn|
241
- score = StringUtil.similarity(cn, name)
242
- score >= NameSimilarity::MINIMUM_SIMILARITY_TO_SUGGEST
243
- end.sort
251
+ corrections = NameSimilarity.find_similar_names(name, cop_names)
244
252
 
245
253
  if corrections.empty?
246
254
  format(message, name: name)
@@ -264,18 +272,18 @@ module RuboCop
264
272
  # rubocop:disable Metrics/AbcSize
265
273
  def validate_compatibility # rubocop:disable Metrics/MethodLength
266
274
  if only_includes_redundant_disable?
267
- raise OptionArgumentError, 'Lint/RedundantCopDisableDirective can ' \
268
- 'not be used with --only.'
275
+ raise OptionArgumentError, 'Lint/RedundantCopDisableDirective cannot ' \
276
+ 'be used with --only.'
269
277
  end
270
278
  if except_syntax?
271
- raise OptionArgumentError, 'Syntax checking can not be turned off.'
279
+ raise OptionArgumentError, 'Syntax checking cannot be turned off.'
272
280
  end
273
281
  unless boolean_or_empty_cache?
274
282
  raise OptionArgumentError, '-C/--cache argument must be true or false'
275
283
  end
276
284
 
277
285
  if display_only_fail_level_offenses_with_autocorrect?
278
- raise OptionArgumentError, '--autocorrect can not be used with ' \
286
+ raise OptionArgumentError, '--autocorrect cannot be used with ' \
279
287
  '--display-only-fail-level-offenses'
280
288
  end
281
289
  validate_auto_gen_config
@@ -329,8 +337,8 @@ module RuboCop
329
337
  auto_gen_config: '-P/--parallel uses caching to speed up execution, ' \
330
338
  'while --auto-gen-config needs a non-cached run, ' \
331
339
  'so they cannot be combined.',
332
- fail_fast: '-P/--parallel can not be combined with -F/--fail-fast.',
333
- auto_correct: '-P/--parallel can not be combined with --auto-correct.'
340
+ fail_fast: '-P/--parallel cannot be combined with -F/--fail-fast.',
341
+ auto_correct: '-P/--parallel cannot be combined with --auto-correct.'
334
342
  }
335
343
 
336
344
  combos.each do |key, msg|
@@ -362,7 +370,7 @@ module RuboCop
362
370
  end
363
371
 
364
372
  def validate_exclude_limit_option
365
- return if @options[:exclude_limit] =~ /^\d+$/
373
+ return if /^\d+$/.match?(@options[:exclude_limit])
366
374
 
367
375
  # Emulate OptionParser's behavior to make failures consistent regardless
368
376
  # of option order.
@@ -373,7 +381,7 @@ module RuboCop
373
381
  # This module contains help texts for command line options.
374
382
  module OptionsHelp
375
383
  MAX_EXCL = RuboCop::Options::DEFAULT_MAXIMUM_EXCLUSION_ITEMS.to_s
376
- # rubocop:disable Metrics/LineLength
384
+ # rubocop:disable Layout/LineLength
377
385
  FORMATTER_OPTION_LIST = RuboCop::Formatter::FormatterSet::BUILTIN_FORMATTERS_FOR_KEYS.keys
378
386
 
379
387
  TEXT = {
@@ -405,6 +413,9 @@ module RuboCop
405
413
  force_exclusion: ['Force excluding files specified in the',
406
414
  'configuration `Exclude` even if they are',
407
415
  'explicitly passed as arguments.'],
416
+ only_recognized_file_types: ['Inspect files given on the command line only if',
417
+ 'they are listed in AllCops/Include parameters',
418
+ 'of user configuration or default configuration.'],
408
419
  ignore_disable_comments: ['Run cops even when they are disabled locally',
409
420
  'with a comment.'],
410
421
  ignore_parent_exclusion: ['Prevent from inheriting AllCops/Exclude from',
@@ -438,7 +449,9 @@ module RuboCop
438
449
  debug: 'Display debug info.',
439
450
  display_cop_names: ['Display cop names in offense messages.',
440
451
  'Default is true.'],
452
+ disable_pending_cops: 'Run without pending cops.',
441
453
  display_style_guide: 'Display style guide URLs in offense messages.',
454
+ enable_pending_cops: 'Run with pending cops.',
442
455
  extra_details: 'Display extra details in offense messages.',
443
456
  lint: 'Run only lint cops.',
444
457
  safe: 'Run only safe cops.',
@@ -455,6 +468,6 @@ module RuboCop
455
468
  'reports. This is useful for editor integration.'],
456
469
  init: 'Generate a .rubocop.yml file in the current directory.'
457
470
  }.freeze
458
- # rubocop:enable Metrics/LineLength
471
+ # rubocop:enable Layout/LineLength
459
472
  end
460
473
  end
@@ -163,7 +163,7 @@ module RuboCop
163
163
  ast, comments, tokens = parser.tokenize(@buffer)
164
164
 
165
165
  ast.respond_to?(:complete!) && ast.complete!
166
- rescue Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
166
+ rescue Parser::SyntaxError
167
167
  # All errors are in diagnostics. No need to handle exception.
168
168
  end
169
169
 
@@ -175,9 +175,6 @@ module RuboCop
175
175
  # rubocop:disable Metrics/MethodLength
176
176
  def parser_class(ruby_version)
177
177
  case ruby_version
178
- when 2.3
179
- require 'parser/ruby23'
180
- Parser::Ruby23
181
178
  when 2.4
182
179
  require 'parser/ruby24'
183
180
  Parser::Ruby24
@@ -70,6 +70,7 @@ module RuboCop
70
70
  RakeFileUtils.verbose(verbose) do
71
71
  yield(*[self, task_args].slice(0, task_block.arity)) if block_given?
72
72
  options = full_options.unshift('--auto-correct')
73
+ options.delete('--parallel')
73
74
  run_cli(verbose, options)
74
75
  end
75
76
  end
@@ -77,12 +77,13 @@ module RuboCop
77
77
  config_store.for('.').for_all_cops['AllowSymlinksInCacheRootDirectory']
78
78
  end
79
79
 
80
- def initialize(file, options, config_store, cache_root = nil)
80
+ def initialize(file, team, options, config_store, cache_root = nil)
81
81
  cache_root ||= ResultCache.cache_root(config_store)
82
82
  @allow_symlinks_in_cache_location =
83
83
  ResultCache.allow_symlinks_in_cache_location?(config_store)
84
- @path = File.join(cache_root, rubocop_checksum,
85
- relevant_options_digest(options),
84
+ @path = File.join(cache_root,
85
+ rubocop_checksum,
86
+ context_checksum(team, options),
86
87
  file_checksum(file, config_store))
87
88
  @cached_data = CachedData.new(file)
88
89
  end
@@ -182,10 +183,25 @@ module RuboCop
182
183
  # don't affect caching.
183
184
  def relevant_options_digest(options)
184
185
  options = options.reject { |key, _| NON_CHANGING.include?(key) }
185
- options = options.to_s.gsub(/[^a-z]+/i, '_')
186
- # We must avoid making file names too long for some filesystems to handle
187
- # If they are short, we can leave them human-readable
188
- options.length <= 32 ? options : Digest::SHA1.hexdigest(options)
186
+ options.to_s.gsub(/[^a-z]+/i, '_')
187
+ end
188
+
189
+ # The external dependency checksums are cached per RuboCop team so that
190
+ # the checksums don't need to be recomputed for each file.
191
+ def team_checksum(team)
192
+ @checksum_by_team ||= {}
193
+ @checksum_by_team[team.object_id] ||= team.external_dependency_checksum
194
+ end
195
+
196
+ # We combine team and options into a single "context" checksum to avoid
197
+ # making file names that are too long for some filesystems to handle.
198
+ # This context is for anything that's not (1) the RuboCop executable
199
+ # checksum or (2) the inspected file checksum.
200
+ def context_checksum(team, options)
201
+ Digest::SHA1.hexdigest([
202
+ team_checksum(team),
203
+ relevant_options_digest(options)
204
+ ].join)
189
205
  end
190
206
  end
191
207
  end
@@ -6,7 +6,7 @@ require 'tempfile'
6
6
  module CopHelper
7
7
  extend RSpec::SharedContext
8
8
 
9
- let(:ruby_version) { 2.3 }
9
+ let(:ruby_version) { 2.4 }
10
10
  let(:rails_version) { false }
11
11
 
12
12
  def inspect_source_file(source)
@@ -153,7 +153,7 @@ module RuboCop
153
153
  annotations = []
154
154
 
155
155
  annotated_source.each_line do |source_line|
156
- if source_line =~ ANNOTATION_PATTERN
156
+ if ANNOTATION_PATTERN.match?(source_line)
157
157
  annotations << [source.size, source_line]
158
158
  else
159
159
  source << source_line
@@ -52,6 +52,7 @@ RSpec.shared_context 'config', :config do
52
52
  cop_name = described_class.cop_name
53
53
  hash[cop_name] = RuboCop::ConfigLoader
54
54
  .default_configuration[cop_name]
55
+ .merge('Enabled' => true) # in case it is 'pending'
55
56
  .merge(cop_config)
56
57
  end
57
58
 
@@ -73,10 +74,6 @@ RSpec.shared_context 'mock console output' do
73
74
  end
74
75
  end
75
76
 
76
- RSpec.shared_context 'ruby 2.3', :ruby23 do
77
- let(:ruby_version) { 2.3 }
78
- end
79
-
80
77
  RSpec.shared_context 'ruby 2.4', :ruby24 do
81
78
  let(:ruby_version) { 2.4 }
82
79
  end
@@ -88,3 +85,7 @@ end
88
85
  RSpec.shared_context 'ruby 2.6', :ruby26 do
89
86
  let(:ruby_version) { 2.6 }
90
87
  end
88
+
89
+ RSpec.shared_context 'ruby 2.7', :ruby27 do
90
+ let(:ruby_version) { 2.7 }
91
+ end
@@ -61,7 +61,12 @@ module RuboCop
61
61
 
62
62
  def find_target_files(paths)
63
63
  target_finder = TargetFinder.new(@config_store, @options)
64
- target_files = target_finder.find(paths)
64
+ mode = if @options[:only_recognized_file_types]
65
+ :only_recognized_file_types
66
+ else
67
+ :all_file_types
68
+ end
69
+ target_files = target_finder.find(paths, mode)
65
70
  target_files.each(&:freeze).freeze
66
71
  end
67
72
 
@@ -121,8 +126,14 @@ module RuboCop
121
126
  end
122
127
  end
123
128
 
129
+ def cached_result(file, team)
130
+ ResultCache.new(file, team, @options, @config_store)
131
+ end
132
+
124
133
  def file_offense_cache(file)
125
- cache = ResultCache.new(file, @options, @config_store) if cached_run?
134
+ config = @config_store.for(file)
135
+ cache = cached_result(file, standby_team(config)) if cached_run?
136
+
126
137
  if cache&.valid?
127
138
  offenses = cache.load
128
139
  # If we're running --auto-correct and the cache says there are
@@ -208,7 +219,7 @@ module RuboCop
208
219
  @config_store.for(Dir.pwd).for_all_cops['UseCache']) &&
209
220
  # When running --auto-gen-config, there's some processing done in the
210
221
  # cops related to calculating the Max parameters for Metrics cops. We
211
- # need to do that processing and can not use caching.
222
+ # need to do that processing and cannot use caching.
212
223
  !@options[:auto_gen_config] &&
213
224
  # We can't cache results from code which is piped in to stdin
214
225
  !@options[:stdin]
@@ -308,7 +319,7 @@ module RuboCop
308
319
 
309
320
  cop_classes.reject! { |c| c.match?(@options[:except]) }
310
321
 
311
- Cop::Registry.new(cop_classes)
322
+ Cop::Registry.new(cop_classes, @options)
312
323
  end
313
324
  end
314
325
 
@@ -359,5 +370,15 @@ module RuboCop
359
370
  ProcessedSource.from_file(file, ruby_version)
360
371
  end
361
372
  end
373
+
374
+ # A Cop::Team instance is stateful and may change when inspecting.
375
+ # The "standby" team for a given config is an initialized but
376
+ # otherwise dormant team that can be used for config- and option-
377
+ # level caching in ResultCache.
378
+ def standby_team(config)
379
+ @team_by_config ||= {}
380
+ @team_by_config[config.object_id] ||=
381
+ Cop::Team.new(mobilized_cop_classes(config), config, @options)
382
+ end
362
383
  end
363
384
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  # (if any). If args is empty, recursively find all Ruby source
28
28
  # files under the current directory
29
29
  # @return [Array] array of file paths
30
- def find(args)
30
+ def find(args, mode)
31
31
  return target_files_in_dir if args.empty?
32
32
 
33
33
  files = []
@@ -36,7 +36,7 @@ module RuboCop
36
36
  files += if File.directory?(arg)
37
37
  target_files_in_dir(arg.chomp(File::SEPARATOR))
38
38
  else
39
- process_explicit_path(arg)
39
+ process_explicit_path(arg, mode)
40
40
  end
41
41
  end
42
42
 
@@ -169,10 +169,12 @@ module RuboCop
169
169
  ruby_file?(file) || configured_include?(file)
170
170
  end
171
171
 
172
- def process_explicit_path(path)
172
+ def process_explicit_path(path, mode)
173
173
  files = path.include?('*') ? Dir[path] : [path]
174
174
 
175
- files.select! { |file| included_file?(file) }
175
+ if mode == :only_recognized_file_types || force_exclusion?
176
+ files.select! { |file| included_file?(file) }
177
+ end
176
178
 
177
179
  return files unless force_exclusion?
178
180
 
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # The kind of Ruby that code inspected by RuboCop is written in.
5
+ class TargetRuby
6
+ KNOWN_RUBIES = [2.4, 2.5, 2.6, 2.7].freeze
7
+ DEFAULT_VERSION = KNOWN_RUBIES.first
8
+
9
+ OBSOLETE_RUBIES = {
10
+ 1.9 => '0.50', 2.0 => '0.50', 2.1 => '0.58', 2.2 => '0.69', 2.3 => '0.82'
11
+ }.freeze
12
+ private_constant :KNOWN_RUBIES, :OBSOLETE_RUBIES
13
+
14
+ # A place where information about a target ruby version is found.
15
+ class Source
16
+ attr_reader :version, :name
17
+
18
+ def initialize(config)
19
+ @config = config
20
+ @version = find_version
21
+ end
22
+
23
+ def to_s
24
+ name
25
+ end
26
+ end
27
+
28
+ # The target ruby version may be configured in RuboCop's config.
29
+ class RuboCopConfig < Source
30
+ def name
31
+ "`TargetRubyVersion` parameter (in #{@config.smart_loaded_path})"
32
+ end
33
+
34
+ private
35
+
36
+ def find_version
37
+ @config.for_all_cops['TargetRubyVersion']&.to_f
38
+ end
39
+ end
40
+
41
+ # The target ruby version may be found in a .ruby-version file.
42
+ class RubyVersionFile < Source
43
+ FILENAME = '.ruby-version'
44
+
45
+ def name
46
+ "`#{FILENAME}`"
47
+ end
48
+
49
+ private
50
+
51
+ def find_version
52
+ file = ruby_version_file
53
+ return unless file && File.file?(file)
54
+
55
+ File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
56
+ md[:version].to_f
57
+ end
58
+ end
59
+
60
+ def ruby_version_file
61
+ @ruby_version_file ||=
62
+ @config.find_file_upwards(FILENAME,
63
+ @config.base_dir_for_path_parameters)
64
+ end
65
+ end
66
+
67
+ # The lock file of Bundler may identify the target ruby version.
68
+ class BundlerLockFile < Source
69
+ def name
70
+ "`#{bundler_lock_file_path}`"
71
+ end
72
+
73
+ private
74
+
75
+ def find_version
76
+ lock_file_path = bundler_lock_file_path
77
+ return nil unless lock_file_path
78
+
79
+ in_ruby_section = false
80
+ File.foreach(lock_file_path) do |line|
81
+ # If ruby is in Gemfile.lock or gems.lock, there should be two lines
82
+ # towards the bottom of the file that look like:
83
+ # RUBY VERSION
84
+ # ruby W.X.YpZ
85
+ # We ultimately want to match the "ruby W.X.Y.pZ" line, but there's
86
+ # extra logic to make sure we only start looking once we've seen the
87
+ # "RUBY VERSION" line.
88
+ in_ruby_section ||= line.match(/^\s*RUBY\s*VERSION\s*$/)
89
+ next unless in_ruby_section
90
+
91
+ # We currently only allow this feature to work with MRI ruby. If
92
+ # jruby (or something else) is used by the project, it's lock file
93
+ # will have a line that looks like:
94
+ # RUBY VERSION
95
+ # ruby W.X.YpZ (jruby x.x.x.x)
96
+ # The regex won't match in this situation.
97
+ result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
98
+ return result.captures.first.to_f if result
99
+ end
100
+ end
101
+
102
+ def bundler_lock_file_path
103
+ @config.bundler_lock_file_path
104
+ end
105
+ end
106
+
107
+ # If all else fails, a default version will be picked.
108
+ class Default < Source
109
+ def name
110
+ 'default'
111
+ end
112
+
113
+ private
114
+
115
+ def find_version
116
+ DEFAULT_VERSION
117
+ end
118
+ end
119
+
120
+ def self.supported_versions
121
+ KNOWN_RUBIES
122
+ end
123
+
124
+ SOURCES = [RuboCopConfig, RubyVersionFile, BundlerLockFile, Default].freeze
125
+ private_constant :SOURCES
126
+
127
+ def initialize(config)
128
+ @config = config
129
+ end
130
+
131
+ def source
132
+ @source ||= SOURCES.each.lazy.map { |c| c.new(@config) }.detect(&:version)
133
+ end
134
+
135
+ def version
136
+ source.version
137
+ end
138
+
139
+ def supported?
140
+ KNOWN_RUBIES.include?(version)
141
+ end
142
+
143
+ def rubocop_version_with_support
144
+ if supported?
145
+ RuboCop::Version.version
146
+ else
147
+ OBSOLETE_RUBIES[version]
148
+ end
149
+ end
150
+ end
151
+ end