rubocop 1.50.2 → 1.54.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +80 -6
  4. data/lib/rubocop/cli/command/lsp.rb +19 -0
  5. data/lib/rubocop/cli.rb +3 -0
  6. data/lib/rubocop/config.rb +4 -0
  7. data/lib/rubocop/config_loader_resolver.rb +4 -3
  8. data/lib/rubocop/config_obsoletion.rb +2 -2
  9. data/lib/rubocop/cop/base.rb +5 -1
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  11. data/lib/rubocop/cop/bundler/gem_version.rb +2 -2
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  13. data/lib/rubocop/cop/gemspec/dependency_version.rb +2 -2
  14. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  15. data/lib/rubocop/cop/internal_affairs/cop_description.rb +32 -8
  16. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +5 -5
  18. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  19. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -2
  20. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +27 -4
  21. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +2 -0
  22. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  23. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  24. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  25. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  26. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  27. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  28. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
  29. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  30. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +1 -1
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
  32. data/lib/rubocop/cop/lint/debugger.rb +9 -5
  33. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +2 -1
  34. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +46 -19
  35. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -4
  36. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +1 -1
  37. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  38. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
  39. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  40. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  41. data/lib/rubocop/cop/lint/missing_super.rb +34 -5
  42. data/lib/rubocop/cop/lint/mixed_case_range.rb +111 -0
  43. data/lib/rubocop/cop/lint/number_conversion.rb +5 -0
  44. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  45. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  46. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  47. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +130 -0
  48. data/lib/rubocop/cop/lint/redundant_require_statement.rb +8 -3
  49. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +1 -1
  50. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  51. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  52. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  53. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  54. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  55. data/lib/rubocop/cop/lint/useless_assignment.rb +59 -1
  56. data/lib/rubocop/cop/lint/void.rb +57 -7
  57. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  58. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +30 -2
  59. data/lib/rubocop/cop/migration/department_name.rb +2 -2
  60. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  61. data/lib/rubocop/cop/mixin/comments_help.rb +7 -3
  62. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
  63. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  64. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  65. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  66. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  67. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  68. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +25 -10
  69. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  70. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  71. data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
  72. data/lib/rubocop/cop/style/attr.rb +11 -1
  73. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  74. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  75. data/lib/rubocop/cop/style/block_delimiters.rb +3 -3
  76. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  77. data/lib/rubocop/cop/style/class_equality_comparison.rb +17 -39
  78. data/lib/rubocop/cop/style/collection_compact.rb +16 -6
  79. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  80. data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
  81. data/lib/rubocop/cop/style/conditional_assignment.rb +5 -3
  82. data/lib/rubocop/cop/style/copyright.rb +5 -2
  83. data/lib/rubocop/cop/style/dir.rb +1 -1
  84. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  85. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  86. data/lib/rubocop/cop/style/documentation.rb +1 -1
  87. data/lib/rubocop/cop/style/eval_with_location.rb +5 -5
  88. data/lib/rubocop/cop/style/exact_regexp_match.rb +68 -0
  89. data/lib/rubocop/cop/style/file_read.rb +2 -2
  90. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  91. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
  92. data/lib/rubocop/cop/style/hash_except.rb +19 -8
  93. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  94. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  95. data/lib/rubocop/cop/style/identical_conditional_branches.rb +6 -2
  96. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  97. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -0
  98. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  99. data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -6
  100. data/lib/rubocop/cop/style/lambda.rb +3 -3
  101. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -4
  102. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  103. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  104. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  105. data/lib/rubocop/cop/style/preferred_hash_methods.rb +1 -1
  106. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  107. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  108. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -1
  109. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +38 -0
  110. data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
  111. data/lib/rubocop/cop/style/redundant_line_continuation.rb +7 -3
  112. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  113. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +100 -0
  114. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  115. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  116. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +3 -1
  117. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  118. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -0
  119. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  120. data/lib/rubocop/cop/style/require_order.rb +11 -5
  121. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  122. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +81 -0
  123. data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
  124. data/lib/rubocop/cop/style/semicolon.rb +12 -1
  125. data/lib/rubocop/cop/style/signal_exception.rb +1 -1
  126. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  127. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  128. data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
  129. data/lib/rubocop/cop/style/yaml_file_read.rb +66 -0
  130. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  131. data/lib/rubocop/cop/team.rb +1 -1
  132. data/lib/rubocop/cop/util.rb +1 -1
  133. data/lib/rubocop/cop/utils/regexp_ranges.rb +100 -0
  134. data/lib/rubocop/cop/variable_force/assignment.rb +47 -4
  135. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  136. data/lib/rubocop/cop/variable_force.rb +1 -0
  137. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  138. data/lib/rubocop/ext/regexp_parser.rb +4 -1
  139. data/lib/rubocop/lsp/logger.rb +22 -0
  140. data/lib/rubocop/lsp/routes.rb +231 -0
  141. data/lib/rubocop/lsp/runtime.rb +82 -0
  142. data/lib/rubocop/lsp/server.rb +66 -0
  143. data/lib/rubocop/lsp/severity.rb +27 -0
  144. data/lib/rubocop/options.rb +11 -1
  145. data/lib/rubocop/result_cache.rb +1 -1
  146. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  147. data/lib/rubocop/server/client_command/exec.rb +2 -1
  148. data/lib/rubocop/target_ruby.rb +3 -2
  149. data/lib/rubocop/version.rb +10 -6
  150. data/lib/rubocop.rb +13 -0
  151. metadata +38 -6
