rubocop 0.93.1 → 1.6.1

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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +276 -82
  4. data/config/obsoletion.yml +196 -0
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop.rb +31 -2
  7. data/lib/rubocop/cli.rb +5 -1
  8. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  9. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  11. data/lib/rubocop/cli/command/version.rb +1 -1
  12. data/lib/rubocop/comment_config.rb +1 -1
  13. data/lib/rubocop/config.rb +4 -0
  14. data/lib/rubocop/config_loader.rb +34 -8
  15. data/lib/rubocop/config_loader_resolver.rb +12 -6
  16. data/lib/rubocop/config_obsoletion.rb +65 -247
  17. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  18. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  19. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  20. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  21. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  22. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  23. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  24. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  25. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  26. data/lib/rubocop/config_regeneration.rb +1 -1
  27. data/lib/rubocop/config_validator.rb +25 -10
  28. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  29. data/lib/rubocop/cop/badge.rb +9 -24
  30. data/lib/rubocop/cop/base.rb +33 -16
  31. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  32. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  33. data/lib/rubocop/cop/commissioner.rb +37 -23
  34. data/lib/rubocop/cop/cop.rb +2 -2
  35. data/lib/rubocop/cop/corrector.rb +3 -1
  36. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  37. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  38. data/lib/rubocop/cop/force.rb +1 -1
  39. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  40. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  42. data/lib/rubocop/cop/generator.rb +3 -10
  43. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  44. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  45. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  46. data/lib/rubocop/cop/layout/class_structure.rb +22 -3
  47. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  49. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  50. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  51. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  52. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  53. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  54. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  55. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  56. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  58. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
  59. data/lib/rubocop/cop/layout/line_length.rb +10 -13
  60. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  61. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  62. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  63. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  64. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  65. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  66. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  67. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  68. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  69. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  71. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  72. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  73. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  74. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  75. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  76. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  77. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
  78. data/lib/rubocop/cop/lint/loop.rb +4 -4
  79. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  80. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  81. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  82. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  83. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  84. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  85. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  86. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  87. data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
  88. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  89. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +199 -0
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  93. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  94. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  95. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  96. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  97. data/lib/rubocop/cop/metrics/parameter_lists.rb +68 -2
  98. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  99. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  100. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  101. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  102. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  103. data/lib/rubocop/cop/mixin/configurable_numbering.rb +4 -3
  104. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  105. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  107. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  108. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  110. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  111. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  112. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  113. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  116. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  117. data/lib/rubocop/cop/naming/variable_number.rb +100 -8
  118. data/lib/rubocop/cop/offense.rb +3 -3
  119. data/lib/rubocop/cop/security/open.rb +12 -10
  120. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  121. data/lib/rubocop/cop/style/and_or.rb +11 -3
  122. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  123. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  124. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  125. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  127. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  128. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
  129. data/lib/rubocop/cop/style/documentation.rb +12 -1
  130. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  131. data/lib/rubocop/cop/style/float_division.rb +44 -1
  132. data/lib/rubocop/cop/style/format_string.rb +8 -3
  133. data/lib/rubocop/cop/style/format_string_token.rb +47 -2
  134. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  135. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  136. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  137. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -3
  138. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  139. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  140. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  141. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  143. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -13
  144. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  145. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  146. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  147. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  148. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  149. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  150. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  151. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  152. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  153. data/lib/rubocop/cop/style/redundant_argument.rb +88 -0
  154. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  156. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  157. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  158. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  159. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  160. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  161. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  162. data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
  163. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  164. data/lib/rubocop/cop/style/static_class.rb +97 -0
  165. data/lib/rubocop/cop/style/string_concatenation.rb +39 -2
  166. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  167. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  168. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  169. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  170. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  171. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  172. data/lib/rubocop/cop/team.rb +6 -1
  173. data/lib/rubocop/cop/util.rb +6 -2
  174. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  176. data/lib/rubocop/core_ext/hash.rb +20 -0
  177. data/lib/rubocop/ext/regexp_node.rb +36 -11
  178. data/lib/rubocop/ext/regexp_parser.rb +95 -0
  179. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  180. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  181. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  182. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  183. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  184. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  185. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  186. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  187. data/lib/rubocop/lockfile.rb +40 -0
  188. data/lib/rubocop/magic_comment.rb +2 -2
  189. data/lib/rubocop/options.rb +11 -1
  190. data/lib/rubocop/rake_task.rb +2 -2
  191. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  192. data/lib/rubocop/runner.rb +1 -1
  193. data/lib/rubocop/target_finder.rb +1 -1
  194. data/lib/rubocop/target_ruby.rb +65 -1
  195. data/lib/rubocop/version.rb +56 -6
  196. metadata +50 -9
  197. data/bin/console +0 -10
  198. data/bin/rubocop-profile +0 -32
  199. data/bin/setup +0 -7
@@ -3,8 +3,14 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # This cop looks for uses of flip-flop operator.
7
- # flip-flop operator is deprecated since Ruby 2.6.0.
6
+ # This cop looks for uses of flip-flop operator
7
+ # based on the Ruby Style Guide.
8
+ #
9
+ # Here is the history of flip-flops in Ruby.
10
+ # flip-flop operator is deprecated in Ruby 2.6.0 and
11
+ # the deprecation has been reverted by Ruby 2.7.0 and
12
+ # backported to Ruby 2.6.
13
+ # See: https://bugs.ruby-lang.org/issues/5400
8
14
  #
9
15
  # @example
10
16
  # # bad
@@ -23,10 +23,11 @@ module RuboCop
23
23
  'Use double quoted strings if you need interpolation.'
24
24
 
25
25
  def on_str(node)
26
- parent = node.parent
27
- return if parent && (parent.dstr_type? || parent.regexp_type?)
26
+ return unless node
27
+ return if string_or_regex?(node.parent)
28
28
  return unless /(?<!\\)#\{.*\}/.match?(node.source)
29
29
  return if heredoc?(node)
30
+ return unless node.loc.begin && node.loc.end
30
31
 
31
32
  add_offense(node) do |corrector|
32
33
  autocorrect(corrector, node)
@@ -35,6 +36,10 @@ module RuboCop
35
36
 
36
37
  private
37
38
 
39
+ def string_or_regex?(node)
40
+ node&.dstr_type? || node&.regexp_type?
41
+ end
42
+
38
43
  def autocorrect(corrector, node)
39
44
  starting_token, ending_token = if node.source.include?('"')
40
45
  ['%{', '}']
@@ -27,21 +27,33 @@ module RuboCop
27
27
 
28
28
  def on_interpolation(begin_node)
29
29
  final_node = begin_node.children.last
30
- return unless final_node
31
- return if special_keyword?(final_node)
32
- return unless prints_as_self?(final_node)
30
+ return unless offending?(final_node)
31
+
32
+ # %W and %I split the content into words before expansion
33
+ # treating each interpolation as a word component, so
34
+ # interpolation should not be removed if the expanded value
35
+ # contains a space character.
36
+ expanded_value = autocorrected_value(final_node)
37
+ return if in_array_percent_literal?(begin_node) &&
38
+ /\s/.match?(expanded_value)
33
39
 
34
40
  add_offense(final_node) do |corrector|
35
41
  return if final_node.dstr_type? # nested, fixed in next iteration
36
42
 
37
- value = autocorrected_value(final_node)
38
-
39
- corrector.replace(final_node.parent, value)
43
+ corrector.replace(final_node.parent, expanded_value)
40
44
  end
41
45
  end
42
46
 
43
47
  private
44
48
 
49
+ def offending?(node)
50
+ node &&
51
+ !special_keyword?(node) &&
52
+ prints_as_self?(node) &&
53
+ # Special case for Layout/TrailingWhitespace
54
+ !(space_literal?(node) && ends_heredoc_line?(node))
55
+ end
56
+
45
57
  def special_keyword?(node)
46
58
  # handle strings like __FILE__
47
59
  (node.str_type? && !node.loc.respond_to?(:begin)) ||
@@ -83,7 +95,7 @@ module RuboCop
83
95
  def autocorrected_value_for_array(node)
84
96
  return node.source.gsub('"', '\"') unless node.percent_literal?
85
97
 
86
- contents_range(node).source.split(' ').to_s.gsub('"', '\"')
98
+ contents_range(node).source.split.to_s.gsub('"', '\"')
87
99
  end
88
100
 
89
101
  # Does node print its own source when converted to a string?
@@ -92,6 +104,26 @@ module RuboCop
92
104
  (COMPOSITE.include?(node.type) &&
93
105
  node.children.all? { |child| prints_as_self?(child) })
94
106
  end