@@ -3,7 +3,6 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- #
7
6
  # Checks the proper ordering of magic comments and whether
8
7
  # a magic comment is not placed before a shebang.
9
8
  #
@@ -65,13 +65,13 @@ module RuboCop
65
65
  def on_when(node)
66
66
  regexp_conditions = node.conditions.select(&:regexp_type?)
67
67
 
68
- @valid_ref = regexp_conditions.map { |condition| check_regexp(condition) }.compact.max
68
+ @valid_ref = regexp_conditions.filter_map { |condition| check_regexp(condition) }.max
69
69
  end
70
70
 
71
71
  def on_in_pattern(node)
72
72
  regexp_patterns = regexp_patterns(node)
73
73
 
74
- @valid_ref = regexp_patterns.map { |pattern| check_regexp(pattern) }.compact.max
74
+ @valid_ref = regexp_patterns.filter_map { |pattern| check_regexp(pattern) }.max
75
75
  end
76
76
 
77
77
  def on_nth_ref(node)
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for redundant quantifiers inside Regexp literals.
7
+ #
8
+ # It is always allowed when interpolation is used in a regexp literal,
9
+ # because it's unknown what kind of string will be expanded as a result:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # /(?:a*#{interpolation})?/x
14
+ # ----
15
+ #
16
+ # @example
17
+ # # bad
18
+ # /(?:x+)+/
19
+ #
20
+ # # good
21
+ # /(?:x)+/
22
+ #
23
+ # # good
24
+ # /(?:x+)/
25
+ #
26
+ # # bad
27
+ # /(?:x+)?/
28
+ #
29
+ # # good
30
+ # /(?:x)*/
31
+ #
32
+ # # good
33
+ # /(?:x*)/
34
+ class RedundantRegexpQuantifiers < Base
35
+ include RangeHelp
36
+ extend AutoCorrector
37
+
38
+ MSG_REDUNDANT_QUANTIFIER = 'Replace redundant quantifiers ' \
39
+ '`%<inner_quantifier>s` and `%<outer_quantifier>s` ' \
40
+ 'with a single `%<replacement>s`.'
41
+
42
+ def on_regexp(node)
43
+ return if node.interpolation?
44
+
45
+ each_redundantly_quantified_pair(node) do |group, child|
46
+ replacement = merged_quantifier(group, child)
47
+ add_offense(
48
+ quantifier_range(group, child),
49
+ message: message(group, child, replacement)
50
+ ) do |corrector|
51
+ # drop outer quantifier
52
+ corrector.replace(group.loc.quantifier, '')
53
+ # replace inner quantifier
54
+ corrector.replace(child.loc.quantifier, replacement)
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def each_redundantly_quantified_pair(node)
62
+ seen = Set.new
63
+ node.parsed_tree&.each_expression do |(expr)|
64
+ next if seen.include?(expr) || !redundant_group?(expr) || !mergeable_quantifier(expr)
65
+
66
+ expr.each_expression do |(subexp)|
67
+ seen << subexp
68
+ break unless redundantly_quantifiable?(subexp)
69
+
70
+ yield(expr, subexp) if mergeable_quantifier(subexp)
71
+ end
72
+ end
73
+ end
74
+
75
+ def redundant_group?(expr)
76
+ expr.is?(:passive, :group) && expr.count { |child| child.type != :free_space } == 1
77
+ end
78
+
79
+ def redundantly_quantifiable?(node)
80
+ redundant_group?(node) || character_set?(node) || node.terminal?
81
+ end
82
+
83
+ def character_set?(expr)
84
+ expr.is?(:character, :set)
85
+ end
86
+
87
+ def mergeable_quantifier(expr)
88
+ # Merging reluctant or possessive quantifiers would be more complex,
89
+ # and Ruby does not emit warnings for these cases.
90
+ return unless expr.quantifier&.greedy?
91
+
92
+ # normalize quantifiers, e.g. "{1,}" => "+"
93
+ case expr.quantity
94
+ when [0, -1]
95
+ '*'
96
+ when [0, 1]
97
+ '?'
98
+ when [1, -1]
99
+ '+'
100
+ end
101
+ end
102
+
103
+ def merged_quantifier(exp1, exp2)
104
+ quantifier1 = mergeable_quantifier(exp1)
105
+ quantifier2 = mergeable_quantifier(exp2)
106
+ if quantifier1 == quantifier2
107
+ # (?:a+)+ equals (?:a+) ; (?:a*)* equals (?:a*) ; # (?:a?)? equals (?:a?)
108
+ quantifier1
109
+ else
110
+ # (?:a+)*, (?:a+)?, (?:a*)+, (?:a*)?, (?:a?)+, (?:a?)* - all equal (?:a*)
111
+ '*'
112
+ end
113
+ end
114
+
115
+ def quantifier_range(group, child)
116
+ range_between(child.loc.quantifier.begin_pos, group.loc.quantifier.end_pos)
117
+ end
118
+
119
+ def message(group, child, replacement)
120
+ format(
121
+ MSG_REDUNDANT_QUANTIFIER,
122
+ inner_quantifier: child.quantifier.to_s,
123
+ outer_quantifier: group.quantifier.to_s,
124
+ replacement: replacement
125
+ )
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
@@ -49,6 +49,11 @@ module RuboCop
49
49
  (str #redundant_feature?))
50
50
  PATTERN
51
51
 
52
+ # @!method pp_const?(node)
53
+ def_node_matcher :pp_const?, <<~PATTERN
54
+ (const {nil? cbase} :PP)
55
+ PATTERN
56
+
52
57
  def on_send(node)
53
58
  return unless redundant_require_statement?(node)
54
59
 
@@ -72,16 +77,16 @@ module RuboCop
72
77
  feature_name == 'enumerator' ||
73
78
  (target_ruby_version >= 2.1 && feature_name == 'thread') ||
74
79
  (target_ruby_version >= 2.2 && RUBY_22_LOADED_FEATURES.include?(feature_name)) ||
75
- (target_ruby_version >= 2.5 && feature_name == 'pp' && !use_pretty_print_method?) ||
80
+ (target_ruby_version >= 2.5 && feature_name == 'pp' && !need_to_require_pp?) ||
76
81
  (target_ruby_version >= 2.7 && feature_name == 'ruby2_keywords') ||
77
82
  (target_ruby_version >= 3.1 && feature_name == 'fiber') ||
78
83
  (target_ruby_version >= 3.2 && feature_name == 'set')
79
84
  end
80
85
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
81
86
 
82
- def use_pretty_print_method?
87
+ def need_to_require_pp?
83
88
  processed_source.ast.each_descendant(:send).any? do |node|
84
- PRETTY_PRINT_METHODS.include?(node.method_name)
89
+ pp_const?(node.receiver) || PRETTY_PRINT_METHODS.include?(node.method_name)
85
90
  end
86
91
  end
87
92
  end
@@ -47,7 +47,7 @@ module RuboCop
47
47
  return if node.receiver
48
48
 
49
49
  node.each_child_node(:send) do |child|
50
- next unless child.method?(:to_s)
50
+ next if !child.method?(:to_s) || child.arguments.any?
51
51
 
52
52
  register_offense(child, "`#{node.method_name}`")
53
53
  end
@@ -3,8 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- #
7
- # This cop checks for `send`, `public_send`, and `__send__` methods
6
+ # Checks for `send`, `public_send`, and `__send__` methods
8
7
  # when using mix-in.
9
8
  #
10
9
  # `include` and `prepend` methods were private methods until Ruby 2.0,
@@ -121,18 +121,12 @@ module RuboCop
121
121
 
122
122
  if rescued_exceptions.any?
123
123
  rescued_exceptions.each_with_object([]) do |exception, converted|
124
- # FIXME: Workaround `rubocop:disable` comment for JRuby.
125
- # https://github.com/jruby/jruby/issues/6642
126
- # rubocop:disable Style/RedundantBegin
127
- begin
128
- RuboCop::Util.silence_warnings do
129
- # Avoid printing deprecation warnings about constants
130
- converted << Kernel.const_get(exception.source)
131
- end
132
- rescue NameError
133
- converted << nil
124
+ RuboCop::Util.silence_warnings do
125
+ # Avoid printing deprecation warnings about constants
126
+ converted << Kernel.const_get(exception.source)
134
127
  end
135
- # rubocop:enable Style/RedundantBegin
128
+ rescue NameError
129
+ converted << nil
136
130
  end
137
131
  else
138
132
  # treat an empty `rescue` as `rescue StandardError`
@@ -117,7 +117,7 @@ module RuboCop
117
117
 
118
118
  def comment_between_rescue_and_end?(node)
119
119
  ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block, :numblock).first