107
+
108
+ def space_literal?(node)
109
+ node.str_type? && node.value.blank?
110
+ end
111
+
112
+ def ends_heredoc_line?(node)
113
+ grandparent = node.parent.parent
114
+ return false unless grandparent&.dstr_type? && grandparent&.heredoc?
115
+
116
+ line = processed_source.lines[node.last_line - 1]
117
+ line.size == node.loc.last_column + 1
118
+ end
119
+
120
+ def in_array_percent_literal?(node)
121
+ parent = node.parent
122
+ return false unless parent.dstr_type? || parent.dsym_type?
123
+
124
+ grandparent = parent.parent
125
+ grandparent&.array_type? && grandparent&.percent_literal?
126
+ end
95
127
  end
96
128
  end
97
129
  end
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Lint
6
6
  # This cop checks for uses of `begin...end while/until something`.
7
7
  #
8
+ # The cop is marked as unsafe because behaviour can change in some cases, including
9
+ # if a local variable inside the loop body is accessed outside of it, or if the
10
+ # loop body raises a `StopIteration` exception (which `Kernel#loop` rescues).
11
+ #
8
12
  # @example
9
13
  #
10
14
  # # bad
@@ -76,10 +80,6 @@ module RuboCop
76
80
  conditional_keyword = node.while_post_type? ? 'unless' : 'if'
77
81
  "break #{conditional_keyword} #{node.condition.source}\n#{indent(node)}"
78
82
  end
79
-
80
- def indent(node)
81
- ' ' * node.loc.column
82
- end
83
83
  end
84
84
  end
85
85
  end
@@ -6,6 +6,11 @@ module RuboCop
6
6
  # This cop checks for the presence of constructors and lifecycle callbacks
7
7
  # without calls to `super`.
8
8
  #
9
+ # This cop does not consider `method_missing` (and `respond_to_missing?`)
10
+ # because in some cases it makes sense to overtake what is considered a
11
+ # missing method. In other cases, the theoretical ideal handling could be
12
+ # challenging or verbose for no actual gain.
13
+ #
9
14
  # @example
10
15
  # # bad
11
16
  # class Employee < Person
@@ -43,15 +48,13 @@ module RuboCop
43
48
 
44
49
  STATELESS_CLASSES = %w[BasicObject Object].freeze
45
50
 
46
- OBJECT_LIFECYCLE_CALLBACKS = %i[method_missing respond_to_missing?].freeze
47
51
  CLASS_LIFECYCLE_CALLBACKS = %i[inherited].freeze
48
52
  METHOD_LIFECYCLE_CALLBACKS = %i[method_added method_removed method_undefined
49
53
  singleton_method_added singleton_method_removed
50
54
  singleton_method_undefined].freeze
51
55
 