120
- return unless ancestor
120
+ return false unless ancestor
121
121
 
122
122
  end_line = ancestor.loc.end.line
123
123
  processed_source[node.first_line...end_line].any? { |line| comment_line?(line) }
@@ -124,7 +124,7 @@ module RuboCop
124
124
  source == value ||
125
125
  # `Symbol#inspect` uses double quotes, but allow single-quoted
126
126
  # symbols to work as well.
127
- source.tr("'", '"') == value
127
+ source.gsub('"', '\"').tr("'", '"') == value
128
128
  end
129
129
 
130
130
  def requires_quotes?(sym_node)
@@ -8,25 +8,39 @@ module RuboCop
8
8
  # always ignored. This is detected automatically since Ruby 2.7.
9
9
  #
10
10
  # @example
11
+ # # bad
12
+ # return 1
11
13
  #
12
- # # Detected since Ruby 2.7
13
- # return 1 # 1 is always ignored.
14
+ # # good
15
+ # return
14
16
  class TopLevelReturnWithArgument < Base
15
- # This cop works by validating the ancestors of the return node. A
16
- # top-level return node's ancestors should not be of block, def, or
17
- # defs type.
17
+ extend AutoCorrector
18
18
 
19
19
  MSG = 'Top level return with argument detected.'
20
20
 
21
21
  def on_return(return_node)
22
- add_offense(return_node) if return_node.arguments? && ancestors_valid?(return_node)
22
+ return unless top_level_return_with_any_argument?(return_node)
23
+
24
+ add_offense(return_node) do |corrector|
25
+ remove_arguments(corrector, return_node)
26
+ end
23
27
  end
24
28
 
25
29
  private
26
30
 
27
- def ancestors_valid?(return_node)
28
- prohibited_ancestors = return_node.each_ancestor(:block, :def, :defs)
29
- prohibited_ancestors.none?
31
+ def top_level_return_with_any_argument?(return_node)
32
+ top_level_return?(return_node) && return_node.arguments?
33
+ end
34
+
35
+ def remove_arguments(corrector, return_node)
36
+ corrector.replace(return_node, 'return')
37
+ end
38
+
39
+ # This cop works by validating the ancestors of the return node. A
40
+ # top-level return node's ancestors should not be of block, def, or
41
+ # defs type.
42
+ def top_level_return?(return_node)
43
+ return_node.each_ancestor(:block, :def, :defs).none?
30
44
  end
31
45
  end
32
46
  end
@@ -13,6 +13,12 @@ module RuboCop
13
13
  # reassignments and properly handles varied cases such as branch, loop,
14
14
  # rescue, ensure, etc.
15
15
  #