52
- CALLBACKS = (OBJECT_LIFECYCLE_CALLBACKS +
53
- CLASS_LIFECYCLE_CALLBACKS +
54
- METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
56
+ CALLBACKS = (CLASS_LIFECYCLE_CALLBACKS +
57
+ METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
55
58
 
56
59
  def on_def(node)
57
60
  return unless offender?(node)
@@ -15,6 +15,20 @@ module RuboCop
15
15
  # valid_attributes: %i[name content],
16
16
  # nested_attributes: %i[name content %i[incorrectly nested]]
17
17
  # }
18
+ #
19
+ # # good
20
+ #
21
+ # # Neither is incompatible with the bad case, but probably the intended code.
22
+ # attributes = {
23
+ # valid_attributes: %i[name content],
24
+ # nested_attributes: [:name, :content, %i[incorrectly nested]]
25
+ # }
26
+ #
27
+ # attributes = {
28
+ # valid_attributes: %i[name content],
29
+ # nested_attributes: [:name, :content, [:incorrectly, :nested]]
30
+ # }
31
+ #
18
32
  class NestedPercentLiteral < Base
19
33
  include PercentLiteral
20
34
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for the presence of a `return` inside a `begin..end` block
7
+ # in assignment contexts.
8
+ # In this situation, the `return` will result in an exit from the current
9
+ # method, possibly leading to unexpected behavior.
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ #
15
+ # @some_variable ||= begin
16
+ # return some_value if some_condition_is_met
17
+ #
18
+ # do_something
19
+ # end
20
+ #
21
+ # @example
22
+ #
23
+ # # good
24
+ #
25
+ # @some_variable ||= begin
26
+ # if some_condition_is_met
27
+ # some_value
28
+ # else
29
+ # do_something
30
+ # end
31
+ # end
32
+ #
33
+ # # good
34
+ #
35
+ # some_variable = if some_condition_is_met
36
+ # return if another_condition_is_met
37
+ #
38
+ # some_value
39
+ # else
40
+ # do_something
41
+ # end
42
+ #
43
+ class NoReturnInBeginEndBlocks < Base
44
+ MSG = 'Do not `return` in `begin..end` blocks in assignment contexts.'
45
+
46
+ def on_lvasgn(node)
47
+ node.each_node(:kwbegin) do |kwbegin_node|
48
+ kwbegin_node.each_node(:return) do |return_node|
49
+ add_offense(return_node)
50
+ end
51
+ end
52
+ end
53
+ alias on_or_asgn on_lvasgn
54
+ alias on_op_asgn on_lvasgn
55
+ end
56
+ end
57
+ end
58
+ end
@@ -7,6 +7,17 @@ module RuboCop
7
7
  # number conversion can cause unexpected error if auto type conversion
8
8
  # fails. Cop prefer parsing with number class instead.
9
9
  #
10
+ # Conversion with `Integer`, `Float`, etc. will raise an `ArgumentError`
11
+ # if given input that is not numeric (eg. an empty string), whereas
12
+ # `to_i`, etc. will try to convert regardless of input (`''.to_i => 0`).
13
+ # As such, this cop is disabled by default because it's not necessarily
14
+ # always correct to raise if a value is not numeric.
15
+ #
16
+ # NOTE: Some values cannot be converted properly using one of the `Kernel`
17
+ # method (for instance, `Time` and `DateTime` values are allowed by this
18
+ # cop by default). Similarly, Rails' duration methods do not work well
19
+ # with `Integer()` and can be ignored with `IgnoredMethods`.
20
+ #
10
21
  # @example
11
22
  #
12
23
  # # bad
@@ -20,8 +31,19 @@ module RuboCop
20
31
  # Integer('10', 10)
21
32
  # Float('10.2')
22
33
  # Complex('10')
34
+ #
35
+ # @example IgnoredMethods: [minutes]
36
+ #
37
+ # # good
38
+ # 10.minutes.to_i
39
+ #
40
+ # @example IgnoredClasses: [Time, DateTime] (default)
41
+ #
42
+ # # good
43
+ # Time.now.to_datetime.to_i
23
44
  class NumberConversion < Base
24
45
  extend AutoCorrector
46
+ include IgnoredMethods
25
47
 
26
48
  CONVERSION_METHOD_CLASS_MAPPING = {
27
49
  to_i: "#{Integer.name}(%<number_object>s, 10)",
@@ -38,13 +60,9 @@ module RuboCop
38
60
  (send $_ ${:to_i :to_f :to_c})
39
61
  PATTERN
40
62
 
41
- def_node_matcher :datetime?, <<~PATTERN
42
- (send (const {nil? (cbase)} {:Time :DateTime}) ...)
43
- PATTERN
44
-
45
63
  def on_send(node)
46
64
  to_method(node) do |receiver, to_method|
47
- next if receiver.nil? || date_time_object?(receiver)
65
+ next if receiver.nil? || ignore_receiver?(receiver)
48
66
 
49
67
  message = format(
50
68
  MSG,
@@ -60,18 +78,33 @@ module RuboCop
60
78
 
61
79
  private
62
80
 
63
- def date_time_object?(node)
64
- child = node
65
- while child&.send_type?
66
- return true if datetime? child
81
+ def correct_method(node, receiver)
82
+ format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name],
83
+ number_object: receiver.source)
84
+ end
67
85
 
68
- child = child.children[0]
86
+ def ignore_receiver?(receiver)
87
+ if receiver.send_type? && ignored_method?(receiver.method_name)
88
+ true
89
+ elsif (receiver = top_receiver(receiver))
90
+ receiver.const_type? && ignored_class?(receiver.const_name)
91
+ else
92
+ false
69
93
  end
70
94
  end
71
95
 
72
- def correct_method(node, receiver)
73
- format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name],
74
- number_object: receiver.source)
96
+ def top_receiver(node)
97
+ receiver = node
98
+ receiver = receiver.receiver until receiver.receiver.nil?
99
+ receiver
100
+ end
101
+
102
+ def ignored_classes
103
+ cop_config.fetch('IgnoredClasses', [])
104
+ end
105
+
106
+ def ignored_class?(name)
107
+ ignored_classes.include?(name.to_s)
75
108
  end
76
109
  end
77
110
  end
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # puts $1 # => foo
20
20
  #
21
21
  class OutOfRangeRegexpRef < Base