16
+ # @safety
17
+ # This cop's autocorrection is unsafe because removing assignment from
18
+ # operator assignment can cause NameError if this assignment has been used to declare
19
+ # local variable. For example, replacing `a ||= 1` to `a || 1` may cause
20
+ # "undefined local variable or method `a' for main:Object (NameError)".
21
+ #
16
22
  # @example
17
23
  #
18
24
  # # bad
@@ -31,6 +37,10 @@ module RuboCop
31
37
  # do_something(some_var)
32
38
  # end
33
39
  class UselessAssignment < Base
40
+ extend AutoCorrector
41
+
42
+ include RangeHelp
43
+
34
44
  MSG = 'Useless assignment to variable - `%<variable>s`.'
35
45
 
36
46
  def self.joining_forces
@@ -55,7 +65,9 @@ module RuboCop
55
65
  assignment.node.loc.name
56
66
  end
57
67
 
58
- add_offense(location, message: message)
68
+ add_offense(location, message: message) do |corrector|
69
+ autocorrect(corrector, assignment)
70
+ end
59
71
  end
60
72
  end
61
73
 
@@ -119,6 +131,52 @@ module RuboCop
119
131
 
120
132
  node.receiver.nil? && !node.arguments?
121
133
  end
134
+
135
+ # rubocop:disable Metrics/AbcSize
136
+ def autocorrect(corrector, assignment)
137
+ if assignment.exception_assignment?
138
+ remove_exception_assignment_part(corrector, assignment.node)
139
+ elsif assignment.multiple_assignment? || assignment.rest_assignment? ||
140
+ assignment.for_assignment?
141
+ rename_variable_with_underscore(corrector, assignment.node)
142
+ elsif assignment.operator_assignment?
143
+ remove_trailing_character_from_operator(corrector, assignment.node)
144
+ elsif assignment.regexp_named_capture?
145
+ replace_named_capture_group_with_non_capturing_group(corrector, assignment.node,
146
+ assignment.variable.name)
147
+ else
148
+ remove_local_variable_assignment_part(corrector, assignment.node)
149
+ end
150
+ end
151
+ # rubocop:enable Metrics/AbcSize
152
+
153
+ def remove_exception_assignment_part(corrector, node)
154
+ corrector.remove(
155
+ range_between(
156
+ (node.parent.children.first&.source_range || node.parent.location.keyword).end_pos,
157
+ node.source_range.end_pos
158
+ )
159
+ )
160
+ end
161
+
162
+ def rename_variable_with_underscore(corrector, node)
163
+ corrector.replace(node, '_')
164
+ end
165
+
166
+ def remove_trailing_character_from_operator(corrector, node)
167
+ corrector.remove(node.parent.location.operator.end.adjust(begin_pos: -1))
168
+ end
169
+
170
+ def replace_named_capture_group_with_non_capturing_group(corrector, node, variable_name)
171
+ corrector.replace(
172
+ node.children.first,
173
+ node.children.first.source.sub(/\(\?<#{variable_name}>/, '(?:')
174
+ )
175
+ end
176
+
177
+ def remove_local_variable_assignment_part(corrector, node)
178
+ corrector.replace(node, node.expression.source)
179
+ end
122
180
  end
123
181
  end
124
182
  end
@@ -41,6 +41,10 @@ module RuboCop
41
41
  # do_something(some_array)
42
42
  # end
43
43
  class Void < Base
44
+ extend AutoCorrector
45
+
46
+ include RangeHelp
47
+
44
48
  OP_MSG = 'Operator `%<op>s` used in void context.'
45
49
  VAR_MSG = 'Variable `%<var>s` used in void context.'
46
50
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
@@ -100,35 +104,53 @@ module RuboCop
100
104
  def check_void_op(node)
101
105
  return unless node.send_type? && OPERATORS.include?(node.method_name)
102
106
 
103
- add_offense(node.loc.selector, message: format(OP_MSG, op: node.method_name))
107
+ add_offense(node.loc.selector,
108
+ message: format(OP_MSG, op: node.method_name)) do |corrector|
109
+ autocorrect_void_op(corrector, node)
110
+ end
104
111
  end
105
112
 
106
113
  def check_var(node)
107
114
  return unless node.variable? || node.const_type?
108
115
 
109
- add_offense(node.loc.name, message: format(VAR_MSG, var: node.loc.name.source))
116
+ if node.const_type? && node.special_keyword?
117
+ add_offense(node, message: format(VAR_MSG, var: node.source)) do |corrector|
118
+ autocorrect_void_expression(corrector, node)
119
+ end
120
+ else
121
+ add_offense(node.loc.name,
122
+ message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
123
+ autocorrect_void_expression(corrector, node)
124
+ end
125
+ end
110
126
  end
111
127
 
112
128
  def check_literal(node)
113
129
  return if !node.literal? || node.xstr_type? || node.range_type?
114
130
 
115
- add_offense(node, message: format(LIT_MSG, lit: node.source))
131
+ add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
132
+ autocorrect_void_expression(corrector, node)
133
+ end
116
134
  end
117
135
 
118
136
  def check_self(node)
119
137
  return unless node.self_type?
120
138
 
121
- add_offense(node, message: SELF_MSG)
139
+ add_offense(node, message: SELF_MSG) do |corrector|
140
+ autocorrect_void_expression(corrector, node)
141
+ end
122
142
  end
123
143
 
124
144
  def check_void_expression(node)
125
145
  return unless node.defined_type? || node.lambda_or_proc?
126
146
 
127
- add_offense(node, message: format(EXPRESSION_MSG, expression: node.source))
147
+ add_offense(node, message: format(EXPRESSION_MSG, expression: node.source)) do |corrector|
148
+ autocorrect_void_expression(corrector, node)
149
+ end
128
150
  end
129
151
 
130
152
  def check_nonmutating(node)
131
- return unless node.respond_to?(:method_name)
153
+ return if !node.send_type? && !node.block_type? && !node.numblock_type?
132
154
 
133
155
  method_name = node.method_name
134
156
  return unless NONMUTATING_METHODS.include?(method_name)
@@ -139,7 +161,10 @@ module RuboCop
139
161
  "#{method_name}!"
140
162
  end
141
163
  add_offense(node,
142
- message: format(NONMUTATING_MSG, method: method_name, suggest: suggestion))
164
+ message: format(NONMUTATING_MSG, method: method_name,
165
+ suggest: suggestion)) do |corrector|
166
+ autocorrect_nonmutating_send(corrector, node, suggestion)
167
+ end
143
168
  end
144
169
 
145
170
  def in_void_context?(node)
@@ -149,6 +174,31 @@ module RuboCop
149
174
 
150
175
  VOID_CONTEXT_TYPES.include?(parent.type) && parent.void_context?
151
176
  end
177
+
178
+ def autocorrect_void_op(corrector, node)
179
+ if node.arguments.empty?
180
+ corrector.replace(node, node.receiver.source)
181
+ else
182
+ corrector.replace(
183
+ range_with_surrounding_space(range: node.loc.selector, side: :both,
184
+ newlines: false),
185
+ "\n"
186
+ )
187
+ end
188
+ end
189
+
190
+ def autocorrect_void_expression(corrector, node)
191
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
192
+ end
193
+
194
+ def autocorrect_nonmutating_send(corrector, node, suggestion)
195
+ send_node = if node.send_type?
196
+ node
197
+ else
198
+ node.send_node
199
+ end
200
+ corrector.replace(send_node.loc.selector, suggestion)
201
+ end
152
202
  end
153
203
  end
154
204
  end
@@ -117,8 +117,7 @@ module RuboCop
117
117
  end
118
118
 
119
119
  def capturing_variable?(name)
120
- # TODO: Remove `Symbol#to_s` after supporting only Ruby >= 2.7.
121
- name && !name.to_s.start_with?('_')
120
+ name && !name.start_with?('_')
122
121
  end
123
122
 
124
123
  def branch?(node)
@@ -63,7 +63,7 @@ module RuboCop
63
63
  types
64
64
  end
65
65
 
66
- def code_length(node)
66
+ def code_length(node) # rubocop:disable Metrics/MethodLength
67
67
  if classlike_node?(node)
68
68
  classlike_code_length(node)
69
69
  elsif heredoc_node?(node)
@@ -72,7 +72,14 @@ module RuboCop
72
72
  body = extract_body(node)
73
73
  return 0 unless body
74
74
 
75
- body.source.each_line.count { |line| !irrelevant_line?(line) }
75
+ source =
76
+ if node_with_heredoc?(body)
77
+ source_from_node_with_heredoc(body)
78
+ else
79
+ body.source.lines
80
+ end
81
+
82
+ source.count { |line| !irrelevant_line?(line) }
76
83
  end
77
84
  end
78
85
 
@@ -175,6 +182,27 @@ module RuboCop
175
182
  def another_args?(node)
176
183
  node.call_type? && node.arguments.count > 1
177
184
  end
185
+
186
+ def node_with_heredoc?(node)
187
+ node.each_descendant(:str, :dstr).any? { |descendant| heredoc_node?(descendant) }
188
+ end
189
+
190
+ def source_from_node_with_heredoc(node)
191
+ last_line = -1
192
+ node.each_descendant do |descendant|
193
+ next unless descendant.source
194
+
195
+ descendant_last_line =
196
+ if heredoc_node?(descendant)
197
+ descendant.loc.heredoc_end.line
198
+ else
199
+ descendant.last_line
200
+ end
201
+
202
+ last_line = [last_line, descendant_last_line].max
203
+ end
204
+ @processed_source[(node.first_line - 1)..(last_line - 1)]
205
+ end
178
206
  end
179
207
  end
180
208
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # The token that makes up a disable comment.
17
17
  # The allowed specification for comments after `# rubocop: disable` is
18
18
  # `DepartmentName/CopName` or` all`.
19
- DISABLING_COPS_CONTENT_TOKEN = %r{[A-z]+/[A-z]+|all}.freeze
19
+ DISABLING_COPS_CONTENT_TOKEN = %r{[A-Za-z]+/[A-Za-z]+|all}.freeze
20
20
 
21
21
  def on_new_investigation
22
22
  processed_source.comments.each do |comment|
@@ -67,7 +67,7 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def contain_unexpected_character_for_department_name?(name)
70
- name.match?(%r{[^A-z/, ]})
70
+ name.match?(%r{[^A-Za-z/, ]})
71
71
  end
72
72
 
73
73
  def qualified_legacy_cop_name(cop_name)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module encapsulates the ability to allow certain receivers in a cop.
6
+ module AllowedReceivers
7
+ def allowed_receiver?(receiver)
8
+ receiver_name = receiver_name(receiver)
9
+
10
+ allowed_receivers.include?(receiver_name)
11
+ end
12
+
13
+ def receiver_name(receiver)
14
+ if receiver.receiver && !receiver.receiver.const_type?
15
+ return receiver_name(receiver.receiver)
16
+ end
17
+
18
+ if receiver.send_type?
19
+ if receiver.receiver
20
+ "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
21
+ else
22
+ receiver.method_name.to_s
23
+ end
24
+ else
25
+ receiver.source
26
+ end
27
+ end
28
+
29
+ def allowed_receivers
30
+ cop_config.fetch('AllowedReceivers', [])
31
+ end
32
+ end
33
+ end
34
+ end
@@ -25,7 +25,7 @@ module RuboCop
25
25
  def comments_contain_disables?(node, cop_name)
26
26
  disabled_ranges = processed_source.disabled_line_ranges[cop_name]
27
27
 
28
- return unless disabled_ranges
28
+ return false unless disabled_ranges
29
29
 
30
30
  node_range = node.source_range.line...find_end_line(node)
31
31
 
@@ -62,12 +62,16 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
66
66
  def find_end_line(node)
67
67
  if node.if_type? && node.else?
68
68
  node.loc.else.line
69
69
  elsif node.if_type? && node.ternary?
70
70
  node.else_branch.loc.line
71
+ elsif node.if_type? && node.elsif?
72
+ node.each_ancestor(:if).find(&:if?).loc.end.line
73
+ elsif node.block_type? || node.numblock_type?
74
+ node.loc.end.line
71
75
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
72
76
  next_sibling.loc.line
73
77
  elsif (parent = node.parent)
@@ -76,7 +80,7 @@ module RuboCop
76
80
  node.loc.end.line
77
81
  end
78
82
  end
79
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
80
84
  end
81
85
  end
82
86
  end
@@ -67,7 +67,7 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def variable_alignment?(whole_expression, rhs, end_alignment_style)
70
- return if end_alignment_style == :keyword
70
+ return false if end_alignment_style == :keyword
71
71
 
72
72
  !line_break_before_keyword?(whole_expression, rhs)
73
73
  end