22
- MSG = 'Do not use out of range reference for the Regexp.'
22
+ MSG = '$%<backref>s is out of range (%<count>s regexp capture %<group>s detected).'
23
23
 
24
24
  REGEXP_RECEIVER_METHODS = %i[=~ === match].to_set.freeze
25
25
  REGEXP_ARGUMENT_METHODS = %i[=~ match grep gsub gsub! sub sub! [] slice slice! index rindex
@@ -35,14 +35,13 @@ module RuboCop
35
35
  check_regexp(node.children.first)
36
36
  end
37
37
 
38
- def on_send(node)
38
+ def after_send(node)
39
39
  @valid_ref = nil
40
40
 
41
- if node.receiver&.regexp_type?
42
- check_regexp(node.receiver)
43
- elsif node.first_argument&.regexp_type? \
44
- && REGEXP_ARGUMENT_METHODS.include?(node.method_name)
41
+ if regexp_first_argument?(node)
45
42
  check_regexp(node.first_argument)
43
+ elsif regexp_receiver?(node)
44
+ check_regexp(node.receiver)
46
45
  end
47
46
  end
48
47
 
@@ -56,9 +55,16 @@ module RuboCop
56
55
 
57
56
  def on_nth_ref(node)
58
57
  backref, = *node
59
- return if @valid_ref.nil?
58
+ return if @valid_ref.nil? || backref <= @valid_ref
59
+
60
+ message = format(
61
+ MSG,
62
+ backref: backref,
63
+ count: @valid_ref.zero? ? 'no' : @valid_ref,
64
+ group: @valid_ref == 1 ? 'group' : 'groups'
65
+ )
60
66
 
61
- add_offense(node) if backref > @valid_ref
67
+ add_offense(node, message: message)
62
68
  end
63
69
 
64
70
  private
@@ -73,6 +79,19 @@ module RuboCop
73
79
  node.each_capture(named: false).count
74
80
  end
75
81
  end
82
+
83
+ def regexp_first_argument?(send_node)
84
+ send_node.first_argument&.regexp_type? \
85
+ && REGEXP_ARGUMENT_METHODS.include?(send_node.method_name)
86
+ end
87
+
88
+ def regexp_receiver?(send_node)
89
+ send_node.receiver&.regexp_type?
90
+ end
91
+
92
+ def nth_ref_receiver?(send_node)
93
+ send_node.receiver&.nth_ref_type?
94
+ end
76
95
  end
77
96
  end
78
97
  end
@@ -88,31 +88,34 @@ module RuboCop
88
88
  begin_pos = reposition(source, begin_pos, -1)
89
89
  end_pos = reposition(source, end_pos, 1)
90
90
 
91
- comma_pos =
92
- if source[begin_pos - 1] == ','
93
- :before
94
- elsif source[end_pos] == ','
95
- :after
96
- else
97
- :none
98
- end
99
-
100
- range_to_remove(begin_pos, end_pos, comma_pos, comment)
91
+ range_to_remove(begin_pos, end_pos, comment)
101
92
  end
102
93
 
103
- def range_to_remove(begin_pos, end_pos, comma_pos, comment)
94
+ def range_to_remove(begin_pos, end_pos, comment)
104
95
  start = comment_start(comment)
96
+ source = comment.loc.expression.source
105
97
 
106
- case comma_pos
107
- when :before
108
- range_between(start + begin_pos - 1, start + end_pos)
109
- when :after
110
- range_between(start + begin_pos, start + end_pos + 1)
98
+ if source[begin_pos - 1] == ','
99
+ range_with_comma_before(start, begin_pos, end_pos)
100
+ elsif source[end_pos] == ','
101
+ range_with_comma_after(comment, start, begin_pos, end_pos)
111
102
  else
112
103
  range_between(start, comment.loc.expression.end_pos)
113
104
  end
114
105
  end
115
106
 
107
+ def range_with_comma_before(start, begin_pos, end_pos)
108
+ range_between(start + begin_pos - 1, start + end_pos)
109
+ end
110
+
111
+ # If the list of cops is comma-separated, but without a empty space after the comma,
112
+ # we should **not** remove the prepending empty space, thus begin_pos += 1
113
+ def range_with_comma_after(comment, start, begin_pos, end_pos)
114
+ begin_pos += 1 if comment.loc.expression.source[end_pos + 1] != ' '
115
+
116
+ range_between(start + begin_pos, start + end_pos + 1)
117
+ end
118
+
116
119
  def all_or_name(name)
117
120
  name == 'all' ? 'all cops' : name
118
121
  end